def doc_one(package_name, plugin_name, part=None, prefix=None, long_doc=True, include_details=False): """Document one plug-in If the plug-in is not part of the package an UnknownPluginError is raised. If there are several functions registered in a plug-in and `part` is not specified, then the first function registered in the plug-in will be documented. Args: package_name (String): Name of package containing plug-ins. plugin_name (String): Name of the plug-in, i.e. the module containing the plug-in. part (String): Name of function to call within the plug-in (optional). prefix (String): Prefix of the plug-in name, used if the plug-in name is unknown (optional). long_doc (Boolean): Whether to return the long doc-string or the short one-line string (optional). include_details (Boolean): Whether to include development details like parameters and return values (optional). Returns: String: Documentation of the plug-in. """ # Get Plugin-object and pick out doc-string plugin_name = load_one(package_name, plugin_name, prefix=prefix) part = "__default__" if part is None else part try: plugin = _PLUGINS[package_name][plugin_name][part] except KeyError: raise exceptions.UnknownPluginError( "Plugin '{}' not found for '{}' in '{}'" "".format(part, plugin_name, package_name)) from None doc = plugin.function.__doc__ if plugin.function.__doc__ else "" if long_doc: # Strip short description and indentation lines = [ d.strip() for d in "\n\n".join(doc.split("\n\n")[1:]).split("\n") ] # Stop before Args:, Returns: etc if details should not be included idx_args = len(lines) if not include_details: re_args = re.compile( "(Args:|Returns:|Details:|Examples?:|Attributes:)$") try: idx_args = [re_args.match(l) is not None for l in lines].index(True) except ValueError: pass return "\n".join(lines[:idx_args]).strip() else: # Return short description return doc.split("\n\n")[0].replace("\n", " ").strip()
def _import_one(package_name, plugin_name): """Import a plugin from a package This is essentially just a regular python import. As the module is imported, the _PLUGINS-dict will be populated by @register decorated functions in the file. Args: package_name (String): Name of package containing plug-ins. plugin_name (String): Name of the plug-in (module). """ try: importlib.import_module(package_name + "." + plugin_name) except ImportError: raise exceptions.UnknownPluginError( "Plug-in '{}' not found in package '{}'" "".format(plugin_name, package_name)) from None
def _import_all(package_name): """Import the relevant .py-files in the given package directory As each file is imported, the _PLUGINS-dict will be populated by @register decorated functions in the files. Args: package_name (String): Name of package containing plug-ins. """ # Figure out the directory of the package by importing it try: package = importlib.import_module(package_name) except ImportError: raise exceptions.UnknownPluginError( "Plug-in package '{}' not found".format(package_name)) from None # Import all .py files in the given directory directory = pathlib.Path(package.__file__).parent for file_path in directory.glob("*.py"): plugin_name = file_path.stem if not plugin_name.startswith("_"): _import_one(package_name, plugin_name)
def call_one(package_name, plugin_name, part=None, prefix=None, logger=log.time, use_timer=True, do_report=True, **kwargs): """Call one plug-in If the plug-in is not part of the package an UnknownPluginError is raised. If there are several functions registered in a plug-in and `part` is not specified, then the first function registered in the plug-in will be called. The file containing the source code of the plug-in is added to the list of dependencies. Args: package_name (String): Name of package containing plug-ins. plugin_name (String): Name of the plug-in, i.e. the module containing the plug-in. part (String): Name of function to call within the plug-in (optional). prefix (String): Prefix of the plug-in name, used if the plug-in name is unknown (optional). logger (Function): Logger from the lib.log package specifying the level of logging to be used (optional). use_timer (Boolean): Whether to time and log the call to the plug-in (optional). do_report (Boolean): Whether to add the call to the plug-in to the report (optional). kwargs: Named arguments passed on to the plug-in. Returns: Return value of the plug-in. """ # Get Plugin-object plugin_name = load_one(package_name, plugin_name, prefix=prefix) part = "__default__" if part is None else part try: plugin = _PLUGINS[package_name][plugin_name][part] except KeyError: raise exceptions.UnknownPluginError( "Plugin '{}' not found for '{}' in '{}'" "".format(part, plugin_name, package_name)) from None # Add plug-in to report if do_report: from where.reports import report code_kwargs = kwargs.copy() if "dset" in code_kwargs: code_kwargs["dset"] = code_kwargs["dset"].repr report.add( package_name, __plugin__=plugin.name, __doc__=plugin.function.__doc__, __text__="TODO", __code__= "kwargs = {}\n{} = plugins.call_one('{}', '{}', part='{}', **kwargs)" "".format(code_kwargs, plugin_name, package_name, plugin_name, part), **kwargs, ) # Call plug-in dependencies.add(plugin.file_path, label="plugin") if logger: logger(f"Start {plugin.name} in {package_name}") time_logger = log.time if use_timer else None else: time_logger = None with timer(f"Finish {plugin.name} ({package_name}) in", logger=time_logger): return plugin.function(**kwargs)