mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 01:26:51 +03:00
0.0.3 version
This commit is contained in:
parent
2a29c43115
commit
028e5e9ef7
102
README.md
102
README.md
|
@ -4,7 +4,7 @@ Objects
|
|||
Python catalogs of objects providers.
|
||||
|
||||
|
||||
Example:
|
||||
Example of objects catalog definition and usage:
|
||||
|
||||
```python
|
||||
"""
|
||||
|
@ -16,12 +16,12 @@ import sqlite3
|
|||
|
||||
|
||||
# Some example classes.
|
||||
class A(object):
|
||||
class ObjectA(object):
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
|
||||
class B(object):
|
||||
class ObjectB(object):
|
||||
def __init__(self, a, db):
|
||||
self.a = a
|
||||
self.db = db
|
||||
|
@ -38,14 +38,14 @@ class AppCatalog(Catalog):
|
|||
Attribute('row_factory', sqlite3.Row))
|
||||
""" :type: (objects.Provider) -> sqlite3.Connection """
|
||||
|
||||
object_a = NewInstance(A,
|
||||
object_a = NewInstance(ObjectA,
|
||||
InitArg('db', database))
|
||||
""" :type: (objects.Provider) -> A """
|
||||
""" :type: (objects.Provider) -> ObjectA """
|
||||
|
||||
object_b = NewInstance(B,
|
||||
object_b = NewInstance(ObjectB,
|
||||
InitArg('a', object_a),
|
||||
InitArg('db', database))
|
||||
""" :type: (objects.Provider) -> B """
|
||||
""" :type: (objects.Provider) -> ObjectB """
|
||||
|
||||
|
||||
# Catalog injection into consumer class.
|
||||
|
@ -69,3 +69,91 @@ assert a1 is not a2
|
|||
assert b1 is not b2
|
||||
assert a1.db is a2.db is b1.db is b2.db
|
||||
```
|
||||
|
||||
Example of injections using objects.catalog:
|
||||
|
||||
```python
|
||||
"""
|
||||
Concept example of objects injections.
|
||||
"""
|
||||
|
||||
from objects import Catalog, Singleton, NewInstance, InitArg, Attribute, inject
|
||||
import sqlite3
|
||||
|
||||
|
||||
# Some example class.
|
||||
class ObjectA(object):
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
|
||||
# Catalog of objects providers.
|
||||
class AppCatalog(Catalog):
|
||||
"""
|
||||
Objects catalog.
|
||||
"""
|
||||
|
||||
database = Singleton(sqlite3.Connection,
|
||||
InitArg('database', ':memory:'),
|
||||
Attribute('row_factory', sqlite3.Row))
|
||||
""" :type: (objects.Provider) -> sqlite3.Connection """
|
||||
|
||||
object_a = NewInstance(ObjectA,
|
||||
InitArg('db', database))
|
||||
""" :type: (objects.Provider) -> ObjectA """
|
||||
|
||||
|
||||
# Class attributes injections.
|
||||
@inject(Attribute('a', AppCatalog.object_a))
|
||||
@inject(Attribute('database', AppCatalog.database))
|
||||
class Consumer(object):
|
||||
"""
|
||||
Some consumer class with database dependency via attribute.
|
||||
"""
|
||||
|
||||
a = None
|
||||
""" :type: (objects.Provider) -> ObjectA """
|
||||
|
||||
database = None
|
||||
""" :type: (objects.Provider) -> sqlite3.Connection """
|
||||
|
||||
def tests(self):
|
||||
a1, a2 = self.a(), self.a()
|
||||
|
||||
assert a1 is not a2
|
||||
assert a1.db is a2.db is self.database()
|
||||
|
||||
|
||||
consumer = Consumer()
|
||||
consumer.tests()
|
||||
|
||||
|
||||
# Class __init__ injections.
|
||||
@inject(InitArg('a1', AppCatalog.object_a))
|
||||
@inject(InitArg('a2', AppCatalog.object_a))
|
||||
@inject(InitArg('database', AppCatalog.database))
|
||||
class ConsumerWithInitArg(object):
|
||||
"""
|
||||
Some consumer class with database dependency via init arg.
|
||||
"""
|
||||
|
||||
def __init__(self, a1, a2, database):
|
||||
"""
|
||||
Initializer.
|
||||
|
||||
:param a1: ObjectA
|
||||
:param a2: ObjectA
|
||||
:param database: sqlite3.Connection
|
||||
"""
|
||||
self.a1 = a1
|
||||
self.a2 = a2
|
||||
self.database = database
|
||||
|
||||
def tests(self):
|
||||
assert self.a1 is not self.a2
|
||||
assert self.a1.db is self.a2.db is self.database
|
||||
|
||||
|
||||
consumer = ConsumerWithInitArg()
|
||||
consumer.tests()
|
||||
```
|
||||
|
|
|
@ -7,12 +7,12 @@ import sqlite3
|
|||
|
||||
|
||||
# Some example classes.
|
||||
class A(object):
|
||||
class ObjectA(object):
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
|
||||
class B(object):
|
||||
class ObjectB(object):
|
||||
def __init__(self, a, db):
|
||||
self.a = a
|
||||
self.db = db
|
||||
|
@ -29,14 +29,14 @@ class AppCatalog(Catalog):
|
|||
Attribute('row_factory', sqlite3.Row))
|
||||
""" :type: (objects.Provider) -> sqlite3.Connection """
|
||||
|
||||
object_a = NewInstance(A,
|
||||
object_a = NewInstance(ObjectA,
|
||||
InitArg('db', database))
|
||||
""" :type: (objects.Provider) -> A """
|
||||
""" :type: (objects.Provider) -> ObjectA """
|
||||
|
||||
object_b = NewInstance(B,
|
||||
object_b = NewInstance(ObjectB,
|
||||
InitArg('a', object_a),
|
||||
InitArg('db', database))
|
||||
""" :type: (objects.Provider) -> B """
|
||||
""" :type: (objects.Provider) -> ObjectB """
|
||||
|
||||
|
||||
# Catalog injection into consumer class.
|
||||
|
|
83
examples/injections.py
Normal file
83
examples/injections.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
"""
|
||||
Concept example of objects injections.
|
||||
"""
|
||||
|
||||
from objects import Catalog, Singleton, NewInstance, InitArg, Attribute, inject
|
||||
import sqlite3
|
||||
|
||||
|
||||
# Some example class.
|
||||
class ObjectA(object):
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
|
||||
# Catalog of objects providers.
|
||||
class AppCatalog(Catalog):
|
||||
"""
|
||||
Objects catalog.
|
||||
"""
|
||||
|
||||
database = Singleton(sqlite3.Connection,
|
||||
InitArg('database', ':memory:'),
|
||||
Attribute('row_factory', sqlite3.Row))
|
||||
""" :type: (objects.Provider) -> sqlite3.Connection """
|
||||
|
||||
object_a = NewInstance(ObjectA,
|
||||
InitArg('db', database))
|
||||
""" :type: (objects.Provider) -> ObjectA """
|
||||
|
||||
|
||||
# Class attributes injections.
|
||||
@inject(Attribute('a', AppCatalog.object_a))
|
||||
@inject(Attribute('database', AppCatalog.database))
|
||||
class Consumer(object):
|
||||
"""
|
||||
Some consumer class with database dependency via attribute.
|
||||
"""
|
||||
|
||||
a = None
|
||||
""" :type: (objects.Provider) -> ObjectA """
|
||||
|
||||
database = None
|
||||
""" :type: (objects.Provider) -> sqlite3.Connection """
|
||||
|
||||
def tests(self):
|
||||
a1, a2 = self.a(), self.a()
|
||||
|
||||
assert a1 is not a2
|
||||
assert a1.db is a2.db is self.database()
|
||||
|
||||
|
||||
consumer = Consumer()
|
||||
consumer.tests()
|
||||
|
||||
|
||||
# Class __init__ injections.
|
||||
@inject(InitArg('a1', AppCatalog.object_a))
|
||||
@inject(InitArg('a2', AppCatalog.object_a))
|
||||
@inject(InitArg('database', AppCatalog.database))
|
||||
class ConsumerWithInitArg(object):
|
||||
"""
|
||||
Some consumer class with database dependency via init arg.
|
||||
"""
|
||||
|
||||
def __init__(self, a1, a2, database):
|
||||
"""
|
||||
Initializer.
|
||||
|
||||
:param a1: ObjectA
|
||||
:param a2: ObjectA
|
||||
:param database: sqlite3.Connection
|
||||
"""
|
||||
self.a1 = a1
|
||||
self.a2 = a2
|
||||
self.database = database
|
||||
|
||||
def tests(self):
|
||||
assert self.a1 is not self.a2
|
||||
assert self.a1.db is self.a2.db is self.database
|
||||
|
||||
|
||||
consumer = ConsumerWithInitArg()
|
||||
consumer.tests()
|
33
manage.py
Normal file
33
manage.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
CLI Commands.
|
||||
"""
|
||||
|
||||
import os
|
||||
from setup import version
|
||||
from manager import Manager
|
||||
|
||||
|
||||
manager = Manager()
|
||||
|
||||
|
||||
@manager.command
|
||||
def publish(with_tag=True):
|
||||
"""
|
||||
Publishes current version to PyPi.
|
||||
"""
|
||||
os.system('python setup.py sdist upload')
|
||||
if with_tag:
|
||||
tag()
|
||||
|
||||
|
||||
@manager.command
|
||||
def tag():
|
||||
"""
|
||||
Makes tag from current version.
|
||||
"""
|
||||
os.system('git tag -a {0} -m \'version {0}\''.format(version))
|
||||
os.system('git push --tags')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
manager.main()
|
|
@ -3,9 +3,9 @@
|
|||
"""
|
||||
|
||||
from .catalog import Catalog
|
||||
from .std_providers import (Provider, NewInstance, Singleton, Class, Object,
|
||||
from .providers import (Provider, NewInstance, Singleton, Class, Object,
|
||||
Function, Value)
|
||||
from .injections import InitArg, Attribute, Method
|
||||
from .injections import InitArg, Attribute, Method, inject
|
||||
|
||||
|
||||
__all__ = ['Catalog',
|
||||
|
@ -15,4 +15,4 @@ __all__ = ['Catalog',
|
|||
'Object', 'Function', 'Value',
|
||||
|
||||
# Injections
|
||||
'InitArg', 'Attribute', 'Method']
|
||||
'InitArg', 'Attribute', 'Method', 'inject']
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Catalog module.
|
||||
"""
|
||||
|
||||
from .std_providers import Provider
|
||||
from .providers import Provider
|
||||
|
||||
|
||||
class Catalog(object):
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Injections module.
|
||||
"""
|
||||
|
||||
from inspect import isclass
|
||||
from functools import wraps
|
||||
|
||||
|
||||
class Injection(object):
|
||||
"""
|
||||
|
@ -15,14 +18,14 @@ class Injection(object):
|
|||
self.name = name
|
||||
self.injectable = injectable
|
||||
|
||||
@classmethod
|
||||
def fetch(cls, injections):
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Fetches injections of self type from list.
|
||||
Returns injectable value.
|
||||
"""
|
||||
return tuple([injection
|
||||
for injection in injections
|
||||
if isinstance(injection, cls)])
|
||||
if hasattr(self.injectable, '__is_objects_provider__'):
|
||||
return self.injectable()
|
||||
return self.injectable
|
||||
|
||||
|
||||
class InitArg(Injection):
|
||||
|
@ -41,3 +44,27 @@ class Method(Injection):
|
|||
"""
|
||||
Method injection.
|
||||
"""
|
||||
|
||||
|
||||
def inject(injection):
|
||||
"""
|
||||
Injection decorator.
|
||||
"""
|
||||
def decorator(callback_or_cls):
|
||||
if isclass(callback_or_cls):
|
||||
cls = callback_or_cls
|
||||
if isinstance(injection, Attribute):
|
||||
setattr(cls, injection.name, injection.injectable)
|
||||
elif isinstance(injection, InitArg):
|
||||
cls.__init__ = decorator(cls.__init__)
|
||||
return cls
|
||||
else:
|
||||
callback = callback_or_cls
|
||||
|
||||
@wraps(callback)
|
||||
def wrapped(*args, **kwargs):
|
||||
if injection.name not in kwargs:
|
||||
kwargs[injection.name] = injection.value
|
||||
return callback(*args, **kwargs)
|
||||
return wrapped
|
||||
return decorator
|
||||
|
|
|
@ -10,25 +10,29 @@ class Provider(object):
|
|||
Base provider class.
|
||||
"""
|
||||
|
||||
__is_objects_provider__ = True
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""
|
||||
Returns provided instance.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
def prepare_injections(injections):
|
||||
|
||||
def prepare_injections(injections):
|
||||
"""
|
||||
Prepares injections list to injection.
|
||||
"""
|
||||
prepared_injections = dict()
|
||||
for injection in injections:
|
||||
if isinstance(injection.injectable, Provider):
|
||||
value = injection.injectable.__call__()
|
||||
else:
|
||||
value = injection.injectable
|
||||
prepared_injections[injection.name] = value
|
||||
return prepared_injections
|
||||
return [(injection.name, injection.value) for injection in injections]
|
||||
|
||||
|
||||
def fetch_injections(injections, injection_type):
|
||||
"""
|
||||
Fetches injections of injection type from list.
|
||||
"""
|
||||
return tuple([injection
|
||||
for injection in injections
|
||||
if isinstance(injection, injection_type)])
|
||||
|
||||
|
||||
class NewInstance(Provider):
|
||||
|
@ -41,26 +45,26 @@ class NewInstance(Provider):
|
|||
Initializer.
|
||||
"""
|
||||
self.provides = provides
|
||||
self.init_injections = InitArg.fetch(injections)
|
||||
self.attribute_injections = Attribute.fetch(injections)
|
||||
self.method_injections = Method.fetch(injections)
|
||||
self.init_injections = fetch_injections(injections, InitArg)
|
||||
self.attribute_injections = fetch_injections(injections, Attribute)
|
||||
self.method_injections = fetch_injections(injections, Method)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""
|
||||
Returns provided instance.
|
||||
"""
|
||||
init_injections = Provider.prepare_injections(self.init_injections)
|
||||
init_injections = prepare_injections(self.init_injections)
|
||||
init_injections = dict(init_injections)
|
||||
init_injections.update(kwargs)
|
||||
|
||||
provided = self.provides(*args, **init_injections)
|
||||
|
||||
attribute_injections = Provider.prepare_injections(
|
||||
self.attribute_injections)
|
||||
for name, injectable in attribute_injections.iteritems():
|
||||
attribute_injections = prepare_injections(self.attribute_injections)
|
||||
for name, injectable in attribute_injections:
|
||||
setattr(provided, name, injectable)
|
||||
|
||||
method_injections = Provider.prepare_injections(self.method_injections)
|
||||
for name, injectable in method_injections.iteritems():
|
||||
method_injections = prepare_injections(self.method_injections)
|
||||
for name, injectable in method_injections:
|
||||
getattr(provided, name)(injectable)
|
||||
|
||||
return provided
|
15
setup.py
15
setup.py
|
@ -2,8 +2,6 @@
|
|||
`Objects` setup script.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
|
@ -26,16 +24,9 @@ with open('requirements.txt') as version:
|
|||
with open('VERSION') as version:
|
||||
version = version.read().strip()
|
||||
|
||||
# Helper commands.
|
||||
if sys.argv[-1] == 'publish':
|
||||
os.system('python setup.py sdist upload')
|
||||
sys.exit()
|
||||
if sys.argv[-1] == 'tag':
|
||||
os.system('git tag -a {0} -m \'version {0}\''.format(version))
|
||||
os.system('git push --tags')
|
||||
sys.exit()
|
||||
|
||||
setup(
|
||||
if __name__ == '__main__':
|
||||
setup(
|
||||
name='Objects',
|
||||
version=version,
|
||||
description='Python catalogs of objects providers',
|
||||
|
@ -78,4 +69,4 @@ setup(
|
|||
'Topic :: Software Development :: Libraries',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
]
|
||||
)
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user