Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-injector for openSUSE:Factory 
checked in at 2022-09-29 18:13:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-injector (Old)
 and      /work/SRC/openSUSE:Factory/.python-injector.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-injector"

Thu Sep 29 18:13:53 2022 rev:9 rq:1006876 version:0.20.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-injector/python-injector.changes  
2022-01-10 23:54:23.136845479 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-injector.new.2275/python-injector.changes    
    2022-09-29 18:14:53.899436112 +0200
@@ -1,0 +2,15 @@
+Thu Sep 29 02:41:09 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com>
+
+- Update to 0.20.1
+  - Added support for PEP 604 union types (Python 3.10+), thanks to David 
P??rsson 
+  - Fixed building with pypandoc 1.8+, thanks to S??ren Fuglede J??rgensen
+
+- Update to 0.20.0
+  - Fixed handling of Union combined with Annotated, thanks to Tobias Nilsson
+  - Fixed AssitedBuilder/child Injector interaction, thanks to Erik Cederberg
+  - Made get_bindings() and injections work even if a injectee's return type
+    annotation is a forward reference that can't be resolved
+  Backwards incompatible:
+  - Dropped Python 3.6 support 
+
+-------------------------------------------------------------------

Old:
----
  0.19.0.tar.gz

New:
----
  0.20.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-injector.spec ++++++
--- /var/tmp/diff_new_pack.2Y5nQR/_old  2022-09-29 18:14:54.503437296 +0200
+++ /var/tmp/diff_new_pack.2Y5nQR/_new  2022-09-29 18:14:54.511437312 +0200
@@ -19,7 +19,7 @@
 %define skip_python2 1
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-injector
-Version:        0.19.0
+Version:        0.20.1
 Release:        0
 Summary:        Python dependency injection framework, inspired by Guice
 License:        BSD-3-Clause

++++++ 0.19.0.tar.gz -> 0.20.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/injector-0.19.0/.github/workflows/ci.yml 
new/injector-0.20.1/.github/workflows/ci.yml
--- old/injector-0.19.0/.github/workflows/ci.yml        2021-12-20 
23:18:57.000000000 +0100
+++ new/injector-0.20.1/.github/workflows/ci.yml        2022-08-17 
01:03:10.000000000 +0200
@@ -8,11 +8,11 @@
     strategy:
       matrix:
         os: [ubuntu-latest]
-        python-version: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3]
+        python-version: [3.7, 3.8, 3.9, "3.10", "pypy3.7", "pypy3.8", 
"pypy3.9"]
     steps:
     - uses: actions/checkout@v2
     - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: ${{ matrix.python-version }}
     - name: Install dependencies
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/injector-0.19.0/CHANGES new/injector-0.20.1/CHANGES
--- old/injector-0.19.0/CHANGES 2021-12-20 23:18:57.000000000 +0100
+++ new/injector-0.20.1/CHANGES 2022-08-17 01:03:10.000000000 +0200
@@ -1,6 +1,24 @@
 Injector Change Log
 ===================
 
+0.20.1
+------
+
+- Added support for PEP 604 union types (Python 3.10+), thanks to David 
P??rsson 
+- Fixed building with pypandoc 1.8+, thanks to S??ren Fuglede J??rgensen
+
+0.20.0
+------
+
+- Fixed handling of Union combined with Annotated, thanks to Tobias Nilsson
+- Fixed AssitedBuilder/child Injector interaction, thanks to Erik Cederberg
+- Made get_bindings() and injections work even if a injectee's return type
+  annotation is a forward reference that can't be resolved
+
+Backwards incompatible:
+
+- Dropped Python 3.6 support
+
 0.19.0
 ------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/injector-0.19.0/README.md 
new/injector-0.20.1/README.md
--- old/injector-0.19.0/README.md       2021-12-20 23:18:57.000000000 +0100
+++ new/injector-0.20.1/README.md       2022-08-17 01:03:10.000000000 +0200
@@ -72,7 +72,7 @@
 * Documentation: https://injector.readthedocs.org
 * Change log: https://injector.readthedocs.io/en/latest/changelog.html
 
-Injector works with CPython 3.6+ and PyPy 3 implementing Python 3.6+.
+Injector works with CPython 3.7+ and PyPy 3 implementing Python 3.7+.
 
 A Quick Example
 ---------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/injector-0.19.0/injector/__init__.py 
