Skip to content

Commit

Permalink
add resource support to config command
Browse files Browse the repository at this point in the history
  • Loading branch information
jfischer committed Mar 27, 2020
1 parent 098a1be commit 47dbbd8
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 47 deletions.
25 changes: 25 additions & 0 deletions dataworkspaces/backends/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,31 @@ def _add_local_params_for_resource(self, resource_name:str,
self._save_json_to_file(self.resource_local_params_by_name,
RESOURCE_LOCAL_PARAMS_PATH)

def _set_global_param_for_resource(self, resource_name:str, name:str, value:Any) -> None:
"""It is up to the caller to verify that the resource exists and has
this parameter defined. Value should be json-serializable (via the to_json() method
of the param type). Setting does not necessarily take effect until save() is called"""
assert resource_name in self.resource_params_by_name, "Missing resource params entry for resource %s"%resource_name
self.resource_params_by_name[resource_name][name] = value
for pdict in self.resource_params:
if pdict['name']==resource_name:
pdict[name] = value
self._save_json_to_file(self.resource_params, RESOURCES_FILE_PATH)
return
assert 0, "Did not find resource params entry"


def _set_local_param_for_resource(self, resource_name:str, name:str, value:Any) -> None:
"""It is up to the caller to verify that the resource exists and has
this parameter defined. Value should be json-serializable (via the to_json() method
of the param type). Setting does not necessarily take effect until save() is called"""
assert resource_name in self.resource_local_params_by_name, \
"Missing resource local params entry for resource %s"%resource_name
self.resource_local_params_by_name[resource_name][name] = value
self._save_json_to_file(self.resource_local_params_by_name,
RESOURCE_LOCAL_PARAMS_PATH)


def get_workspace_local_path_if_any(self) -> Optional[str]:
return self.workspace_dir

Expand Down
192 changes: 147 additions & 45 deletions dataworkspaces/commands/config.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,138 @@
# Copyright 2018,2019 by MPI-SWS and Data-ken Research. Licensed under Apache 2.0. See LICENSE.txt.
import click
from typing import Optional
from typing import Optional, Dict, Any, List
from abc import ABCMeta, abstractmethod

from dataworkspaces.workspace import Workspace
from dataworkspaces.workspace import Workspace, Resource, LocalStateResourceMixin
from dataworkspaces.utils.param_utils import PARAM_DEFS, LOCAL_PARAM_DEFS,\
ParamNotFoundError
ParamNotFoundError, ParamDef
from dataworkspaces.utils.print_utils import print_columns, ColSpec
from dataworkspaces.errors import ConfigurationError


class ParamConfigHandler(metaclass=ABCMeta):
def __init__(self, params:Dict[str,Any], defs:Dict[str, ParamDef]):
self.params = params
self.defs = defs

def get_value(self, name:str) -> Any:
if name in self.params:
return self.params[name]
else:
return self.defs[name].default_value

def is_default(self, name) -> bool:
return (name not in self.params) or (self.params[name]==self.defs[name].default_value)

@abstractmethod
def get_scope(self) -> str:
"""Return 'global' or 'local'
"""
pass

@abstractmethod
def get_what_for(self) -> str:
"""This should return either 'workspace' or
'resource NAME'
"""
pass

@abstractmethod
def set_value(self, name:str, value:Any) -> None:
pass


class GlobalWorkspaceHandler(ParamConfigHandler):
def __init__(self, workspace):
super().__init__(workspace._get_global_params(),
PARAM_DEFS)
self.workspace = workspace

def get_scope(self) -> str:
return 'global'

def set_value(self, name:str, value:Any) -> None:
self.workspace.set_global_param(name, value)

def get_what_for(self) -> str:
return 'workspace'


class LocalWorkspaceHandler(ParamConfigHandler):
def __init__(self, workspace):
super().__init__(workspace._get_local_params(),
LOCAL_PARAM_DEFS)
self.workspace = workspace

def get_scope(self) -> str:
return 'local'

def set_value(self, name:str, value:Any) -> None:
self.workspace.set_local_param(name, value)

def get_what_for(self) -> str:
return 'workspace'


class GlobalResourceHandler(ParamConfigHandler):
def __init__(self, resource:Resource, workspace:Workspace):
super().__init__(resource.get_params(),
resource.param_defs.global_defs)
self.resource = resource
self.workspace = workspace

def get_scope(self) -> str:
return 'global'

def set_value(self, name:str, value:Any) -> None:
self.workspace._set_global_param_for_resource(self.resource.name, name, value)

def get_what_for(self) -> str:
return "resource '%s'"%self.resource.name


class LocalResourceHandler(ParamConfigHandler):
def __init__(self, resource:Resource, workspace:Workspace):
assert isinstance(resource, LocalStateResourceMixin)
super().__init__(resource.get_local_params(),
resource.param_defs.local_defs)
self.resource = resource
self.workspace = workspace

def get_scope(self) -> str:
return 'local'

def set_value(self, name:str, value:Any) -> None:
self.workspace._set_local_param_for_resource(self.resource.name, name, value)

def get_what_for(self) -> str:
return "resource '%s'"%self.resource.name


def config_command(workspace:Workspace, param_name:Optional[str],
param_value:Optional[str]):
param_value:Optional[str], resource:Optional[str]):
if param_name is None and param_value is None:
names = []
scopes = []
values = []
isdefaults = []
helps = []
for local_or_global in ['local', 'global']:
if local_or_global=='local':
params = workspace._get_local_params()
defs = LOCAL_PARAM_DEFS
else:
params = workspace._get_global_params()
defs = PARAM_DEFS
for (name, pdef) in defs.items():
if resource is None:
handlers = [GlobalWorkspaceHandler(workspace), LocalWorkspaceHandler(workspace)] # type: List[ParamConfigHandler]
else:
if resource not in workspace.get_resource_names():
raise ConfigurationError("No resource in this workspace with name '%s'"%resource)
resource_obj = workspace.get_resource(resource)
handlers = [GlobalResourceHandler(resource_obj, workspace)]
if isinstance(resource_obj, LocalStateResourceMixin):
handlers.append(LocalResourceHandler(resource_obj, workspace))
for handler in handlers:
for name in handler.defs.keys():
names.append(name)
scopes.append(local_or_global)
helps.append(pdef.help)
if name in params:
values.append(params[name])
isdefaults.append('N')
else:
values.append(pdef.default_value)
isdefaults.append('Y')
scopes.append(handler.get_scope())
helps.append(handler.defs[name].help)
values.append(handler.get_value(name))
isdefaults.append('Y' if handler.is_default(name) else 'N')
print_columns({'Name':names,
'Scope':scopes,
'Value':values,
Expand All @@ -46,40 +142,46 @@ def config_command(workspace:Workspace, param_name:Optional[str],
paginate=False)
click.echo()
else:
if param_name in PARAM_DEFS:
local_or_global='global'
params = workspace._get_global_params()
defs = PARAM_DEFS
elif param_name in LOCAL_PARAM_DEFS:
local_or_global='local'
params = workspace._get_local_params()
defs = LOCAL_PARAM_DEFS
else:
raise ParamNotFoundError("No parameter named '%s'" % param_name)
assert param_name is not None
if resource is None:
if param_name in PARAM_DEFS:
handler = GlobalWorkspaceHandler(workspace)
elif param_name in LOCAL_PARAM_DEFS:
handler = LocalWorkspaceHandler(workspace)
else:
raise ParamNotFoundError("No workspace parameter named '%s'" % param_name)
else: # resource-specific
if resource not in workspace.get_resource_names():
raise ConfigurationError("No resource in this workspace with name '%s'"%resource)
resource_obj = workspace.get_resource(resource)
if isinstance(resource_obj, LocalStateResourceMixin) and \
(param_name in resource_obj.get_local_params()):
handler = LocalResourceHandler(resource_obj, workspace)
elif param_name in resource_obj.get_params().keys():
handler = GlobalResourceHandler(resource_obj, workspace)
else:
raise ParamNotFoundError("Resource %s has no parameter named '%s'" % (resource, param_name))

if param_value is None:
# just print for the specific param
title = "%s Parameter %s" % ('Local' if local_or_global=='local' else 'Global',
param_name)
title = "%s parameter '%s'" % (handler.get_what_for().capitalize(), param_name)
click.echo(title)
click.echo('='* len(title))
click.echo()
print_columns(
{'Value':[params[param_name] if param_name in params
else defs[param_name].default_value],
'Scope':[local_or_global],
'Default?':['Y' if param_name not in params else 'N'],
'Description':[defs[param_name].help]},
{'Value':[handler.get_value(param_name)],
'Scope':[handler.get_scope()],
'Default?':['Y' if handler.is_default(param_name) else 'N'],
'Description':[handler.defs[param_name].help]},
spec={'Description':ColSpec(width=60)},
paginate=False)
click.echo()
else: # setting the parameter
parsed_value = defs[param_name].parse(param_value)
if local_or_global=='local':
workspace.set_local_param(param_name, parsed_value)
else:
workspace.set_global_param(param_name, parsed_value)
workspace.save('Update of parameter %s' % param_name)
click.echo("Successfully set %s parameter %s to '%s'."%
(local_or_global, param_name, param_value))
parsed_value = handler.defs[param_name].parse(param_value)
handler.set_value(param_name, handler.defs[param_name].to_json(parsed_value))
param_for = handler.get_what_for()
workspace.save("Update of %s parameter %s"% (param_for, param_name))
click.echo("Successfully set %s %s parameter '%s' to %s."%
(param_for, handler.get_scope(), param_name, repr(parsed_value)))


6 changes: 4 additions & 2 deletions dataworkspaces/dws.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,10 +800,12 @@ def deploy_run(ctx, image_name:Optional[str], no_mount_ssh_keys:bool):

@click.command()
@click.option('--workspace-dir', type=WORKSPACE_PARAM, default=DWS_PATHDIR)
@click.option('--resource', type=str, default=None,
help="If specified, get/set parameters for the specified resource, rather than the workspace.")
@click.argument('param_name', metavar="[PARAMETER_NAME]", default=None, required=False)
@click.argument('param_value', metavar="[PARAMETER_VALUE]", default=None, required=False)
@click.pass_context
def config(ctx, workspace_dir, param_name, param_value):
def config(ctx, workspace_dir, resource, param_name, param_value):
"""Get or set configuration parameters. Local parameters are only for this
copy of the workspace, while global parameters are stored centrally and
affect all copies.
Expand All @@ -822,7 +824,7 @@ def config(ctx, workspace_dir, param_name, param_value):
workspace_dir = click.prompt("Please enter the workspace root dir",
type=WORKSPACE_PARAM)
workspace = find_and_load_workspace(ns.batch, ns.verbose, workspace_dir)
config_command(workspace, param_name, param_value)
config_command(workspace, param_name, param_value, resource)

cli.add_command(config)

Expand Down
14 changes: 14 additions & 0 deletions dataworkspaces/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,20 @@ def _add_local_params_for_resource(self, resource_name:str,
"""
pass

@abstractmethod
def _set_global_param_for_resource(self, resource_name:str, name:str, value:Any) -> None:
"""It is up to the caller to verify that the resource exists and has
this parameter defined. Value should be json-serializable (via the to_json() method
of the param type). Setting does not necessarily take effect until save() is called"""
pass

@abstractmethod
def _set_local_param_for_resource(self, resource_name:str, name:str, value:Any) -> None:
"""It is up to the caller to verify that the resource exists and has
this parameter defined. Value should be json-serializable (via the to_json() method
of the param type). Setting does not necessarily take effect until save() is called"""
pass

def get_resource(self, name:str) -> 'Resource':
"""Get the associated resource from the workspace metadata.
"""
Expand Down

0 comments on commit 47dbbd8

Please sign in to comment.