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

Allow local values when constructing the injector. #18

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 7 additions & 2 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=True)``
----------------------------------------------
``Injector.__init__(self, *dicts, **values)``
---------------------------------------------

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

Expand Down Expand Up @@ -173,6 +173,11 @@ The injector provides itself as the `'injector'` service::
with Injector() as injector:
injector.get('injector')

Local values can be provided when constructing the injector::

with RequestInjector(request=request) as injector:
injector.get('form:username')

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes should go into README.rst.in given the project's custom doc builder.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the built doc generated from the jeni.Injector docstring.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, I see that now. Reading the diff view isn't always optimal.


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

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

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

An Injector subclass inherits the provider registry of its base
Expand Down Expand Up @@ -399,13 +398,23 @@ class Injector(BaseInjector):

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

Local values can be provided when constructing the injector::

with RequestInjector(request=request) as injector:
injector.get('form:username')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc example doesn't make sense without more context. A more trivial illustration would be clearer:

with Injector(foo='bar') as injector:
    injector.get('foo') # 'bar'

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sort of on the fence here. The docs could use some more concrete examples that demonstrate useful patterns. This hints at such an example, but it's obviously incomplete.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe include both? Start with the trivial, then mention that a useful pattern is in creating request lifecycles.

This allows values to be provided trivially:

with Injector(foo='bar') as injector:
    injector.get('foo') # 'bar'

A useful pattern is to have the injector's providers key off input data:

with RequestInjector(request=request) as injector:
    injector.get('form:username')
    injector.apply(handler)


"""

self.annotator = self.annotator_class()

self.closed = False
self.instances = {}
self.values = {}
for d in dicts:
self.values.update(d)
self.values.update(values)
self.values['injector'] = self

self.finalizers = []

Expand All @@ -417,12 +426,6 @@ 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 Expand Up @@ -802,15 +805,8 @@ class SubInjector(cls):
SubInjector.__bases__ = tuple(mixins) + SubInjector.__bases__

dicts = [ x for x in mixins_and_dicts if not isinstance(x, type) ]
for d in reversed(dicts):
for k,v in d.items():
if k not in values:
values[k] = v

for k,v in values.items():
SubInjector.value(k, v)

return SubInjector()
return SubInjector(*dicts, **values)


class InjectorProxy(object):
Expand Down
43 changes: 31 additions & 12 deletions test_jeni.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,25 +153,44 @@ class SubInjector(BasicInjector):


class InjectSelfTestCase(unittest.TestCase):
def test_provide_self_default(self):
def test_injector_is_self(self):
self.injector = jeni.Injector()
self.assertEqual(self.injector, self.injector.get('injector'))

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

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)
def test_injector_is_local(self):
injector1 = jeni.Injector()
injector2 = jeni.Injector()
self.assertEqual(injector1, injector1.get('injector'))
self.assertEqual(injector2, injector2.get('injector'))


class InjectorLocals(unittest.TestCase):
def test_ctor_kwargs(self):
injector = jeni.Injector(flavor='banana')
self.assertEqual('banana', injector.get('flavor'))

def test_ctor_args(self):
injector = jeni.Injector(dict(flavor='banana'))
self.assertEqual('banana', injector.get('flavor'))

def test_ctor_overrides(self):
class SubInjector(jeni.Injector): pass
SubInjector.value('flavor', 'banana')
injector = jeni.Injector(flavor='triangle')
self.assertEqual('triangle', injector.get('flavor'))

def test_ctor_priority(self):
injector = jeni.Injector(dict(flavor='banana'), flavor='triangle')
self.assertEqual('triangle', injector.get('flavor'))

def test_isolation(self):
injector1 = jeni.Injector(flavor='banana')
injector2 = jeni.Injector()
injector3 = injector1.sub()
self.assertRaises(LookupError, injector2.get, 'flavor')
self.assertRaises(LookupError, injector3.get, 'flavor')


class SubInjectorTestCase(BasicInjectorTestCase):
def setUp(self):
self.injector = SubInjector()
Expand Down