From 028e5e9ef78452c3ceefb3dfd16e23e448cdc899 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Sun, 11 Jan 2015 15:03:45 +0200 Subject: [PATCH] 0.0.3 version --- README.md | 102 +++++++++++++++++++-- VERSION | 2 +- examples/concept.py | 12 +-- examples/injections.py | 83 +++++++++++++++++ manage.py | 33 +++++++ objects/__init__.py | 8 +- objects/catalog.py | 2 +- objects/injections.py | 39 ++++++-- objects/{std_providers.py => providers.py} | 48 +++++----- setup.py | 99 +++++++++----------- 10 files changed, 327 insertions(+), 101 deletions(-) create mode 100644 examples/injections.py create mode 100644 manage.py rename objects/{std_providers.py => providers.py} (66%) diff --git a/README.md b/README.md index 5cb65d7a..ad2205e0 100644 --- a/README.md +++ b/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() +``` diff --git a/VERSION b/VERSION index 4e379d2b..bcab45af 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.2 +0.0.3 diff --git a/examples/concept.py b/examples/concept.py index 2c0616c1..ad41daca 100644 --- a/examples/concept.py +++ b/examples/concept.py @@ -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. diff --git a/examples/injections.py b/examples/injections.py new file mode 100644 index 00000000..8ee78af6 --- /dev/null +++ b/examples/injections.py @@ -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() diff --git a/manage.py b/manage.py new file mode 100644 index 00000000..98071a46 --- /dev/null +++ b/manage.py @@ -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() diff --git a/objects/__init__.py b/objects/__init__.py index 7fe08499..5839e635 100644 --- a/objects/__init__.py +++ b/objects/__init__.py @@ -3,9 +3,9 @@ """ from .catalog import Catalog -from .std_providers import (Provider, NewInstance, Singleton, Class, Object, - Function, Value) -from .injections import InitArg, Attribute, Method +from .providers import (Provider, NewInstance, Singleton, Class, Object, + Function, Value) +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'] diff --git a/objects/catalog.py b/objects/catalog.py index 66a706a4..23b0520d 100644 --- a/objects/catalog.py +++ b/objects/catalog.py @@ -2,7 +2,7 @@ Catalog module. """ -from .std_providers import Provider +from .providers import Provider class Catalog(object): diff --git a/objects/injections.py b/objects/injections.py index 0b24b5c4..a6daf8c4 100644 --- a/objects/injections.py +++ b/objects/injections.py @@ -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 diff --git a/objects/std_providers.py b/objects/providers.py similarity index 66% rename from objects/std_providers.py rename to objects/providers.py index 6d74bcb6..287cd96c 100644 --- a/objects/std_providers.py +++ b/objects/providers.py @@ -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): - """ - 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 + +def prepare_injections(injections): + """ + Prepares injections list to injection. + """ + 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 diff --git a/setup.py b/setup.py index 1e9003f0..2c1b8234 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,6 @@ `Objects` setup script. """ -import os -import sys from setuptools import setup @@ -26,56 +24,49 @@ 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( - name='Objects', - version=version, - description='Python catalogs of objects providers', - long_description=description, - author='Roman Mogilatov', - author_email='rmogilatov@gmail.com', - maintainer='Roman Mogilatov', - maintainer_email='rmogilatov@gmail.com', - url='https://github.com/rmk135/objects', - license='BSD New', - packages=['objects'], - zip_safe=True, - install_requires=requirements, - # keywords=['Dependency injection', - # 'Dependency injection container', - # 'DI', - # 'DIC', - # 'Dependency injector', - # 'Inversion of Control', - # 'Inversion of Control container', - # 'IoC', - # 'IoC container'], - classifiers=[ - 'Development Status :: 1 - Planning', - # 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - # 'Programming Language :: Python :: 3', - # 'Programming Language :: Python :: 3.2', - # 'Programming Language :: Python :: 3.3', - # 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: Implementation :: CPython', - # 'Programming Language :: Python :: Implementation :: PyPy', - 'Topic :: Software Development', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules', - ] -) +if __name__ == '__main__': + setup( + name='Objects', + version=version, + description='Python catalogs of objects providers', + long_description=description, + author='Roman Mogilatov', + author_email='rmogilatov@gmail.com', + maintainer='Roman Mogilatov', + maintainer_email='rmogilatov@gmail.com', + url='https://github.com/rmk135/objects', + license='BSD New', + packages=['objects'], + zip_safe=True, + install_requires=requirements, + # keywords=['Dependency injection', + # 'Dependency injection container', + # 'DI', + # 'DIC', + # 'Dependency injector', + # 'Inversion of Control', + # 'Inversion of Control container', + # 'IoC', + # 'IoC container'], + classifiers=[ + 'Development Status :: 1 - Planning', + # 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + # 'Programming Language :: Python :: 3', + # 'Programming Language :: Python :: 3.2', + # 'Programming Language :: Python :: 3.3', + # 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: Implementation :: CPython', + # 'Programming Language :: Python :: Implementation :: PyPy', + 'Topic :: Software Development', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Python Modules', + ] + )