Skip to content

Commit c72d72f

Browse files
committed
additional functionality and cleanup
1 parent 0695ec6 commit c72d72f

11 files changed

+1249
-101
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11

22
Unreleased
33
----------
4-
-
4+
**Improvements**
5+
- Added `update_module` and `delete_module` methods to MAS service.
6+
57

68
v1.0.1 (2019-07-31)
79
-------------------

src/sasctl/_services/microanalytic_store.py

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,106 @@
44
# Copyright © 2019, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
55
# SPDX-License-Identifier: Apache-2.0
66

7+
"""A stateless, memory-resident, high-performance program execution service."""
8+
79
from collections import OrderedDict
810

911
from .service import Service
10-
from sasctl.core import HTTPError
1112

1213

1314
class MicroAnalyticScore(Service):
14-
"""A stateless, memory-resident, high-performance program execution
15-
service.
15+
"""Micro Analytic Service (MAS) client."""
1616

17-
"""
1817
_SERVICE_ROOT = '/microanalyticScore'
1918

20-
def list_modules(self, filter=None):
21-
params = 'filter={}'.format(filter) if filter is not None else {}
19+
@classmethod
20+
def is_uuid(cls, id):
21+
"""Check if the ID appears to be a valid MAS id.
2222
23-
return self.get('/modules', params=params)
23+
Indicates whether `id` appears to be a correctly formatted ID. Does
24+
**not** check whether a module with `id` actually exists.
2425
25-
def get_module(self, module):
26-
if isinstance(module, dict) and all([k in module for k in ('id', 'name')]):
27-
return module
26+
Parameters
27+
----------
28+
id : str
2829
29-
try:
30-
# MAS module IDs appear to just be the lower case name.
31-
# Try to find by ID first
32-
return self.get('/modules/{}'.format(str(
33-
module).lower()))
34-
except HTTPError as e:
35-
if e.code == 404:
36-
pass
37-
else:
38-
raise e
30+
Returns
31+
-------
32+
bool
3933
40-
# Wasn't able to find by id, try searching by name
41-
results = self.list_modules(filter='eq(name, "{}")'.format(module))
34+
Notes
35+
-----
36+
Overrides the :meth:`Service.is_uuid` method since MAS modules do
37+
not currently use IDs that are actually UUIDs.
4238
43-
# Not sure why, but as of 19w04 the filter doesn't seem to work.
44-
for result in results:
45-
if result['name'] == str(module):
46-
return result
39+
"""
40+
return True
41+
42+
list_modules, get_module, update_module, \
43+
delete_module = Service._crud_funcs('/modules', 'module')
4744

4845
def get_module_step(self, module, step):
46+
"""Details of a single step in a given module.
47+
48+
Parameters
49+
----------
50+
module : str or dict
51+
Name, id, or dictionary representation of a module
52+
step : str
53+
Name of the step
54+
55+
Returns
56+
-------
57+
RestObj
58+
59+
"""
4960
module = self.get_module(module)
5061

5162
r = self.get('/modules/{}/steps/{}'.format(module.id, step))
5263
return r
5364

5465
def list_module_steps(self, module):
66+
"""List all steps defined for a module.
67+
68+
Parameters
69+
----------
70+
module : str or dict
71+
Name, id, or dictionary representation of a module
72+
73+
Returns
74+
-------
75+
list
76+
List of :class:`.RestObj` instances representing each step.
77+
78+
"""
5579
module = self.get_module(module)
5680

57-
return self.get('/modules/{}/steps'.format(module.id))
81+
steps = self.get('/modules/{}/steps'.format(module.id))
82+
return steps if isinstance(steps, list) else [steps]
5883

5984
def execute_module_step(self, module, step, return_dict=True, **kwargs):
85+
"""Call a module step with the given parameters.
86+
87+
Parameters
88+
----------
89+
module : str or dict
90+
Name, id, or dictionary representation of a module
91+
step : str
92+
Name of the step
93+
return_dict : bool, optional
94+
Whether the results should be returned as a dictionary instead
95+
of a tuple
96+
kwargs : any
97+
Passed as arguments to the module step
98+
99+
Returns
100+
-------
101+
any
102+
Results of the step execution. Returned as a dictionary if
103+
`return_dict` is True, otherwise returned as a tuple if more
104+
than one value is returned, otherwise the single value.
105+
106+
"""
60107
module_name = module.name if hasattr(module, 'name') else str(module)
61108
module = self.get_module(module)
62109

@@ -92,7 +139,7 @@ def execute_module_step(self, module, step, return_dict=True, **kwargs):
92139

93140
def create_module(self, name=None, description=None, source=None,
94141
language='python', scope='public'):
95-
"""
142+
"""Create a new module in MAS.
96143
97144
Parameters
98145
----------
@@ -104,9 +151,9 @@ def create_module(self, name=None, description=None, source=None,
104151
105152
Returns
106153
-------
154+
RestObj
107155
108156
"""
109-
110157
if source is None:
111158
raise ValueError('The `source` parameter is required.')
112159
else:
@@ -117,7 +164,8 @@ def create_module(self, name=None, description=None, source=None,
117164
elif language == 'ds2':
118165
t = 'text/vnd.sas.source.ds2'
119166
else:
120-
raise ValueError('Unrecognized source code language `%s`.' % language)
167+
raise ValueError('Unrecognized source code language `%s`.'
168+
% language)
121169

122170
data = {'id': name,
123171
'type': t,
@@ -129,7 +177,9 @@ def create_module(self, name=None, description=None, source=None,
129177
return r
130178

131179
def define_steps(self, module):
132-
"""Defines python methods on a module that automatically call the
180+
"""Map MAS steps to Python methods.
181+
182+
Defines python methods on a module that automatically call the
133183
corresponding MAS steps.
134184
135185
Parameters
@@ -161,10 +211,11 @@ def define_steps(self, module):
161211
type_string = ' # type: ({})'.format(', '.join(arg_types))
162212

163213
# Method signature
164-
signature = 'def _%s_%s(%s, **kwargs):' % (module.name,
165-
step.id,
166-
', '.join(a for a
167-
in arguments))
214+
signature = 'def _%s_%s(%s, **kwargs):' \
215+
% (module.name,
216+
step.id,
217+
', '.join(a for a in arguments))
218+
168219
# MAS always lower-cases variable names
169220
# Since the original Python variables may have a different case,
170221
# allow kwargs to be used to input alternative caps
@@ -180,7 +231,8 @@ def define_steps(self, module):
180231
type_string,
181232
' """Execute step %s of module %s."""' % (step, module),
182233
'\n'.join([' %s' % a for a in arg_checks]),
183-
' r = execute_module_step(module, step, {})'.format(', '.join(call_params)),
234+
' r = execute_module_step(module, step, {})'.format(
235+
', '.join(call_params)),
184236
' r.pop("rc", None)',
185237
' r.pop("msg", None)',
186238
' if len(r) == 1:',

0 commit comments

Comments
 (0)