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

Commit a242ef7

Browse files
committed
Merge remote-tracking branch 'origin/master' into experiment-logs-PS-9868
# Conflicts: # paperspace/cli/experiments.py
2 parents fb5d5e4 + 954e826 commit a242ef7

22 files changed

+1307
-82
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,5 @@ ENV/
104104
.vscode/*
105105

106106
.idea
107+
paperspace-python.zip
108+
*.env

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Paperspace Python
22
=================
33

4-
Release 0.2.0a1
4+
Release 0.2.0a2
55

66
See [releasenotes.md](https://github.com/Paperspace/paperspace-python/blob/master/releasenotes.md) for details on the current release, as well as release history.
77

@@ -17,8 +17,20 @@ Getting Started
1717
To install/update prerelease (Alpha/Beta) version version of paperspace-python, use:
1818

1919
`pip install -U --pre paperspace`
20-
21-
3. Download your api key by executing the following:
20+
3. Enable autocomplete:
21+
22+
Add following to your `.bashrc` (or `.zshrc`) to enable autocomplete anytime you activate your shell.
23+
24+
`eval $(_PAPERSPACE_PYTHON_COMPLETE=source paperspace-python)`
25+
26+
For other shell (eg. `zsh` or `fish`), use proper version of `source` (`source_zsh` and `source-fish` respectively)
27+
28+
Alternatively, you can create activation script by:
29+
30+
`(_PAPERSPACE_PYTHON_COMPLETE=source paperspace-python > ~/paperspace_complete.sh`
31+
32+
More: https://click.palletsprojects.com/en/7.x/bashcomplete/
33+
4. Download your api key by executing the following:
2234

2335
`paperspace-python login`
2436

@@ -33,7 +45,7 @@ Getting Started
3345

3446
`paperspace-python logout`
3547

36-
4. Run the sample script hello.py using Python:
48+
5. Run the sample script hello.py using Python:
3749

3850
`python hello.py`
3951

@@ -49,7 +61,7 @@ Getting Started
4961
5062
Note: the source is modified before transfer to the job cluster in order to remove imported `paperspace` references.
5163
52-
5. Use paperspace-python to run a python script remotely:
64+
6. Use paperspace-python to run a python script remotely:
5365
5466
`paperspace-python run myscript.py`
5567

paperspace/cli/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import paperspace.cli.auth
66
import paperspace.cli.deployments
77
import paperspace.cli.experiments
8+
import paperspace.cli.hyperparameters
89
import paperspace.cli.jobs
910
import paperspace.cli.machines
1011
import paperspace.cli.models

paperspace/cli/auth.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,48 @@
11
import click
22

3-
from paperspace import client, config
3+
from paperspace import logger
4+
from paperspace.cli import common
45
from paperspace.cli.cli import cli
5-
from paperspace.cli.validators import validate_email
66
from paperspace.commands import login as login_commands
77

8+
LOGIN_DEPRECATION_MESSAGE = """The login command is currently disabled for logging in using `--email` and `--password`.
89
9-
@cli.command("login", help="Log in with email and password")
10+
Instead, obtain an API Key from https://www.paperspace.com/console/account/api.
11+
12+
Then use the `apiKey` command to save your API Key locally.
13+
14+
Visit the docs @ https://docs.paperspace.com for more info!"""
15+
16+
17+
@cli.command("login", help=LOGIN_DEPRECATION_MESSAGE, hidden=True)
1018
@click.option(
1119
"--email",
1220
"email",
13-
required=True,
14-
callback=validate_email,
1521
help="Email used to create Paperspace account",
1622
)
1723
@click.option(
1824
"--password",
1925
"password",
20-
prompt=True,
21-
hide_input=True,
2226
help="Password used to create Paperspace account",
2327
)
2428
@click.option(
2529
"--apiTokenName",
2630
"api_token_name",
2731
help="Name of api token used to log in",
2832
)
29-
def login(email, password, api_token_name):
30-
machines_api = client.API(config.CONFIG_HOST)
31-
command = login_commands.LogInCommand(api=machines_api)
32-
command.execute(email, password, api_token_name)
33+
def login(**kwargs):
34+
logger.warning(LOGIN_DEPRECATION_MESSAGE)
3335

3436

3537
@cli.command("logout", help="Log out / remove apiKey from config file")
3638
def logout():
3739
command = login_commands.LogOutCommand()
3840
command.execute()
41+
42+
43+
@cli.command("apiKey", help="Save your api key")
44+
@click.argument("api_key", required=False, callback=common.prompt_for_secret("Enter your API Key: "))
45+
def save_api_key(api_key):
46+
command = login_commands.SetApiKeyCommand()
47+
api_key = api_key.strip()
48+
command.execute(api_key)

paperspace/cli/cli.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import click
2+
import click_completion
23

34
from paperspace import config
45
from paperspace.cli import common
56
from paperspace.commands import login as login_commands
67

8+
click_completion.init()
9+
710

811
@click.group(cls=common.ClickGroup, **config.HELP_COLORS_DICT)
912
def cli():

paperspace/cli/common.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import getpass
2+
13
import click
24
from click_didyoumean import DYMMixin
35
from click_help_colors import HelpColorsGroup
@@ -18,3 +20,13 @@ def del_if_value_is_none(dict_):
1820

1921
class ClickGroup(DYMMixin, HelpColorsGroup):
2022
pass
23+
24+
25+
def prompt_for_secret(prompt):
26+
def callback_fun(ctx, param, value):
27+
if value is None:
28+
value = getpass.getpass(prompt)
29+
30+
return value
31+
32+
return callback_fun

paperspace/cli/deployments.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def deployments():
2222
)
2323

2424
DEPLOYMENT_MACHINE_TYPES = ("G1", "G6", "G12",
25-
"K80", "P100", "V100")
25+
"K80", "P100", "GV100")
2626

2727
@deployments.command("create", help="Create new deployment")
2828
@click.option(

paperspace/cli/experiments.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,11 @@ def common_experiments_create_options(f):
108108
def common_experiment_create_multi_node_options(f):
109109
options = [
110110
click.option(
111-
"--experimentTypeId",
112-
"experimentTypeId",
111+
"--experimentType",
112+
"experimentType",
113113
type=ChoiceType(MULTI_NODE_EXPERIMENT_TYPES_MAP, case_sensitive=False),
114114
required=True,
115-
help="Experiment Type ID",
115+
help="Experiment Type",
116116
),
117117
click.option(
118118
"--workerContainer",
@@ -256,7 +256,7 @@ def create_multi_node(api_key, **kwargs):
256256
@common_experiments_create_options
257257
@common_experiments_create_single_node_options
258258
def create_single_node(api_key, **kwargs):
259-
kwargs["experimentTypeId"] = constants.ExperimentType.SINGLE_NODE
259+
kwargs["experimentType"] = constants.ExperimentType.SINGLE_NODE
260260
del_if_value_is_none(kwargs)
261261
experiments_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
262262
command = experiments_commands.CreateExperimentCommand(api=experiments_api)
@@ -283,7 +283,7 @@ def create_and_start_multi_node(ctx, api_key, show_logs, **kwargs):
283283
@show_logs_option
284284
@click.pass_context
285285
def create_and_start_single_node(ctx, api_key, show_logs, **kwargs):
286-
kwargs["experimentTypeId"] = constants.ExperimentType.SINGLE_NODE
286+
kwargs["experimentType"] = constants.ExperimentType.SINGLE_NODE
287287
del_if_value_is_none(kwargs)
288288
experiments_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
289289
command = experiments_commands.CreateAndStartExperimentCommand(api=experiments_api)

paperspace/cli/hyperparameters.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import functools
2+
3+
import click
4+
5+
from paperspace import client, config
6+
from paperspace.cli import common
7+
from paperspace.cli.cli import cli
8+
from paperspace.cli.common import ClickGroup
9+
from paperspace.commands import hyperparameters as hyperparameters_commands
10+
11+
12+
@cli.group("hyperparameters", help="Manage hyperparameters", cls=ClickGroup)
13+
def hyperparameters_group():
14+
pass
15+
16+
17+
def common_hyperparameter_create_options(f):
18+
options = [
19+
click.option(
20+
"--name",
21+
"name",
22+
required=True,
23+
),
24+
click.option(
25+
"--projectId",
26+
"projectHandle",
27+
required=True,
28+
),
29+
click.option(
30+
"--tuningCommand",
31+
"tuningCommand",
32+
required=True,
33+
),
34+
click.option(
35+
"--workerContainer",
36+
"workerContainer",
37+
required=True,
38+
),
39+
click.option(
40+
"--workerMachineType",
41+
"workerMachineType",
42+
required=True,
43+
),
44+
click.option(
45+
"--workerCommand",
46+
"workerCommand",
47+
required=True,
48+
),
49+
click.option(
50+
"--workerCount",
51+
"workerCount",
52+
required=True,
53+
type=int,
54+
),
55+
click.option(
56+
"--serverRegistryUsername",
57+
"hyperparameterServerRegistryUsername",
58+
),
59+
click.option(
60+
"--serverRegistryPassword",
61+
"hyperparameterServerRegistryPassword",
62+
),
63+
click.option(
64+
"--serverContainerUser",
65+
"hyperparameterServerContainerUser",
66+
),
67+
]
68+
return functools.reduce(lambda x, opt: opt(x), reversed(options), f)
69+
70+
71+
@hyperparameters_group.command("create", help="Create hyperparameter")
72+
@common_hyperparameter_create_options
73+
@common.api_key_option
74+
def create_hyperparameter(api_key, **hyperparameter):
75+
common.del_if_value_is_none(hyperparameter)
76+
hyperparameters_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
77+
command = hyperparameters_commands.CreateHyperparameterCommand(api=hyperparameters_api)
78+
command.execute(hyperparameter)
79+
80+
81+
@hyperparameters_group.command("createAndStart", help="Create hyperparameter")
82+
@common_hyperparameter_create_options
83+
@common.api_key_option
84+
def create_and_start_hyperparameter(api_key, **hyperparameter):
85+
common.del_if_value_is_none(hyperparameter)
86+
hyperparameters_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
87+
command = hyperparameters_commands.CreateAndStartHyperparameterCommand(api=hyperparameters_api)
88+
command.execute(hyperparameter)
89+
90+
91+
@hyperparameters_group.command("list", help="List hyperparameters")
92+
@common.api_key_option
93+
def list_hyperparameters(api_key):
94+
hyperparameters_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
95+
command = hyperparameters_commands.ListHyperparametersCommand(api=hyperparameters_api)
96+
command.execute()
97+
98+
99+
# TODO: 'unhidden' command and test it when api is updated to support deleting hyperparameters
100+
@hyperparameters_group.command("delete", help="Delete hyperparameter", hidden=True)
101+
@click.option(
102+
"--id",
103+
"id_",
104+
required=True,
105+
)
106+
@common.api_key_option
107+
def delete_hyperparameter(api_key, id_):
108+
hyperparameters_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
109+
command = hyperparameters_commands.DeleteHyperparameterCommand(api=hyperparameters_api)
110+
command.execute(id_)
111+
112+
113+
@hyperparameters_group.command("details", help="Show details of hyperparameter")
114+
@click.option(
115+
"--id",
116+
"id_",
117+
required=True,
118+
)
119+
@common.api_key_option
120+
def get_hyperparameter_details(api_key, id_):
121+
hyperparameters_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
122+
command = hyperparameters_commands.HyperparameterDetailsCommand(api=hyperparameters_api)
123+
command.execute(id_)
124+
125+
126+
@hyperparameters_group.command("start", help="Start hyperparameter tuning")
127+
@click.option(
128+
"--id",
129+
"id_",
130+
required=True,
131+
)
132+
@common.api_key_option
133+
def start_hyperparameter_tuning(api_key, id_):
134+
hyperparameters_api = client.API(config.CONFIG_EXPERIMENTS_HOST, api_key=api_key)
135+
command = hyperparameters_commands.HyperparameterStartCommand(api=hyperparameters_api)
136+
command.execute(id_)

paperspace/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,12 @@ def get(self, url, json=None, params=None):
5454
logger.debug("Response status code: {}".format(response.status_code))
5555
logger.debug("Response content: {}".format(response.content))
5656
return response
57+
58+
def delete(self, url, json=None, params=None):
59+
path = self.get_path(url)
60+
response = requests.delete(path, params=params, headers=self.headers, json=json)
61+
logger.debug("DELETE request sent to: {} \n\theaders: {}\n\tjson: {}\n\tparams: {}"
62+
.format(response.url, self.headers, json, params))
63+
logger.debug("Response status code: {}".format(response.status_code))
64+
logger.debug("Response content: {}".format(response.content))
65+
return response

paperspace/commands/experiments.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def _get_objects(self, response, kwargs):
113113

114114

115115
def _make_details_table(experiment):
116-
if experiment["experimentTypeId"] == constants.ExperimentType.SINGLE_NODE:
116+
if experiment["experimentType"] == constants.ExperimentType.SINGLE_NODE:
117117
data = (
118118
("Name", experiment["templateHistory"]["params"].get("name")),
119119
("ID", experiment.get("handle")),
@@ -128,7 +128,7 @@ def _make_details_table(experiment):
128128
("Model Type", experiment["templateHistory"]["params"].get("modelType")),
129129
("Model Path", experiment["templateHistory"]["params"].get("modelPath")),
130130
)
131-
elif experiment["experimentTypeId"] in (constants.ExperimentType.GRPC_MULTI_NODE,
131+
elif experiment["experimentType"] in (constants.ExperimentType.GRPC_MULTI_NODE,
132132
constants.ExperimentType.MPI_MULTI_NODE):
133133
data = (
134134
("Name", experiment["templateHistory"]["params"].get("name")),
@@ -138,7 +138,7 @@ def _make_details_table(experiment):
138138
("Cluster ID", experiment["templateHistory"]["params"].get("clusterId")),
139139
("Experiment Env", experiment["templateHistory"]["params"].get("experimentEnv")),
140140
("Experiment Type",
141-
constants.ExperimentType.get_type_str(experiment["templateHistory"]["params"].get("experimentTypeId"))),
141+
constants.ExperimentType.get_type_str(experiment["templateHistory"]["params"].get("experimentType"))),
142142
("Model Type", experiment["templateHistory"]["params"].get("modelType")),
143143
("Model Path", experiment["templateHistory"]["params"].get("modelPath")),
144144
("Parameter Server Command", experiment["templateHistory"]["params"].get("parameter_server_command")),
@@ -156,7 +156,7 @@ def _make_details_table(experiment):
156156
("Workspace URL", experiment["templateHistory"]["params"].get("workspaceUrl")),
157157
)
158158
else:
159-
raise ValueError("Wrong experiment type: {}".format(experiment["experimentTypeId"]))
159+
raise ValueError("Wrong experiment type: {}".format(experiment["experimentType"]))
160160

161161
ascii_table = terminaltables.AsciiTable(data)
162162
table_string = ascii_table.table

0 commit comments

Comments
 (0)