Source code for TOSLibrary.dynamic_library

"""Dynamic library core."""
import inspect


[docs]class DynamicLibrary: """ Dynamic library core inspired by SeleniumLibrary for Robot Framework. You can create dynamic libraries by inheriting this class. Example usage: .. code-block:: python from tos.task_object_storage import TaskObjectStorage from module import another_module class TOSLibrary(DynamicLibrary): def __init__(self): super(TOSLibrary, self).__init__() tos = TaskObjectStorage() self.add_component(self) self.add_component(tos) self.add_component(another_module) """ # TODO: # In the future it might be sensible to use PythonLibCore: # https://github.com/robotframework/PythonLibCore def __init__(self): self.keywords = {}
[docs] def add_component(self, component): """Add external (class) methods to the library. This registers an arbitrary collection of functions as keywords callable from Robot Framework. :param component: class, instance, or module that contains functions or methods (callables). """ for name in dir(component): func = getattr(component, name) # FIXME: consider using @keyword decorator in TaskObjectStorage if not callable(func) or name.startswith("_") or name == "tos": continue # pragma: no cover # (The above line doesn't show up in coverage # correctly even though it's executed) self.keywords[name] = func
[docs] def get_keyword_names(self): """Robot Framework dynamic API keyword collector. :returns: all keywords registered for the library. :rtype: list """ return sorted(self.keywords)
[docs] def run_keyword(self, name, args, kwargs): """Call the keyword method by name. :param name: Name of the keyword. :param args: args passed from Robot Framework (can be empty). :param kwargs: kwargs passed from Robot Framework (can be empty). :returns: call to a registered keyword by name. """ print(f"Running keyword {name}") return self.keywords[name](*args, **kwargs)
def __getattr__(self, name): """Expose the dynamically added keywords as class attributes.""" if name in self.keywords: return self.keywords[name] raise AttributeError( f"{type(self).__name__} object has no attribute {name}" ) def __dir__(self): """Expose the dynamically added keywords as class attributes.""" my_attrs = super().__dir__() return sorted(set(my_attrs) | set(self.keywords)) # The below methods mainly help with robot.libdoc # TODO: they should be reviewed and refactored as needed
[docs] def get_keyword_arguments(self, name): kw = self.keywords[name] if name != "__init__" else self.__init__ args, defaults, varargs, kwargs = self._get_arg_spec(kw) args += ['{}={}'.format(name, value) for name, value in defaults] if varargs: args.append(f"*{varargs}") if kwargs: args.append(f"**{kwargs}") return args
def _get_arg_spec(self, kw): spec = inspect.getfullargspec(kw) keywords = spec.varkw args = spec.args[1:] if inspect.ismethod(kw) else spec.args # drop self defaults = spec.defaults or () nargs = len(args) - len(defaults) mandatory = args[:nargs] defaults = zip(args[nargs:], defaults) return mandatory, defaults, spec.varargs, keywords
[docs] def get_keyword_documentation(self, name): if name == '__init__': return inspect.getdoc(self.__init__) or '' kw = self.keywords[name] doc = inspect.getdoc(kw) or '' return doc