diff --git a/docs/coding/functions.rst b/docs/coding/functions.rst index a929934062f5d55757d6e714f87cf04da7ce9e93..97d4a0a8dd4585f23dd8606920b5eee073a95fae 100644 --- a/docs/coding/functions.rst +++ b/docs/coding/functions.rst @@ -69,6 +69,14 @@ Function overloading .. autoclass:: satella.coding.overload :members: +.. autofunction:: satella.coding.is_signature_a_more_generic_than_b + + +.. autofunction:: satella.coding.is_type_a_more_generic_than_b + + +.. autofunction:: satella.coding.extract_type_signature_from + DocsFromParent -------------- diff --git a/satella/__init__.py b/satella/__init__.py index 32815b254730f32de279dbc62335d4a5877b39aa..1c24e3331d9c331f3786944f0d8c003c36aa6c6a 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.23.1a1' +__version__ = '2.23.1b1' diff --git a/satella/coding/__init__.py b/satella/coding/__init__.py index 72369906bb3573798f10f2a4e46f1d937e6bf79d..9752da5be0a9fcef608046b273b8c8b21582e1e8 100644 --- a/satella/coding/__init__.py +++ b/satella/coding/__init__.py @@ -18,7 +18,8 @@ from .misc import update_if_not_none, update_key_if_none, update_attr_if_none, q get_arguments, call_with_arguments, chain_callables, Closeable, contains, \ enum_value, length from .environment import Context -from .overloading import overload, class_or_instancemethod +from .overloading import overload, class_or_instancemethod, is_signature_a_more_generic_than_b, \ + is_type_a_more_generic_than_b, extract_type_signature_from from .recast_exceptions import rethrow_as, silence_excs, catch_exception, log_exceptions, \ raises_exception, reraise_as from .expect_exception import expect_exception @@ -29,7 +30,8 @@ __all__ = [ 'assert_equal', 'InequalityReason', 'Inequal', 'Closeable', 'contains', 'enum_value', 'reraise_as', 'expect_exception', - 'overload', 'class_or_instancemethod', + 'overload', 'class_or_instancemethod', 'extract_type_signature_from', 'is_signature_a_more_generic_than_b', + 'is_type_a_more_generic_than_b', 'update_if_not_none', 'DocsFromParent', 'update_key_if_none', 'queue_iterator', 'update_attr_if_none', 'update_key_if_not_none', 'source_to_function', 'update_key_if_true', diff --git a/satella/coding/overloading.py b/satella/coding/overloading.py index 8270de48803d56eab6fe99ccac20020a7109b7bc..d52567bed94b500a7fa4d08ad6f57040bdd1cb89 100644 --- a/satella/coding/overloading.py +++ b/satella/coding/overloading.py @@ -3,16 +3,27 @@ import typing as tp from inspect import Parameter -def extract_type_signature_from(fun: tp.Callable) -> tp.Tuple[type, ...]: - sign = [] +def extract_type_signature_from(fun: tp.Callable) -> tp.Dict[str, type]: + """ + Extract type signature of a function + :param fun: function to extract signature from + :return: a dict, having all parameters normally passed to the function + """ + sign = {} params = inspect.signature(fun).parameters for parameter in params.values(): if parameter.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD): if parameter.annotation == Parameter.empty: - sign.append(None) + sign[parameter.name] = None + else: + sign[parameter.name] = parameter.annotation + elif parameter.kind in (Parameter.KEYWORD_ONLY, Parameter.VAR_KEYWORD): + if parameter.annotation == Parameter.empty: + annot = None else: - sign.append(parameter.annotation) - return tuple(sign) + annot = parameter.annotation + sign[parameter.name] = (parameter.kind == Parameter.KEYWORD_ONLY), annot + return sign # Taken from https://stackoverflow.com/questions/28237955/same-name-for-classmethod-and- @@ -38,6 +49,37 @@ class class_or_instancemethod(classmethod): return descr_get(instance, type_) +def is_type_a_more_generic_than_b(a: tp.Dict[str, tp.Type], b: tp.Dict[str, tp.Type]) -> bool: + """ + Can it be said that type a is more generic than b + :param a: type extracted from a with :func:`~satella.coding.overloading. + :param b: + :return: + """ + if a is None: + return True + for key in a: + key = b.get(key, None) + + a_ = a[key] + b_ = b[key] + if isinstance(a_, tuple): + if not isinstance(b_, tuple): + raise TypeError('Type mismatch %s to %s' % (a_, b_)) + if issubclass(b_, a_): + return True + return False + + +def is_signature_a_more_generic_than_b(a: tp.Tuple[{}, [], []], b) -> bool: + """ + + :param a: + @param b: + :return: is A more generic than B + """ + + class overload: """ A class used for method overloading.