diff --git a/examples/concept.py b/examples/concept.py index 0af24bcb..2c0616c1 100644 --- a/examples/concept.py +++ b/examples/concept.py @@ -2,7 +2,7 @@ Concept example of objects catalogs. """ -import objects +from objects import Catalog, Singleton, NewInstance, InitArg, Attribute import sqlite3 @@ -19,29 +19,30 @@ class B(object): # Catalog of objects providers. -class Catalog(objects.Catalog): +class AppCatalog(Catalog): """ Objects catalog. """ - database = objects.Singleton(provides=sqlite3.Connection, - database='example.db') + database = Singleton(sqlite3.Connection, + InitArg('database', ':memory:'), + Attribute('row_factory', sqlite3.Row)) """ :type: (objects.Provider) -> sqlite3.Connection """ - object_a = objects.NewInstance(provides=A, - db=database) + object_a = NewInstance(A, + InitArg('db', database)) """ :type: (objects.Provider) -> A """ - object_b = objects.NewInstance(provides=B, - a=object_a, - db=database) + object_b = NewInstance(B, + InitArg('a', object_a), + InitArg('db', database)) """ :type: (objects.Provider) -> B """ # Catalog injection into consumer class. class Consumer(object): - catalog = Catalog(Catalog.object_a, - Catalog.object_b) + catalog = AppCatalog(AppCatalog.object_a, + AppCatalog.object_b) def return_a_b(self): return (self.catalog.object_a(), @@ -51,8 +52,8 @@ a1, b1 = Consumer().return_a_b() # Catalog static provides. -a2 = Catalog.object_a() -b2 = Catalog.object_b() +a2 = AppCatalog.object_a() +b2 = AppCatalog.object_b() # Some asserts. assert a1 is not a2 diff --git a/examples/concept_v1.py b/examples/concept_v1.py deleted file mode 100644 index 1c8faed5..00000000 --- a/examples/concept_v1.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Concept example of objects catalogs. -""" - -from objects import Catalog, Singleton, NewInstance, KwArg, Attribute -import sqlite3 - - -# Some example classes. -class A(object): - def __init__(self, db): - self.db = db - - -class B(object): - def __init__(self, a, db): - self.a = a - self.db = db - - -# Catalog of objects providers. -class AppCatalog(Catalog): - """ - Objects catalog. - """ - - database = Singleton(provides=sqlite3.Connection, - database=KwArg('example.db'), - row_factory=Attribute(sqlite3.Row)) - """ :type: (Provider) -> sqlite3.Connection """ - - object_a = NewInstance(provides=A, - db=KwArg(database)) - """ :type: (Provider) -> A """ - - object_b = NewInstance(provides=B, - a=KwArg(object_a), - db=KwArg(database)) - """ :type: (Provider) -> B """ - - -# Catalog injection into consumer class. -class Consumer(object): - catalog = AppCatalog(AppCatalog.object_a, - AppCatalog.object_b) - - def return_a_b(self): - return (self.catalog.object_a(), - self.catalog.object_b()) - -a1, b1 = Consumer().return_a_b() - - -# Catalog static provides. -a2 = AppCatalog.object_a() -b2 = AppCatalog.object_b() - -# Some asserts. -assert a1 is not a2 -assert b1 is not b2 -assert a1.db is a2.db is b1.db is b2.db diff --git a/examples/concept_v2.py b/examples/concept_v2.py deleted file mode 100644 index d7b23240..00000000 --- a/examples/concept_v2.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Concept example of objects catalogs. -""" - -from objects import Catalog, Singleton, NewInstance, KwArg, Attribute -import sqlite3 - - -# Some example classes. -class A(object): - def __init__(self, db): - self.db = db - - -class B(object): - def __init__(self, a, db): - self.a = a - self.db = db - - -# Catalog of objects providers. -class AppCatalog(Catalog): - """ - Objects catalog. - """ - - database = Singleton(sqlite3.Connection, - KwArg('database', ':memory:'), - Attribute('row_factory', sqlite3.Row)) - """ :type: (objects.Provider) -> sqlite3.Connection """ - - object_a = NewInstance(A, - KwArg('db', database)) - """ :type: (objects.Provider) -> A """ - - object_b = NewInstance(B, - KwArg('a', object_a), - KwArg('db', database)) - """ :type: (objects.Provider) -> B """ - - -# Catalog injection into consumer class. -class Consumer(object): - catalog = AppCatalog(AppCatalog.object_a, - AppCatalog.object_b) - - def return_a_b(self): - return (self.catalog.object_a(), - self.catalog.object_b()) - -a1, b1 = Consumer().return_a_b() - - -# Catalog static provides. -a2 = AppCatalog.object_a() -b2 = AppCatalog.object_b() - -# Some asserts. -assert a1 is not a2 -assert b1 is not b2 -assert a1.db is a2.db is b1.db is b2.db diff --git a/objects/__init__.py b/objects/__init__.py index b206d951..7fe08499 100644 --- a/objects/__init__.py +++ b/objects/__init__.py @@ -5,7 +5,14 @@ from .catalog import Catalog from .std_providers import (Provider, NewInstance, Singleton, Class, Object, Function, Value) +from .injections import InitArg, Attribute, Method -__all__ = ['Catalog', 'Provider', 'NewInstance', 'Singleton', 'Class', - 'Object', 'Function', 'Value'] +__all__ = ['Catalog', + + # Providers + 'Provider', 'NewInstance', 'Singleton', 'Class', + 'Object', 'Function', 'Value', + + # Injections + 'InitArg', 'Attribute', 'Method'] diff --git a/objects/injections.py b/objects/injections.py new file mode 100644 index 00000000..0b24b5c4 --- /dev/null +++ b/objects/injections.py @@ -0,0 +1,43 @@ +""" +Injections module. +""" + + +class Injection(object): + """ + Base injection class. + """ + + def __init__(self, name, injectable): + """ + Initializer. + """ + self.name = name + self.injectable = injectable + + @classmethod + def fetch(cls, injections): + """ + Fetches injections of self type from list. + """ + return tuple([injection + for injection in injections + if isinstance(injection, cls)]) + + +class InitArg(Injection): + """ + Init argument injection. + """ + + +class Attribute(Injection): + """ + Attribute injection. + """ + + +class Method(Injection): + """ + Method injection. + """ diff --git a/objects/std_providers.py b/objects/std_providers.py index 460987e3..6d74bcb6 100644 --- a/objects/std_providers.py +++ b/objects/std_providers.py @@ -2,6 +2,8 @@ Standard providers. """ +from .injections import InitArg, Attribute, Method + class Provider(object): """ @@ -14,34 +16,54 @@ class Provider(object): """ 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 + class NewInstance(Provider): """ New instance providers will create and return new instance on every call. """ - def __init__(self, provides, **dependencies): + def __init__(self, provides, *injections): """ Initializer. """ self.provides = provides - self.dependencies = dependencies + self.init_injections = InitArg.fetch(injections) + self.attribute_injections = Attribute.fetch(injections) + self.method_injections = Method.fetch(injections) def __call__(self, *args, **kwargs): """ Returns provided instance. """ - for name, dependency in self.dependencies.iteritems(): - if name in kwargs: - continue + init_injections = Provider.prepare_injections(self.init_injections) + init_injections.update(kwargs) - if isinstance(dependency, Provider): - value = dependency.__call__() - else: - value = dependency + provided = self.provides(*args, **init_injections) - kwargs[name] = value - return self.provides(*args, **kwargs) + attribute_injections = Provider.prepare_injections( + self.attribute_injections) + for name, injectable in attribute_injections.iteritems(): + setattr(provided, name, injectable) + + method_injections = Provider.prepare_injections(self.method_injections) + for name, injectable in method_injections.iteritems(): + getattr(provided, name)(injectable) + + return provided class Singleton(NewInstance):