new/injector-0.20.1/injector/__init__.py
--- old/injector-0.19.0/injector/__init__.py    2021-12-20 23:18:57.000000000 
+0100
+++ new/injector-0.20.1/injector/__init__.py    2022-08-17 01:03:10.000000000 
+0200
@@ -46,40 +46,22 @@
 except ImportError:
     from typing_extensions import NoReturn
 
-HAVE_ANNOTATED = sys.version_info >= (3, 7, 0)
-
 # This is a messy, type-wise, because we not only have two potentially 
conflicting imports here
-# but we also define our own versions in the else block in case we operate on 
Python 3.6
-# which didn't get Annotated support in get_type_hints(). The easiest way to 
make mypy
-# happy here is to tell it the versions from typing_extensions are canonical. 
Since this
-# typing_extensions import is only for mypy it'll work even without 
typing_extensions actually
-# installed so all's good.
+# The easiest way to make mypy happy here is to tell it the versions from 
typing_extensions are
+# canonical. Since this typing_extensions import is only for mypy it'll work 
even without
+# typing_extensions actually installed so all's good.
 if TYPE_CHECKING:
     from typing_extensions import _AnnotatedAlias, Annotated, get_type_hints
-elif HAVE_ANNOTATED:
+else:
     # Ignoring errors here as typing_extensions stub doesn't know about those 
things yet
     try:
         from typing import _AnnotatedAlias, Annotated, get_type_hints
     except ImportError:
         from typing_extensions import _AnnotatedAlias, Annotated, 
get_type_hints
-else:
-
-    class Annotated:
-        pass
-
-    from typing import get_type_hints as _get_type_hints
-
-    def get_type_hints(
-        obj: Callable[..., Any],
-        globalns: Optional[Dict[str, Any]] = None,
-        localns: Optional[Dict[str, Any]] = None,
-        include_extras: bool = False,
-    ) -> Dict[str, Any]:
-        return _get_type_hints(obj, globalns, localns)
 
 
 __author__ = 'Alec Thomas <a...@swapoff.org>'
-__version__ = '0.19.0'
+__version__ = '0.20.1'
 __version_tag__ = ''
 
 log = logging.getLogger('injector')
@@ -119,86 +101,85 @@
 _inject_marker = object()
 _noinject_marker = object()
 
-if HAVE_ANNOTATED:
-    InjectT = TypeVar('InjectT')
-    Inject = Annotated[InjectT, _inject_marker]
-    """An experimental way to declare injectable dependencies utilizing a `PEP 
593`_ implementation
-    in Python 3.9 and backported to Python 3.7+ in `typing_extensions`.
+InjectT = TypeVar('InjectT')
+Inject = Annotated[InjectT, _inject_marker]
+"""An experimental way to declare injectable dependencies utilizing a `PEP 
593`_ implementation
+in Python 3.9 and backported to Python 3.7+ in `typing_extensions`.
 
-    Those two declarations are equivalent::
+Those two declarations are equivalent::
 
-        @inject
-        def fun(t: SomeType) -> None:
-            pass
+    @inject
+    def fun(t: SomeType) -> None:
+        pass
 
-        def fun(t: Inject[SomeType]) -> None:
-            pass
+    def fun(t: Inject[SomeType]) -> None:
+        pass
 
-    The advantage over using :func:`inject` is that if you have some 
noninjectable parameters
-    it may be easier to spot what are they. Those two are equivalent::
+The advantage over using :func:`inject` is that if you have some noninjectable 
parameters
+it may be easier to spot what are they. Those two are equivalent::
 
-        @inject
-        @noninjectable('s')
-        def fun(t: SomeType, s: SomeOtherType) -> None:
-            pass
+    @inject
+    @noninjectable('s')
+    def fun(t: SomeType, s: SomeOtherType) -> None:
+        pass
 
-        def fun(t: Inject[SomeType], s: SomeOtherType) -> None:
-            pass
+    def fun(t: Inject[SomeType], s: SomeOtherType) -> None:
+        pass
 
-    .. seealso::
+.. seealso::
 
-        Function :func:`get_bindings`
-            A way to inspect how various injection declarations interact with 
each other.
+    Function :func:`get_bindings`
+        A way to inspect how various injection declarations interact with each 
other.
 
-    .. versionadded:: 0.18.0
-    .. note:: Requires Python 3.7+.
-    .. note::
+.. versionadded:: 0.18.0
+.. note:: Requires Python 3.7+.
+.. note::
 
