Format the code using Black
This is in order to keep the formatting consistent in the future.
This commit is contained in:
parent
6358f5d304
commit
0d1b4a4798
135
docs/conf.py
135
docs/conf.py
|
@ -16,12 +16,12 @@ import injector
|
|||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
|
@ -34,7 +34,7 @@ templates_path = ['_templates']
|
|||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
@ -54,40 +54,40 @@ release = version
|
|||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
# keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
@ -99,26 +99,26 @@ html_theme = 'default'
|
|||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
|
@ -127,48 +127,47 @@ html_static_path = ['_static']
|
|||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'index': ('sidebar.html', 'sourcelink.html', 'searchbox.html'),
|
||||
'**' : ('localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.html'),
|
||||
'index': ('sidebar.html', 'sourcelink.html', 'searchbox.html'),
|
||||
'**': ('localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'),
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Injectordoc'
|
||||
|
@ -177,55 +176,47 @@ htmlhelp_basename = 'Injectordoc'
|
|||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Injector.tex', u'Injector Documentation',
|
||||
u'Alec Thomas', 'manual'),
|
||||
]
|
||||
latex_documents = [('index', 'Injector.tex', u'Injector Documentation', u'Alec Thomas', 'manual')]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'injector', u'Injector Documentation',
|
||||
[u'Alec Thomas'], 1)
|
||||
]
|
||||
man_pages = [('index', 'injector', u'Injector Documentation', [u'Alec Thomas'], 1)]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
@ -234,22 +225,28 @@ man_pages = [
|
|||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Injector', u'Injector Documentation',
|
||||
u'Alec Thomas', 'Injector', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
'index',
|
||||
'Injector',
|
||||
u'Injector Documentation',
|
||||
u'Alec Thomas',
|
||||
'Injector',
|
||||
'One line description of project.',
|
||||
'Miscellaneous',
|
||||
)
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
|
@ -257,13 +254,13 @@ intersphinx_mapping = {'http://docs.python.org/': None}
|
|||
|
||||
|
||||
def setup(app):
|
||||
app.connect('autodoc-skip-member', skip_member)
|
||||
app.connect('autodoc-skip-member', skip_member)
|
||||
|
||||
|
||||
def skip_member(app, what, name, obj, skip, options):
|
||||
return (
|
||||
skip or
|
||||
getattr(obj, '__doc__', None) is None or
|
||||
getattr(obj, '__private__', False) is True or
|
||||
getattr(getattr(obj, '__func__', None), '__private__', False) is True
|
||||
)
|
||||
return (
|
||||
skip
|
||||
or getattr(obj, '__doc__', None) is None
|
||||
or getattr(obj, '__private__', False) is True
|
||||
or getattr(getattr(obj, '__func__', None), '__private__', False) is True
|
||||
)
|
||||
|
|
|
@ -53,9 +53,12 @@ def synchronized(lock):
|
|||
def wrapper(*args, **kwargs):
|
||||
with lock:
|
||||
return function(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return outside_wrapper
|
||||
|
||||
|
||||
lock = threading.RLock()
|
||||
|
||||
|
||||
|
@ -76,8 +79,7 @@ class UnsatisfiedRequirement(Error):
|
|||
|
||||
def __str__(self):
|
||||
on = '%s has an ' % _describe(self.args[0]) if self.args[0] else ''
|
||||
return '%sunsatisfied requirement on %s' % (
|
||||
on, _describe(self.args[1].interface),)
|
||||
return '%sunsatisfied requirement on %s' % (on, _describe(self.args[1].interface))
|
||||
|
||||
|
||||
class CallError(Error):
|
||||
|
@ -98,12 +100,17 @@ class CallError(Error):
|
|||
|
||||
full_method = '.'.join((cls, method_name)).strip('.')
|
||||
|
||||
parameters = ', '.join(itertools.chain(
|
||||
(repr(arg) for arg in args),
|
||||
('%s=%r' % (key, value) for (key, value) in kwargs.items())
|
||||
))
|
||||
parameters = ', '.join(
|
||||
itertools.chain(
|
||||
(repr(arg) for arg in args), ('%s=%r' % (key, value) for (key, value) in kwargs.items())
|
||||
)
|
||||
)
|
||||
return 'Call to %s(%s) failed: %s (injection stack: %r)' % (
|
||||
full_method, parameters, original_error, [level[0] for level in stack])
|
||||
full_method,
|
||||
parameters,
|
||||
original_error,
|
||||
[level[0] for level in stack],
|
||||
)
|
||||
|
||||
|
||||
class CircularDependency(Error):
|
||||
|
@ -197,7 +204,6 @@ class InstanceProvider(Provider):
|
|||
return '%s(%r)' % (type(self).__name__, self._instance)
|
||||
|
||||
|
||||
|
||||
@private
|
||||
class ListOfProviders(Provider):
|
||||
"""Provide a list of instances via other Providers."""
|
||||
|
@ -241,13 +247,11 @@ class BindingKey(tuple):
|
|||
def create(cls, what: Any) -> 'BindingKey':
|
||||
if isinstance(what, list):
|
||||
if len(what) != 1:
|
||||
raise Error('list bindings must have a single interface '
|
||||
'element')
|
||||
raise Error('list bindings must have a single interface ' 'element')
|
||||
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')
|
||||
raise Error('dictionary bindings must have a single interface ' 'key and value')
|
||||
what = (dict, BindingKey.create(list(what.items())[0]))
|
||||
return tuple.__new__(cls, (what,))
|
||||
|
||||
|
@ -295,8 +299,7 @@ class Binder:
|
|||
if type(interface) is type and issubclass(interface, (BaseMappingKey, BaseSequenceKey)):
|
||||
return self.multibind(interface, to, scope=scope)
|
||||
key = BindingKey.create(interface)
|
||||
self._bindings[key] = \
|
||||
self.create_binding(interface, to, scope)
|
||||
self._bindings[key] = self.create_binding(interface, to, scope)
|
||||
|
||||
def bind_scope(self, scope):
|
||||
"""Bind a Scope.
|
||||
|
@ -326,9 +329,7 @@ class Binder:
|
|||
"""
|
||||
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):
|
||||
provider = MapBindProvider()
|
||||
else:
|
||||
provider = MultiBindProvider()
|
||||
|
@ -372,17 +373,18 @@ class Binder:
|
|||
binder.install(MyModule)
|
||||
"""
|
||||
if (
|
||||
hasattr(module, '__bindings__') or
|
||||
hasattr(getattr(module, 'configure', None), '__bindings__') or
|
||||
hasattr(getattr(module, '__init__', None), '__bindings__')
|
||||
hasattr(module, '__bindings__')
|
||||
or hasattr(getattr(module, 'configure', None), '__bindings__')
|
||||
or hasattr(getattr(module, '__init__', None), '__bindings__')
|
||||
):
|
||||
warnings.warn(
|
||||
'Injector Modules (ie. %s) constructors and their configure methods should '
|
||||
'not have injectable parameters. This can result in non-deterministic '
|
||||
'initialization. If you need injectable objects in order to provide something '
|
||||
' you can use @provider-decorated methods. \n'
|
||||
' This feature will be removed in next Injector release.' %
|
||||
module.__name__ if hasattr(module, '__name__') else module.__class__.__name__,
|
||||
' This feature will be removed in next Injector release.' % module.__name__
|
||||
if hasattr(module, '__name__')
|
||||
else module.__class__.__name__,
|
||||
RuntimeWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
@ -390,11 +392,7 @@ class Binder:
|
|||
instance = self.injector.create_object(module)
|
||||
instance(self)
|
||||
else:
|
||||
self.injector.call_with_injection(
|
||||
callable=module,
|
||||
self_=None,
|
||||
args=(self,),
|
||||
)
|
||||
self.injector.call_with_injection(callable=module, self_=None, args=(self,))
|
||||
|
||||
def create_binding(self, interface, to=None, scope=None):
|
||||
provider = self.provider_for(interface, to)
|
||||
|
@ -420,13 +418,21 @@ class Binder:
|
|||
return to
|
||||
elif isinstance(interface, Provider):
|
||||
return interface
|
||||
elif isinstance(to, (types.FunctionType, types.LambdaType,
|
||||
types.MethodType, types.BuiltinFunctionType,
|
||||
types.BuiltinMethodType)):
|
||||
elif isinstance(
|
||||
to,
|
||||
(
|
||||
types.FunctionType,
|
||||
types.LambdaType,
|
||||
types.MethodType,
|
||||
types.BuiltinFunctionType,
|
||||
types.BuiltinMethodType,
|
||||
),
|
||||
):
|
||||
return CallableProvider(to)
|
||||
elif issubclass(type(to), type):
|
||||
return ClassProvider(to)
|
||||
elif isinstance(interface, BoundKey):
|
||||
|
||||
def proxy(**kwargs):
|
||||
return interface.interface(**kwargs)
|
||||
|
||||
|
@ -436,11 +442,7 @@ class Binder:
|
|||
(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 isinstance(base_type, (tuple, type)) and interface is not Any and isinstance(to, base_type):
|
||||
return InstanceProvider(to)
|
||||
elif issubclass(type(interface), type) or isinstance(interface, (tuple, list)):
|
||||
if to is not None:
|
||||
|
@ -450,8 +452,7 @@ class Binder:
|
|||
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))
|
||||
raise UnknownProvider('couldn\'t determine provider for %r to %r' % (interface, to))
|
||||
|
||||
def _get_binding(self, key, *, only_this_binder: bool = False):
|
||||
binding = self._bindings.get(key)
|
||||
|
@ -484,13 +485,11 @@ class Binder:
|
|||
# "Special" interfaces are ones that you cannot bind yourself but
|
||||
# you can request them (for example you cannot bind ProviderOf(SomeClass)
|
||||
# to anything but you can inject ProviderOf(SomeClass) just fine
|
||||
return any(
|
||||
_is_specialization(interface, cls)
|
||||
for cls in [AssistedBuilder, ProviderOf]
|
||||
)
|
||||
return any(_is_specialization(interface, cls) for cls in [AssistedBuilder, ProviderOf])
|
||||
|
||||
|
||||
if TYPING353:
|
||||
|
||||
def _is_specialization(cls, generic_class):
|
||||
# Starting with typing 3.5.3/Python 3.6 it is no longer necessarily true that
|
||||
# issubclass(SomeGeneric[X], SomeGeneric) so we need some other way to
|
||||
|
@ -508,6 +507,7 @@ if TYPING353:
|
|||
# by design).
|
||||
return origin is generic_class or issubclass(origin, generic_class)
|
||||
|
||||
|
||||
else:
|
||||
# To maintain compatibility we fall back to an issubclass check.
|
||||
def _is_specialization(cls, generic_class):
|
||||
|
@ -549,9 +549,7 @@ class ScopeDecorator:
|
|||
cls.__scope__ = self.scope
|
||||
binding = getattr(cls, '__binding__', None)
|
||||
if binding:
|
||||
new_binding = Binding(interface=binding.interface,
|
||||
provider=binding.provider,
|
||||
scope=self.scope)
|
||||
new_binding = Binding(interface=binding.interface, provider=binding.provider, scope=self.scope)
|
||||
setattr(cls, '__binding__', new_binding)
|
||||
return cls
|
||||
|
||||
|
@ -561,6 +559,7 @@ class ScopeDecorator:
|
|||
|
||||
class NoScope(Scope):
|
||||
"""An unscoped provider."""
|
||||
|
||||
def __init__(self, injector=None):
|
||||
super(NoScope, self).__init__(injector)
|
||||
|
||||
|
@ -585,6 +584,7 @@ class SingletonScope(Scope):
|
|||
>>> a is b
|
||||
True
|
||||
"""
|
||||
|
||||
def configure(self):
|
||||
self._context = {}
|
||||
|
||||
|
@ -603,6 +603,7 @@ singleton = ScopeDecorator(SingletonScope)
|
|||
|
||||
class ThreadLocalScope(Scope):
|
||||
"""A :class:`Scope` that returns a per-thread instance for a key."""
|
||||
|
||||
def configure(self):
|
||||
self._locals = threading.local()
|
||||
|
||||
|
@ -629,9 +630,7 @@ class Module:
|
|||
if hasattr(function, '__binding__'):
|
||||
binding = function.__binding__
|
||||
binder.bind(
|
||||
binding.interface,
|
||||
to=types.MethodType(binding.provider, self),
|
||||
scope=binding.scope,
|
||||
binding.interface, to=types.MethodType(binding.provider, self), scope=binding.scope
|
||||
)
|
||||
self.configure(binder)
|
||||
|
||||
|
@ -725,8 +724,9 @@ class Injector:
|
|||
scope_binding, _ = binder.get_binding(None, scope_key)
|
||||
scope_instance = scope_binding.provider.get(self)
|
||||
|
||||
log.debug('%sInjector.get(%r, scope=%r) using %r',
|
||||
self._log_prefix, interface, scope, binding.provider)
|
||||
log.debug(
|
||||
'%sInjector.get(%r, scope=%r) using %r', self._log_prefix, interface, scope, binding.provider
|
||||
)
|
||||
result = scope_instance.get(key, binding.provider).get(self)
|
||||
log.debug('%s -> %r', self._log_prefix, result)
|
||||
return result
|
||||
|
@ -742,19 +742,21 @@ class Injector:
|
|||
try:
|
||||
instance = cls.__new__(cls)
|
||||
except TypeError as e:
|
||||
reraise(e, CallError(
|
||||
cls,
|
||||
getattr(cls.__new__, '__func__', cls.__new__),
|
||||
(), {}, e, self._stack,),
|
||||
maximum_frames=2)
|
||||
reraise(
|
||||
e,
|
||||
CallError(cls, getattr(cls.__new__, '__func__', cls.__new__), (), {}, e, self._stack),
|
||||
maximum_frames=2,
|
||||
)
|
||||
try:
|
||||
self.install_into(instance, _internal = True)
|
||||
self.install_into(instance, _internal=True)
|
||||
installed = True
|
||||
except AttributeError:
|
||||
installed = False
|
||||
if hasattr(instance, '__slots__'):
|
||||
raise Error('Can\'t create an instance of type %r due to presence of __slots__, '
|
||||
'remove __slots__ to fix that' % (cls,))
|
||||
raise Error(
|
||||
'Can\'t create an instance of type %r due to presence of __slots__, '
|
||||
'remove __slots__ to fix that' % (cls,)
|
||||
)
|
||||
|
||||
# Else do nothing - some builtin types can not be modified.
|
||||
try:
|
||||
|
@ -764,17 +766,23 @@ class Injector:
|
|||
except TypeError as e:
|
||||
# The reason why getattr() fallback is used here is that
|
||||
# __init__.__func__ apparently doesn't exist for Key-type objects
|
||||
reraise(e, CallError(
|
||||
instance,
|
||||
getattr(instance.__init__, '__func__', instance.__init__),
|
||||
(), additional_kwargs, e, self._stack,)
|
||||
reraise(
|
||||
e,
|
||||
CallError(
|
||||
instance,
|
||||
getattr(instance.__init__, '__func__', instance.__init__),
|
||||
(),
|
||||
additional_kwargs,
|
||||
e,
|
||||
self._stack,
|
||||
),
|
||||
)
|
||||
return instance
|
||||
finally:
|
||||
if installed:
|
||||
self._uninstall_from(instance)
|
||||
|
||||
def install_into(self, instance, *, _internal = False):
|
||||
def install_into(self, instance, *, _internal=False):
|
||||
"""Put injector reference in given object.
|
||||
|
||||
This method has, in general, two applications:
|
||||
|
@ -836,6 +844,7 @@ class Injector:
|
|||
:type kwargs: dict of string -> object
|
||||
:return: Value returned by callable.
|
||||
"""
|
||||
|
||||
def _get_callable_bindings(callable):
|
||||
if not hasattr(callable, '__bindings__'):
|
||||
return {}
|
||||
|
@ -847,23 +856,18 @@ class Injector:
|
|||
|
||||
bindings = _get_callable_bindings(callable)
|
||||
noninjectables = getattr(callable, '__noninjectables__', set())
|
||||
needed = dict(
|
||||
(k, v) for (k, v) in bindings.items()
|
||||
if k not in kwargs and k not in noninjectables
|
||||
)
|
||||
needed = dict((k, v) for (k, v) in bindings.items() if k not in kwargs and k not in noninjectables)
|
||||
|
||||
dependencies = self.args_to_inject(
|
||||
function=callable,
|
||||
bindings=needed,
|
||||
owner_key=self_.__class__ if self_ is not None else callable.__module__,
|
||||
)
|
||||
)
|
||||
|
||||
dependencies.update(kwargs)
|
||||
|
||||
try:
|
||||
return callable(
|
||||
*((self_,) if self_ is not None else ()) + tuple(args),
|
||||
**dependencies)
|
||||
return callable(*((self_,) if self_ is not None else ()) + tuple(args), **dependencies)
|
||||
except TypeError as e:
|
||||
reraise(e, CallError(self_, callable, args, dependencies, e, self._stack))
|
||||
|
||||
|
@ -890,8 +894,8 @@ class Injector:
|
|||
|
||||
if key in self._stack:
|
||||
raise CircularDependency(
|
||||
'circular dependency detected: %s -> %s' %
|
||||
(' -> '.join(map(repr_key, self._stack)), repr_key(key))
|
||||
'circular dependency detected: %s -> %s'
|
||||
% (' -> '.join(map(repr_key, self._stack)), repr_key(key))
|
||||
)
|
||||
|
||||
self._stack += (key,)
|
||||
|
@ -964,7 +968,7 @@ def with_injector(*injector_args, **injector_kwargs):
|
|||
@functools.wraps(f)
|
||||
def setup(self_, *args, **kwargs):
|
||||
injector = Injector(*injector_args, **injector_kwargs)
|
||||
injector.install_into(self_, _internal = True)
|
||||
injector.install_into(self_, _internal=True)
|
||||
return f(self_, *args, **kwargs)
|
||||
|
||||
return setup
|
||||
|
@ -1039,13 +1043,12 @@ def inject(function=None, **bindings):
|
|||
raise AssertionError(
|
||||
'Passing keyword arguments to inject is no longer supported. '
|
||||
'Use inject in combination with parameter annotations to declare dependencies. '
|
||||
'See documentation for details',
|
||||
'See documentation for details'
|
||||
)
|
||||
|
||||
if not function:
|
||||
raise AssertionError(
|
||||
'No function being decorated, make sure you decorate your function with '
|
||||
'@inject, not @inject()',
|
||||
'No function being decorated, make sure you decorate your function with ' '@inject, not @inject()'
|
||||
)
|
||||
|
||||
try:
|
||||
|
@ -1078,23 +1081,25 @@ def noninjectable(*args):
|
|||
:func:`inject` and :func:`noninjectable`
|
||||
doesn't matter.
|
||||
"""
|
||||
|
||||
def decorator(function):
|
||||
argspec = inspect.getfullargspec(inspect.unwrap(function))
|
||||
for arg in args:
|
||||
if arg not in argspec.args and arg not in argspec.kwonlyargs:
|
||||
raise UnknownArgument('Unable to mark unknown argument %s '
|
||||
'as non-injectable.' % arg)
|
||||
raise UnknownArgument('Unable to mark unknown argument %s ' 'as non-injectable.' % arg)
|
||||
|
||||
existing = getattr(function, '__noninjectables__', set())
|
||||
merged = existing | set(args)
|
||||
function.__noninjectables__ = merged
|
||||
return function
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def method_wrapper(f, bindings):
|
||||
argspec = inspect.getfullargspec(f)
|
||||
if argspec.args and argspec.args[0] == 'self':
|
||||
|
||||
@functools.wraps(f)
|
||||
def inject(self_, *args, **kwargs):
|
||||
try:
|
||||
|
@ -1102,12 +1107,7 @@ def method_wrapper(f, bindings):
|
|||
except RuntimeError:
|
||||
injector = None
|
||||
if injector:
|
||||
return injector.call_with_injection(
|
||||
callable=f,
|
||||
self_=self_,
|
||||
args=args,
|
||||
kwargs=kwargs
|
||||
)
|
||||
return injector.call_with_injection(callable=f, self_=self_, args=args, kwargs=kwargs)
|
||||
else:
|
||||
return f(self_, *args, **kwargs)
|
||||
|
||||
|
@ -1141,8 +1141,10 @@ class BaseKey:
|
|||
"""Base type for binding keys."""
|
||||
|
||||
def __init__(self):
|
||||
raise Exception('Instantiation of %s prohibited - it is derived from BaseKey '
|
||||
'so most likely you should bind it to something.' % (self.__class__,))
|
||||
raise Exception(
|
||||
'Instantiation of %s prohibited - it is derived from BaseKey '
|
||||
'so most likely you should bind it to something.' % (self.__class__,)
|
||||
)
|
||||
|
||||
|
||||
def Key(name):
|
||||
|
@ -1160,9 +1162,12 @@ def Key(name):
|
|||
@private
|
||||
class BaseMappingKey(dict):
|
||||
"""Base type for mapping binding keys."""
|
||||
|
||||
def __init__(self):
|
||||
raise Exception('Instantiation of %s prohibited - it is derived from BaseMappingKey '
|
||||
'so most likely you should bind it to something.' % (self.__class__,))
|
||||
raise Exception(
|
||||
'Instantiation of %s prohibited - it is derived from BaseMappingKey '
|
||||
'so most likely you should bind it to something.' % (self.__class__,)
|
||||
)
|
||||
|
||||
|
||||
def MappingKey(name):
|
||||
|
@ -1173,9 +1178,12 @@ def MappingKey(name):
|
|||
@private
|
||||
class BaseSequenceKey(list):
|
||||
"""Base type for mapping sequence keys."""
|
||||
|
||||
def __init__(self):
|
||||
raise Exception('Instantiation of %s prohibited - it is derived from BaseSequenceKey '
|
||||
'so most likely you should bind it to something.' % (self.__class__,))
|
||||
raise Exception(
|
||||
'Instantiation of %s prohibited - it is derived from BaseSequenceKey '
|
||||
'so most likely you should bind it to something.' % (self.__class__,)
|
||||
)
|
||||
|
||||
|
||||
def SequenceKey(name):
|
||||
|
@ -1211,7 +1219,6 @@ class BoundKey(tuple):
|
|||
|
||||
|
||||
class AssistedBuilder(Generic[T]):
|
||||
|
||||
def __init__(self, injector, target):
|
||||
self._injector = injector
|
||||
self._target = target
|
||||
|
@ -1224,7 +1231,8 @@ class AssistedBuilder(Generic[T]):
|
|||
if not isinstance(provider, ClassProvider):
|
||||
raise Error(
|
||||
'Assisted interface building works only with ClassProviders, '
|
||||
'got %r for %r' % (provider, binding.interface))
|
||||
'got %r for %r' % (provider, binding.interface)
|
||||
)
|
||||
|
||||
return self._build_class(provider._cls, **kwargs)
|
||||
|
||||
|
@ -1268,8 +1276,7 @@ class ProviderOf(Generic[T]):
|
|||
self._interface = interface
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %r)' % (
|
||||
type(self).__name__, self._injector, self._interface)
|
||||
return '%s(%r, %r)' % (type(self).__name__, self._injector, self._interface)
|
||||
|
||||
def get(self) -> T:
|
||||
"""Get an implementation for the specified interface."""
|
||||
|
|
163
injector_test.py
163
injector_test.py
|
@ -20,13 +20,33 @@ import warnings
|
|||
import pytest
|
||||
|
||||
from injector import (
|
||||
Binder, CallError, Injector, Scope, InstanceProvider, ClassProvider,
|
||||
inject, noninjectable, singleton, threadlocal, UnsatisfiedRequirement,
|
||||
CircularDependency, Module, Key, SingletonScope,
|
||||
ScopeDecorator, with_injector, AssistedBuilder, BindingKey,
|
||||
SequenceKey, MappingKey, provider, ProviderOf, ClassAssistedBuilder,
|
||||
Error, UnknownArgument,
|
||||
)
|
||||
Binder,
|
||||
CallError,
|
||||
Injector,
|
||||
Scope,
|
||||
InstanceProvider,
|
||||
ClassProvider,
|
||||
inject,
|
||||
noninjectable,
|
||||
singleton,
|
||||
threadlocal,
|
||||
UnsatisfiedRequirement,
|
||||
CircularDependency,
|
||||
Module,
|
||||
Key,
|
||||
SingletonScope,
|
||||
ScopeDecorator,
|
||||
with_injector,
|
||||
AssistedBuilder,
|
||||
BindingKey,
|
||||
SequenceKey,
|
||||
MappingKey,
|
||||
provider,
|
||||
ProviderOf,
|
||||
ClassAssistedBuilder,
|
||||
Error,
|
||||
UnknownArgument,
|
||||
)
|
||||
|
||||
|
||||
def prepare_basic_injection():
|
||||
|
@ -55,21 +75,23 @@ def check_exception_contains_stuff(exception, stuff):
|
|||
stringified = str(exception)
|
||||
|
||||
for thing in stuff:
|
||||
assert thing in stringified, (
|
||||
'%r should be present in the exception representation: %s' % (
|
||||
thing, stringified))
|
||||
assert thing in stringified, '%r should be present in the exception representation: %s' % (
|
||||
thing,
|
||||
stringified,
|
||||
)
|
||||
|
||||
|
||||
def test_child_injector_inherits_parent_bindings():
|
||||
parent, child = prepare_nested_injectors()
|
||||
assert (child.get(str) == parent.get(str))
|
||||
assert child.get(str) == parent.get(str)
|
||||
|
||||
|
||||
def test_child_injector_overrides_parent_bindings():
|
||||
parent, child = prepare_nested_injectors()
|
||||
child.binder.bind(str, to='qwe')
|
||||
|
||||
assert ((parent.get(str), child.get(str)) == ('asd', 'qwe'))
|
||||
assert (parent.get(str), child.get(str)) == ('asd', 'qwe')
|
||||
|
||||
|
||||
def test_child_injector_rebinds_arguments_for_parent_scope():
|
||||
I = Key("interface")
|
||||
|
@ -88,9 +110,10 @@ def test_child_injector_rebinds_arguments_for_parent_scope():
|
|||
binder.bind(I, to="Child")
|
||||
|
||||
parent = Injector(configure_parent)
|
||||
assert (parent.get(Cls).val == "Parent")
|
||||
assert parent.get(Cls).val == "Parent"
|
||||
child = parent.create_child_injector(configure_child)
|
||||
assert (child.get(Cls).val == "Child")
|
||||
assert child.get(Cls).val == "Child"
|
||||
|
||||
|
||||
def test_scopes_are_only_bound_to_root_injector():
|
||||
parent, child = prepare_nested_injectors()
|
||||
|
@ -99,7 +122,7 @@ def test_scopes_are_only_bound_to_root_injector():
|
|||
pass
|
||||
|
||||
parent.binder.bind(A, to=A, scope=singleton)
|
||||
assert (parent.get(A) is child.get(A))
|
||||
assert parent.get(A) is child.get(A)
|
||||
|
||||
|
||||
def test_key_cannot_be_instantiated():
|
||||
|
@ -120,20 +143,20 @@ def test_get_default_injected_instances():
|
|||
binder.bind(B)
|
||||
|
||||
injector = Injector(configure)
|
||||
assert (injector.get(Injector) is injector)
|
||||
assert (injector.get(Binder) is injector.binder)
|
||||
assert injector.get(Injector) is injector
|
||||
assert injector.get(Binder) is injector.binder
|
||||
|
||||
|
||||
def test_instantiate_injected_method():
|
||||
A, _ = prepare_basic_injection()
|
||||
a = A('Bob')
|
||||
assert (a.b == 'Bob')
|
||||
assert a.b == 'Bob'
|
||||
|
||||
|
||||
def test_method_decorator_is_wrapped():
|
||||
A, _ = prepare_basic_injection()
|
||||
assert (A.__init__.__doc__ == 'Construct a new A.')
|
||||
assert (A.__init__.__name__ == '__init__')
|
||||
assert A.__init__.__doc__ == 'Construct a new A.'
|
||||
assert A.__init__.__name__ == '__init__'
|
||||
|
||||
|
||||
def test_decorator_works_for_function_with_no_args():
|
||||
|
@ -169,8 +192,8 @@ def test_inject_direct():
|
|||
|
||||
injector = Injector(configure)
|
||||
a = injector.get(A)
|
||||
assert (isinstance(a, A))
|
||||
assert (isinstance(a.b, B))
|
||||
assert isinstance(a, A)
|
||||
assert isinstance(a.b, B)
|
||||
|
||||
|
||||
def test_configure_multiple_modules():
|
||||
|
@ -184,8 +207,8 @@ def test_configure_multiple_modules():
|
|||
|
||||
injector = Injector([configure_a, configure_b])
|
||||
a = injector.get(A)
|
||||
assert (isinstance(a, A))
|
||||
assert (isinstance(a.b, B))
|
||||
assert isinstance(a, A)
|
||||
assert isinstance(a.b, B)
|
||||
|
||||
|
||||
def test_inject_with_missing_dependency():
|
||||
|
@ -214,8 +237,8 @@ def test_inject_named_interface():
|
|||
|
||||
injector = Injector(configure)
|
||||
a = injector.get(A)
|
||||
assert (isinstance(a, A))
|
||||
assert (isinstance(a.b, B))
|
||||
assert isinstance(a, A)
|
||||
assert isinstance(a.b, B)
|
||||
|
||||
|
||||
def prepare_transitive_injection():
|
||||
|
@ -245,9 +268,9 @@ def test_transitive_injection():
|
|||
|
||||
injector = Injector(configure)
|
||||
a = injector.get(A)
|
||||
assert (isinstance(a, A))
|
||||
assert (isinstance(a.b, B))
|
||||
assert (isinstance(a.b.c, C))
|
||||
assert isinstance(a, A)
|
||||
assert isinstance(a.b, B)
|
||||
assert isinstance(a.b.c, C)
|
||||
|
||||
|
||||
def test_transitive_injection_with_missing_dependency():
|
||||
|
@ -280,7 +303,7 @@ def test_inject_singleton():
|
|||
injector1 = Injector(configure)
|
||||
a1 = injector1.get(A)
|
||||
a2 = injector1.get(A)
|
||||
assert (a1.b is a2.b)
|
||||
assert a1.b is a2.b
|
||||
|
||||
|
||||
def test_inject_decorated_singleton_class():
|
||||
|
@ -300,7 +323,7 @@ def test_inject_decorated_singleton_class():
|
|||
injector1 = Injector(configure)
|
||||
a1 = injector1.get(A)
|
||||
a2 = injector1.get(A)
|
||||
assert (a1.b is a2.b)
|
||||
assert a1.b is a2.b
|
||||
|
||||
|
||||
def test_threadlocal():
|
||||
|
@ -316,7 +339,7 @@ def test_threadlocal():
|
|||
a1 = injector.get(A)
|
||||
a2 = injector.get(A)
|
||||
|
||||
assert (a1 is a2)
|
||||
assert a1 is a2
|
||||
|
||||
a3 = [None]
|
||||
ready = threading.Event()
|
||||
|
@ -328,7 +351,7 @@ def test_threadlocal():
|
|||
threading.Thread(target=inject_a3).start()
|
||||
ready.wait(1.0)
|
||||
|
||||
assert (a2 is not a3[0] and a3[0] is not None)
|
||||
assert a2 is not a3[0] and a3[0] is not None
|
||||
|
||||
|
||||
def test_injecting_interface_implementation():
|
||||
|
@ -349,7 +372,7 @@ def test_injecting_interface_implementation():
|
|||
|
||||
injector = Injector(configure)
|
||||
a = injector.get(A)
|
||||
assert (isinstance(a.i, Implementation))
|
||||
assert isinstance(a.i, Implementation)
|
||||
|
||||
|
||||
def test_cyclic_dependencies():
|
||||
|
@ -421,7 +444,7 @@ def test_that_injection_is_lazy():
|
|||
injector = Injector(configure)
|
||||
assert not (Interface.constructed)
|
||||
injector.get(A)
|
||||
assert (Interface.constructed)
|
||||
assert Interface.constructed
|
||||
|
||||
|
||||
def test_module_provider():
|
||||
|
@ -443,7 +466,7 @@ def test_module_class_gets_instantiated():
|
|||
binder.bind(str, to=name)
|
||||
|
||||
injector = Injector(MyModule)
|
||||
assert (injector.get(str) == name)
|
||||
assert injector.get(str) == name
|
||||
|
||||
|
||||
def test_with_injector_works():
|
||||
|
@ -459,7 +482,7 @@ def test_with_injector_works():
|
|||
self.username = username
|
||||
|
||||
aaa = Aaa()
|
||||
assert (aaa.username == name)
|
||||
assert aaa.username == name
|
||||
|
||||
|
||||
def test_bind_using_key():
|
||||
|
@ -475,8 +498,8 @@ def test_bind_using_key():
|
|||
binder.bind(Age, to=25)
|
||||
|
||||
injector = Injector(MyModule())
|
||||
assert (injector.get(Age) == 25)
|
||||
assert (injector.get(Name) == 'Bob')
|
||||
assert injector.get(Age) == 25
|
||||
assert injector.get(Name) == 'Bob'
|
||||
|
||||
|
||||
def test_inject_using_key():
|
||||
|
@ -493,7 +516,7 @@ def test_inject_using_key():
|
|||
def provide_description(self, name: Name) -> Description:
|
||||
return '%s is cool!' % name
|
||||
|
||||
assert (Injector(MyModule()).get(Description) == 'Bob is cool!')
|
||||
assert Injector(MyModule()).get(Description) == 'Bob is cool!'
|
||||
|
||||
|
||||
def test_inject_and_provide_coexist_happily():
|
||||
|
@ -512,7 +535,7 @@ def test_inject_and_provide_coexist_happily():
|
|||
def provide_description(self, age: int, weight: float) -> str:
|
||||
return 'Bob is %d and weighs %0.1fkg' % (age, weight)
|
||||
|
||||
assert (Injector(MyModule()).get(str) == 'Bob is 25 and weighs 50.0kg')
|
||||
assert Injector(MyModule()).get(str) == 'Bob is 25 and weighs 50.0kg'
|
||||
|
||||
|
||||
def test_multibind():
|
||||
|
@ -524,7 +547,7 @@ def test_multibind():
|
|||
def configure_two(binder):
|
||||
binder.multibind(Names, to=['Tom'])
|
||||
|
||||
assert (Injector([configure_one, configure_two]).get(Names) == ['Bob', 'Tom'])
|
||||
assert Injector([configure_one, configure_two]).get(Names) == ['Bob', 'Tom']
|
||||
|
||||
|
||||
def test_provider_sequence_decorator():
|
||||
|
@ -539,16 +562,15 @@ def test_provider_sequence_decorator():
|
|||
def tom(self) -> Names:
|
||||
return ['Tom']
|
||||
|
||||
assert (Injector(MyModule()).get(Names) == ['Bob', 'Tom'])
|
||||
assert Injector(MyModule()).get(Names) == ['Bob', 'Tom']
|
||||
|
||||
|
||||
def test_auto_bind():
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
injector = Injector()
|
||||
assert (isinstance(injector.get(A), A))
|
||||
assert isinstance(injector.get(A), A)
|
||||
|
||||
|
||||
def test_custom_scope():
|
||||
|
@ -604,20 +626,19 @@ def test_custom_scope():
|
|||
|
||||
with scope(request):
|
||||
handler = injector.get(Handler)
|
||||
assert (handler.request is request)
|
||||
assert handler.request is request
|
||||
|
||||
with pytest.raises(UnsatisfiedRequirement):
|
||||
injector.get(Handler)
|
||||
|
||||
|
||||
def test_bind_interface_of_list_of_types():
|
||||
|
||||
def configure(binder):
|
||||
binder.multibind([int], to=[1, 2, 3])
|
||||
binder.multibind([int], to=[4, 5, 6])
|
||||
|
||||
injector = Injector(configure)
|
||||
assert (injector.get([int]) == [1, 2, 3, 4, 5, 6])
|
||||
assert injector.get([int]) == [1, 2, 3, 4, 5, 6]
|
||||
|
||||
|
||||
def test_provider_mapping():
|
||||
|
@ -638,7 +659,7 @@ def test_provider_mapping():
|
|||
return {'four': 4}
|
||||
|
||||
injector = Injector([configure, MyModule()])
|
||||
assert (injector.get(StrInt) == {'one': 1, 'two': 2, 'three': 3, 'four': 4})
|
||||
assert injector.get(StrInt) == {'one': 1, 'two': 2, 'three': 3, 'four': 4}
|
||||
|
||||
|
||||
def test_binder_install():
|
||||
|
@ -651,31 +672,31 @@ def test_binder_install():
|
|||
binder.install(ModuleA())
|
||||
|
||||
injector = Injector([ModuleB()])
|
||||
assert (injector.get(str) == 'hello world')
|
||||
assert injector.get(str) == 'hello world'
|
||||
|
||||
|
||||
def test_binder_provider_for_method_with_explicit_provider():
|
||||
injector = Injector()
|
||||
binder = injector.binder
|
||||
provider = binder.provider_for(int, to=InstanceProvider(1))
|
||||
assert (type(provider) is InstanceProvider)
|
||||
assert (provider.get(injector) == 1)
|
||||
assert type(provider) is InstanceProvider
|
||||
assert provider.get(injector) == 1
|
||||
|
||||
|
||||
def test_binder_provider_for_method_with_instance():
|
||||
injector = Injector()
|
||||
binder = injector.binder
|
||||
provider = binder.provider_for(int, to=1)
|
||||
assert (type(provider) is InstanceProvider)
|
||||
assert (provider.get(injector) == 1)
|
||||
assert type(provider) is InstanceProvider
|
||||
assert provider.get(injector) == 1
|
||||
|
||||
|
||||
def test_binder_provider_for_method_with_class():
|
||||
injector = Injector()
|
||||
binder = injector.binder
|
||||
provider = binder.provider_for(int)
|
||||
assert (type(provider) is ClassProvider)
|
||||
assert (provider.get(injector) == 0)
|
||||
assert type(provider) is ClassProvider
|
||||
assert provider.get(injector) == 0
|
||||
|
||||
|
||||
def test_binder_provider_for_method_with_class_to_specific_subclass():
|
||||
|
@ -688,8 +709,8 @@ def test_binder_provider_for_method_with_class_to_specific_subclass():
|
|||
injector = Injector()
|
||||
binder = injector.binder
|
||||
provider = binder.provider_for(A, B)
|
||||
assert (type(provider) is ClassProvider)
|
||||
assert (isinstance(provider.get(injector), B))
|
||||
assert type(provider) is ClassProvider
|
||||
assert isinstance(provider.get(injector), B)
|
||||
|
||||
|
||||
def test_binder_provider_for_type_with_metaclass():
|
||||
|
@ -697,11 +718,11 @@ def test_binder_provider_for_type_with_metaclass():
|
|||
# otherwise should be:
|
||||
# class A(object, metaclass=abc.ABCMeta):
|
||||
# passa
|
||||
A = abc.ABCMeta('A', (object, ), {})
|
||||
A = abc.ABCMeta('A', (object,), {})
|
||||
|
||||
injector = Injector()
|
||||
binder = injector.binder
|
||||
assert (isinstance(binder.provider_for(A, None).get(injector), A))
|
||||
assert isinstance(binder.provider_for(A, None).get(injector), A)
|
||||
|
||||
|
||||
def test_injecting_undecorated_class_with_missing_dependencies_raises_the_right_error():
|
||||
|
@ -747,7 +768,7 @@ def test_assisted_builder_works_when_got_directly_from_injector():
|
|||
injector = Injector()
|
||||
builder = injector.get(AssistedBuilder[NeedsAssistance])
|
||||
obj = builder.build(b=123)
|
||||
assert ((obj.a, obj.b) == (str(), 123))
|
||||
assert (obj.a, obj.b) == (str(), 123)
|
||||
|
||||
|
||||
def test_assisted_builder_works_when_injected():
|
||||
|
@ -758,7 +779,7 @@ def test_assisted_builder_works_when_injected():
|
|||
|
||||
injector = Injector()
|
||||
x = injector.get(X)
|
||||
assert ((x.obj.a, x.obj.b) == (str(), 234))
|
||||
assert (x.obj.a, x.obj.b) == (str(), 234)
|
||||
|
||||
|
||||
def test_assisted_builder_uses_bindings():
|
||||
|
@ -770,7 +791,7 @@ def test_assisted_builder_uses_bindings():
|
|||
injector = Injector(configure)
|
||||
builder = injector.get(AssistedBuilder[Interface])
|
||||
x = builder.build(b=333)
|
||||
assert ((type(x), x.b) == (NeedsAssistance, 333))
|
||||
assert (type(x), x.b) == (NeedsAssistance, 333)
|
||||
|
||||
|
||||
def test_assisted_builder_uses_concrete_class_when_specified():
|
||||
|
@ -795,7 +816,7 @@ def test_assisted_builder_injection_is_safe_to_use_with_multiple_injectors():
|
|||
i1, i2 = Injector(), Injector()
|
||||
b1 = i1.get(X).builder
|
||||
b2 = i2.get(X).builder
|
||||
assert ((b1._injector, b2._injector) == (i1, i2))
|
||||
assert (b1._injector, b2._injector) == (i1, i2)
|
||||
|
||||
|
||||
def test_assisted_builder_injection_uses_the_same_binding_key_every_time():
|
||||
|
@ -842,12 +863,12 @@ class TestThreadSafety:
|
|||
|
||||
def test_injection_is_thread_safe(self):
|
||||
objects = self.gather_results(2)
|
||||
assert (len(objects) == 2)
|
||||
assert len(objects) == 2
|
||||
|
||||
def test_singleton_scope_is_thread_safe(self):
|
||||
self.injector.binder.bind(self.cls, scope=singleton)
|
||||
a, b = self.gather_results(2)
|
||||
assert (a is b)
|
||||
assert a is b
|
||||
|
||||
|
||||
def test_provider_and_scope_decorator_collaboration():
|
||||
|
@ -879,6 +900,7 @@ def test_injecting_into_method_of_object_that_is_falseish_works():
|
|||
|
||||
def test_injection_fails_when_injector_cant_install_itself_into_an_object_with_slots():
|
||||
try:
|
||||
|
||||
class ClassName:
|
||||
__slots__ = ()
|
||||
|
||||
|
@ -1008,6 +1030,7 @@ def test_special_interfaces_work_with_auto_bind_disabled():
|
|||
|
||||
def test_binding_an_instance_regression():
|
||||
text = b'hello'.decode()
|
||||
|
||||
def configure(binder):
|
||||
# Yes, this binding doesn't make sense strictly speaking but
|
||||
# it's just a sample case.
|
||||
|
@ -1046,12 +1069,12 @@ def test_implicit_injection_for_python3():
|
|||
|
||||
class B:
|
||||
@inject
|
||||
def __init__(self, a:A):
|
||||
def __init__(self, a: A):
|
||||
self.a = a
|
||||
|
||||
class C:
|
||||
@inject
|
||||
def __init__(self, b:B):
|
||||
def __init__(self, b: B):
|
||||
self.b = b
|
||||
|
||||
injector = Injector()
|
||||
|
@ -1323,7 +1346,6 @@ def test_custom_scopes_work_as_expected_with_child_injectors():
|
|||
# Test for https://github.com/alecthomas/injector/issues/75
|
||||
def test_inject_decorator_does_not_break_manual_construction_of_pyqt_objects():
|
||||
class PyQtFake:
|
||||
|
||||
@inject
|
||||
def __init__(self):
|
||||
pass
|
||||
|
@ -1333,7 +1355,8 @@ def test_inject_decorator_does_not_break_manual_construction_of_pyqt_objects():
|
|||
raise RuntimeError(
|
||||
'A PyQt class would raise this exception if getting '
|
||||
'self.__injector__ before __init__ is called and '
|
||||
'self.__injector__ has not been set by Injector.')
|
||||
'self.__injector__ has not been set by Injector.'
|
||||
)
|
||||
return object.__getattribute__(self, item)
|
||||
|
||||
instance = PyQtFake() # This used to raise the exception
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[tool.black]
|
||||
line-length = 110
|
||||
target_version = ['py36', 'py37']
|
||||
skip_string_normalization = true
|
|
@ -2413,6 +2413,7 @@ import base64
|
|||
import zlib
|
||||
import imp
|
||||
|
||||
|
||||
class DictImporter:
|
||||
def __init__(self, sources):
|
||||
self.sources = sources
|
||||
|
@ -2427,6 +2428,7 @@ class DictImporter:
|
|||
def load_module(self, fullname):
|
||||
# print "load_module:", fullname
|
||||
from types import ModuleType
|
||||
|
||||
try:
|
||||
s = self.sources[fullname]
|
||||
is_pkg = False
|
||||
|
@ -2451,14 +2453,17 @@ class DictImporter:
|
|||
res = self.sources.get(name + '.__init__')
|
||||
return res
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.version_info >= (3, 0):
|
||||
exec("def do_exec(co, loc): exec(co, loc)\n")
|
||||
import pickle
|
||||
sources = sources.encode("ascii") # ensure bytes
|
||||
|
||||
sources = sources.encode("ascii") # ensure bytes
|
||||
sources = pickle.loads(zlib.decompress(base64.decodebytes(sources)))
|
||||
else:
|
||||
import pickle as pickle
|
||||
|
||||
exec("def do_exec(co, loc): exec co in loc\n")
|
||||
sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
|
||||
|
||||
|
|
15
setup.py
15
setup.py
|
@ -17,6 +17,7 @@ class PyTest(Command):
|
|||
|
||||
def run(self):
|
||||
import subprocess
|
||||
|
||||
errno = subprocess.call([sys.executable, 'runtest.py'])
|
||||
raise SystemExit(errno)
|
||||
|
||||
|
@ -35,10 +36,10 @@ version_tag = read_injector_variable('__version_tag__')
|
|||
|
||||
try:
|
||||
import pypandoc
|
||||
|
||||
long_description = pypandoc.convert('README.md', 'rst')
|
||||
except ImportError:
|
||||
warnings.warn('Could not locate pandoc, using Markdown long_description.',
|
||||
ImportWarning)
|
||||
warnings.warn('Could not locate pandoc, using Markdown long_description.', ImportWarning)
|
||||
with open('README.md') as f:
|
||||
long_description = f.read()
|
||||
|
||||
|
@ -56,12 +57,16 @@ setup(
|
|||
license='BSD',
|
||||
platforms=['any'],
|
||||
packages=['injector'],
|
||||
package_data = {'injector': ['py.typed']},
|
||||
package_data={'injector': ['py.typed']},
|
||||
author='Alec Thomas',
|
||||
author_email='alec@swapoff.org',
|
||||
cmdclass={'test': PyTest},
|
||||
keywords=[
|
||||
'Dependency Injection', 'DI', 'Dependency Injection framework',
|
||||
'Inversion of Control', 'IoC', 'Inversion of Control container',
|
||||
'Dependency Injection',
|
||||
'DI',
|
||||
'Dependency Injection framework',
|
||||
'Inversion of Control',
|
||||
'IoC',
|
||||
'Inversion of Control container',
|
||||
],
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue