#!/usr/bin/env python
##############################################################################
##
# This file is part of Sardana
##
# http://www.sardana-controls.org/
##
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
# Sardana is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
##
# Sardana is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
##
# You should have received a copy of the GNU Lesser General Public License
# along with Sardana. If not, see <http://www.gnu.org/licenses/>.
##
##############################################################################
"""This module contains the class definition for the MacroServer meta macro
information"""
__all__ = ["MACRO_TEMPLATE", "MacroLibrary", "MacroClass", "MacroFunction"]
__docformat__ = 'restructuredtext'
import inspect
import operator
from sardana import InvalidId, ElementType
from sardana.sardanameta import SardanaLibrary, SardanaClass, SardanaFunction
from sardana.macroserver.msparameter import Type
import collections
MACRO_TEMPLATE = """class @macro_name@(Macro):
\"\"\"@macro_name@ description.\"\"\"
# uncomment the following lines as necessary. Otherwise you may delete them
#param_def = []
#result_def = []
#hints = {}
#env = (,)
# uncomment the following lines if need prepare. Otherwise you may delete them
#def prepare(self):
# pass
def run(self):
pass
"""
MACRO_TEMPLATE = """\
@macro()
def @macro_name@(self):
self.output("Running @macro_name@...")
"""
[docs]
class MacroLibrary(SardanaLibrary):
"""Object representing a python module containing macro classes and/or
macro functions. Public members:
- module - reference to python module
- file_path - complete (absolute) path (with file name at the end)
- file_name - file name (including file extension)
- path - complete (absolute) path
- name - (=module name) module name (without file extension)
- macros - dict<str, MacroClass>
- exc_info - exception information if an error occurred when loading
the module"""
def __init__(self, **kwargs):
kwargs['manager'] = kwargs.pop('macro_server')
kwargs['elem_type'] = ElementType.MacroLibrary
SardanaLibrary.__init__(self, **kwargs)
[docs]
def serialize(self, *args, **kwargs):
kwargs = SardanaLibrary.serialize(self, *args, **kwargs)
kwargs['macro_server'] = self.get_manager().name
kwargs['id'] = InvalidId
return kwargs
get_macro = SardanaLibrary.get_meta
get_macros = SardanaLibrary.get_metas
has_macro = SardanaLibrary.has_meta
has_macros = SardanaLibrary.has_metas
add_macro_class = SardanaLibrary.add_meta_class
get_macro_class = SardanaLibrary.get_meta_class
get_macro_classes = SardanaLibrary.get_meta_classes
has_macro_class = SardanaLibrary.has_meta_class
add_macro_function = SardanaLibrary.add_meta_function
get_macro_function = SardanaLibrary.get_meta_function
get_macro_functions = SardanaLibrary.get_meta_functions
has_macro_function = SardanaLibrary.has_meta_function
@property
def macros(self):
ret = {}
ret.update(self.meta_classes)
ret.update(self.meta_functions)
return ret
[docs]
class Parameterizable(object):
"""Helper class to handle parameter and result definition for a
:class:`~sardana.macroserver.msmetamacro.MacroClass` or a
:class:`~sardana.macroserver.msmetamacro.MacroFunction`"""
def __init__(self):
self._parameter = self.build_parameter()
self._result = self.build_result()
def get_parameter_definition(self):
raise NotImplementedError
def get_result_definition(self):
raise NotImplementedError
def get_hints_definition(self):
raise NotImplementedError
def get_parameter(self):
return self._parameter
def get_result(self):
return self._result
def build_parameter(self):
try:
built_param = self._build_parameter(
self.get_parameter_definition())
except:
raise Exception("Error in the parameters definition")
return built_param
def build_result(self):
return self._build_parameter(self.get_result_definition())
def _build_parameter(self, param_def):
'''Builds a list of parameters, each of them represented by a dictionary
containing information: name, type, default_value, description, min and
max values. In case of simple parameters, type is the parameter type.
In case of repeat parameter, type is a list containing its definition.
'''
ret = []
param_def = param_def or ()
for p in param_def:
t = p[1]
ret_p = {'min': None, 'max': None}
if isinstance(t, collections.abc.Sequence) and not isinstance(t, str):
ret_p = {'min': 1, 'max': None}
if isinstance(t[-1], collections.abc.Mapping):
ret_p.update(t[-1])
t = self._build_parameter(t[:-1])
else:
t = self._build_parameter(t)
ret_p['name'] = p[0]
ret_p['type'] = t
ret_p['default_value'] = p[2]
ret_p['description'] = p[3]
ret.append(ret_p)
return ret
def build_parameter_info(self, param_def=None):
param_def = param_def or self.get_parameter_definition()
info = [str(len(param_def))]
for name, type_class, def_val, desc in param_def:
repeat = isinstance(type_class, list)
info.append(name)
type_name = (repeat and 'ParamRepeat') or type_class
info.append(type_name)
info.append(desc)
if repeat:
rep = type_class
opts = sep = ''
for opt, val in list(rep.items()):
opts += '%s%s=%s' % (sep, opt, val)
sep = ', '
info.append(opts)
info += self.get_parameter_info(rep.param_def)
else:
info.append(str(def_val))
return info
def build_result_info(self, result_def=None):
result_def = result_def or self.get_result_definition()
info = [str(len(result_def))]
for name, type_class, def_val, desc in result_def:
repeat = isinstance(type_class, list)
info.append(name)
type_name = (repeat and 'ParamRepeat') or type_class
info.append(type_name)
info.append(desc)
if repeat:
rep = type_class
opts = sep = ''
for opt, val in list(rep.items()):
opts += '%s%s=%s' % (sep, opt, val)
sep = ', '
info.append(opts)
info += self.get_parameter_info(rep.param_def)
else:
info.append(str(def_val))
return info
def get_info(self):
info = [self.full_name, self.description, str(self.code_object.hints)]
info += self.get_parameter_info()
info += self.get_result_info()
return info
def serialize(self, *args, **kwargs):
kwargs['macro_server'] = self.get_manager().name
kwargs['id'] = InvalidId
kwargs['hints'] = self.code_object.hints
param, result = self.get_parameter(), self.get_result()
kwargs['parameters'] = param
kwargs['result'] = result
return kwargs
[docs]
class MacroClass(SardanaClass, Parameterizable):
def __init__(self, **kwargs):
kwargs['manager'] = kwargs.pop('macro_server')
kwargs['elem_type'] = ElementType.MacroClass
SardanaClass.__init__(self, **kwargs)
Parameterizable.__init__(self)
def __lt__(self, o):
return self.name < o.name
[docs]
def serialize(self, *args, **kwargs):
kwargs = SardanaClass.serialize(self, *args, **kwargs)
kwargs = Parameterizable.serialize(self, *args, **kwargs)
return kwargs
@property
def macro_class(self):
return self.klass
def get_parameter_definition(self):
return self.klass.param_def
def get_result_definition(self):
return self.klass.result_def
def get_hints_definition(self):
return self.klass.hints
[docs]
class MacroFunction(SardanaFunction, Parameterizable):
def __init__(self, **kwargs):
kwargs['manager'] = kwargs.pop('macro_server')
kwargs['elem_type'] = ElementType.MacroFunction
SardanaFunction.__init__(self, **kwargs)
Parameterizable.__init__(self)
def __lt__(self, o):
return self.name < o.name
[docs]
def serialize(self, *args, **kwargs):
kwargs = SardanaFunction.serialize(self, *args, **kwargs)
kwargs = Parameterizable.serialize(self, *args, **kwargs)
return kwargs
@property
def macro_function(self):
return self.function
def to_parameter_definition(self):
param_def = []
args, varargs, varkw, defaults, *_ = inspect.getfullargspec(self.function)
assert varkw is None
assert len(args) > 0
if varargs is None:
for arg in args[1:]:
param_def.append((arg, Type.Any, None, arg + " parameter"))
i = len(param_def) - 1
if defaults is not None:
for default in reversed(defaults):
param_def[i][2] = default
i -= 1
else:
param_def.append(
(varargs, [[varargs, Type.Any, None, varargs + " parameter"]],
None, "list of " + varargs))
return param_def
def get_parameter_definition(self):
param_def = self.function.param_def
if param_def is None:
param_def = self.to_parameter_definition()
return param_def
def get_result_definition(self):
result_def = self.function.result_def
return result_def
def get_hints_definition(self):
return self.function.hints or ()