|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
Various utilities functions
|
|
|
|
"""
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
from inspect import isclass
|
|
|
|
try:
|
|
|
|
from inspect import getfullargspec as getargspec
|
|
|
|
|
|
|
|
_FULLARGSPEC_SUPPORTED = True
|
|
|
|
except ImportError:
|
|
|
|
_FULLARGSPEC_SUPPORTED = False
|
|
|
|
from inspect import getargspec
|
|
|
|
|
|
|
|
from .utils import is_iterable
|
|
|
|
|
|
|
|
if sys.version_info < (3, 4, 0): # pragma: no cover
|
|
|
|
def _constructor(class_):
|
|
|
|
"""
|
|
|
|
Retrieves constructor from given class
|
|
|
|
|
|
|
|
:param class_:
|
|
|
|
:type class_: class
|
|
|
|
:return: constructor from given class
|
|
|
|
:rtype: callable
|
|
|
|
"""
|
|
|
|
return class_.__init__
|
|
|
|
else: # pragma: no cover
|
|
|
|
def _constructor(class_):
|
|
|
|
"""
|
|
|
|
Retrieves constructor from given class
|
|
|
|
|
|
|
|
:param class_:
|
|
|
|
:type class_: class
|
|
|
|
:return: constructor from given class
|
|
|
|
:rtype: callable
|
|
|
|
"""
|
|
|
|
return class_
|
|
|
|
|
|
|
|
|
|
|
|
def call(function, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Call a function or constructor with given args and kwargs after removing args and kwargs that doesn't match
|
|
|
|
function or constructor signature
|
|
|
|
|
|
|
|
:param function: Function or constructor to call
|
|
|
|
:type function: callable
|
|
|
|
:param args:
|
|
|
|
:type args:
|
|
|
|
:param kwargs:
|
|
|
|
:type kwargs:
|
|
|
|
:return: sale vakye as default function call
|
|
|
|
:rtype: object
|
|
|
|
"""
|
|
|
|
func = constructor_args if isclass(function) else function_args
|
|
|
|
call_args, call_kwargs = func(function, *args, ignore_unused=True, **kwargs) # @see #20
|
|
|
|
return function(*call_args, **call_kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
def function_args(callable_, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Return (args, kwargs) matching the function signature
|
|
|
|
|
|
|
|
:param callable: callable to inspect
|
|
|
|
:type callable: callable
|
|
|
|
:param args:
|
|
|
|
:type args:
|
|
|
|
:param kwargs:
|
|
|
|
:type kwargs:
|
|
|
|
:return: (args, kwargs) matching the function signature
|
|
|
|
:rtype: tuple
|
|
|
|
"""
|
|
|
|
argspec = getargspec(callable_) # pylint:disable=deprecated-method
|
|
|
|
return argspec_args(argspec, False, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
def constructor_args(class_, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Return (args, kwargs) matching the function signature
|
|
|
|
|
|
|
|
:param callable: callable to inspect
|
|
|
|
:type callable: Callable
|
|
|
|
:param args:
|
|
|
|
:type args:
|
|
|
|
:param kwargs:
|
|
|
|
:type kwargs:
|
|
|
|
:return: (args, kwargs) matching the function signature
|
|
|
|
:rtype: tuple
|
|
|
|
"""
|
|
|
|
argspec = getargspec(_constructor(class_)) # pylint:disable=deprecated-method
|
|
|
|
return argspec_args(argspec, True, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
def argspec_args(argspec, constructor, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Return (args, kwargs) matching the argspec object
|
|
|
|
|
|
|
|
:param argspec: argspec to use
|
|
|
|
:type argspec: argspec
|
|
|
|
:param constructor: is it a constructor ?
|
|
|
|
:type constructor: bool
|
|
|
|
:param args:
|
|
|
|
:type args:
|
|
|
|
:param kwargs:
|
|
|
|
:type kwargs:
|
|
|
|
:return: (args, kwargs) matching the function signature
|
|
|
|
:rtype: tuple
|
|
|
|
"""
|
|
|
|
if argspec.varkw:
|
|
|
|
call_kwarg = kwargs
|
|
|
|
else:
|
|
|
|
call_kwarg = dict((k, kwargs[k]) for k in kwargs if k in argspec.args) # pylint:disable=consider-using-dict-items
|
|
|
|
if argspec.varargs:
|
|
|
|
call_args = args
|
|
|
|
else:
|
|
|
|
call_args = args[:len(argspec.args) - (1 if constructor else 0)]
|
|
|
|
return call_args, call_kwarg
|
|
|
|
|
|
|
|
|
|
|
|
if not _FULLARGSPEC_SUPPORTED:
|
|
|
|
def argspec_args_legacy(argspec, constructor, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Return (args, kwargs) matching the argspec object
|
|
|
|
|
|
|
|
:param argspec: argspec to use
|
|
|
|
:type argspec: argspec
|
|
|
|
:param constructor: is it a constructor ?
|
|
|
|
:type constructor: bool
|
|
|
|
:param args:
|
|
|
|
:type args:
|
|
|
|
:param kwargs:
|
|
|
|
:type kwargs:
|
|
|
|
:return: (args, kwargs) matching the function signature
|
|
|
|
:rtype: tuple
|
|
|
|
"""
|
|
|
|
if argspec.keywords:
|
|
|
|
call_kwarg = kwargs
|
|
|
|
else:
|
|
|
|
call_kwarg = dict((k, kwargs[k]) for k in kwargs if k in argspec.args) # pylint:disable=consider-using-dict-items
|
|
|
|
if argspec.varargs:
|
|
|
|
call_args = args
|
|
|
|
else:
|
|
|
|
call_args = args[:len(argspec.args) - (1 if constructor else 0)]
|
|
|
|
return call_args, call_kwarg
|
|
|
|
|
|
|
|
|
|
|
|
argspec_args = argspec_args_legacy
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_list(param):
|
|
|
|
"""
|
|
|
|
Retrieves a list from given parameter.
|
|
|
|
|
|
|
|
:param param:
|
|
|
|
:type param:
|
|
|
|
:return:
|
|
|
|
:rtype:
|
|
|
|
"""
|
|
|
|
if not param:
|
|
|
|
param = []
|
|
|
|
elif not is_iterable(param):
|
|
|
|
param = [param]
|
|
|
|
return param
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_dict(param, default_value, default_key=None):
|
|
|
|
"""
|
|
|
|
Retrieves a dict and a default value from given parameter.
|
|
|
|
|
|
|
|
if parameter is not a dict, it will be promoted as the default value.
|
|
|
|
|
|
|
|
:param param:
|
|
|
|
:type param:
|
|
|
|
:param default_value:
|
|
|
|
:type default_value:
|
|
|
|
:param default_key:
|
|
|
|
:type default_key:
|
|
|
|
:return:
|
|
|
|
:rtype:
|
|
|
|
"""
|
|
|
|
if not param:
|
|
|
|
param = default_value
|
|
|
|
if not isinstance(param, dict):
|
|
|
|
if param:
|
|
|
|
default_value = param
|
|
|
|
return {default_key: param}, default_value
|
|
|
|
return param, default_value
|
|
|
|
|
|
|
|
|
|
|
|
def filter_index(collection, predicate=None, index=None):
|
|
|
|
"""
|
|
|
|
Filter collection with predicate function and index.
|
|
|
|
|
|
|
|
If index is not found, returns None.
|
|
|
|
:param collection:
|
|
|
|
:type collection: collection supporting iteration and slicing
|
|
|
|
:param predicate: function to filter the collection with
|
|
|
|
:type predicate: function
|
|
|
|
:param index: position of a single element to retrieve
|
|
|
|
:type index: int
|
|
|
|
:return: filtered list, or single element of filtered list if index is defined
|
|
|
|
:rtype: list or object
|
|
|
|
"""
|
|
|
|
if index is None and isinstance(predicate, int):
|
|
|
|
index = predicate
|
|
|
|
predicate = None
|
|
|
|
if predicate:
|
|
|
|
collection = collection.__class__(filter(predicate, collection))
|
|
|
|
if index is not None:
|
|
|
|
try:
|
|
|
|
collection = collection[index]
|
|
|
|
except IndexError:
|
|
|
|
collection = None
|
|
|
|
return collection
|
|
|
|
|
|
|
|
|
|
|
|
def set_defaults(defaults, kwargs, override=False):
|
|
|
|
"""
|
|
|
|
Set defaults from defaults dict to kwargs dict
|
|
|
|
|
|
|
|
:param override:
|
|
|
|
:type override:
|
|
|
|
:param defaults:
|
|
|
|
:type defaults:
|
|
|
|
:param kwargs:
|
|
|
|
:type kwargs:
|
|
|
|
:return:
|
|
|
|
:rtype:
|
|
|
|
"""
|
|
|
|
if 'clear' in defaults.keys() and defaults.pop('clear'):
|
|
|
|
kwargs.clear()
|
|
|
|
for key, value in defaults.items():
|
|
|
|
if key in kwargs:
|
|
|
|
if isinstance(value, list) and isinstance(kwargs[key], list):
|
|
|
|
kwargs[key] = list(value) + kwargs[key]
|
|
|
|
elif isinstance(value, dict) and isinstance(kwargs[key], dict):
|
|
|
|
set_defaults(value, kwargs[key])
|
|
|
|
if key not in kwargs or override:
|
|
|
|
kwargs[key] = value
|