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

Commit 509dd84

Browse files
committed
Add support for creating experiments - WIP
1 parent 90fdef5 commit 509dd84

File tree

9 files changed

+149
-16
lines changed

9 files changed

+149
-16
lines changed

Pipfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ cryptography = {extras = ["security"]}
1111
botocore = "*"
1212
six = "*"
1313
gradient-statsd = "*"
14+
click = "*"
1415

1516
[dev-packages]
1617
twine = "*"
1718
pypandoc = "*"
18-
pylint = "*"
19-
hacking = "*"

paperspace/cli.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import click
2+
3+
from paperspace import commands
4+
5+
6+
class OptionRequiredIfMultinode(click.Option):
7+
def full_process_value(self, ctx, value):
8+
value = super(OptionRequiredIfMultinode, self).full_process_value(ctx, value)
9+
10+
if value is None and ctx.params["workercount"] > 1:
11+
msg = "Required if --workerCount > 1"
12+
raise click.MissingParameter(ctx=ctx, param=self, message=msg)
13+
14+
return value
15+
16+
17+
def del_if_value_is_none(d):
18+
"""Remove all elements with value == None"""
19+
for key, val in list(d.items()):
20+
if val is None:
21+
del d[key]
22+
23+
24+
@click.group()
25+
def cli():
26+
pass
27+
28+
29+
@cli.group()
30+
def experiments():
31+
pass
32+
33+
34+
@experiments.command()
35+
@click.option("--name", required=True)
36+
@click.option("--workerCount", "workerCount", required=True, type=int)
37+
@click.option("--workerContainer", "workerContainer", cls=OptionRequiredIfMultinode)
38+
@click.option("--workerMachineType", "workerMachineType", cls=OptionRequiredIfMultinode)
39+
@click.option("--workerCommand", "workerCommand", cls=OptionRequiredIfMultinode)
40+
@click.option("--parameterServerContainer", "parameterServerContainer", cls=OptionRequiredIfMultinode)
41+
@click.option("--parameterServerMachineType", "parameterServerMachineType", cls=OptionRequiredIfMultinode)
42+
@click.option("--parameterServerCommand", "parameterServerCommand", cls=OptionRequiredIfMultinode)
43+
@click.option("--parameterServerCount", "parameterServerCount", type=int, cls=OptionRequiredIfMultinode)
44+
@click.option("--ports", type=int)
45+
@click.option("--workspaceUrl", "workspaceUrl")
46+
@click.option("--projectHandler", "projectHandler")
47+
@click.option("--workingDirectory", "workingDirectory")
48+
@click.option("--artifactDirectory", "artifactDirectory")
49+
@click.option("--clusterId", "clusterId", type=int)
50+
# @click.option("--experimentEnv", type=dict)
51+
@click.option("--experimentTypeId", "experimentTypeId", type=int)
52+
@click.option("--workerContainerUser", "workerContainerUser")
53+
@click.option("--workerRegistryUsername", "workerRegistryUsername")
54+
@click.option("--workerRegistryPassword", "workerRegistryPassword")
55+
@click.option("--parameterServerContainerUser", "parameterServerContainerUser")
56+
@click.option("--parameterServerRegistryContainerUser", "parameterServerRegistryContainerUser")
57+
@click.option("--parameterServerRegistryPassword", "parameterServerRegistryPassword")
58+
def create(**kwargs):
59+
del_if_value_is_none(kwargs)
60+
commands.create_experiments(kwargs)

paperspace/client.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import requests
2+
3+
4+
class API(object):
5+
def __init__(self, api_url, headers=None):
6+
self.api_url = api_url
7+
self.headers = headers or {}
8+
9+
def get_path(self, url):
10+
template = "{}{}" if url.startswith("/") else "{}/{}"
11+
return template.format(self.api_url, url)
12+
13+
def post(self, url, json=None):
14+
path = self.get_path(url)
15+
response = requests.post(path, json=json, headers=self.headers)
16+
return response

