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

Commit 973b9e1

Browse files
committed
send workspace with multipart encoder
1 parent cfdef94 commit 973b9e1

File tree

3 files changed

+109
-17
lines changed

3 files changed

+109
-17
lines changed

paperspace/commands/jobs.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from paperspace.commands import common
77
from paperspace.exceptions import BadResponseError
88
from paperspace.utils import get_terminal_lines
9-
from paperspace.workspace import WorkspaceHandler
9+
from paperspace.workspace import WorkspaceHandler, MultipartEncoder
1010

1111

1212
class JobsCommandBase(common.CommandBase):
@@ -130,32 +130,40 @@ def __init__(self, workspace_handler=None, **kwargs):
130130

131131
def execute(self, json_):
132132
url = "/jobs/createJob/"
133-
files = None
133+
data = None
134+
self.set_project(json_)
135+
134136
workspace_url = self._workspace_handler.handle(json_)
135137
if workspace_url:
136138
if self._workspace_handler.archive_path:
137-
archive_basename = self._workspace_handler.archive_basename
138-
json_["workspaceFileName"] = archive_basename
139-
self.api.headers["Content-Type"] = "multipart/form-data"
140-
files = {"file": open(workspace_url, "rb")}
139+
data = self._get_multipart_data(json_)
141140
else:
142-
json_["workspaceFileName"] = workspace_url
143-
144-
self.set_project(json_)
141+
data["workspaceFileName"] = workspace_url
145142

146143
self.logger.log("Creating job...")
147-
response = self.api.post(url, params=json_, files=files)
144+
response = self.api.post(url, params=json_, data=data)
148145
self._log_message(response,
149-
"Job created",
146+
"Job created - ID: {id}",
150147
"Unknown error while creating job")
151148

149+
def _get_multipart_data(self, json_):
150+
archive_basename = self._workspace_handler.archive_basename
151+
json_["workspaceFileName"] = archive_basename
152+
job_data = {'file': (archive_basename, open(self._workspace_handler.archive_path, 'rb'), 'text/plain')}
153+
monitor = MultipartEncoder(job_data).get_monitor()
154+
self.api.headers["Content-Type"] = monitor.content_type
155+
data = monitor
156+
return data
157+
152158
@staticmethod
153159
def set_project(json_):
154-
project_id = json_.get("projectId", json_.get("projectHandle"))
155-
if not project_id:
156-
json_["project"] = "paperspace-python"
160+
if json_.get("projectId"):
161+
return
162+
163+
if json_.get("projectHandle"):
164+
json_["projectId"] = json_.pop("projectHandle")
157165
else:
158-
json_["projectId"] = project_id
166+
json_["project"] = "paperspace-python"
159167

160168

161169
class ArtifactsDestroyCommand(JobsCommandBase):

paperspace/workspace.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ def _create_callback(encoder_obj):
2626
bar = progressbar.ProgressBar(max_value=encoder_obj.len)
2727

2828
def callback(monitor):
29-
bar.update(monitor.bytes_read)
30-
29+
if monitor.bytes_read == bar.max_value:
30+
bar.finish()
31+
else:
32+
bar.update(monitor.bytes_read)
3133
return callback
3234

3335

tests/functional/test_jobs.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,85 @@ def test_should_send_valid_get_request_with_valid_param_for_a_list_of_artifacts_
342342
params={"jobId": job_id,
343343
param: True})
344344
assert result.exit_code == 0
345+
346+
347+
class TestJobsCreate(object):
348+
URL = "https://api.paperspace.io"
349+
EXPECTED_HEADERS = default_headers.copy()
350+
EXPECTED_HEADERS_WITH_CHANGED_API_KEY = default_headers.copy()
351+
EXPECTED_HEADERS_WITH_CHANGED_API_KEY["X-API-Key"] = "some_key"
352+
BASIC_OPTIONS_COMMAND = [
353+
"jobs", "create",
354+
"--name", "exp1",
355+
"--projectId", "testHandle",
356+
"--container", "testContainer",
357+
"--machineType", "testType",
358+
"--command", "testCommand",
359+
"--workspaceUrl", "some-workspace",
360+
]
361+
FULL_OPTIONS_COMMAND = [
362+
"jobs", "create",
363+
"--name", "exp1",
364+
"--ports", 4567,
365+
"--workspaceUrl", "wsp.url",
366+
"--workingDirectory", "/work/dir/",
367+
"--artifactDirectory", "/artifact/dir/",
368+
"--clusterId", 42,
369+
"--experimentEnv", '{"key":"val"}',
370+
"--projectId", "testHandle",
371+
"--container", "testContainer",
372+
"--machineType", "testType",
373+
"--command", "testCommand",
374+
"--containerUser", "conUser",
375+
"--registryUsername", "userName",
376+
"--registryPassword", "passwd",
377+
"--apiKey", "some_key",
378+
]
379+
BASIC_OPTIONS_REQUEST = {
380+
"name": u"exp1",
381+
"projectId": u"testHandle",
382+
"container": u"testContainer",
383+
"machineType": u"testType",
384+
"command": u"testCommand",
385+
"workspaceUrl": u"some-workspace",
386+
}
387+
FULL_OPTIONS_REQUEST = {
388+
"name": u"exp1",
389+
"ports": 4567,
390+
"workspaceUrl": u"wsp.url",
391+
"workingDirectory": u"/work/dir/",
392+
"artifactDirectory": u"/artifact/dir/",
393+
"clusterId": 42,
394+
"experimentEnv": {u"key": u"val"},
395+
"projectHandle": u"testHandle",
396+
"container": u"testContainer",
397+
"machineType": u"testType",
398+
"command": u"testCommand",
399+
"containerUser": u"conUser",
400+
"registryUsername": u"userName",
401+
"registryPassword": u"passwd",
402+
}
403+
RESPONSE_JSON_200 = {"id": "sadkfhlskdjh", "message": "success"}
404+
RESPONSE_CONTENT_200 = b'{"handle":"sadkfhlskdjh","message":"success"}\n'
405+
EXPECTED_STDOUT = u'Creating job...\nJob created - ID: sadkfhlskdjh\n'
406+
407+
RESPONSE_JSON_404_PROJECT_NOT_FOUND = {"details": {"handle": "wrong_handle"}, "error": "Project not found"}
408+
RESPONSE_CONTENT_404_PROJECT_NOT_FOUND = b'{"details":{"handle":"wrong_handle"},"error":"Project not found"}\n'
409+
EXPECTED_STDOUT_PROJECT_NOT_FOUND = "Project not found\nhandle: wrong_handle\n"
410+
411+
@mock.patch("paperspace.client.requests.post")
412+
def test_should_send_proper_data_and_print_message_when_create_job_was_run_with_basic_options(self, post_patched):
413+
post_patched.return_value = MockResponse(self.RESPONSE_JSON_200, 200, self.RESPONSE_CONTENT_200)
414+
415+
runner = CliRunner()
416+
result = runner.invoke(cli.cli, self.BASIC_OPTIONS_COMMAND)
417+
418+
post_patched.assert_called_once_with(self.URL + '/jobs/createJob/',
419+
headers=self.EXPECTED_HEADERS,
420+
json=None,
421+
params=self.BASIC_OPTIONS_REQUEST,
422+
files=None,
423+
data=None)
424+
425+
assert result.output == self.EXPECTED_STDOUT
426+
assert result.exit_code == 0

0 commit comments

Comments
 (0)