Skip to content
This repository has been archived by the owner on Feb 21, 2020. It is now read-only.

Don't leak the injector instance into the class. #14

Merged
merged 5 commits into from
Jan 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ method, annotate ``__init__`` and access the value via `self`.
Collects dependencies and reads annotations to inject them.


``Injector.__init__(self, provide_self=False)``
-----------------------------------------------
``Injector.__init__(self, provide_self=True)``
----------------------------------------------

A subclass could take arguments, but should pass keywords to super.

Expand All @@ -155,17 +155,23 @@ or providing alternative dependencies in a different runtime::
class Injector(BaseInjector):
"Subclass provides namespace when registering providers."

By default, the injector does not provide itself, but will when asked::
Injector instances may be used as a context manager::

injector = Injector(provide_self=True)
injector.get('injector')
with Injector() as injector:
injector.apply(annotated_fn)

This is useful in a context manager::
The injector lifecycle can also be managed asynchronously using the
`enter()` and `exit()` methods::

with Injector(provide_self=True) as injector:
injector.get('injector')
injector = Injector().enter()
injector.apply(annotated_fn)
...
injector.exit()

Annotate with note 'injector' to inject the injector.
The injector provides itself as the `'injector'` service::

with Injector() as injector:
injector.get('injector')


``Injector.sub(cls, *mixins_and_dicts, **values)``
Expand Down
31 changes: 21 additions & 10 deletions jeni.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import functools
import inspect
import re
import warnings
import sys

import six
Expand Down Expand Up @@ -365,7 +366,7 @@ class Injector(object):
generator_provider = GeneratorProvider
re_note = re.compile(r'^(.*?)(?::(.*))?$') # annotation is 'object:name'

def __init__(self, provide_self=False):
def __init__(self, provide_self=True):
"""A subclass could take arguments, but should pass keywords to super.

An Injector subclass inherits the provider registry of its base
Expand All @@ -381,20 +382,24 @@ def __init__(self, provide_self=False):
class Injector(BaseInjector):
"Subclass provides namespace when registering providers."

By default, the injector does not provide itself, but will when asked::
Injector instances may be used as a context manager::

injector = Injector(provide_self=True)
injector.get('injector')
with Injector() as injector:
injector.apply(annotated_fn)

This is useful in a context manager::
The injector lifecycle can also be managed asynchronously using the
`enter()` and `exit()` methods::

with Injector(provide_self=True) as injector:
injector.get('injector')
injector = Injector().enter()
injector.apply(annotated_fn)
...
injector.exit()

The injector provides itself as the `'injector'` service::

Annotate with note 'injector' to inject the injector.
with Injector() as injector:
injector.get('injector')
"""
if provide_self:
self.value('injector', self)

self.annotator = self.annotator_class()

Expand All @@ -412,6 +417,12 @@ class Injector(BaseInjector):
#: This allows for dependency cycle checks.
self.instantiating = []

if provide_self:
self.values['injector'] = self
else:
warnings.warn(
DeprecationWarning('provide_self=False is not supported'))

@classmethod
def provider(cls, note, provider=None, name=False):
"""Register a provider, either a Provider class or a generator.
Expand Down
12 changes: 11 additions & 1 deletion test_jeni.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class SubInjector(BasicInjector):
class InjectSelfTestCase(unittest.TestCase):
def test_provide_self_default(self):
self.injector = jeni.Injector()
self.assertRaises(LookupError, self.injector.get, 'injector')
self.assertEqual(self.injector, self.injector.get('injector'))

def test_provide_self_true(self):
self.injector = jeni.Injector(provide_self=True)
Expand All @@ -165,6 +165,12 @@ def test_provide_self_false(self):
self.injector = jeni.Injector(provide_self=False)
self.assertRaises(LookupError, self.injector.get, 'injector')

def test_provide_self_is_self(self):
injector1 = jeni.Injector(provide_self=True)
injector2 = jeni.Injector(provide_self=True)
self.assertEqual(injector1, injector1.get('injector'))
self.assertEqual(injector2, injector2.get('injector'))


class SubInjectorTestCase(BasicInjectorTestCase):
def setUp(self):
Expand Down Expand Up @@ -221,6 +227,10 @@ def test_subinjector_mixins(self):
self.assertIsInstance(subinjector, WithOrderedSpace)
self.assertIsInstance(subinjector.get('space'), odict)

def test_subinjector_provides_injector(self):
subinjector = self.injector.sub()
self.assertEqual(subinjector, subinjector.get('injector'))


class BasicInjectorAnnotationTestCase(unittest.TestCase):
def setUp(self):
Expand Down