-        If you're using mypy you need the version 0.750 or newer to fully 
type-check code using this
-        construct.
+    If you're using mypy you need the version 0.750 or newer to fully 
type-check code using this
+    construct.
 
-    .. _PEP 593: https://www.python.org/dev/peps/pep-0593/
-    .. _typing_extensions: https://pypi.org/project/typing-extensions/
-    """
+.. _PEP 593: https://www.python.org/dev/peps/pep-0593/
+.. _typing_extensions: https://pypi.org/project/typing-extensions/
+"""
 
-    NoInject = Annotated[InjectT, _noinject_marker]
-    """An experimental way to declare noninjectable dependencies utilizing a 
`PEP 593`_ implementation
-    in Python 3.9 and backported to Python 3.7+ in `typing_extensions`.
+NoInject = Annotated[InjectT, _noinject_marker]
+"""An experimental way to declare noninjectable dependencies utilizing a `PEP 
593`_ implementation
+in Python 3.9 and backported to Python 3.7+ in `typing_extensions`.
 
-    Since :func:`inject` declares all function's parameters to be injectable 
there needs to be a way
-    to opt out of it. This has been provided by :func:`noninjectable` but 
`noninjectable` suffers from
-    two issues:
+Since :func:`inject` declares all function's parameters to be injectable there 
needs to be a way
+to opt out of it. This has been provided by :func:`noninjectable` but 
`noninjectable` suffers from
+two issues:
 
-    * You need to repeat the parameter name
-    * The declaration may be relatively distance in space from the actual 
parameter declaration, thus
-      hindering readability
+* You need to repeat the parameter name
+* The declaration may be relatively distance in space from the actual 
parameter declaration, thus
+  hindering readability
 
-    `NoInject` solves both of those concerns, for example (those two 
declarations are equivalent)::
+`NoInject` solves both of those concerns, for example (those two declarations 
are equivalent)::
 
-        @inject
-        @noninjectable('b')
-        def fun(a: TypeA, b: TypeB) -> None:
-            pass
+    @inject
+    @noninjectable('b')
+    def fun(a: TypeA, b: TypeB) -> None:
+        pass
 
-        @inject
-        def fun(a: TypeA, b: NoInject[TypeB]) -> None:
-            pass
+    @inject
+    def fun(a: TypeA, b: NoInject[TypeB]) -> None:
+        pass
 
-    .. seealso::
+.. seealso::
 
-        Function :func:`get_bindings`
-            A way to inspect how various injection declarations interact with 
each other.
+    Function :func:`get_bindings`
+        A way to inspect how various injection declarations interact with each 
other.
 
-    .. versionadded:: 0.18.0
-    .. note:: Requires Python 3.7+.
-    .. note::
+.. versionadded:: 0.18.0
+.. note:: Requires Python 3.7+.
+.. note::
 
-        If you're using mypy you need the version 0.750 or newer to fully 
type-check code using this
-        construct.
+    If you're using mypy you need the version 0.750 or newer to fully 
type-check code using this
+    construct.
 
-    .. _PEP 593: https://www.python.org/dev/peps/pep-0593/
-    .. _typing_extensions: https://pypi.org/project/typing-extensions/
-    """
+.. _PEP 593: https://www.python.org/dev/peps/pep-0593/
+.. _typing_extensions: https://pypi.org/project/typing-extensions/
+"""
 
 
 def reraise(original: Exception, exception: Exception, maximum_frames: int = 
1) -> NoReturn:
@@ -651,8 +632,9 @@
 
     def get_binding(self, interface: type) -> Tuple[Binding, 'Binder']:
         is_scope = isinstance(interface, type) and issubclass(interface, Scope)
+        is_assisted_builder = _is_specialization(interface, AssistedBuilder)
         try:
-            return self._get_binding(interface, only_this_binder=is_scope)
+            return self._get_binding(interface, only_this_binder=is_scope or 
is_assisted_builder)
         except (KeyError, UnsatisfiedRequirement):
             if is_scope:
                 scope = interface
@@ -683,7 +665,7 @@
     # We need to special-case Annotated as its __origin__ behaves differently 
than
     # other typing generic classes. See 
https://github.com/python/typing/pull/635
     # for some details.
-    if HAVE_ANNOTATED and generic_class is Annotated and isinstance(cls, 
_AnnotatedAlias):
+    if generic_class is Annotated and isinstance(cls, _AnnotatedAlias):
         return True
 
     if not hasattr(cls, '__origin__'):
@@ -1166,10 +1148,37 @@
     pass
 
 
