Hello community,
here is the log from the commit of package python-injector for openSUSE:Factory
checked in at 2019-07-26 12:39:43
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-injector (Old)
and /work/SRC/openSUSE:Factory/.python-injector.new.4126 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-injector"
Fri Jul 26 12:39:43 2019 rev:3 rq:718269 version:0.17.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-injector/python-injector.changes
2019-06-12 13:17:32.572612599 +0200
+++
/work/SRC/openSUSE:Factory/.python-injector.new.4126/python-injector.changes
2019-07-26 12:39:44.813929745 +0200
@@ -1,0 +2,15 @@
+Wed Jul 24 13:38:20 UTC 2019 - [email protected]
+
+- version update to 0.17.0
+ * Added support for using `typing.Dict` and `typing.List` in multibindings.
See :meth:`multibind <injector.Binder.multibind>`.
+ * Added multibinding-specific :func:`provider <injector.provider>` variant:
:func:`multiprovider <injector.multiprovider>`
+ * Deprecated using :func:`provider <injector.provider>` for multibindings
+ * Fixed failure to provide a default value to a `NewType`-aliased type with
auto_bind enabled
+ * Deprecated :func:`Key <injector.Key>`, :func:`SequenceKey
<injector.SequenceKey>` and
+ :func:`MappingKey <injector.MappingKey>` – use real types or type aliases
instead
+ * Deprecated using single-item lists and dictionaries for multibindings -
use real types or type aliases instead
+ Technically backwards incompatible:
+ * typing.List and typing.Dict specializations are now explicitly disallowed
as :meth:`bind <injector.Binder.bind>`
+ interfaces and types returned by :func:`provider
<injector.provider>`-decorated methods
+
+-------------------------------------------------------------------
Old:
----
0.16.2.tar.gz
New:
----
0.17.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-injector.spec ++++++
--- /var/tmp/diff_new_pack.CTJabc/_old 2019-07-26 12:39:45.281929472 +0200
+++ /var/tmp/diff_new_pack.CTJabc/_new 2019-07-26 12:39:45.285929470 +0200
@@ -19,7 +19,7 @@
%define skip_python2 1
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-injector
-Version: 0.16.2
+Version: 0.17.0
Release: 0
Summary: Python dependency injection framework, inspired by Guice
License: BSD-3-Clause
@@ -60,7 +60,7 @@
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
-%python_exec setup.py test
+%pytest
%files %{python_files}
%license COPYING
++++++ 0.16.2.tar.gz -> 0.17.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/injector-0.16.2/.gitignore
new/injector-0.17.0/.gitignore
--- old/injector-0.16.2/.gitignore 2019-05-22 10:18:55.000000000 +0200
+++ new/injector-0.17.0/.gitignore 2019-06-15 16:23:44.000000000 +0200
@@ -6,3 +6,4 @@
*,cover
.mypy_cache/
.pytest_cache/
+coverage.xml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/injector-0.16.2/.travis.yml
new/injector-0.17.0/.travis.yml
--- old/injector-0.16.2/.travis.yml 2019-05-22 10:18:55.000000000 +0200
+++ new/injector-0.17.0/.travis.yml 2019-06-15 16:23:44.000000000 +0200
@@ -6,16 +6,13 @@
- "3.6"
- "nightly"
- "pypy3.5-5.8.0"
-env:
- - TYPING_VERSION="<3.5.3"
- - TYPING_VERSION=">=3.5.3"
matrix:
allow_failures:
- python: "nightly"
include:
- { python: "3.7", dist: xenial, sudo: true }
install:
- - pip install --upgrade coveralls pytest "typing$TYPING_VERSION"
"pytest-cov>=2.5.1" dataclasses
+ - pip install --upgrade coveralls pytest "pytest-cov>=2.5.1" dataclasses
# mypy can't be installed on pypy
- if [[ "${TRAVIS_PYTHON_VERSION}" != "pypy"* ]] ; then pip install mypy ; fi
# Black is Python 3.6+-only
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/injector-0.16.2/CHANGES new/injector-0.17.0/CHANGES
--- old/injector-0.16.2/CHANGES 2019-05-22 10:18:55.000000000 +0200
+++ new/injector-0.17.0/CHANGES 2019-06-15 16:23:44.000000000 +0200
@@ -1,6 +1,22 @@
Injector Change Log
===================
+0.17.0
+------
+
+- Added support for using `typing.Dict` and `typing.List` in multibindings.
See :meth:`multibind <injector.Binder.multibind>`.
+- Added multibinding-specific :func:`provider <injector.provider>` variant:
:func:`multiprovider <injector.multiprovider>`
+- Deprecated using :func:`provider <injector.provider>` for multibindings
+- Fixed failure to provide a default value to a `NewType`-aliased type with
auto_bind enabled
+- Deprecated :func:`Key <injector.Key>`, :func:`SequenceKey
<injector.SequenceKey>` and
+ :func:`MappingKey <injector.MappingKey>` – use real types or type aliases
instead
+- Deprecated using single-item lists and dictionaries for multibindings - use
real types or type aliases instead
+
+Technically backwards incompatible:
+
+- typing.List and typing.Dict specializations are now explicitly disallowed as
:meth:`bind <injector.Binder.bind>`
+ interfaces and types returned by :func:`provider
<injector.provider>`-decorated methods
+
0.16.2
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/injector-0.16.2/README.md
new/injector-0.17.0/README.md
--- old/injector-0.16.2/README.md 2019-05-22 10:18:55.000000000 +0200
+++ new/injector-0.17.0/README.md 2019-06-15 16:23:44.000000000 +0200
@@ -105,20 +105,22 @@
```
-Next, for the sake of the example, we'll create a "configuration" annotated
type:
+Next, for the sake of the example, we'll create a configuration type:
```python
->>> Configuration = Key('configuration')
+>>> class Configuration:
+... def __init__(self, connection_string):
+... self.connection_string = connection_string
```
-Key is used to uniquely identify the configuration dictionary. Next, we bind
the configuration to the injector, using a module:
+Next, we bind the configuration to the injector, using a module:
```python
>>> def configure_for_testing(binder):
-... configuration = {'db_connection_string': ':memory:'}
+... configuration = Configuration(':memory:')
... binder.bind(Configuration, to=configuration, scope=singleton)
```
@@ -131,7 +133,7 @@
... @singleton
... @provider
... def provide_sqlite_connection(self, configuration: Configuration) ->
sqlite3.Connection:
-... conn = sqlite3.connect(configuration['db_connection_string'])
+... conn = sqlite3.connect(configuration.connection_string)
... cursor = conn.cursor()
... cursor.execute('CREATE TABLE IF NOT EXISTS data (key PRIMARY KEY,
value)')
... cursor.execute('INSERT OR REPLACE INTO data VALUES ("hello", "world")')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/injector-0.16.2/injector/__init__.py
new/injector-0.17.0/injector/__init__.py
--- old/injector-0.16.2/injector/__init__.py 2019-05-22 10:18:55.000000000
+0200
+++ new/injector-0.17.0/injector/__init__.py 2019-06-15 16:23:44.000000000
+0200
@@ -21,15 +21,29 @@
import sys
import threading
import types
+import warnings
from abc import ABCMeta, abstractmethod
from collections import namedtuple
-from typing import Any, Callable, cast, Dict, Generic, get_type_hints, List,
Tuple, Type, TypeVar, Union
+from typing import (
+ Any,
+ Callable,
+ cast,
+ Dict,
+ Generic,
+ get_type_hints,
+ List,
+ overload,
+ Tuple,
+ Type,
+ TypeVar,
+ Union,
+)
TYPING353 = hasattr(Union[str, int], '__origin__')
__author__ = 'Alec Thomas <[email protected]>'
-__version__ = '0.16.2'
+__version__ = '0.17.0'
__version_tag__ = ''
log = logging.getLogger('injector')
@@ -39,6 +53,8 @@
log.setLevel(logging.WARN)
T = TypeVar('T')
+K = TypeVar('K')
+V = TypeVar('V')
def private(something: T) -> T:
@@ -250,10 +266,20 @@
if isinstance(what, list):
if len(what) != 1:
raise Error('list bindings must have a single interface '
'element')
+ warnings.warn(
+ 'Multibinding using the %s form is deprecated, use typing.List
instead.' % (what,),
+ RuntimeWarning,
+ stacklevel=3,
+ )
what = (list, BindingKey.create(what[0]))
elif isinstance(what, dict):
if len(what) != 1:
raise Error('dictionary bindings must have a single interface
' 'key and value')
+ warnings.warn(
+ 'Multibinding using the %s form is deprecated, use typing.Dict
instead.' % (what,),
+ RuntimeWarning,
+ stacklevel=3,
+ )
what = (dict, BindingKey.create(list(what.items())[0]))
return tuple.__new__(cls, (what,))
@@ -269,6 +295,9 @@
class Binding(_BindingBase):
"""A binding from an (interface,) to a provider in a scope."""
+ def is_multibinding(self) -> bool:
+ return _get_origin(_punch_through_alias(self.interface)) in {dict,
list}
+
class Binder:
"""Bind interfaces to implementations.
@@ -293,6 +322,11 @@
def bind(self, interface, to=None, scope=None):
"""Bind an interface to an implementation.
+ `typing.List` and `typing.Dict` instances are reserved for
multibindings and trying to bind them
+ here will result in an error (use :meth:`multibind` instead)::
+
+ binder.bind(List[str], to=['hello', 'there']) # Error
+
:param interface: Interface or :func:`Key` to bind.
:param to: Instance or class to bind to, or an explicit
:class:`Provider` subclass.
@@ -300,23 +334,71 @@
"""
if type(interface) is type and issubclass(interface, (BaseMappingKey,
BaseSequenceKey)):
return self.multibind(interface, to, scope=scope)
+ if _get_origin(_punch_through_alias(interface)) in {dict, list}:
+ raise Error(
+ 'Type %s is reserved for multibindings. Use multibind instead
of bind.' % (interface,)
+ )
key = BindingKey.create(interface)
self._bindings[key] = self.create_binding(interface, to, scope)
+ @overload
+ def multibind(
+ self,
+ interface: Union['BaseSequenceKey', 'BaseMappingKey'],
+ to: Any,
+ scope: Union[Type['Scope'], 'ScopeDecorator'] = None,
+ ) -> None:
+ pass
+
+ @overload
+ def multibind(
+ self,
+ interface: Type[List[T]],
+ to: Union[List[T], Callable[..., List[T]], Provider[List[T]]],
+ scope: Union[Type['Scope'], 'ScopeDecorator'] = None,
+ ) -> None:
+ pass
+
+ @overload
+ def multibind(
+ self,
+ interface: Type[Dict[K, V]],
+ to: Union[Dict[K, V], Callable[..., Dict[K, V]], Provider[Dict[K, V]]],
+ scope: Union[Type['Scope'], 'ScopeDecorator'] = None,
+ ) -> None:
+ pass
+
def multibind(self, interface, to, scope=None):
"""Creates or extends a multi-binding.
- A multi-binding maps from a key to a sequence, where each element in
- the sequence is provided separately.
+ A multi-binding contributes values to a list or to a dictionary. For
example::
- :param interface: :func:`MappingKey` or :func:`SequenceKey` to bind to.
+ binder.multibind(List[str], to=['some', 'strings'])
+ binder.multibind(List[str], to=['other', 'strings'])
+ injector.get(List[str]) # ['some', 'strings', 'other', 'strings']
+
+ binder.multibind(Dict[str, int], to={'key': 11})
+ binder.multibind(Dict[str, int], to={'other_key': 33})
+ injector.get(Dict[str, int]) # {'key': 11, 'other_key': 33}
+
+ .. versionchanged:: 0.17.0
+ Added support for using `typing.Dict` and `typing.List` instances
as interfaces.
+ Deprecated support for `MappingKey`, `SequenceKey` and single-item
lists and
+ dictionaries as interfaces.
+
+ :param interface: :func:`MappingKey`, :func:`SequenceKey` or
typing.Dict or typing.List instance to bind to.
:param to: Instance, class to bind to, or an explicit :class:`Provider`
- subclass. Must provide a sequence.
+ subclass. Must provide a list or a dictionary, depending on
the interface.
:param scope: Optional Scope in which to bind.
"""
key = BindingKey.create(interface)
if key not in self._bindings:
- if isinstance(interface, dict) or isinstance(interface, type) and
issubclass(interface, dict):
+ if (
+ isinstance(interface, dict)
+ or isinstance(interface, type)
+ and issubclass(interface, dict)
+ or _get_origin(_punch_through_alias(interface)) is dict
+ ):
provider = MapBindProvider()
else:
provider = MultiBindProvider()
@@ -373,10 +455,8 @@
return Binding(interface, provider, scope)
def provider_for(self, interface, to=None):
- if getattr(interface, '__qualname__', '') ==
'NewType.<locals>.new_type':
- base_type = interface.__supertype__
- else:
- base_type = interface
+ base_type = _punch_through_alias(interface)
+ origin = _get_origin(base_type)
if interface is Any:
raise TypeError('Injecting Any is not supported')
@@ -411,15 +491,20 @@
(target,) = interface.__args__
builder = interface(self.injector, target)
return InstanceProvider(builder)
- elif isinstance(base_type, (tuple, type)) and interface is not Any and
isinstance(to, base_type):
+ elif (
+ origin is None
+ and isinstance(base_type, (tuple, type))
+ and interface is not Any
+ and isinstance(to, base_type)
+ or origin in {dict, list}
+ and isinstance(to, origin)
+ ):
return InstanceProvider(to)
- elif issubclass(type(interface), type) or isinstance(interface,
(tuple, list)):
+ elif issubclass(type(base_type), type) or isinstance(base_type,
(tuple, list)):
if to is not None:
return InstanceProvider(to)
- return ClassProvider(interface)
+ return ClassProvider(base_type)
- elif hasattr(interface, '__call__'):
- raise TypeError('Injecting partially applied functions is no
longer supported.')
else:
raise UnknownProvider('couldn\'t determine provider for %r to %r'
% (interface, to))
@@ -432,7 +517,7 @@
raise KeyError
- def get_binding(self, cls, key):
+ def get_binding(self, key):
is_scope = isinstance(key.interface, type) and
issubclass(key.interface, Scope)
try:
return self._get_binding(key, only_this_binder=is_scope)
@@ -448,7 +533,7 @@
self._bindings[key] = binding
return binding, self
- raise UnsatisfiedRequirement(cls, key)
+ raise UnsatisfiedRequirement(key)
def _is_special_interface(self, interface):
# "Special" interfaces are ones that you cannot bind yourself but
@@ -483,6 +568,23 @@
return isinstance(cls, type) and cls is not Any and issubclass(cls,
generic_class)
+def _punch_through_alias(type_: Any) -> type:
+ if getattr(type_, '__qualname__', '') == 'NewType.<locals>.new_type':
+ return type_.__supertype__
+ else:
+ return type_
+
+
+def _get_origin(type_: type) -> type:
+ origin = getattr(type_, '__origin__', None)
+ # Older typing behaves differently there and stores Dict and List as
origin, we need to be flexible.
+ if origin is List:
+ return list
+ elif origin is Dict:
+ return dict
+ return origin
+
+
class Scope:
"""A Scope looks up the Provider for a binding.
@@ -598,7 +700,8 @@
binding = None
if hasattr(function, '__binding__'):
binding = function.__binding__
- binder.bind(
+ bind_method = binder.multibind if binding.is_multibinding()
else binder.bind
+ bind_method(
binding.interface, to=types.MethodType(binding.provider,
self), scope=binding.scope
)
self.configure(binder)
@@ -684,13 +787,13 @@
:returns: An implementation of interface.
"""
key = BindingKey.create(interface)
- binding, binder = self.binder.get_binding(None, key)
+ binding, binder = self.binder.get_binding(key)
scope = scope or binding.scope
if isinstance(scope, ScopeDecorator):
scope = scope.scope
# Fetch the corresponding Scope instance from the Binder.
scope_key = BindingKey.create(scope)
- scope_binding, _ = binder.get_binding(None, scope_key)
+ scope_binding, _ = binder.get_binding(scope_key)
scope_instance = scope_binding.provider.get(self)
log.debug(
@@ -892,11 +995,42 @@
>>> injector.get(str)
'654'
"""
+ _mark_provider_function(function, allow_multi=False)
+ return function
+
+
+def multiprovider(function: CallableT) -> CallableT:
+ """Like :func:`provider`, but for multibindings. Example usage::
+
+ class MyModule(Module):
+ @multiprovider
+ def provide_strs(self) -> List[str]:
+ return ['str1']
+
+ class OtherModule(Module):
+ @multiprovider
+ def provide_strs_also(self) -> List[str]:
+ return ['str2']
+
+ Injector([MyModule, OtherModule]).get(List[str]) # ['str1', 'str2']
+
+ See also: :meth:`Binder.multibind`."""
+ _mark_provider_function(function, allow_multi=True)
+ return function
+
+
+def _mark_provider_function(function: Callable, *, allow_multi: bool) -> None:
scope_ = getattr(function, '__scope__', None)
annotations = inspect.getfullargspec(function).annotations
return_type = annotations['return']
- function.__binding__ = Binding(return_type, inject(function), scope_)
- return function
+ origin = _get_origin(_punch_through_alias(return_type))
+ if origin in {dict, list} and not allow_multi:
+ raise Error(
+ 'Function %s needs to be decorated with multiprovider instead of
provider if it is to '
+ 'provide values to a multibinding of type %s' %
(function.__name__, return_type)
+ )
+ binding = Binding(return_type, inject(function), scope_)
+ function.__binding__ = binding # type: ignore
def inject(constructor_or_class):
@@ -1030,12 +1164,16 @@
def Key(name: str) -> BaseKey:
"""Create a new type key.
+ .. versionchanged:: 0.17.0
+ Deprecated, use `typing.NewType` with a real type or subclass a real
type instead.
+
>>> Age = Key('Age')
>>> def configure(binder):
... binder.bind(Age, to=90)
>>> Injector(configure).get(Age)
90
"""
+ warnings.warn('Key is deprecated, use a real type instead',
RuntimeWarning, stacklevel=3)
return cast(BaseKey, type(name, (BaseKey,), {}))
@@ -1051,7 +1189,12 @@
def MappingKey(name: str) -> BaseMappingKey:
- """As for Key, but declares a multibind mapping."""
+ """As for Key, but declares a multibind mapping.
+
+ .. versionchanged:: 0.17.0
+ Deprecated, use `typing.Dict` instance instead.
+ """
+ warnings.warn('SequenceKey is deprecated, use typing.Dict instead',
RuntimeWarning, stacklevel=3)
return cast(BaseMappingKey, type(name, (BaseMappingKey,), {}))
@@ -1067,7 +1210,12 @@
def SequenceKey(name: str) -> BaseSequenceKey:
- """As for Key, but declares a multibind sequence."""
+ """As for Key, but declares a multibind sequence.
+
+ .. versionchanged:: 0.17.0
+ Deprecated, use `typing.List` instance instead.
+ """
+ warnings.warn('SequenceKey is deprecated, use typing.List instead',
RuntimeWarning, stacklevel=3)
return cast(BaseSequenceKey, type(name, (BaseSequenceKey,), {}))
@@ -1106,7 +1254,7 @@
def build(self, **kwargs: Any) -> T:
key = BindingKey.create(self._target)
binder = self._injector.binder
- binding, _ = binder.get_binding(None, key)
+ binding, _ = binder.get_binding(key)
provider = binding.provider
if not isinstance(provider, ClassProvider):
raise Error(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/injector-0.16.2/injector_test.py
new/injector-0.17.0/injector_test.py
--- old/injector-0.16.2/injector_test.py 2019-05-22 10:18:55.000000000
+0200
+++ new/injector-0.17.0/injector_test.py 2019-06-15 16:23:44.000000000
+0200
@@ -18,6 +18,8 @@
import traceback
import warnings
+from typing import Dict, List, NewType
+
import pytest
from injector import (
@@ -28,6 +30,7 @@
InstanceProvider,
ClassProvider,
inject,
+ multiprovider,
noninjectable,
singleton,
threadlocal,
@@ -522,7 +525,7 @@
assert Injector(MyModule()).get(str) == 'Bob is 25 and weighs 50.0kg'
-def test_multibind():
+def test_multibind_old():
Names = Key('names')
def configure_one(binder):
@@ -534,6 +537,103 @@
assert Injector([configure_one, configure_two]).get(Names) == ['Bob',
'Tom']
+def test_multibind():
+ Names = NewType('Names', List[str])
+ Passwords = NewType('Ages', Dict[str, str])
+
+ # First let's have some explicit multibindings
+ def configure(binder):
+ binder.multibind(List[str], to=['not a name'])
+ binder.multibind(Dict[str, str], to={'asd': 'qwe'})
+ # To make sure Lists and Dicts of different subtypes are treated
distinctly
+ binder.multibind(List[int], to=[1, 2, 3])
+ binder.multibind(Dict[str, int], to={'weight': 12})
+ # To see that NewTypes are treated distinctly
+ binder.multibind(Names, to=['Bob'])
+ binder.multibind(Passwords, to={'Bob': 'password1'})
+
+ # Then @multiprovider-decorated Module methods
+ class CustomModule(Module):
+ @multiprovider
+ def provide_some_ints(self) -> List[int]:
+ return [4, 5, 6]
+
+ @multiprovider
+ def provide_some_strs(self) -> List[str]:
+ return ['not a name either']
+
+ @multiprovider
+ def provide_str_to_str_mapping(self) -> Dict[str, str]:
+ return {'xxx': 'yyy'}
+
+ @multiprovider
+ def provide_str_to_int_mapping(self) -> Dict[str, int]:
+ return {'height': 33}
+
+ @multiprovider
+ def provide_names(self) -> Names:
+ return ['Alice', 'Clarice']
+
+ @multiprovider
+ def provide_passwords(self) -> Passwords:
+ return {'Alice': 'aojrioeg3', 'Clarice': 'clarice30'}
+
+ injector = Injector([configure, CustomModule])
+ assert injector.get(List[str]) == ['not a name', 'not a name either']
+ assert injector.get(List[int]) == [1, 2, 3, 4, 5, 6]
+ assert injector.get(Dict[str, str]) == {'asd': 'qwe', 'xxx': 'yyy'}
+ assert injector.get(Dict[str, int]) == {'weight': 12, 'height': 33}
+ assert injector.get(Names) == ['Bob', 'Alice', 'Clarice']
+ assert injector.get(Passwords) == {'Bob': 'password1', 'Alice':
'aojrioeg3', 'Clarice': 'clarice30'}
+
+
+def test_regular_bind_and_provider_dont_work_with_multibind():
+ # We only want multibind and multiprovider to work to avoid confusion
+
+ Names = NewType('Names', List[str])
+ Passwords = NewType('Passwords', Dict[str, str])
+
+ class MyModule(Module):
+ with pytest.raises(Error):
+
+ @provider
+ def provide_strs(self) -> List[str]:
+ return []
+
+ with pytest.raises(Error):
+
+ @provider
+ def provide_names(self) -> Names:
+ return []
+
+ with pytest.raises(Error):
+
+ @provider
+ def provide_strs_in_dict(self) -> Dict[str, str]:
+ return {}
+
+ with pytest.raises(Error):
+
+ @provider
+ def provide_passwords(self) -> Passwords:
+ return {}
+
+ injector = Injector()
+ binder = injector.binder
+
+ with pytest.raises(Error):
+ binder.bind(List[str], to=[])
+
+ with pytest.raises(Error):
+ binder.bind(Names, to=[])
+
+ with pytest.raises(Error):
+ binder.bind(Dict[str, str], to={})
+
+ with pytest.raises(Error):
+ binder.bind(Passwords, to={})
+
+
def test_provider_sequence_decorator():
Names = SequenceKey('names')
@@ -557,6 +657,16 @@
assert isinstance(injector.get(A), A)
+def test_auto_bind_with_newtype():
+ # Reported in https://github.com/alecthomas/injector/issues/117
+ class A:
+ pass
+
+ AliasOfA = NewType('AliasOfA', A)
+ injector = Injector()
+ assert isinstance(injector.get(AliasOfA), A)
+
+
def test_custom_scope():
class RequestScope(Scope):
def configure(self):