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+
79from collections import OrderedDict
810
911from .service import Service
10- from sasctl .core import HTTPError
1112
1213
1314class 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