From 08b7ce953f77167953aaadc12fd1e7cb4ff9ad4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl>
Date: Sun, 1 Jan 2023 21:35:15 +0100
Subject: [PATCH] did some work on overloading and typing

---
 docs/coding/functions.rst     |  8 ++++++
 satella/__init__.py           |  2 +-
 satella/coding/__init__.py    |  6 ++--
 satella/coding/overloading.py | 52 +++++++++++++++++++++++++++++++----
 4 files changed, 60 insertions(+), 8 deletions(-)

diff --git a/docs/coding/functions.rst b/docs/coding/functions.rst
index a9299340..97d4a0a8 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 32815b25..1c24e333 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 72369906..9752da5b 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 8270de48..d52567be 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.
-- 
GitLab