1- import requests
1+ import inspect
22import json
3- import time
43import os
5- import zipfile
4+ import sys
65import tempfile
6+ import time
7+ import zipfile
78
8- PAPERSPACE_API_KEY = '14a4bc1cbc414...'
9+ import boto3
10+ import botocore
11+ import requests
12+
13+ PAPERSPACE_API_KEY = ''
914CONFIG_HOST = 'https://api.paperspace.io'
1015CONFIG_LOG_HOST = 'https://logs.paperspace.io'
1116
17+ if 'PAPERSPACE_API_KEY' in os .environ :
18+ PAPERSPACE_API_KEY = os .environ ['PAPERSPACE_API_KEY' ]
19+
1220
13- def zip (obj_name ):
21+ def zip_to_tmp (obj_name ):
1422 zipname = os .path .join (tempfile .gettempdir (),
1523 os .path .basename (obj_name )) + '.zip'
1624 outZipFile = zipfile .ZipFile (zipname , 'w' , zipfile .ZIP_DEFLATED )
@@ -20,12 +28,12 @@ def zip(obj_name):
2028 for filename in dirnames + filenames :
2129 filepath = os .path .join (dirpath , filename )
2230 basename = os .path .basename (filepath )
23- if '/.git/' not in filepath and \
24- basename not in ['.git' , '.gitignore' ]:
31+ if ( '/.git/' not in filepath
32+ and basename not in ['.git' , '.gitignore' ]) :
2533 arcname = os .path .relpath (filepath , obj_name )
2634 outZipFile .write (filepath , arcname )
2735 else :
28- outZipFile .write (obj_name )
36+ outZipFile .write (obj_name , os . path . basename ( obj_name ) )
2937
3038 outZipFile .close ()
3139 return zipname
@@ -61,9 +69,9 @@ def paperspace(category, method, params):
6169 if workspace and workspace != 'none' :
6270 workspace_path = os .path .expanduser (workspace )
6371 if os .path .exists (workspace_path ):
64- if not workspace_path .endswith ('.zip' ) \
65- and not workspace_path .endswith ('.gz' ):
66- workspace_file = zip (workspace_path )
72+ if ( not workspace_path .endswith ('.zip' )
73+ and not workspace_path .endswith ('.gz' )) :
74+ workspace_file = zip_to_tmp (workspace_path )
6775 else :
6876 workspace_file = workspace_path
6977 files = {'file' : open (workspace_file , 'rb' )}
@@ -76,28 +84,8 @@ def paperspace(category, method, params):
7684
7785 return r .json ()
7886
79- # Tests:
80-
81- # jobs = paperspace('jobs', 'getJobs', {'project': 'paperspace-node'})
82- # for job in jobs:
83- # print(job['id'])
84-
85- # job = paperspace('jobs', 'getJob', {'jobId': 'j8eww41akg9h0'})
86- # print(job['state'])
87- # print_json_pretty(job)
88-
89- # job = paperspace('jobs', 'stop', {'jobId': 'j8eww41akg9h0'})
90- # print_json_pretty(job)
9187
92- # job = paperspace('jobs', 'clone', {'jobId': 'j8eww41akg9h0'})
93- # print_json_pretty(job)
94-
95- # job = paperspace('jobs', 'createJob', {'project': 'paperspace-python',
96- # 'workspace':'none', 'machineType': 'GPU+', 'container': 'Test-Container'})
97- # print_json_pretty(job)
98-
99-
100- def paperspace_jobs_logs (params , tail = False , json = False ):
88+ def jobs_logs (params , tail = False , json = False ):
10189 last_line = 0
10290 PSEOF = False
10391 json_res = []
@@ -123,7 +111,7 @@ def paperspace_jobs_logs(params, tail=False, json=False):
123111 if m != 'PSEOF' :
124112 print (m )
125113
126- if len ( res ) :
114+ if res :
127115 last_line = res [- 1 ]['line' ]
128116 PSEOF = res [- 1 ]['message' ] == 'PSEOF'
129117
@@ -146,53 +134,156 @@ def paperspace_jobs_logs(params, tail=False, json=False):
146134
147135 if json :
148136 print_json_pretty (json_res )
137+ return json_res
138+ return True
149139
150- # Test
151- # paperspace_jobs_logs({'jobId': 'jsjo03tmsh6kzy', 'limit': 4}, tail = True)
152140
153-
154- def paperspace_jobs_waitfor (params ):
141+ def jobs_waitfor (params ):
155142 while True :
156143 job = paperspace ('jobs' , 'getJob' , params )
144+ if 'state' not in job :
145+ return job
157146 state = job ['state' ]
158147
159- if state == params ['state' ] \
160- or (state == 'Running' and params ['state' ] == 'Pending' ) \
161- or state == 'Error' \
162- or state == 'Stopped' \
163- or state == 'Failed' :
148+ if ( state == params ['state' ]
149+ or (state == 'Running' and params ['state' ] == 'Pending' )
150+ or state == 'Error'
151+ or state == 'Stopped'
152+ or state == 'Failed' ) :
164153 return job
165154 time .sleep (5 )
166155
167156
168- def paperspace_jobs_create (params ):
157+ def jobs_create (params ):
169158 job = paperspace ('jobs' , 'createJob' , params )
159+ if 'id' not in job :
160+ print_json_pretty (job )
161+ return job
170162 jobId = job ['id' ]
171- print_json_pretty (job )
172163 print ('New jobId: %s' % jobId )
173164 print ('Job %s' % job ['state' ])
174165
175166 if job ['state' ] == 'Pending' :
176167 print ('Waiting for job to run...' )
177- job = paperspace_jobs_waitfor ({'jobId' : jobId , 'state' : 'Running' })
168+ job = jobs_waitfor ({'jobId' : jobId , 'state' : 'Running' })
169+ if 'state' not in job :
170+ print_json_pretty (job )
171+ return job
178172
179173 if job ['state' ] != 'Error' :
180174 print ('Awaiting logs...' )
181- paperspace_jobs_logs ({'jobId' : jobId }, tail = True )
175+ jobs_logs ({'jobId' : jobId }, tail = True )
182176 job = paperspace ('jobs' , 'getJob' , {'jobId' : jobId })
177+ if 'state' not in job :
178+ print_json_pretty (job )
179+ return job
183180
184181 if job ['state' ] != 'Error' :
185182 print ('Job %s; exitCode %d' % (job ['state' ], job ['exitCode' ]))
186183 else :
187184 print ('Job %s: %s' % (job ['state' ], job ['jobError' ]))
185+ return job
188186
189187
190- # Test
191- paperspace_jobs_create ({'project' : 'myproject3' ,
192- 'machineType' : 'GPU+' , 'container' : 'Test-Container' ,
193- 'command' : './do.sh' , 'workspace' : '~/myproject3' })
194-
195- # TODO:
196- # download artifacts
188+ def jobs_artifactsGet (params ):
189+ if 'dest' in params :
190+ dest = os .path .abspath (os .path .expanduser (params ['dest' ]))
191+ if not os .path .exists (dest ):
192+ os .makedirs (dest )
193+ else :
194+ if not os .path .isdir (dest ):
195+ print ('Destination path not is not directory: %s' % dest )
196+ return False
197+ del params ['dest' ]
198+ else :
199+ dest = os .getcwd ()
200+
201+ artifacts_list = paperspace ('jobs' , 'artifactsList' , params )
202+ if artifacts_list :
203+
204+ creds = paperspace ('jobs' , 'artifactsGet' , params )
205+ if creds :
206+ bucket = creds ['bucket' ]
207+ folder = creds ['folder' ]
208+ credentials = creds ['Credentials' ]
209+
210+ session = boto3 .Session (
211+ aws_access_key_id = credentials ['AccessKeyId' ],
212+ aws_secret_access_key = credentials ['SecretAccessKey' ],
213+ aws_session_token = credentials ['SessionToken' ]
214+ )
215+ s3 = session .resource ('s3' )
216+
217+ for item in artifacts_list :
218+ file = item ['file' ]
219+ dest_file = os .path .join (dest , file )
220+
221+ dest_dir = os .path .dirname (dest_file )
222+ if not os .path .exists (dest_dir ):
223+ os .makedirs (dest_dir )
224+
225+ key = folder + '/' + file
226+ print ('Downloading %s' % file )
227+
228+ try :
229+ s3 .Bucket (bucket ).download_file (key , dest_file )
230+ except botocore .exceptions .ClientError as e :
231+ if e .response ['Error' ]['Code' ] == "404" :
232+ print ("The s3 object does not exist: %s" % key )
233+ else :
234+ raise
235+
236+ print ('Download complete' )
237+ return True
238+
239+ return False
240+
241+
242+ # TO DO:
197243# create/use project config
198244# deal with timeouts/server unreachable
245+ # deal with returned errors
246+ # deal with invalid directories, e.g. root for workspace
247+ # detect running interactively
248+ # stream file uploads/downloads
249+
250+
251+ def runas_job (params = {}):
252+ if 'PAPERSPACE_JOB_RUNNER' in os .environ :
253+ return
254+
255+ stack = inspect .stack ()
256+ obj = __import__ (stack [1 ][0 ].f_globals ['__name__' ])
257+ src = inspect .getsource (obj )
258+ src_file = os .path .basename (inspect .getsourcefile (obj ))
259+
260+ # TO DO: remove these replacements once we are auto importing paperspace on the job runner
261+ # and have defined the PAPERSPACE_JOB_RUNNER env var and passed it into the container
262+ src = src .replace ('import paperspace' , '# import paperspace' )
263+ src = src .replace ('paperspace.PAPERSPACE_API_KEY' , 'pass # paperspace.PAPERSPACE_API_KEY' )
264+ src = src .replace ('paperspace.CONFIG_HOST' , 'pass # paperspace.CONFIG_HOST' )
265+ src = src .replace ('paperspace.CONFIG_LOG_HOST' , 'pass # paperspace.CONFIG_LOG_HOST' )
266+ src = src .replace ('paperspace.runas_job' , 'paperspace_null_runas_job' )
267+ src = "\n def paperspace_null_runas_job(*args, **kwargs):\n return None\n " + src
268+
269+ src_path = os .path .join (tempfile .gettempdir (), src_file )
270+ with open (src_path , "w" ) as file :
271+ file .write (src )
272+
273+ if 'project' not in params :
274+ params ['project' ] = 'paperspace-python'
275+ if 'machineType' not in params :
276+ params ['machineType' ] = 'GPU+'
277+ if 'container' not in params :
278+ params ['container' ] = 'Test-Container'
279+ params ['command' ] = 'python3 ' + src_file
280+ params ['workspace' ] = src_path
281+
282+ jobs_create (params )
283+ sys .exit (0 )
284+
285+
286+ # TO DO:
287+ # automatic install of imported dependencies
288+ # make console logging optional
289+ # allow return results
0 commit comments