From 7216b8a5b97d0f1be0f092a3a09effa4efb49de3 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 00:46:48 +0300 Subject: [PATCH 01/15] Adding Objects introduction --- README.md | 30 ++++++++++++++++++++++++++++-- objects/__init__.py | 5 ++++- setup.py | 21 +++++++++++---------- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d12430e7..6d124cd7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Objects ======= -Python catalogs of objects providers +Dependency management tool for Python projects. [![Latest Version](https://pypip.in/version/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) [![Downloads](https://pypip.in/download/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) @@ -11,7 +11,33 @@ Python catalogs of objects providers [![Supported Python versions](https://pypip.in/py_versions/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) [![Supported Python implementations](https://pypip.in/implementation/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) -Example: +Introduction +------------ + +Python ecosystem consists of a big amount of various classes, functions and +objects that could be used for applications development. Each of them has its +own role. + +Modern Python applications are mostly the composition of well-known open +source systems, frameworks, libraries and some turnkey functionality. + +When application goes bigger, an amount of objects and dependencies in it also +increased extremely fast and became hard to maintain. + +`Objects` is designed to be developer's friendly tool for managing objects +and their dependencies in formal, pretty way. Main idea of `Objects` is to +keep dependencies of Python projects under control. + +Entities +-------- + +- Providers are strategies of accessing to objects. +- Injections are additional instructions, that are used for determining objects +dependencies. +- Catalogs are named set of providers. + +Example +------- ```python """Concept example of `Objects`.""" diff --git a/objects/__init__.py b/objects/__init__.py index 1486e968..b6d6d6bb 100644 --- a/objects/__init__.py +++ b/objects/__init__.py @@ -1,4 +1,7 @@ -"""Objects.""" +"""Objects. + +Dependency management tool for Python projects. +""" from .catalog import AbstractCatalog from .catalog import override diff --git a/setup.py b/setup.py index 5eb50a30..0946aa08 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ if __name__ == '__main__': setup( name='Objects', version=version, - description='Python catalogs of objects providers', + description='Dependency management tool for Python projects', long_description=description, author='Roman Mogilatov', author_email='rmogilatov@gmail.com', @@ -40,15 +40,16 @@ if __name__ == '__main__': 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'], + keywords=['Dependency management', + 'Dependency injection', + 'Dependency injection container', + 'DI', + 'DIC', + 'Dependency injector', + 'Inversion of Control', + 'Inversion of Control container', + 'IoC', + 'IoC container'], classifiers=[ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', From 5967044458d25f317a2a6b07c268b9eb6526924b Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 00:59:14 +0300 Subject: [PATCH 02/15] Updating Introduction --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d124cd7..0f3ff565 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ own role. Modern Python applications are mostly the composition of well-known open source systems, frameworks, libraries and some turnkey functionality. -When application goes bigger, an amount of objects and dependencies in it also -increased extremely fast and became hard to maintain. +When application goes bigger, an amount of objects and their dependencies in +it also increased extremely fast and became hard to maintain. `Objects` is designed to be developer's friendly tool for managing objects and their dependencies in formal, pretty way. Main idea of `Objects` is to From d7268853d40f008502d4309cf02cdda612a6fdd4 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 01:00:52 +0300 Subject: [PATCH 03/15] Updating Introduction --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f3ff565..30c50101 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ own role. Modern Python applications are mostly the composition of well-known open source systems, frameworks, libraries and some turnkey functionality. -When application goes bigger, an amount of objects and their dependencies in -it also increased extremely fast and became hard to maintain. +When application goes bigger, its amount of objects and their dependencies +also increased extremely fast and became hard to maintain. `Objects` is designed to be developer's friendly tool for managing objects and their dependencies in formal, pretty way. Main idea of `Objects` is to From 245c8a0aaf723e9a9f4ccdf0d6d0e758ff549478 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 01:03:39 +0300 Subject: [PATCH 04/15] Updating Introduction --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 30c50101..78bdeb1b 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ also increased extremely fast and became hard to maintain. `Objects` is designed to be developer's friendly tool for managing objects and their dependencies in formal, pretty way. Main idea of `Objects` is to -keep dependencies of Python projects under control. +keep dependencies under control. Entities -------- From 27cf7acc351f3d51bbecd0fc370320e1b9a4d545 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 01:07:45 +0300 Subject: [PATCH 05/15] Updating Introduction --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 78bdeb1b..5f6850a2 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ keep dependencies under control. Entities -------- -- Providers are strategies of accessing to objects. -- Injections are additional instructions, that are used for determining objects -dependencies. +- Providers are strategies of accessing objects. +- Injections are additional instructions, that are used for determining +dependencies of objects. - Catalogs are named set of providers. Example From b1800bfc083780e88b8dd4ed6b1ba3385c240cc9 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 30 Mar 2015 15:45:51 +0300 Subject: [PATCH 06/15] Update README.md --- README.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5f6850a2..7d6bad29 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Objects -======= +# Objects Dependency management tool for Python projects. @@ -11,8 +10,7 @@ Dependency management tool for Python projects. [![Supported Python versions](https://pypip.in/py_versions/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) [![Supported Python implementations](https://pypip.in/implementation/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) -Introduction ------------- +## Introduction Python ecosystem consists of a big amount of various classes, functions and objects that could be used for applications development. Each of them has its @@ -28,16 +26,24 @@ also increased extremely fast and became hard to maintain. and their dependencies in formal, pretty way. Main idea of `Objects` is to keep dependencies under control. -Entities --------- +## Entities -- Providers are strategies of accessing objects. -- Injections are additional instructions, that are used for determining +Current section describes main `Objects` entities and their interaction. + +### Providers + +Providers are strategies of accessing objects. + +### Injections + +Injections are additional instructions, that are used for determining dependencies of objects. -- Catalogs are named set of providers. -Example -------- +### Catalogs + +Catalogs are named set of providers. + +## Example ```python """Concept example of `Objects`.""" From 90ff917267f24a37442b7f2375875ffefb71adc1 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 30 Mar 2015 15:56:46 +0300 Subject: [PATCH 07/15] Update README.md --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 7d6bad29..1cfdeeb4 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,32 @@ Current section describes main `Objects` entities and their interaction. Providers are strategies of accessing objects. +They describe how particular object need to be provided. For example: + +```python +from objects.providers import NewInstance +from objects.providers import Singleton + + +# NewInstance provider will create new instance of specified class on every call. + +new_object = NewInstance(object) + +object_1 = new_object() +object_2 = new_object() + +assert object_1 is not object_2 + +# Singleton provider will create new instance of specified class on first call, and return same instance on every next call. + +single_object = Singleton(object) + +single_object_1 = single_object() +single_object_2 = single_object() + +assert single_object_1 is single_object_2 +``` + ### Injections Injections are additional instructions, that are used for determining From f64d155154ec297872af0043ac6e82fc540f503e Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 16:11:33 +0300 Subject: [PATCH 08/15] Adding Injections desciption --- README.md | 21 ++++++++++++++++++--- examples/readme/providers.py | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 examples/readme/providers.py diff --git a/README.md b/README.md index 1cfdeeb4..ffee1d25 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,17 @@ Current section describes main `Objects` entities and their interaction. Providers are strategies of accessing objects. -They describe how particular object need to be provided. For example: +They describe how particular object will be provided. For example: ```python +"""`NewInstance` and `Singleton` providers example.""" + from objects.providers import NewInstance from objects.providers import Singleton -# NewInstance provider will create new instance of specified class on every call. +# NewInstance provider will create new instance of specified class +# on every call. new_object = NewInstance(object) @@ -50,7 +53,8 @@ object_2 = new_object() assert object_1 is not object_2 -# Singleton provider will create new instance of specified class on first call, and return same instance on every next call. +# Singleton provider will create new instance of specified class on first call, +# and return same instance on every next call. single_object = Singleton(object) @@ -65,6 +69,17 @@ assert single_object_1 is single_object_2 Injections are additional instructions, that are used for determining dependencies of objects. +Objects can take dependencies in various forms. Some objects take init +arguments, other are using attributes or methods to be initialized. Injection, +in terms of `Objects`, is an instruction how to provide dependency for the +particular object. + +Every Python object could be an injection value. Special case is a `Objects` +provider as an injection value. In such case, injection value is a result of +injectable provider call (every time injection is done). + +Injections are used by providers. + ### Catalogs Catalogs are named set of providers. diff --git a/examples/readme/providers.py b/examples/readme/providers.py new file mode 100644 index 00000000..11e33989 --- /dev/null +++ b/examples/readme/providers.py @@ -0,0 +1,25 @@ +"""`NewInstance` and `Singleton` providers example.""" + +from objects.providers import NewInstance +from objects.providers import Singleton + + +# NewInstance provider will create new instance of specified class +# on every call. + +new_object = NewInstance(object) + +object_1 = new_object() +object_2 = new_object() + +assert object_1 is not object_2 + +# Singleton provider will create new instance of specified class on first call, +# and return same instance on every next call. + +single_object = Singleton(object) + +single_object_1 = single_object() +single_object_2 = single_object() + +assert single_object_1 is single_object_2 From 48ff686ae8ed52a502a15c7f587ea0be5c3164a4 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 16:25:53 +0300 Subject: [PATCH 09/15] Removing unnecessary nl --- README.md | 2 -- examples/readme/providers.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/README.md b/README.md index ffee1d25..0acd90b4 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ from objects.providers import Singleton # NewInstance provider will create new instance of specified class # on every call. - new_object = NewInstance(object) object_1 = new_object() @@ -55,7 +54,6 @@ assert object_1 is not object_2 # Singleton provider will create new instance of specified class on first call, # and return same instance on every next call. - single_object = Singleton(object) single_object_1 = single_object() diff --git a/examples/readme/providers.py b/examples/readme/providers.py index 11e33989..ce662c38 100644 --- a/examples/readme/providers.py +++ b/examples/readme/providers.py @@ -6,7 +6,6 @@ from objects.providers import Singleton # NewInstance provider will create new instance of specified class # on every call. - new_object = NewInstance(object) object_1 = new_object() @@ -16,7 +15,6 @@ assert object_1 is not object_2 # Singleton provider will create new instance of specified class on first call, # and return same instance on every next call. - single_object = Singleton(object) single_object_1 = single_object() From d8c7a32220c8f29b5a9b3208ee5f9c6d3f60758e Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 17:34:32 +0300 Subject: [PATCH 10/15] Adding providers description --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0acd90b4..38af1bf5 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ Current section describes main `Objects` entities and their interaction. Providers are strategies of accessing objects. -They describe how particular object will be provided. For example: +All providers are callable. They describe how particular object will be +provided. For example: ```python """`NewInstance` and `Singleton` providers example.""" From 3ee60fa01bf68f212d6c10d33f7fb5f599bc4753 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 30 Mar 2015 17:49:11 +0300 Subject: [PATCH 11/15] Updating providers description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38af1bf5..bdfd5026 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Current section describes main `Objects` entities and their interaction. Providers are strategies of accessing objects. -All providers are callable. They describe how particular object will be +All providers are callable. They describe how particular objects will be provided. For example: ```python From e1009373bc9fc74bfc2c1c2071bce3546087e949 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Tue, 31 Mar 2015 12:37:16 +0300 Subject: [PATCH 12/15] Adding injections example --- README.md | 52 +++++++++++++++++++++++++++++++++-- examples/readme/injections.py | 45 ++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 examples/readme/injections.py diff --git a/README.md b/README.md index bdfd5026..d565d466 100644 --- a/README.md +++ b/README.md @@ -73,12 +73,60 @@ arguments, other are using attributes or methods to be initialized. Injection, in terms of `Objects`, is an instruction how to provide dependency for the particular object. -Every Python object could be an injection value. Special case is a `Objects` -provider as an injection value. In such case, injection value is a result of +Every Python object could be an injection's value. Special case is a `Objects` +provider as an injection's value. In such case, injection value is a result of injectable provider call (every time injection is done). Injections are used by providers. +```python +"""`KwArg` and `Attribute` injections example.""" + +import sqlite3 + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1').fetchone()[0] + + +# Database and `ObjectA` providers. +database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + +object_a = NewInstance(ObjectA, + KwArg('database', database)) + +# Creating several `ObjectA` instances. +object_a_1 = object_a() +object_a_2 = object_a() + +# Making some asserts. +assert object_a_1 is not object_a_2 +assert object_a_1.database is object_a_2.database +assert object_a_1.get_one() == object_a_2.get_one() == 1 +``` + ### Catalogs Catalogs are named set of providers. diff --git a/examples/readme/injections.py b/examples/readme/injections.py new file mode 100644 index 00000000..52590a7e --- /dev/null +++ b/examples/readme/injections.py @@ -0,0 +1,45 @@ +"""`KwArg` and `Attribute` injections example.""" + +import sqlite3 + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1').fetchone()[0] + + +# Database and `ObjectA` providers. +database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + +object_a = NewInstance(ObjectA, + KwArg('database', database)) + +# Creating several `ObjectA` instances. +object_a_1 = object_a() +object_a_2 = object_a() + +# Making some asserts. +assert object_a_1 is not object_a_2 +assert object_a_1.database is object_a_2.database +assert object_a_1.get_one() == object_a_2.get_one() == 1 From 0db7a67ef29a4bb5eb77edf7201e017255e08716 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Tue, 31 Mar 2015 14:44:15 +0300 Subject: [PATCH 13/15] @inject description --- README.md | 83 +++++++---------------------- examples/readme/inject_decorator.py | 25 +++++++++ 2 files changed, 44 insertions(+), 64 deletions(-) create mode 100644 examples/readme/inject_decorator.py diff --git a/README.md b/README.md index d565d466..2db46a38 100644 --- a/README.md +++ b/README.md @@ -127,81 +127,36 @@ assert object_a_1.database is object_a_2.database assert object_a_1.get_one() == object_a_2.get_one() == 1 ``` -### Catalogs - -Catalogs are named set of providers. - -## Example +Also injections could be used by any callable with `@inject` decorator: ```python -"""Concept example of `Objects`.""" +"""`@inject` decorator example.""" -from objects.catalog import AbstractCatalog - -from objects.providers import Singleton from objects.providers import NewInstance from objects.injections import KwArg -from objects.injections import Attribute from objects.injections import inject -import sqlite3 + +object_a = NewInstance(object) +object_b = NewInstance(object) -class ObjectA(object): +@inject(KwArg('a', object_a)) +@inject(KwArg('b', object_b)) +def example_callback(a, b): + """This function has dependencies on object a and b. - """Example class ObjectA, that has dependency on database.""" - - def __init__(self, db): - """Initializer.""" - self.db = db + Dependencies are injected using `@inject` decorator. + """ + assert a is not b + assert isinstance(a, object) + assert isinstance(b, object) -class ObjectB(object): - - """Example class ObjectB, that has dependencies on ObjectA and database.""" - - def __init__(self, a, db): - """Initializer.""" - self.a = a - self.db = db - - -class Catalog(AbstractCatalog): - - """Catalog of objects providers.""" - - database = Singleton(sqlite3.Connection, - KwArg('database', ':memory:'), - Attribute('row_factory', sqlite3.Row)) - """:type: (objects.Provider) -> sqlite3.Connection""" - - object_a = NewInstance(ObjectA, - KwArg('db', database)) - """:type: (objects.Provider) -> ObjectA""" - - object_b = NewInstance(ObjectB, - KwArg('a', object_a), - KwArg('db', database)) - """:type: (objects.Provider) -> ObjectB""" - - -# Catalog static provides. -a1, a2 = Catalog.object_a(), Catalog.object_a() -b1, b2 = Catalog.object_b(), Catalog.object_b() - -assert a1 is not a2 -assert b1 is not b2 -assert a1.db is a2.db is b1.db is b2.db is Catalog.database() - - -# Example of inline injections. -@inject(KwArg('a', Catalog.object_a)) -@inject(KwArg('b', Catalog.object_b)) -@inject(KwArg('database', Catalog.database)) -def example(a, b, database): - assert a.db is b.db is database is Catalog.database() - - -example() +example_callback() ``` + +### Catalogs + +Catalogs are named set of providers. diff --git a/examples/readme/inject_decorator.py b/examples/readme/inject_decorator.py new file mode 100644 index 00000000..e0143b87 --- /dev/null +++ b/examples/readme/inject_decorator.py @@ -0,0 +1,25 @@ +"""`@inject` decorator example.""" + +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import inject + + +object_a = NewInstance(object) +object_b = NewInstance(object) + + +@inject(KwArg('a', object_a)) +@inject(KwArg('b', object_b)) +def example_callback(a, b): + """This function has dependencies on object a and b. + + Dependencies are injected using `@inject` decorator. + """ + assert a is not b + assert isinstance(a, object) + assert isinstance(b, object) + + +example_callback() From b5870e9a5743f8fb4f24b5b5c49ee18189cb9f4d Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Tue, 31 Mar 2015 18:00:11 +0300 Subject: [PATCH 14/15] Updating examples --- README.md | 34 +++++++++++++++++++---------- examples/readme/inject_decorator.py | 15 ++++++------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 2db46a38..f245fc06 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,20 @@ assert object_a_1.database is object_a_2.database assert object_a_1.get_one() == object_a_2.get_one() == 1 ``` -Also injections could be used by any callable with `@inject` decorator: +### Catalogs + +Catalogs are named set of providers. + +## Advanced usage + +Below you can find some variants of advanced usage of `Objects`. + +### Inject decorator + +`@inject` decorator could be used for patching any callable with injection. +Any Python object will be injected 'as is', except `Objects` providers, +that will be called to provide injectable value. + ```python """`@inject` decorator example.""" @@ -138,25 +151,24 @@ from objects.injections import KwArg from objects.injections import inject -object_a = NewInstance(object) -object_b = NewInstance(object) +new_object = NewInstance(object) -@inject(KwArg('a', object_a)) -@inject(KwArg('b', object_b)) -def example_callback(a, b): +@inject(KwArg('object_a', new_object)) +@inject(KwArg('some_setting', 1334)) +def example_callback(object_a, some_setting): """This function has dependencies on object a and b. Dependencies are injected using `@inject` decorator. """ - assert a is not b - assert isinstance(a, object) - assert isinstance(b, object) + assert isinstance(object_a, object) + assert some_setting == 1334 +example_callback() example_callback() ``` -### Catalogs +### Overriding providers -Catalogs are named set of providers. +### Overriding catalogs diff --git a/examples/readme/inject_decorator.py b/examples/readme/inject_decorator.py index e0143b87..702f2727 100644 --- a/examples/readme/inject_decorator.py +++ b/examples/readme/inject_decorator.py @@ -6,20 +6,19 @@ from objects.injections import KwArg from objects.injections import inject -object_a = NewInstance(object) -object_b = NewInstance(object) +new_object = NewInstance(object) -@inject(KwArg('a', object_a)) -@inject(KwArg('b', object_b)) -def example_callback(a, b): +@inject(KwArg('object_a', new_object)) +@inject(KwArg('some_setting', 1334)) +def example_callback(object_a, some_setting): """This function has dependencies on object a and b. Dependencies are injected using `@inject` decorator. """ - assert a is not b - assert isinstance(a, object) - assert isinstance(b, object) + assert isinstance(object_a, object) + assert some_setting == 1334 example_callback() +example_callback() From 73b8af1cbb462f2e11fc68bd85656ccf86ca9ffb Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Tue, 31 Mar 2015 18:44:27 +0300 Subject: [PATCH 15/15] Updating readme, adding examples of overriding catalog and providers, adding catalog example --- README.md | 236 +++++++++++++++++++++++++ examples/readme/catalogs.py | 68 +++++++ examples/readme/overriding_catalog.py | 77 ++++++++ examples/readme/overriding_provider.py | 66 +++++++ 4 files changed, 447 insertions(+) create mode 100644 examples/readme/catalogs.py create mode 100644 examples/readme/overriding_catalog.py create mode 100644 examples/readme/overriding_provider.py diff --git a/README.md b/README.md index f245fc06..8723c207 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,85 @@ assert object_a_1.get_one() == object_a_2.get_one() == 1 Catalogs are named set of providers. +`Objects` catalogs can be used for grouping of providers by some +kind of rules. In example below, there are two catalogs: +`Resources` and `Models`. + +`Resources` catalog is used to group all common application resources like +database connection and various api clients, while `Models` catalog is used +for application model providers only. + +```python +"""Catalogs example.""" + +import sqlite3 +import httplib + +from objects.catalog import AbstractCatalog + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class SomeModel(object): + + """SomeModel has dependency on database and api client. + + Dependencies need to be injected via init args. + """ + + def __init__(self, database, api_client): + """Initializer.""" + self.database = database + self.api_client = api_client + + def api_request(self): + """Make api request.""" + self.api_client.request('GET', '/') + return self.api_client.getresponse() + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1').fetchone()[0] + + +class Resources(AbstractCatalog): + + """Resource providers catalog.""" + + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + api_client = Singleton(httplib.HTTPConnection, + KwArg('host', 'example.com'), + KwArg('port', 80), + KwArg('timeout', 10)) + + +class Models(AbstractCatalog): + + """Model providers catalog.""" + + some_model = NewInstance(SomeModel, + KwArg('database', Resources.database), + KwArg('api_client', Resources.api_client)) + + +# Creating `SomeModel` instance. +some_model = Models.some_model() + +# Making some asserts. +assert some_model.get_one() == 1 +assert some_model.api_request().status == 200 +``` + ## Advanced usage Below you can find some variants of advanced usage of `Objects`. @@ -171,4 +250,161 @@ example_callback() ### Overriding providers +Any provider can be overridden by another provider. + +Example: + +```python +"""Provider overriding example.""" + +import sqlite3 + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1') + + +class ObjectAMock(ObjectA): + + """Mock of ObjectA. + + Has no dependency on database. + """ + + def __init__(self): + """Initializer.""" + + def get_one(self): + """Select one from database and return it. + + Mock makes no database queries and always returns two instead of one. + """ + return 2 + + +# Database and `ObjectA` providers. +database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + +object_a = NewInstance(ObjectA, + KwArg('database', database)) + + +# Overriding `ObjectA` provider with `ObjectAMock` provider. +object_a.override(NewInstance(ObjectAMock)) + +# Creating several `ObjectA` instances. +object_a_1 = object_a() +object_a_2 = object_a() + +# Making some asserts. +assert object_a_1 is not object_a_2 +assert object_a_1.get_one() == object_a_2.get_one() == 2 +``` + ### Overriding catalogs + +Any catalog can be overridden by another catalog. + +Example: + +```python +"""Catalog overriding example.""" + +import sqlite3 + +from objects.catalog import AbstractCatalog +from objects.catalog import override + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1') + + +class ObjectAMock(ObjectA): + + """Mock of ObjectA. + + Has no dependency on database. + """ + + def __init__(self): + """Initializer.""" + + def get_one(self): + """Select one from database and return it. + + Mock makes no database queries and always returns two instead of one. + """ + return 2 + + +class Catalog(AbstractCatalog): + + """Catalog of objects providers.""" + + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + object_a = NewInstance(ObjectA, + KwArg('database', database)) + + +@override(Catalog) +class SandboxCatalog(Catalog): + + """Sandbox objects catalog with some mocks that overrides Catalog.""" + + object_a = NewInstance(ObjectAMock) + + +# Creating several `ObjectA` instances. +object_a_1 = Catalog.object_a() +object_a_2 = Catalog.object_a() + +# Making some asserts. +assert object_a_1 is not object_a_2 +assert object_a_1.get_one() == object_a_2.get_one() == 2 +``` diff --git a/examples/readme/catalogs.py b/examples/readme/catalogs.py new file mode 100644 index 00000000..30194f85 --- /dev/null +++ b/examples/readme/catalogs.py @@ -0,0 +1,68 @@ +"""Catalogs example.""" + +import sqlite3 +import httplib + +from objects.catalog import AbstractCatalog + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class SomeModel(object): + + """SomeModel has dependency on database and api client. + + Dependencies need to be injected via init args. + """ + + def __init__(self, database, api_client): + """Initializer.""" + self.database = database + self.api_client = api_client + + def api_request(self): + """Make api request.""" + self.api_client.request('GET', '/') + return self.api_client.getresponse() + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1').fetchone()[0] + + +class Resources(AbstractCatalog): + + """Resource providers catalog.""" + + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + api_client = Singleton(httplib.HTTPConnection, + KwArg('host', 'example.com'), + KwArg('port', 80), + KwArg('timeout', 10)) + + +class Models(AbstractCatalog): + + """Model providers catalog.""" + + some_model = NewInstance(SomeModel, + KwArg('database', Resources.database), + KwArg('api_client', Resources.api_client)) + + +# Creating `SomeModel` instance. +some_model = Models.some_model() + +# Making some asserts. +assert some_model.get_one() == 1 +assert some_model.api_request().status == 200 diff --git a/examples/readme/overriding_catalog.py b/examples/readme/overriding_catalog.py new file mode 100644 index 00000000..657def1a --- /dev/null +++ b/examples/readme/overriding_catalog.py @@ -0,0 +1,77 @@ +"""Catalog overriding example.""" + +import sqlite3 + +from objects.catalog import AbstractCatalog +from objects.catalog import override + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1') + + +class ObjectAMock(ObjectA): + + """Mock of ObjectA. + + Has no dependency on database. + """ + + def __init__(self): + """Initializer.""" + + def get_one(self): + """Select one from database and return it. + + Mock makes no database queries and always returns two instead of one. + """ + return 2 + + +class Catalog(AbstractCatalog): + + """Catalog of objects providers.""" + + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + object_a = NewInstance(ObjectA, + KwArg('database', database)) + + +@override(Catalog) +class SandboxCatalog(Catalog): + + """Sandbox objects catalog with some mocks that overrides Catalog.""" + + object_a = NewInstance(ObjectAMock) + + +# Creating several `ObjectA` instances. +object_a_1 = Catalog.object_a() +object_a_2 = Catalog.object_a() + +# Making some asserts. +assert object_a_1 is not object_a_2 +assert object_a_1.get_one() == object_a_2.get_one() == 2 diff --git a/examples/readme/overriding_provider.py b/examples/readme/overriding_provider.py new file mode 100644 index 00000000..41ff0fe3 --- /dev/null +++ b/examples/readme/overriding_provider.py @@ -0,0 +1,66 @@ +"""Provider overriding example.""" + +import sqlite3 + +from objects.providers import Singleton +from objects.providers import NewInstance + +from objects.injections import KwArg +from objects.injections import Attribute + + +class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1') + + +class ObjectAMock(ObjectA): + + """Mock of ObjectA. + + Has no dependency on database. + """ + + def __init__(self): + """Initializer.""" + + def get_one(self): + """Select one from database and return it. + + Mock makes no database queries and always returns two instead of one. + """ + return 2 + + +# Database and `ObjectA` providers. +database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + +object_a = NewInstance(ObjectA, + KwArg('database', database)) + + +# Overriding `ObjectA` provider with `ObjectAMock` provider. +object_a.override(NewInstance(ObjectAMock)) + +# Creating several `ObjectA` instances. +object_a_1 = object_a() +object_a_2 = object_a() + +# Making some asserts. +assert object_a_1 is not object_a_2 +assert object_a_1.get_one() == object_a_2.get_one() == 2