+# See a comment in _infer_injected_bindings() for why this is useful.
+class _NoReturnAnnotationProxy:
+    def __init__(self, callable: Callable) -> None:
+        self.callable = callable
+
+    def __getattribute__(self, name: str) -> Any:
+        # get_type_hints() uses quite complex logic to determine the 
namespaces using which
+        # any forward references should be resolved. Instead of mirroring this 
logic here
+        # let's just take the easy way out and forward all attribute access to 
the original
+        # callable except for the annotations ??? we want to filter them.
+        callable = object.__getattribute__(self, 'callable')
+        if name == '__annotations__':
+            annotations = callable.__annotations__
+            return {name: value for (name, value) in annotations.items() if 
name != 'return'}
+        return getattr(callable, name)
+
+
 def _infer_injected_bindings(callable: Callable, only_explicit_bindings: bool) 
-> Dict[str, type]:
+    def _is_new_union_type(instance: Any) -> bool:
+        new_union_type = getattr(types, 'UnionType', None)
+        return new_union_type is not None and isinstance(instance, 
new_union_type)
+
     spec = inspect.getfullargspec(callable)
+
     try:
-        bindings = get_type_hints(callable, include_extras=True)
+        # Return types don't matter for the purpose of dependency injection so 
instead of
+        # obtaining type hints of the callable directly let's wrap it in 
_NoReturnAnnotationProxy.
+        # The proxy removes the return type annotation (if present) from the 
annotations so that
+        # get_type_hints() works even if the return type is a forward 
reference that can't be
+        # resolved.
+        bindings = get_type_hints(cast(Callable, 
_NoReturnAnnotationProxy(callable)), include_extras=True)
     except NameError as e:
         raise _BindingNotYetAvailable(e)
 
@@ -1199,7 +1208,7 @@
 
         if only_explicit_bindings and _inject_marker not in metadata or 
_noinject_marker in metadata:
             del bindings[k]
-        elif _is_specialization(v, Union):
+        elif _is_specialization(v, Union) or _is_new_union_type(v):
             # We don't treat Optional parameters in any special way at the 
moment.
             union_members = v.__args__
             new_members = tuple(set(union_members) - {type(None)})
@@ -1209,7 +1218,20 @@
             # mypy complains about this construct:
             #     error: The type alias is invalid in runtime context
             # See: https://github.com/python/mypy/issues/5354
-            bindings[k] = new_union  # type: ignore
+            union_metadata = {
+                metadata
+                for member in new_members
+                for metadata in getattr(member, '__metadata__', tuple())
+                if _is_specialization(member, Annotated)
+            }
+            if (
+                only_explicit_bindings
+                and _inject_marker not in union_metadata
+                or _noinject_marker in union_metadata
+            ):
+                del bindings[k]
+            else:
+                bindings[k] = new_union  # type: ignore
 
     return bindings
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/injector-0.19.0/injector_test.py 
new/injector-0.20.1/injector_test.py
--- old/injector-0.19.0/injector_test.py        2021-12-20 23:18:57.000000000 
+0100
+++ new/injector-0.20.1/injector_test.py        2022-08-17 01:03:10.000000000 
+0200
@@ -11,7 +11,7 @@
 """Functional tests for the "Injector" dependency injection framework."""
 
 from contextlib import contextmanager
-from typing import Any, NewType
+from typing import Any, NewType, Optional, Union
 import abc
 import sys
 import threading
@@ -25,7 +25,9 @@
 from injector import (
     Binder,
     CallError,
+    Inject,
     Injector,
+    NoInject,
     Scope,
     InstanceProvider,
     ClassProvider,
@@ -46,12 +48,8 @@
     ClassAssistedBuilder,
     Error,
     UnknownArgument,
-    HAVE_ANNOTATED,
 )
 
-if HAVE_ANNOTATED:
-    from injector import Inject, NoInject
-
 
 class EmptyClass:
     pass
@@ -807,6 +805,19 @@
     assert (b1._injector, b2._injector) == (i1, i2)
 
 
+def test_assisted_builder_injection_is_safe_to_use_with_child_injectors():
+    class X:
+        @inject
+        def __init__(self, builder: AssistedBuilder[NeedsAssistance]):
+            self.builder = builder
+
+    i1 = Injector()
+    i2 = i1.create_child_injector()
+    b1 = i1.get(X).builder
+    b2 = i2.get(X).builder
+    assert (b1._injector, b2._injector) == (i1, i2)
+
+
 class TestThreadSafety:
     def setup(self):
         self.event = threading.Event()
