jeni-python
jeni-python copied to clipboard
Injector.alias()
Instead of writing:
@Injector.factory('send_mail')
def send_mail_factory(send_mail: annotate.partial(send_mail)):
return send_mail
What if you could just write:
Injector.alias('send_mail', annotate.partial(send_mail))
Another possible use:
# In a web application
Injector.alias('user', 'session:user')
# In tests
Injector.alias('user', 'fixture:user')
Here is a quick implementation of Injector.alias().
import functools
from jeni import Injector, Provider, FactoryProvider, annotate
class AliasProvider(Provider):
@classmethod
def bind(cls, note):
basenote, name = note
if name is not None:
@annotate
def alias(v: note):
return FactoryProvider(lambda: v)
return alias
return lambda: cls(basenote)
def __init__(self, basenote):
self.basenote = basenote
@annotate
def get(self, *, name=None, injector: 'injector'):
return injector.get((self.basenote, name))
class AliasingInjector(Injector):
alias_provider = AliasProvider
def __init__(self, provide_self=True):
if provide_self is not True:
raise ValueError('provide_self can only be True')
# Work around #14.
#super().__init__(True)
super().__init__(False)
self.values['injector'] = self
@classmethod
def alias(cls, note, other_note=None, *, partial=None):
if other_note is None:
return functools.partial(cls.alias, note, partial=partial)
if partial:
other_note = annotate.partial(other_note)
else:
other_note = cls.parse_note(other_note)
provider = cls.alias_provider.bind(other_note)
cls.register(note, provider)
if __name__ == "__main__":
import unittest
class TestAliasingInjector(unittest.TestCase):
def setUp(self):
class TestInjector(AliasingInjector): pass
self.TestInjector = TestInjector
@TestInjector.provider('named', name=True)
def named_provider():
name = None
while True:
if name is None:
name = yield 'named'
else:
name = yield 'named[{}]'.format(name)
def test_alias_basenote(self):
self.TestInjector.alias('borrower', 'named')
with self.TestInjector() as inj:
self.assertEqual(inj.get('borrower'), 'named')
self.assertEqual(inj.get('borrower:bar'), 'named[bar]')
def test_alias_namednote(self):
self.TestInjector.alias('borrower', 'named:foo')
with self.TestInjector() as inj:
self.assertEqual(inj.get('borrower'), 'named[foo]')
with self.assertRaises(TypeError):
inj.get('borrower:bar')
def test_alias_partial(self):
class QuuxificationError(Exception): pass
@self.TestInjector.alias('quuxify', partial=True)
@annotate
def quuxify(x: 'named:x'):
raise QuuxificationError()
with self.TestInjector() as inj:
with self.assertRaises(QuuxificationError):
inj.get('quuxify')()
unittest.main()
I just realized that this makes it easier to register annotated functions than plain functions. If Injector.value(note) worked as a decorator, that would fix it.