Skip to content
This repository was archived by the owner on Aug 11, 2020. It is now read-only.

Commit 5e5e09f

Browse files
committed
separate out tests to test.py, add runas_job func and remote_test.py
1 parent 2cd8163 commit 5e5e09f

File tree

3 files changed

+228
-53
lines changed

3 files changed

+228
-53
lines changed

paperspace.py

Lines changed: 144 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1-
import requests
1+
import inspect
22
import json
3-
import time
43
import os
5-
import zipfile
4+
import sys
65
import 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 = ''
914
CONFIG_HOST = 'https://api.paperspace.io'
1015
CONFIG_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 = "\ndef 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

remote_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import os
2+
import paperspace
3+
4+
paperspace.PAPERSPACE_API_KEY = '14a4bc1cbc414...'
5+
6+
paperspace.runas_job({'project': 'myprojec8', 'machineType': 'GPU+', 'container': 'Test-Container'})
7+
8+
print(os.getcwd())
9+
print('something useful')

test.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import paperspace
2+
3+
# Tests:
4+
5+
paperspace.PAPERSPACE_API_KEY = '14a4bc1cbc414...'
6+
7+
project = 'myproject7'
8+
print('project: %s' % project)
9+
10+
print("paperspace.paperspace('jobs', 'getJobs', {'project': project})")
11+
jobs = paperspace.paperspace('jobs', 'getJobs', {'project': project})
12+
if 'error' in jobs:
13+
paperspace.print_json_pretty(jobs)
14+
else:
15+
for job in jobs:
16+
print(job['id'])
17+
18+
print("jobs_create({'project': project, 'machineType': 'GPU+', 'container': 'Test-Container', 'command': './do.sh', 'workspace': '~/myproject3'})")
19+
job = paperspace.jobs_create({'project': project,
20+
'machineType': 'GPU+', 'container': 'Test-Container',
21+
'command': './do.sh', 'workspace': '~/myproject3'})
22+
jobId = job['id']
23+
24+
print("paperspace.paperspace('jobs', 'artifactsList', {'jobId': jobId, 'links': True})")
25+
artifacts = paperspace.paperspace('jobs', 'artifactsList', {'jobId': jobId, 'links': True})
26+
if artifacts:
27+
paperspace.print_json_pretty(artifacts)
28+
29+
print("paperspace.jobs_artifactsGet({'jobId': jobId, 'dest': '~/temp1'})")
30+
paperspace.jobs_artifactsGet({'jobId': jobId, 'dest': '~/temp1'})
31+
32+
print("paperspace.paperspace('jobs', 'getJob', {'jobId': jobId})")
33+
job = paperspace.paperspace('jobs', 'getJob', {'jobId': jobId})
34+
paperspace.print_json_pretty(job)
35+
36+
print("paperspace.jobs_logs({'jobId': jobId, 'limit': 4}, tail=True)")
37+
paperspace.jobs_logs({'jobId': jobId, 'limit': 4}, tail=True)
38+
39+
print("paperspace.paperspace('jobs', 'stop', {'jobId': jobId})")
40+
res = paperspace.paperspace('jobs', 'stop', {'jobId': jobId})
41+
paperspace.print_json_pretty(res)
42+
43+
print("paperspace.paperspace('jobs', 'clone', {'jobId': jobId})")
44+
clonedJob = paperspace.paperspace('jobs', 'clone', {'jobId': jobId})
45+
paperspace.print_json_pretty(clonedJob)
46+
47+
print("paperspace.jobs_waitfor({'jobId': clonedJob['id'], 'state': 'Stopped'})")
48+
waitforJob = paperspace.jobs_waitfor({'jobId': clonedJob['id'], 'state': 'Stopped'})
49+
paperspace.print_json_pretty(waitforJob)
50+
51+
print("paperspace.paperspace('jobs', 'artifactsList', {'jobId': clonedJob['id']})")
52+
artifacts = paperspace.paperspace('jobs', 'artifactsList', {'jobId': clonedJob['id']})
53+
if artifacts:
54+
paperspace.print_json_pretty(artifacts)
55+
print("paperspace.paperspace('jobs', 'artifactsDestroy', {'jobId': clonedJob['id']})")
56+
paperspace.paperspace('jobs', 'artifactsDestroy', {'jobId': clonedJob['id']})
57+
58+
print("paperspace.paperspace('jobs', 'artifactsList', {'jobId': clonedJob['id']})")
59+
artifacts = paperspace.paperspace('jobs', 'artifactsList', {'jobId': clonedJob['id']})
60+
if artifacts:
61+
paperspace.print_json_pretty(artifacts)
62+
63+
print("paperspace.paperspace('jobs', 'getJobs', {'project': project})")
64+
jobs = paperspace.paperspace('jobs', 'getJobs', {'project': project})
65+
for job in jobs:
66+
print(job['id'])
67+
68+
print("paperspace.paperspace('jobs', 'destroy', {'jobId': clonedJob['id']})")
69+
res = paperspace.paperspace('jobs', 'destroy', {'jobId': clonedJob['id']})
70+
paperspace.print_json_pretty(res)
71+
72+
print("paperspace.paperspace('jobs', 'getJobs', {'project': project})")
73+
jobs = paperspace.paperspace('jobs', 'getJobs', {'project': project})
74+
for job in jobs:
75+
print(job['id'])

0 commit comments

Comments
 (0)