@@ -1449,38 +1460,75 @@
 
     assert get_bindings(function3b) == {'a': int}
 
-    if HAVE_ANNOTATED:
-        # The simple case of no @inject but injection requested with 
Inject[...]
-        def function4(a: Inject[int], b: str) -> None:
-            pass
+    # The simple case of no @inject but injection requested with Inject[...]
+    def function4(a: Inject[int], b: str) -> None:
+        pass
 
-        assert get_bindings(function4) == {'a': int}
+    assert get_bindings(function4) == {'a': int}
 
-        # Using @inject with Inject is redundant but it should not break 
anything
-        @inject
-        def function5(a: Inject[int], b: str) -> None:
-            pass
+    # Using @inject with Inject is redundant but it should not break anything
+    @inject
+    def function5(a: Inject[int], b: str) -> None:
+        pass
 
-        assert get_bindings(function5) == {'a': int, 'b': str}
+    assert get_bindings(function5) == {'a': int, 'b': str}
 
-        # We need to be able to exclude a parameter from injection with 
NoInject
-        @inject
-        def function6(a: int, b: NoInject[str]) -> None:
-            pass
+    # We need to be able to exclude a parameter from injection with NoInject
+    @inject
+    def function6(a: int, b: NoInject[str]) -> None:
+        pass
 
-        assert get_bindings(function6) == {'a': int}
+    assert get_bindings(function6) == {'a': int}
 
-        # The presence of NoInject should not trigger anything on its own
-        def function7(a: int, b: NoInject[str]) -> None:
-            pass
+    # The presence of NoInject should not trigger anything on its own
+    def function7(a: int, b: NoInject[str]) -> None:
+        pass
 
-        assert get_bindings(function7) == {}
+    assert get_bindings(function7) == {}
 
-        # There was a bug where in case of multiple NoInject-decorated 
parameters only the first one was
-        # actually made noninjectable and we tried to inject something we 
couldn't possibly provide
-        # into the second one.
-        @inject
-        def function8(a: NoInject[int], b: NoInject[int]) -> None:
-            pass
+    # There was a bug where in case of multiple NoInject-decorated parameters 
only the first one was
+    # actually made noninjectable and we tried to inject something we couldn't 
possibly provide
+    # into the second one.
+    @inject
+    def function8(a: NoInject[int], b: NoInject[int]) -> None:
+        pass
+
+    assert get_bindings(function8) == {}
+
+    # Default arguments to NoInject annotations should behave the same as 
noninjectable decorator w.r.t 'None'
+    @inject
+    @noninjectable('b')
+    def function9(self, a: int, b: Optional[str] = None):
+        pass
+
+    @inject
+    def function10(self, a: int, b: NoInject[Optional[str]] = None):
+        # b:s type is Union[NoInject[Union[str, None]], None]
+        pass
+
+    assert get_bindings(function9) == {'a': int} == get_bindings(function10)
+
+    # If there's a return type annottion that contains an a forward reference 
that can't be
+    # resolved (for whatever reason) we don't want that to break things for us 
??? return types
+    # don't matter for the purpose of dependency injection.
+    @inject
+    def function11(a: int) -> 'InvalidForwardReference':
+        pass
+
+    assert get_bindings(function11) == {'a': int}
+
+
+# Tests https://github.com/alecthomas/injector/issues/202
+@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10+")
+def test_get_bindings_for_pep_604():
+    @inject
+    def function1(a: int | None) -> None:
+        pass
+
+    assert get_bindings(function1) == {'a': int}
+
+    @inject
+    def function1(a: int | str) -> None:
+        pass
 
-        assert get_bindings(function8) == {}
+    assert get_bindings(function1) == {'a': Union[int, str]}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/injector-0.19.0/setup.py new/injector-0.20.1/setup.py
--- old/injector-0.19.0/setup.py        2021-12-20 23:18:57.000000000 +0100
+++ new/injector-0.20.1/setup.py        2022-08-17 01:03:10.000000000 +0200
@@ -49,7 +49,7 @@
 try:
     import pypandoc
 
-    long_description = pypandoc.convert('README.md', 'rst')
+    long_description = pypandoc.convert_file('README.md', 'rst')
 except ImportError:
     warnings.warn('Could not locate pandoc, using Markdown long_description.', 
ImportWarning)
     with open('README.md') as f:

Reply via email to