Source code for pyplugin.utils

import functools
import importlib
import pkgutil
import typing
import inspect
from collections import OrderedDict


empty = object()
""" An object to use when None is a valid value for an argument """


class _MethodDecoratorAdapter:
    def __init__(self, decorator, func):
        self.decorator = decorator
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.decorator(self.func)(*args, **kwargs)

    def __get__(self, instance, owner):
        return self.decorator(self.func.__get__(instance, owner))


[docs] def auto_adapt_to_methods(decorator): def adapt(func): return _MethodDecoratorAdapter(decorator, func) return adapt
[docs] @auto_adapt_to_methods def maybe_decorator(func: typing.Callable): @functools.wraps(func) def wrapper(*args, **kwargs): if not args: return maybe_decorator(functools.partial(func, **kwargs)) return func(*args, **kwargs) return wrapper
[docs] def import_helper(name: str, ignore_missing: bool = True): *module, name = name.split(".") module = ".".join(module) if not module: module, name = name, "" try: module = importlib.import_module(module) except (ModuleNotFoundError, ValueError): if not ignore_missing: raise else: return None if not name: return module if hasattr(module, "__path__"): for loader, module_name, is_pkg in pkgutil.walk_packages(module.__path__): if module_name != name: continue try: submodule = loader.find_module(module_name).load_module(module_name) except ImportError: continue setattr(module, module_name, submodule) try: return getattr(module, name) except AttributeError: if not ignore_missing: raise else: return None
[docs] def void_no_args(): return None
[docs] def void_args(*args, **kwargs): return None
[docs] def infer_return_type(obj: typing.Any): if inspect.isfunction(obj): return obj.__annotations__.get("return", None) elif inspect.isclass(obj): return obj raise TypeError(f"Invalid type {type(obj)}, expected a function or class.")
[docs] def ensure_a_list(data): """Ensure data is a list or wrap it in a list""" if not data: return [] if isinstance(data, (list, tuple, set)): return list(data) return [data]
[docs] def make_safe_args(func: typing.Callable, args=(), kwargs=None, default_args=(), default_kwargs=None): """ Prepares args and kwargs to use to call the function, only passing in what the function signature calls for. Can optionally provide default_args and default_kwargs to default to if not provided. Arguments: func (Callable): The argument to inspect args (Iterable): Positional arguments kwargs (dict | None): Keyword arguments default_args (Iterable): Positional arguments to default to if not provided by args or kwargs default_kwargs (dict | None): Keyword arguments to default to if not provided in kwargs Returns: tuple[tuple, dict]: The args and kwargs to pass into func (e.g. :code:`func(*args, **kwargs`) """ args = list(args) if args else [] kwargs = kwargs if kwargs else {} default_args = list(default_args) if default_args else [] default_kwargs = default_kwargs if default_kwargs else {} param_args = OrderedDict({}) param_kwargs = OrderedDict({}) is_partial = isinstance(func, functools.partial) orig_func = func # unwrap partial and unwrap the args, kwargs if is_partial: args = list(func.args) + args for key, value in func.keywords.items(): kwargs[key] = value func = func.func # Use __init__ function if a class if inspect.isclass(func): func = func.__init__ argspec = inspect.getfullargspec(func) # For each parameter, find a source and populate args / kwargs # Search order is: kwargs, args, default_kwargs, default_args for param in inspect.signature(func).parameters: if param in kwargs: param_kwargs[param] = kwargs.pop(param) elif args: param_args[param] = args[0] args = args[1:] default_args = default_args[1:] elif param in default_kwargs: param_kwargs[param] = default_kwargs.pop(param) elif default_args: param_args[param] = default_args[0] default_args = default_args[1:] args_, kwargs_ = list(param_args.values()), param_kwargs.copy() # Append extra arguments for varargs and varkwargs if argspec.varargs: args_.extend(args) args_.extend(default_args[len(args) :]) if argspec.varkw: for key, value in kwargs.items(): kwargs_.setdefault(key, value) for key, value in default_kwargs.items(): kwargs_.setdefault(key, value) # Remove initial arguments if this was a partial function if is_partial: args_ = args_[len(orig_func.args) :] kwargs_ = {key: value for key, value in kwargs_.items() if key not in orig_func.keywords} return tuple(args_), kwargs_