paperspace/commands.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from paperspace import config, version, logger
2+
3+
from paperspace.client import API
4+
5+
default_headers = {"x-api-key": config.PAPERSPACE_API_KEY,
6+
"ps_client_name": "paperspace-python",
7+
"ps_client_version": version.version}
8+
9+
experiments_api = API(config.CONFIG_EXPERIMENTS_HOST, headers=default_headers)
10+
jobs_api = API(config.CONFIG_HOST, headers=default_headers)
11+
12+
13+
def _log_response(response, success_msg, error_msg):
14+
if response.ok:
15+
logger.log(success_msg)
16+
else:
17+
try:
18+
data = response.json()
19+
logger.log_error_response(data)
20+
except ValueError:
21+
logger.log(error_msg)
22+
23+
24+
def create_experiments(data=None, api=experiments_api):
25+
data = data or {}
26+
response = api.post("/experiments", data)
27+
_log_response(response, "Experiment created", "Unknown error while creating experiment")

paperspace/config.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import os
22

3-
class config: pass
3+
_DEFAULT_PAPERSPACE_API_KEY = ""
4+
_DEFAULT_CONFIG_HOST = "https://api.paperspace.io"
5+
_DEFAULT_CONFIG_LOG_HOST = "https://logs.paperspace.io"
6+
_DEFAULT_CONFIG_EXPERIMENTS_HOST = "https://" # TODO: fill this
47

5-
config.PAPERSPACE_API_KEY = ''
6-
config.CONFIG_HOST = 'https://api.paperspace.io'
7-
config.CONFIG_LOG_HOST = 'https://logs.paperspace.io'
88

9-
if 'PAPERSPACE_API_KEY' in os.environ:
10-
config.PAPERSPACE_API_KEY = os.environ['PAPERSPACE_API_KEY']
11-
if 'PAPERSPACE_CONFIG_HOST' in os.environ:
12-
config.CONFIG_HOST = os.environ['PAPERSPACE_CONFIG_HOST']
13-
if 'PAPERSPACE_CONFIG_LOG_HOST' in os.environ:
14-
config.CONFIG_LOG_HOST = os.environ['PAPERSPACE_CONFIG_LOG_HOST']
9+
class config(object):
10+
PAPERSPACE_API_KEY = os.environ.get("PAPERSPACE_API_KEY", _DEFAULT_PAPERSPACE_API_KEY)
11+
CONFIG_HOST = os.environ.get("PAPERSPACE_CONFIG_HOST", _DEFAULT_CONFIG_HOST)
12+
CONFIG_LOG_HOST = os.environ.get("PAPERSPACE_CONFIG_LOG_HOST", _DEFAULT_CONFIG_LOG_HOST)
13+
CONFIG_EXPERIMENTS_HOST = os.environ.get("PAPERSPACE_CONFIG_EXPERIMENTS_HOST", _DEFAULT_CONFIG_EXPERIMENTS_HOST)

paperspace/jobs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import base64
22
import inspect
33
import re
4+
import time
45

56
import boto3
67
import botocore

paperspace/logger.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import click
2+
from six import string_types
3+
4+
5+
def log(*messages, error=False):
6+
for message in messages:
7+
click.echo(message, err=error)
8+
9+
10+
def log_error_response(data):
11+
error = data.get("error")
12+
details = data.get("details")
13+
14+
if not any((error, details)):
15+
raise ValueError("No error messages found")
16+
17+
if error:
18+
log(str(error), error=True)
19+
20+
if details:
21+
for key, val in details.items():
22+
if isinstance(val, string_types):
23+
val = [val]
24+
25+
for v in val:
26+
msg = "{}: {}".format(key, str(v))
27+
log(msg, error=True)

paperspace/main.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import sys
21
import os
2+
import sys
33

4-
from .version import version
5-
from .login import login, logout, set_apikey
4+
from .cli import cli
65
from .jobs import run, print_json_pretty
6+
from .login import login, logout, set_apikey
7+
from .version import version
78

89

910
def main():
11+
if len(sys.argv) >= 2 and sys.argv[1] == 'experiments':
12+
cli(sys.argv[1:])
13+
1014
args = sys.argv[:]
1115
prog = os.path.basename(args.pop(0))
1216

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
],
4040
keywords='paperspace api development library',
4141
packages=find_packages(exclude=['contrib', 'docs', 'tests']),
42-
install_requires=['requests[security]', 'boto3', 'botocore', 'six', 'gradient-statsd'],
42+
install_requires=['requests[security]', 'boto3', 'botocore', 'six', 'gradient-statsd', 'click'],
4343
entry_points={'console_scripts': [
4444
'paperspace-python = paperspace.main:main',
4545
]},

0 commit comments

Comments
 (0)