3
.gitignore
vendored
|
@ -64,3 +64,6 @@ venv/
|
||||||
|
|
||||||
# JointJS Experiments
|
# JointJS Experiments
|
||||||
jointjs/
|
jointjs/
|
||||||
|
|
||||||
|
# Vim Rope
|
||||||
|
.ropeproject/
|
||||||
|
|
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2015, Roman
|
Copyright (c) 2015, Roman Mogilatov
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
77
README.rst
|
@ -1,27 +1,27 @@
|
||||||
Objects
|
Objects
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Dependency management tool for Python projects
|
Dependency injection framework for Python projects.
|
||||||
|
|
||||||
+---------------------------------------+-------------------------------------------------------------------+
|
+---------------------------------------+-------------------------------------------------------------------+
|
||||||
| *PyPi* | .. image:: https://pypip.in/version/Objects/badge.svg |
|
| *PyPi* | .. image:: https://img.shields.io/pypi/v/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Latest Version |
|
| | :alt: Latest Version |
|
||||||
| | .. image:: https://pypip.in/download/Objects/badge.svg |
|
| | .. image:: https://img.shields.io/pypi/dm/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Downloads |
|
| | :alt: Downloads |
|
||||||
| | .. image:: https://pypip.in/license/Objects/badge.svg |
|
| | .. image:: https://img.shields.io/pypi/l/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: License |
|
| | :alt: License |
|
||||||
+---------------------------------------+-------------------------------------------------------------------+
|
+---------------------------------------+-------------------------------------------------------------------+
|
||||||
| *Python versions and implementations* | .. image:: https://pypip.in/py_versions/Objects/badge.svg |
|
| *Python versions and implementations* | .. image:: https://img.shields.io/pypi/pyversions/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Supported Python versions |
|
| | :alt: Supported Python versions |
|
||||||
| | .. image:: https://pypip.in/implementation/Objects/badge.svg |
|
| | .. image:: https://img.shields.io/pypi/implementation/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Supported Python implementations |
|
| | :alt: Supported Python implementations |
|
||||||
+---------------------------------------+-------------------------------------------------------------------+
|
+---------------------------------------+-------------------------------------------------------------------+
|
||||||
| *Builds and test coverage* | .. image:: https://travis-ci.org/rmk135/objects.svg?branch=master |
|
| *Builds and tests coverage* | .. image:: https://travis-ci.org/rmk135/objects.svg?branch=master |
|
||||||
| | :target: https://travis-ci.org/rmk135/objects |
|
| | :target: https://travis-ci.org/rmk135/objects |
|
||||||
| | :alt: Build Status |
|
| | :alt: Build Status |
|
||||||
| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg |
|
| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg |
|
||||||
|
@ -32,31 +32,45 @@ Dependency management tool for Python projects
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Python ecosystem consists of a big amount of various classes, functions and
|
Python ecosystem consists of a big amount of various libraries that contain
|
||||||
objects that could be used for applications development. Each of them has its
|
different classes and functions that could be used for applications
|
||||||
own role.
|
development. Each of them has its own role.
|
||||||
|
|
||||||
Modern Python applications are mostly the composition of well-known open
|
Modern Python applications are mostly the composition of well-known open
|
||||||
source systems, frameworks, libraries and some turnkey functionality.
|
source systems / frameworks / libraries and some turnkey functionality.
|
||||||
|
|
||||||
When application goes bigger, its amount of objects and their dependencies
|
When application goes bigger, its complexity and SLOC_ are also increased.
|
||||||
also increased extremely fast and became hard to maintain.
|
Being driven by SOLID_ (for example), developers often start to split
|
||||||
|
application's sources into not so big classes, functions and modules. It
|
||||||
|
always helps, but there is another problem on the horizon.
|
||||||
|
|
||||||
**Objects** is designed to be developer's friendly tool for managing objects
|
It sounds like "I have so many classes and functions! They are great, now I can
|
||||||
and their dependencies in formal, pretty way. Main idea of **Objects** is to
|
understand each of them, but it is so hard to see the whole picture! How are
|
||||||
keep dependencies under control.
|
they linked with each other? What dependencies does this class have?". And
|
||||||
|
this is a key question: "What dependencies do certain class / function have?".
|
||||||
|
To resolve this issues developers have to go inside with IoC_ principles and
|
||||||
|
implementation patterns.
|
||||||
|
|
||||||
|
One of such IoC_ implementation patterns is called `dependency injection`_.
|
||||||
|
|
||||||
|
*Objects* is a dependency injection framework for Python projects.
|
||||||
|
|
||||||
|
It was designed to be developer's friendly tool for managing any kind of
|
||||||
|
Python objects and their dependencies in formal, pretty way.
|
||||||
|
|
||||||
|
Main idea of *Objects* is to keep dependencies under control.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
**Objects** library is available on PyPi_::
|
*Objects* library is available on PyPi_::
|
||||||
|
|
||||||
pip install objects
|
pip install objects
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
**Objects** documentation is hosted on ReadTheDocs:
|
*Objects* documentation is hosted on ReadTheDocs:
|
||||||
|
|
||||||
- `Stable version`_
|
- `Stable version`_
|
||||||
- `Latest version`_
|
- `Latest version`_
|
||||||
|
@ -70,12 +84,12 @@ Examples
|
||||||
|
|
||||||
from objects.catalog import AbstractCatalog
|
from objects.catalog import AbstractCatalog
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
from objects.providers import Singleton
|
from objects.providers import Singleton
|
||||||
from objects.providers import NewInstance
|
|
||||||
|
|
||||||
from objects.injections import KwArg
|
from objects.injections import KwArg
|
||||||
from objects.injections import Attribute
|
from objects.injections import Attribute
|
||||||
from objects.injections import inject
|
from objects.decorators import inject
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
@ -108,19 +122,19 @@ Examples
|
||||||
Attribute('row_factory', sqlite3.Row))
|
Attribute('row_factory', sqlite3.Row))
|
||||||
""":type: (objects.Provider) -> sqlite3.Connection"""
|
""":type: (objects.Provider) -> sqlite3.Connection"""
|
||||||
|
|
||||||
object_a = NewInstance(ObjectA,
|
object_a_factory = Factory(ObjectA,
|
||||||
KwArg('db', database))
|
KwArg('db', database))
|
||||||
""":type: (objects.Provider) -> ObjectA"""
|
""":type: (objects.Provider) -> ObjectA"""
|
||||||
|
|
||||||
object_b = NewInstance(ObjectB,
|
object_b_factory = Factory(ObjectB,
|
||||||
KwArg('a', object_a),
|
KwArg('a', object_a_factory),
|
||||||
KwArg('db', database))
|
KwArg('db', database))
|
||||||
""":type: (objects.Provider) -> ObjectB"""
|
""":type: (objects.Provider) -> ObjectB"""
|
||||||
|
|
||||||
|
|
||||||
# Catalog static provides.
|
# Catalog static provides.
|
||||||
a1, a2 = Catalog.object_a(), Catalog.object_a()
|
a1, a2 = Catalog.object_a_factory(), Catalog.object_a_factory()
|
||||||
b1, b2 = Catalog.object_b(), Catalog.object_b()
|
b1, b2 = Catalog.object_b_factory(), Catalog.object_b_factory()
|
||||||
|
|
||||||
assert a1 is not a2
|
assert a1 is not a2
|
||||||
assert b1 is not b2
|
assert b1 is not b2
|
||||||
|
@ -128,8 +142,8 @@ Examples
|
||||||
|
|
||||||
|
|
||||||
# Example of inline injections.
|
# Example of inline injections.
|
||||||
@inject(KwArg('a', Catalog.object_a))
|
@inject(KwArg('a', Catalog.object_a_factory))
|
||||||
@inject(KwArg('b', Catalog.object_b))
|
@inject(KwArg('b', Catalog.object_b_factory))
|
||||||
@inject(KwArg('database', Catalog.database))
|
@inject(KwArg('database', Catalog.database))
|
||||||
def example(a, b, database):
|
def example(a, b, database):
|
||||||
assert a.db is b.db is database is Catalog.database()
|
assert a.db is b.db is database is Catalog.database()
|
||||||
|
@ -137,7 +151,8 @@ Examples
|
||||||
|
|
||||||
example()
|
example()
|
||||||
|
|
||||||
You can get more **Objects** examples in ``/examples`` directory on
|
|
||||||
|
You can get more *Objects* examples in ``/examples`` directory on
|
||||||
GitHub:
|
GitHub:
|
||||||
|
|
||||||
https://github.com/rmk135/objects
|
https://github.com/rmk135/objects
|
||||||
|
@ -147,7 +162,7 @@ Feedback
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Feel free to post questions, bugs, feature requests, proposals etc. on
|
Feel free to post questions, bugs, feature requests, proposals etc. on
|
||||||
**Objects** GitHub Issues:
|
*Objects* GitHub Issues:
|
||||||
|
|
||||||
https://github.com/rmk135/objects/issues
|
https://github.com/rmk135/objects/issues
|
||||||
|
|
||||||
|
@ -157,3 +172,7 @@ Your feedback is quite important!
|
||||||
.. _PyPi: https://pypi.python.org/pypi/Objects
|
.. _PyPi: https://pypi.python.org/pypi/Objects
|
||||||
.. _Stable version: http://objects.readthedocs.org/en/stable/
|
.. _Stable version: http://objects.readthedocs.org/en/stable/
|
||||||
.. _Latest version: http://objects.readthedocs.org/en/latest/
|
.. _Latest version: http://objects.readthedocs.org/en/latest/
|
||||||
|
.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code
|
||||||
|
.. _SOLID: http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29
|
||||||
|
.. _IoC: http://en.wikipedia.org/wiki/Inversion_of_control
|
||||||
|
.. _dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
|
||||||
|
|
83
docs/_providers.rst
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
Providers
|
||||||
|
=========
|
||||||
|
|
||||||
|
|
||||||
|
Providers delegation
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Overriding of providers
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Any provider can be overridden by another provider.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""Providers overriding example."""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.providers import Singleton
|
||||||
|
|
||||||
|
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_factory = Factory(ObjectA,
|
||||||
|
KwArg('database', database))
|
||||||
|
|
||||||
|
|
||||||
|
# Overriding `ObjectA` provider with `ObjectAMock` provider.
|
||||||
|
object_a_factory.override(Factory(ObjectAMock))
|
||||||
|
|
||||||
|
# Creating several `ObjectA` instances.
|
||||||
|
object_a_1 = object_a_factory()
|
||||||
|
object_a_2 = object_a_factory()
|
||||||
|
|
||||||
|
# Making some asserts.
|
||||||
|
assert object_a_1 is not object_a_2
|
||||||
|
assert object_a_1.get_one() == object_a_2.get_one() == 2
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
Advanced usage
|
Advanced usage
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Below you can find some variants of advanced usage of **Objects**.
|
Below you can find some variants of advanced usage of *Objects*.
|
||||||
|
|
||||||
@inject decorator
|
@inject decorator
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
``@inject`` decorator could be used for patching any callable with injection.
|
``@inject`` decorator could be used for patching any callable with injection.
|
||||||
Any Python object will be injected *as is*, except **Objects** providers,
|
Any Python object will be injected *as is*, except *Objects* providers,
|
||||||
that will be called to provide injectable value.
|
that will be called to provide injectable value.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
2
docs/catalogs.rst
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Catalogs
|
||||||
|
========
|
51
docs/decorators.rst
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
Decorators
|
||||||
|
==========
|
||||||
|
|
||||||
|
Current section of *Objects* documentation describes several useful decorators.
|
||||||
|
|
||||||
|
@inject decorator
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
``@inject`` decorator can be used for making *inline* dependency injections.
|
||||||
|
It *patches* decorated callable in such way that dependency injection will be
|
||||||
|
done before every call of decorated callable.
|
||||||
|
|
||||||
|
``@inject`` decorator takes only argument that is supposed to be an
|
||||||
|
``objects.injections.Injection`` instance.
|
||||||
|
|
||||||
|
Any Python object will be injected *as is*, except *Objects* providers,
|
||||||
|
that will be called to provide injectable value.
|
||||||
|
|
||||||
|
Below is an example of how Flask's view could be patched using ``@inject``
|
||||||
|
decorator:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`@inject` decorator example."""
|
||||||
|
|
||||||
|
from objects.providers import NewInstance
|
||||||
|
|
||||||
|
from objects.injections import KwArg
|
||||||
|
from objects.injections import inject
|
||||||
|
|
||||||
|
|
||||||
|
new_object = NewInstance(object)
|
||||||
|
|
||||||
|
|
||||||
|
@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 isinstance(object_a, object)
|
||||||
|
assert some_setting == 1334
|
||||||
|
|
||||||
|
|
||||||
|
example_callback()
|
||||||
|
example_callback()
|
||||||
|
|
||||||
|
|
||||||
|
@override decorator
|
||||||
|
-------------------
|
|
@ -1,7 +1,7 @@
|
||||||
Entities
|
Entities
|
||||||
========
|
========
|
||||||
|
|
||||||
Current section describes main **Objects** entities and their interaction.
|
Current section describes main *Objects* entities and their interaction.
|
||||||
|
|
||||||
Providers
|
Providers
|
||||||
---------
|
---------
|
||||||
|
@ -45,10 +45,10 @@ dependencies of objects.
|
||||||
|
|
||||||
Objects can take dependencies in various forms. Some objects take init
|
Objects can take dependencies in various forms. Some objects take init
|
||||||
arguments, other are using attributes or methods to be initialized. Injection,
|
arguments, other are using attributes or methods to be initialized. Injection,
|
||||||
in terms of **Objects**, is an instruction how to provide dependency for the
|
in terms of *Objects*, is an instruction how to provide dependency for the
|
||||||
particular object.
|
particular object.
|
||||||
|
|
||||||
Every Python object could be an injection's value. Special case is a **Objects**
|
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
|
provider as an injection's value. In such case, injection value is a result of
|
||||||
injectable provider call (every time injection is done).
|
injectable provider call (every time injection is done).
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ Catalogs
|
||||||
|
|
||||||
Catalogs are named set of providers.
|
Catalogs are named set of providers.
|
||||||
|
|
||||||
**Objects** catalogs can be used for grouping of providers by some
|
*Objects* catalogs can be used for grouping of providers by some
|
||||||
kind of rules. In example below, there are two catalogs:
|
kind of rules. In example below, there are two catalogs:
|
||||||
``Resources`` and ``Models``.
|
``Resources`` and ``Models``.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
|
|
||||||
You can get more **Objects** examples in ``/examples`` directory on
|
You can get more *Objects* examples in ``/examples`` directory on
|
||||||
GitHub:
|
GitHub:
|
||||||
|
|
||||||
https://github.com/rmk135/objects
|
https://github.com/rmk135/objects
|
||||||
|
|
|
@ -2,7 +2,7 @@ Feedback
|
||||||
========
|
========
|
||||||
|
|
||||||
Feel free to post questions, bugs, feature requests, proposals etc. on
|
Feel free to post questions, bugs, feature requests, proposals etc. on
|
||||||
**Objects** GitHub Issues:
|
*Objects* GitHub Issues:
|
||||||
|
|
||||||
https://github.com/rmk135/objects/issues
|
https://github.com/rmk135/objects/issues
|
||||||
|
|
||||||
|
|
BIN
docs/images/callable.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/images/external_dependency.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
docs/images/factory.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
docs/images/factory_attribute_injections.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
docs/images/factory_delegation.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/images/factory_init_injections.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/images/factory_init_injections_and_contexts.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
docs/images/factory_method_injections.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
docs/images/singleton.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
docs/images/singleton_internals.png
Normal file
After Width: | Height: | Size: 11 KiB |
|
@ -1,27 +1,27 @@
|
||||||
Objects
|
Objects
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Dependency management tool for Python projects.
|
Dependency injection framework for Python projects.
|
||||||
|
|
||||||
+---------------------------------------+-------------------------------------------------------------------+
|
+---------------------------------------+-------------------------------------------------------------------+
|
||||||
| *PyPi* | .. image:: https://pypip.in/version/Objects/badge.svg |
|
| *PyPi* | .. image:: https://img.shields.io/pypi/v/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Latest Version |
|
| | :alt: Latest Version |
|
||||||
| | .. image:: https://pypip.in/download/Objects/badge.svg |
|
| | .. image:: https://img.shields.io/pypi/dm/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Downloads |
|
| | :alt: Downloads |
|
||||||
| | .. image:: https://pypip.in/license/Objects/badge.svg |
|
| | .. image:: https://img.shields.io/pypi/l/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: License |
|
| | :alt: License |
|
||||||
+---------------------------------------+-------------------------------------------------------------------+
|
+---------------------------------------+-------------------------------------------------------------------+
|
||||||
| *Python versions and implementations* | .. image:: https://pypip.in/py_versions/Objects/badge.svg |
|
| *Python versions and implementations* | .. image:: https://img.shields.io/pypi/pyversions/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Supported Python versions |
|
| | :alt: Supported Python versions |
|
||||||
| | .. image:: https://pypip.in/implementation/Objects/badge.svg |
|
| | .. image:: https://img.shields.io/pypi/implementation/Objects.svg |
|
||||||
| | :target: https://pypi.python.org/pypi/Objects/ |
|
| | :target: https://pypi.python.org/pypi/Objects/ |
|
||||||
| | :alt: Supported Python implementations |
|
| | :alt: Supported Python implementations |
|
||||||
+---------------------------------------+-------------------------------------------------------------------+
|
+---------------------------------------+-------------------------------------------------------------------+
|
||||||
| *Builds and test coverage* | .. image:: https://travis-ci.org/rmk135/objects.svg?branch=master |
|
| *Builds and tests coverage* | .. image:: https://travis-ci.org/rmk135/objects.svg?branch=master |
|
||||||
| | :target: https://travis-ci.org/rmk135/objects |
|
| | :target: https://travis-ci.org/rmk135/objects |
|
||||||
| | :alt: Build Status |
|
| | :alt: Build Status |
|
||||||
| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg |
|
| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg |
|
||||||
|
@ -38,7 +38,9 @@ Contents
|
||||||
|
|
||||||
introduction
|
introduction
|
||||||
installation
|
installation
|
||||||
entities
|
providers/index
|
||||||
advanced_usage
|
injections
|
||||||
|
catalogs
|
||||||
|
decorators
|
||||||
examples
|
examples
|
||||||
feedback
|
feedback
|
||||||
|
|
37
docs/injections.rst
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
Injections
|
||||||
|
==========
|
||||||
|
|
||||||
|
Injections are *Objects* entities that are used for specification of dependency
|
||||||
|
injection types.
|
||||||
|
|
||||||
|
Different functions, classes and objects can take dependency injections in
|
||||||
|
various forms. Some of them take dependencies like keyword arguments during
|
||||||
|
call time, other require setting of attributes or calling of specialized
|
||||||
|
methods for doing dependency injections.
|
||||||
|
|
||||||
|
So, when you are doing dependency injection you need to specify its type and
|
||||||
|
that is the place where *Injections* need to be used.
|
||||||
|
|
||||||
|
Some key points of *Objects* injections:
|
||||||
|
|
||||||
|
- Every *Objects* injection always takes injectable value as an
|
||||||
|
``injectable`` param. Every Python object could be an injectable.
|
||||||
|
- Every *Objects* injection always has ``value`` property that returns
|
||||||
|
injection's injectable. ``value`` property is calculated every time it is
|
||||||
|
accessed. Every Python object, except of *Objects* providers, that was
|
||||||
|
provided as and ``injectable`` will be returned by ``value`` property
|
||||||
|
*"as is"*. *Objects* providers will be called every time during ``value``
|
||||||
|
accessing and result of such calls will be returned.
|
||||||
|
- Every *Objects* *Injection* can have additional params that are needed
|
||||||
|
for doing particular type of injection.
|
||||||
|
|
||||||
|
There are several types of *Injections*:
|
||||||
|
|
||||||
|
- ``KwArg`` - is used for making keyword argument injections for any kind
|
||||||
|
of callables (functions, methods, objects instantiation and so on). Takes
|
||||||
|
keyword argument name as string and injectable.
|
||||||
|
- ``Attribute`` - is used for making injections by setting of injection's
|
||||||
|
value to a particular attribute. Takes attribute name as string and
|
||||||
|
injectable.
|
||||||
|
- ``Method`` - is used for making injections by calling of method with
|
||||||
|
injectable value. Takes method name as string and injectable.
|
|
@ -1,15 +1,19 @@
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
Latest stable version of **Objects** library can be installed from PyPi_::
|
Latest stable version of *Objects* framework can be installed from PyPi_:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
pip install objects
|
pip install objects
|
||||||
|
|
||||||
Sources can be cloned from GitHub_::
|
Sources can be cloned from GitHub_:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
git clone https://github.com/rmk135/objects.git
|
git clone https://github.com/rmk135/objects.git
|
||||||
|
|
||||||
All **Objects** releases can be found on GitHub: https://github.com/rmk135/objects/releases
|
All *Objects* releases can be found on GitHub: https://github.com/rmk135/objects/releases
|
||||||
|
|
||||||
.. _PyPi: https://pypi.python.org/pypi/Objects
|
.. _PyPi: https://pypi.python.org/pypi/Objects
|
||||||
.. _GitHub: https://github.com/rmk135/objects
|
.. _GitHub: https://github.com/rmk135/objects
|
||||||
|
|
|
@ -1,16 +1,36 @@
|
||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
Python ecosystem consists of a big amount of various classes, functions and
|
Python ecosystem consists of a big amount of various libraries that contain
|
||||||
objects that could be used for applications development. Each of them has its
|
different classes and functions that could be used for applications
|
||||||
own role.
|
development. Each of them has its own role.
|
||||||
|
|
||||||
Modern Python applications are mostly the composition of well-known open
|
Modern Python applications are mostly the composition of well-known open
|
||||||
source systems, frameworks, libraries and some turnkey functionality.
|
source systems / frameworks / libraries and some turnkey functionality.
|
||||||
|
|
||||||
When application goes bigger, its amount of objects and their dependencies
|
When application goes bigger, its complexity and SLOC_ are also increased.
|
||||||
also increased extremely fast and became hard to maintain.
|
Being driven by SOLID_ (for example), developers often start to split
|
||||||
|
application's sources into not so big classes, functions and modules. It
|
||||||
|
always helps, but there is another problem on the horizon.
|
||||||
|
|
||||||
**Objects** is designed to be developer's friendly tool for managing objects
|
It sounds like "I have so many classes and functions! They are great, now I can
|
||||||
and their dependencies in formal, pretty way. Main idea of **Objects** is to
|
understand each of them, but it is so hard to see the whole picture! How are
|
||||||
keep dependencies under control.
|
they linked with each other? What dependencies does this class have?". And
|
||||||
|
this is a key question: "What dependencies do certain class / function have?".
|
||||||
|
To resolve this issues developers have to go inside with IoC_ principles and
|
||||||
|
implementation patterns.
|
||||||
|
|
||||||
|
One of such IoC_ implementation patterns is called `dependency injection`_.
|
||||||
|
|
||||||
|
*Objects* is a dependency injection framework for Python projects.
|
||||||
|
|
||||||
|
It was designed to be developer's friendly tool for managing any kind of
|
||||||
|
Python objects and their dependencies in formal, pretty way.
|
||||||
|
|
||||||
|
Main idea of *Objects* is to keep dependencies under control.
|
||||||
|
|
||||||
|
|
||||||
|
.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code
|
||||||
|
.. _SOLID: http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29
|
||||||
|
.. _IoC: http://en.wikipedia.org/wiki/Inversion_of_control
|
||||||
|
.. _dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
|
||||||
|
|
73
docs/providers/callable.rst
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
Callable providers
|
||||||
|
------------------
|
||||||
|
|
||||||
|
``Callable`` provider is a provider that wraps particular callable with
|
||||||
|
some injections. Every call of this provider returns result of call of initial
|
||||||
|
callable.
|
||||||
|
|
||||||
|
Callable providers and injections
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``Callable`` provider uses ``KwArg`` injections. ``KwArg`` injections are
|
||||||
|
done by passing injectable values as keyword arguments during call time.
|
||||||
|
|
||||||
|
Context keyword arguments have higher priority than ``KwArg`` injections.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. image:: /images/callable.png
|
||||||
|
:width: 100%
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Callable` providers example."""
|
||||||
|
|
||||||
|
from passlib.hash import sha256_crypt
|
||||||
|
|
||||||
|
from objects.providers import Callable
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
# Password hasher and verifier providers (hash function could be changed
|
||||||
|
# anytime (for example, to sha512) without any changes in client's code):
|
||||||
|
password_hasher = Callable(sha256_crypt.encrypt,
|
||||||
|
KwArg('salt_size', 16),
|
||||||
|
KwArg('rounds', 10000))
|
||||||
|
password_verifier = Callable(sha256_crypt.verify)
|
||||||
|
|
||||||
|
# Making some asserts (client's code):
|
||||||
|
hashed_password = password_hasher('super secret')
|
||||||
|
assert password_verifier('super secret', hashed_password)
|
||||||
|
|
||||||
|
Callable providers delegation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``Callable`` provider could be delegated to any other provider via any kind of
|
||||||
|
injection. Delegation of ``Callable`` providers is the same as ``Factory`` and
|
||||||
|
``Singleton`` providers delegation, please follow *Factory providers
|
||||||
|
delegation* section for example.
|
||||||
|
|
||||||
|
``Callable`` delegate could be created obviously using
|
||||||
|
``Delegate(Callable())`` or by calling ``Callable.delegate()`` method.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Callable` providers delegation example."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from objects.providers import Callable
|
||||||
|
from objects.providers import Delegate
|
||||||
|
|
||||||
|
|
||||||
|
# Some callable provider and few delegates of it:
|
||||||
|
callable_provider = Callable(sys.exit)
|
||||||
|
callable_provider_delegate1 = callable_provider.delegate()
|
||||||
|
callable_provider_delegate2 = Delegate(callable_provider)
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert callable_provider_delegate1() is callable_provider
|
||||||
|
assert callable_provider_delegate2() is callable_provider
|
2
docs/providers/extending.rst
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Extending of providers
|
||||||
|
----------------------
|
121
docs/providers/external_dependency.rst
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
External dependency providers
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
``ExternalDependency`` provider can be useful for development of
|
||||||
|
self-sufficient libraries / modules / applications that has required external
|
||||||
|
dependencies.
|
||||||
|
|
||||||
|
For example, you have created self-sufficient library / module / application,
|
||||||
|
that has dependency on *database connection*.
|
||||||
|
|
||||||
|
Second step you want to do is to make this software component to be easy
|
||||||
|
reusable by wide amount of developers and to be easily integrated into many
|
||||||
|
applications.
|
||||||
|
|
||||||
|
It may be good idea, to move all external dependencies (like
|
||||||
|
*database connection*) to the top level and make them to be injected on your
|
||||||
|
software component's initialization. It will make third party developers feel
|
||||||
|
themselves free about integration of yours component in their applications,
|
||||||
|
because they would be able to find right place / right way for doing this
|
||||||
|
in their application's architectures.
|
||||||
|
|
||||||
|
At the same time, you can be sure, that your external dependency will be
|
||||||
|
satisfied with appropriate instance.
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Class ``UserService`` is a part of some library. ``UserService`` has
|
||||||
|
dependency on database connection, which can be satisfied with any
|
||||||
|
DBAPI 2.0 database connection. Being a self-sufficient library,
|
||||||
|
``UserService`` doesn't hardcode any kind of database management logic.
|
||||||
|
Instead of this, ``UserService`` has external dependency, that has to
|
||||||
|
be satisfied by cleint's code, out of library's scope.
|
||||||
|
|
||||||
|
.. image:: /images/external_dependency.png
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`ExternalDependency` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import ExternalDependency
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.providers import Singleton
|
||||||
|
|
||||||
|
from objects.injections import KwArg
|
||||||
|
from objects.injections import Attribute
|
||||||
|
|
||||||
|
# Importing SQLITE3 and contextlib.closing for working with cursors:
|
||||||
|
import sqlite3
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
|
||||||
|
# Definition of example UserService:
|
||||||
|
class UserService(object):
|
||||||
|
|
||||||
|
"""Example class UserService.
|
||||||
|
|
||||||
|
UserService has dependency on DBAPI 2.0 database connection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, database):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
Database dependency need to be injected via init arg.
|
||||||
|
"""
|
||||||
|
self.database = database
|
||||||
|
|
||||||
|
def init_database(self):
|
||||||
|
"""Initialize database, if it has not been initialized yet."""
|
||||||
|
with closing(self.database.cursor()) as cursor:
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS users(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name VARCHAR(32)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
def create(self, name):
|
||||||
|
"""Create user with provided name and return his id."""
|
||||||
|
with closing(self.database.cursor()) as cursor:
|
||||||
|
cursor.execute('INSERT INTO users(name) VALUES (?)', (name,))
|
||||||
|
return cursor.lastrowid
|
||||||
|
|
||||||
|
def get_by_id(self, id):
|
||||||
|
"""Return user info by user id."""
|
||||||
|
with closing(self.database.cursor()) as cursor:
|
||||||
|
cursor.execute('SELECT id, name FROM users WHERE id=?', (id,))
|
||||||
|
return cursor.fetchone()
|
||||||
|
|
||||||
|
|
||||||
|
# Database and UserService providers:
|
||||||
|
database = ExternalDependency(instance_of=sqlite3.dbapi2.Connection)
|
||||||
|
users_service_factory = Factory(UserService,
|
||||||
|
KwArg('database', database))
|
||||||
|
|
||||||
|
# Out of library's scope.
|
||||||
|
#
|
||||||
|
# Setting database provider:
|
||||||
|
database.provided_by(Singleton(sqlite3.dbapi2.Connection,
|
||||||
|
KwArg('database', ':memory:'),
|
||||||
|
KwArg('timeout', 30),
|
||||||
|
KwArg('detect_types', True),
|
||||||
|
KwArg('isolation_level', 'EXCLUSIVE'),
|
||||||
|
Attribute('row_factory', sqlite3.Row)))
|
||||||
|
|
||||||
|
# Creating UserService instance:
|
||||||
|
users_service = users_service_factory()
|
||||||
|
|
||||||
|
# Initializing UserService database:
|
||||||
|
users_service.init_database()
|
||||||
|
|
||||||
|
# Creating test user and retrieving full information about him:
|
||||||
|
test_user_id = users_service.create(name='test_user')
|
||||||
|
test_user = users_service.get_by_id(test_user_id)
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert test_user['id'] == 1
|
||||||
|
assert test_user['name'] == 'test_user'
|
430
docs/providers/factory.rst
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
Factory providers
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
``Factory`` provider creates new instance of specified class on every call.
|
||||||
|
|
||||||
|
Nothing could be better than brief example:
|
||||||
|
|
||||||
|
.. image:: /images/factory.png
|
||||||
|
:width: 80%
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Factory` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
# Factory provider creates new instance of specified class on every call.
|
||||||
|
users_factory = Factory(User)
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
user2 = users_factory()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1 is not user2
|
||||||
|
assert isinstance(user1, User) and isinstance(user2, User)
|
||||||
|
|
||||||
|
|
||||||
|
Factory providers and injections
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Objects can take dependencies in different forms. Some objects take init
|
||||||
|
arguments, other are using attributes setting or method calls to be
|
||||||
|
initialized. It affects how such objects need to be created and initialized,
|
||||||
|
and that is the place where ``objects.injections`` need to be used.
|
||||||
|
|
||||||
|
``Factory`` provider takes various number of positional arguments, that define
|
||||||
|
what kind of dependency injections need to be done.
|
||||||
|
|
||||||
|
All of those instructions are defined in ``objects.injections`` module and are
|
||||||
|
subclasses of ``objects.injections.Injection``. There are several types of
|
||||||
|
injections that are used by ``Factory`` provider:
|
||||||
|
|
||||||
|
- ``KwArg`` - injection is done by passing injectable value in object's
|
||||||
|
``__init__()`` method in time of object's creation via keyword argument.
|
||||||
|
Takes keyword name of ``__init__()`` argument and injectable value.
|
||||||
|
- ``Attribute`` - injection is done by setting specified attribute with
|
||||||
|
injectable value right after object's creation. Takes attribute's name
|
||||||
|
and injectable value.
|
||||||
|
- ``Method`` - injection is done by calling of specified method with
|
||||||
|
injectable value right after object's creation and attribute injections
|
||||||
|
are done. Takes method name and injectable value.
|
||||||
|
|
||||||
|
All ``Injection``'s injectable values are provided *"as is"*, except of
|
||||||
|
providers. Providers will be called every time, when injection needs to be
|
||||||
|
done.
|
||||||
|
|
||||||
|
|
||||||
|
Factory providers and __init__ injections
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Example below shows how to create ``Factory`` of particular class with
|
||||||
|
``__init__`` keyword argument injections which injectable values are also
|
||||||
|
provided by another factories:
|
||||||
|
|
||||||
|
.. image:: /images/factory_init_injections.png
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Factory` providers with init injections example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self, main_photo):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
:param main_photo: Photo
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.main_photo = main_photo
|
||||||
|
super(User, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
# User and Photo factories:
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
KwArg('main_photo', photos_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory() # Same as: user1 = User(main_photo=Photo())
|
||||||
|
user2 = users_factory() # Same as: user2 = User(main_photo=Photo())
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert isinstance(user1, User)
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
|
||||||
|
assert isinstance(user2, User)
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
|
||||||
|
assert user1 is not user2
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
||||||
|
|
||||||
|
Next example shows how ``Factory`` provider deals with positional and keyword
|
||||||
|
``__init__`` context arguments. In few words, ``Factory`` provider fully
|
||||||
|
passes positional context arguments to class's ``__init__`` method, but
|
||||||
|
keyword context arguments have priority on ``KwArg`` injections (this could be
|
||||||
|
useful for testing).
|
||||||
|
|
||||||
|
So, please, follow the example below:
|
||||||
|
|
||||||
|
.. image:: /images/factory_init_injections_and_contexts.png
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Factory` providers with init injections and context arguments example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User.
|
||||||
|
|
||||||
|
Class User has to be provided with user id.
|
||||||
|
|
||||||
|
Also Class User has dependencies on class Photo and class CreditCard
|
||||||
|
objects.
|
||||||
|
|
||||||
|
All of the dependencies have to be provided like __init__ arguments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, id, main_photo, credit_card):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
:param id: int
|
||||||
|
:param main_photo: Photo
|
||||||
|
:param credit_card: CreditCard
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.id = id
|
||||||
|
self.main_photo = main_photo
|
||||||
|
self.credit_card = credit_card
|
||||||
|
super(User, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreditCard(object):
|
||||||
|
|
||||||
|
"""Example class CreditCard."""
|
||||||
|
|
||||||
|
# User, Photo and CreditCard factories:
|
||||||
|
credit_cards_factory = Factory(CreditCard)
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
KwArg('main_photo', photos_factory),
|
||||||
|
KwArg('credit_card', credit_cards_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory(1)
|
||||||
|
# Same as: user1 = User(1,
|
||||||
|
# main_photo=Photo(),
|
||||||
|
# credit_card=CreditCard())
|
||||||
|
user2 = users_factory(2)
|
||||||
|
# Same as: user2 = User(2,
|
||||||
|
# main_photo=Photo(),
|
||||||
|
# credit_card=CreditCard())
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1.id == 1
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
assert isinstance(user1.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user2.id == 2
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
assert isinstance(user2.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
||||||
|
assert user1.credit_card is not user2.credit_card
|
||||||
|
|
||||||
|
# Context keyword arguments have priority on KwArg injections priority:
|
||||||
|
main_photo_mock = Photo()
|
||||||
|
credit_card_mock = CreditCard()
|
||||||
|
|
||||||
|
user3 = users_factory(3, main_photo=main_photo_mock,
|
||||||
|
credit_card=credit_card_mock)
|
||||||
|
|
||||||
|
assert user3.id == 3
|
||||||
|
assert user3.main_photo is main_photo_mock
|
||||||
|
assert user3.credit_card is credit_card_mock
|
||||||
|
|
||||||
|
Factory providers and attribute injections
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Example below shows how to create ``Factory`` of particular class with
|
||||||
|
attribute injections. Those injections are done by setting specified attributes
|
||||||
|
with injectable values right after object's creation.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. image:: /images/factory_attribute_injections.png
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Factory` providers with attribute injections example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import Attribute
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initializer."""
|
||||||
|
self.main_photo = None
|
||||||
|
self.credit_card = None
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreditCard(object):
|
||||||
|
|
||||||
|
"""Example class CreditCard."""
|
||||||
|
|
||||||
|
# User, Photo and CreditCard factories:
|
||||||
|
credit_cards_factory = Factory(CreditCard)
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
Attribute('main_photo', photos_factory),
|
||||||
|
Attribute('credit_card', credit_cards_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
# Same as: user1 = User()
|
||||||
|
# user1.main_photo = Photo()
|
||||||
|
# user1.credit_card = CreditCard()
|
||||||
|
user2 = users_factory()
|
||||||
|
# Same as: user2 = User()
|
||||||
|
# user2.main_photo = Photo()
|
||||||
|
# user2.credit_card = CreditCard()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1 is not user2
|
||||||
|
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
assert isinstance(user1.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
assert isinstance(user2.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
||||||
|
assert user1.credit_card is not user2.credit_card
|
||||||
|
|
||||||
|
Factory providers and method injections
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Current example shows how to create ``Factory`` of particular class with
|
||||||
|
method injections. Those injections are done by calling of specified method
|
||||||
|
with injectable value right after object's creation and attribute injections
|
||||||
|
are done.
|
||||||
|
|
||||||
|
Method injections are not very popular in Python due Python best practices
|
||||||
|
(usage of public attributes instead of setter methods), but it may appear in
|
||||||
|
some cases.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. image:: /images/factory_method_injections.png
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Factory` providers with method injections example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import Method
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initializer."""
|
||||||
|
self.main_photo = None
|
||||||
|
self.credit_card = None
|
||||||
|
|
||||||
|
def set_main_photo(self, photo):
|
||||||
|
"""Set user's main photo."""
|
||||||
|
self.main_photo = photo
|
||||||
|
|
||||||
|
def set_credit_card(self, credit_card):
|
||||||
|
"""Set user's credit card."""
|
||||||
|
self.credit_card = credit_card
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreditCard(object):
|
||||||
|
|
||||||
|
"""Example class CreditCard."""
|
||||||
|
|
||||||
|
# User, Photo and CreditCard factories:
|
||||||
|
credit_cards_factory = Factory(CreditCard)
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
Method('set_main_photo', photos_factory),
|
||||||
|
Method('set_credit_card', credit_cards_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
# Same as: user1 = User()
|
||||||
|
# user1.set_main_photo(Photo())
|
||||||
|
# user1.set_credit_card(CreditCard())
|
||||||
|
user2 = users_factory()
|
||||||
|
# Same as: user2 = User()
|
||||||
|
# user2.set_main_photo(Photo())
|
||||||
|
# user2.set_credit_card(CreditCard())
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1 is not user2
|
||||||
|
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
assert isinstance(user1.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
assert isinstance(user2.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
||||||
|
assert user1.credit_card is not user2.credit_card
|
||||||
|
|
||||||
|
Factory providers delegation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``Factory`` provider could be delegated to any other provider via any kind of
|
||||||
|
injection. Saying in other words, delegation of factories - is a way to inject
|
||||||
|
factories themselves, instead of results of their calls.
|
||||||
|
|
||||||
|
As it was mentioned earlier, ``Injection`` calls ``Factory`` if ``Factory`` is
|
||||||
|
injectable value. ``Factory`` delegation is performed by wrapping delegated
|
||||||
|
``Factory`` into special provider type - ``Delegate``, that just returns
|
||||||
|
``Factory`` itself.
|
||||||
|
|
||||||
|
Another one, more *convenient*, method of creating ``Delegate`` for ``Factory``
|
||||||
|
is just calling ``Factory.delegate()`` method that returns delegate for current
|
||||||
|
factory.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. image:: /images/factory_delegation.png
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Factory` providers delegation example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self, photos_factory):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
:param photos_factory: objects.providers.Factory
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.photos_factory = photos_factory
|
||||||
|
self._main_photo = None
|
||||||
|
super(User, self).__init__()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def main_photo(self):
|
||||||
|
"""Return user's main photo."""
|
||||||
|
if not self._main_photo:
|
||||||
|
self._main_photo = self.photos_factory()
|
||||||
|
return self._main_photo
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
# User and Photo factories:
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
KwArg('photos_factory', photos_factory.delegate()))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
user2 = users_factory()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert isinstance(user1, User)
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
|
||||||
|
assert isinstance(user2, User)
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
|
||||||
|
assert user1 is not user2
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
17
docs/providers/index.rst
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
Providers
|
||||||
|
=========
|
||||||
|
|
||||||
|
Providers are strategies of accessing objects.
|
||||||
|
|
||||||
|
All providers are callable. They describe how particular objects are provided.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
factory
|
||||||
|
singleton
|
||||||
|
static
|
||||||
|
callable
|
||||||
|
external_dependency
|
||||||
|
overriding
|
||||||
|
extending
|
4
docs/providers/overriding.rst
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Overriding of providers
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Every provider could be overridden by another provider.
|
134
docs/providers/singleton.rst
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
Singleton providers
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
``Singleton`` provider creates new instance of specified class on first call
|
||||||
|
and returns same instance on every next call.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. image:: /images/singleton.png
|
||||||
|
:width: 80%
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Singleton` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import Singleton
|
||||||
|
|
||||||
|
|
||||||
|
class UserService(object):
|
||||||
|
|
||||||
|
"""Example class UserService."""
|
||||||
|
|
||||||
|
# Singleton provider creates new instance of specified class on first call and
|
||||||
|
# returns same instance on every next call.
|
||||||
|
users_service_provider = Singleton(UserService)
|
||||||
|
|
||||||
|
# Retrieving several UserService objects:
|
||||||
|
user_service1 = users_service_provider()
|
||||||
|
user_service2 = users_service_provider()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user_service1 is user_service2
|
||||||
|
assert isinstance(user_service1, UserService)
|
||||||
|
assert isinstance(user_service2, UserService)
|
||||||
|
|
||||||
|
Singleton providers and injections
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``Singleton`` providers use ``Factory`` providers for first creation of
|
||||||
|
specified class instance, so, all of the rules about injections are the same,
|
||||||
|
as for ``Factory`` providers.
|
||||||
|
|
||||||
|
.. image:: /images/singleton_internals.png
|
||||||
|
:width: 80%
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Due that ``Singleton`` provider creates specified class instance only on
|
||||||
|
the first call, all injections are done once, during the first call, also.
|
||||||
|
Every next call, while instance has been already created and memorized, no
|
||||||
|
injections are done, ``Singleton`` provider just returns memorized earlier
|
||||||
|
instance.
|
||||||
|
|
||||||
|
This may cause some problems, for example, in case of trying to bind
|
||||||
|
``Factory`` provider with ``Singleton`` provider (provided by dependent
|
||||||
|
``Factory`` instance will be injected only once, during the first call).
|
||||||
|
Be aware that such behaviour was made with opened eyes and is not a bug.
|
||||||
|
|
||||||
|
By the way, in such case, ``Delegate`` provider can be useful. It makes
|
||||||
|
possible to inject providers *as is*. Please check out full example in
|
||||||
|
*Providers delegation* section.
|
||||||
|
|
||||||
|
Singleton providers resetting
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Created and memorized by ``Singleton`` instance can be reset. Reset of
|
||||||
|
``Singleton``'s memorized instance is done by clearing reference to it. Further
|
||||||
|
lifecycle of memorized instance is out of ``Singleton`` provider's control.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Singleton` providers resetting example."""
|
||||||
|
|
||||||
|
from objects.providers import Singleton
|
||||||
|
|
||||||
|
|
||||||
|
class UserService(object):
|
||||||
|
|
||||||
|
"""Example class UserService."""
|
||||||
|
|
||||||
|
# Users service singleton provider:
|
||||||
|
users_service_provider = Singleton(UserService)
|
||||||
|
|
||||||
|
# Retrieving several UserService objects:
|
||||||
|
user_service1 = users_service_provider()
|
||||||
|
user_service2 = users_service_provider()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user_service1 is user_service2
|
||||||
|
assert isinstance(user_service1, UserService)
|
||||||
|
assert isinstance(user_service2, UserService)
|
||||||
|
|
||||||
|
# Resetting of memorized instance:
|
||||||
|
users_service_provider.reset()
|
||||||
|
|
||||||
|
# Retrieving one more UserService object:
|
||||||
|
user_service3 = users_service_provider()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user_service3 is not user_service1
|
||||||
|
|
||||||
|
Singleton providers delegation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``Singleton`` provider could be delegated to any other provider via any kind of
|
||||||
|
injection. Delegation of ``Singleton`` providers is the same as ``Factory``
|
||||||
|
providers delegation, please follow *Factory providers delegation* section for
|
||||||
|
example.
|
||||||
|
|
||||||
|
``Singleton`` delegate could be created obviously using
|
||||||
|
``Delegate(Singleton())`` or by calling ``Singleton.delegate()`` method.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Singleton` providers delegation example."""
|
||||||
|
|
||||||
|
from objects.providers import Singleton
|
||||||
|
from objects.providers import Delegate
|
||||||
|
|
||||||
|
|
||||||
|
# Some singleton provider and few delegates of it:
|
||||||
|
singleton_provider = Singleton(object)
|
||||||
|
singleton_provider_delegate1 = singleton_provider.delegate()
|
||||||
|
singleton_provider_delegate2 = Delegate(singleton_provider)
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert singleton_provider_delegate1() is singleton_provider
|
||||||
|
assert singleton_provider_delegate2() is singleton_provider
|
42
docs/providers/static.rst
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
Static providers
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Static providers are family of providers that return their values "as is".
|
||||||
|
There are four types of static providers:
|
||||||
|
|
||||||
|
- ``Class``
|
||||||
|
- ``Object``
|
||||||
|
- ``Function``
|
||||||
|
- ``Value``
|
||||||
|
|
||||||
|
All of them have the same behaviour, but usage of anyone is predicted by
|
||||||
|
readability and providing object's type.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""`Static` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import Class
|
||||||
|
from objects.providers import Object
|
||||||
|
from objects.providers import Function
|
||||||
|
from objects.providers import Value
|
||||||
|
|
||||||
|
|
||||||
|
# Provides class - `object`:
|
||||||
|
cls_provider = Class(object)
|
||||||
|
assert cls_provider() is object
|
||||||
|
|
||||||
|
# Provides object - `object()`:
|
||||||
|
object_provider = Object(object())
|
||||||
|
assert isinstance(object_provider(), object)
|
||||||
|
|
||||||
|
# Provides function - `len`:
|
||||||
|
function_provider = Function(len)
|
||||||
|
assert function_provider() is len
|
||||||
|
|
||||||
|
# Provides value - `123`:
|
||||||
|
value_provider = Value(123)
|
||||||
|
assert value_provider() == 123
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
"""Callable provider examples."""
|
|
||||||
|
|
||||||
from objects.catalog import AbstractCatalog
|
|
||||||
|
|
||||||
from objects.providers import Singleton
|
|
||||||
from objects.providers import Callable
|
|
||||||
|
|
||||||
from objects.injections import KwArg
|
|
||||||
from objects.injections import Attribute
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
|
|
||||||
def consuming_function(arg, db):
|
|
||||||
"""Example function that has input arg and dependency on database."""
|
|
||||||
return arg, 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"""
|
|
||||||
|
|
||||||
consuming_function = Callable(consuming_function,
|
|
||||||
KwArg('db', database))
|
|
||||||
""":type: (objects.Provider) -> consuming_function"""
|
|
||||||
|
|
||||||
|
|
||||||
# Some calls.
|
|
||||||
arg1, db1 = Catalog.consuming_function(1)
|
|
||||||
arg2, db2 = Catalog.consuming_function(2)
|
|
||||||
arg3, db3 = Catalog.consuming_function(3)
|
|
||||||
|
|
||||||
# Some asserts.
|
|
||||||
assert db1 is db2 is db3
|
|
||||||
assert arg1 == 1
|
|
||||||
assert arg2 == 2
|
|
||||||
assert arg3 == 3
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
from objects.catalog import AbstractCatalog
|
from objects.catalog import AbstractCatalog
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
from objects.providers import Singleton
|
from objects.providers import Singleton
|
||||||
from objects.providers import NewInstance
|
|
||||||
|
|
||||||
from objects.injections import KwArg
|
from objects.injections import KwArg
|
||||||
from objects.injections import Attribute
|
from objects.injections import Attribute
|
||||||
from objects.injections import inject
|
from objects.decorators import inject
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
@ -40,19 +40,19 @@ class Catalog(AbstractCatalog):
|
||||||
Attribute('row_factory', sqlite3.Row))
|
Attribute('row_factory', sqlite3.Row))
|
||||||
""":type: (objects.Provider) -> sqlite3.Connection"""
|
""":type: (objects.Provider) -> sqlite3.Connection"""
|
||||||
|
|
||||||
object_a = NewInstance(ObjectA,
|
object_a_factory = Factory(ObjectA,
|
||||||
KwArg('db', database))
|
KwArg('db', database))
|
||||||
""":type: (objects.Provider) -> ObjectA"""
|
""":type: (objects.Provider) -> ObjectA"""
|
||||||
|
|
||||||
object_b = NewInstance(ObjectB,
|
object_b_factory = Factory(ObjectB,
|
||||||
KwArg('a', object_a),
|
KwArg('a', object_a_factory),
|
||||||
KwArg('db', database))
|
KwArg('db', database))
|
||||||
""":type: (objects.Provider) -> ObjectB"""
|
""":type: (objects.Provider) -> ObjectB"""
|
||||||
|
|
||||||
|
|
||||||
# Catalog static provides.
|
# Catalog static provides.
|
||||||
a1, a2 = Catalog.object_a(), Catalog.object_a()
|
a1, a2 = Catalog.object_a_factory(), Catalog.object_a_factory()
|
||||||
b1, b2 = Catalog.object_b(), Catalog.object_b()
|
b1, b2 = Catalog.object_b_factory(), Catalog.object_b_factory()
|
||||||
|
|
||||||
assert a1 is not a2
|
assert a1 is not a2
|
||||||
assert b1 is not b2
|
assert b1 is not b2
|
||||||
|
@ -60,8 +60,8 @@ assert a1.db is a2.db is b1.db is b2.db is Catalog.database()
|
||||||
|
|
||||||
|
|
||||||
# Example of inline injections.
|
# Example of inline injections.
|
||||||
@inject(KwArg('a', Catalog.object_a))
|
@inject(KwArg('a', Catalog.object_a_factory))
|
||||||
@inject(KwArg('b', Catalog.object_b))
|
@inject(KwArg('b', Catalog.object_b_factory))
|
||||||
@inject(KwArg('database', Catalog.database))
|
@inject(KwArg('database', Catalog.database))
|
||||||
def example(a, b, database):
|
def example(a, b, database):
|
||||||
assert a.db is b.db is database is Catalog.database()
|
assert a.db is b.db is database is Catalog.database()
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
"""Provider delegation 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
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectA(object):
|
|
||||||
|
|
||||||
"""Example class ObjectA, that has dependency on database."""
|
|
||||||
|
|
||||||
def __init__(self, db):
|
|
||||||
"""Initializer."""
|
|
||||||
self.db = db
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectB(object):
|
|
||||||
|
|
||||||
"""Example class ObjectB, that has dependency on ObjectA provider."""
|
|
||||||
|
|
||||||
def __init__(self, a_provider):
|
|
||||||
"""Initializer."""
|
|
||||||
self.a_provider = a_provider
|
|
||||||
|
|
||||||
|
|
||||||
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 = Singleton(ObjectB,
|
|
||||||
KwArg('a_provider', object_a.delegate()))
|
|
||||||
""":type: (objects.Provider) -> ObjectB"""
|
|
||||||
|
|
||||||
|
|
||||||
# Catalog static provides.
|
|
||||||
b = Catalog.object_b()
|
|
||||||
a1, a2 = b.a_provider(), b.a_provider()
|
|
||||||
|
|
||||||
# Some asserts.
|
|
||||||
assert a1 is not a2
|
|
||||||
assert a1.db is a2.db is Catalog.database()
|
|
|
@ -1,63 +0,0 @@
|
||||||
"""External dependency example."""
|
|
||||||
|
|
||||||
from objects.catalog import AbstractCatalog
|
|
||||||
|
|
||||||
from objects.providers import Singleton
|
|
||||||
from objects.providers import NewInstance
|
|
||||||
from objects.providers import ExternalDependency
|
|
||||||
|
|
||||||
from objects.injections import KwArg
|
|
||||||
from objects.injections import Attribute
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectA(object):
|
|
||||||
|
|
||||||
"""Example class ObjectA, that has dependency on database."""
|
|
||||||
|
|
||||||
def __init__(self, db):
|
|
||||||
"""Initializer."""
|
|
||||||
self.db = db
|
|
||||||
|
|
||||||
|
|
||||||
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 = ExternalDependency(instance_of=sqlite3.Connection)
|
|
||||||
""":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"""
|
|
||||||
|
|
||||||
|
|
||||||
# Satisfaction of external dependency.
|
|
||||||
Catalog.database.override(Singleton(sqlite3.Connection,
|
|
||||||
KwArg('database', ':memory:'),
|
|
||||||
Attribute('row_factory', sqlite3.Row)))
|
|
||||||
|
|
||||||
# Catalog static provides.
|
|
||||||
a1, a2 = Catalog.object_a(), Catalog.object_a()
|
|
||||||
b1, b2 = Catalog.object_b(), Catalog.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 is Catalog.database()
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Override example."""
|
"""Override example."""
|
||||||
|
|
||||||
from objects.catalog import AbstractCatalog
|
from objects.catalog import AbstractCatalog
|
||||||
from objects.catalog import override
|
|
||||||
|
|
||||||
from objects.providers import Singleton
|
from objects.providers import Singleton
|
||||||
from objects.providers import NewInstance
|
from objects.providers import NewInstance
|
||||||
|
@ -9,6 +8,8 @@ from objects.providers import NewInstance
|
||||||
from objects.injections import KwArg
|
from objects.injections import KwArg
|
||||||
from objects.injections import Attribute
|
from objects.injections import Attribute
|
||||||
|
|
||||||
|
from objects.decorators import override
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
|
16
examples/providers/callable_delegation.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
"""`Callable` providers delegation example."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from objects.providers import Callable
|
||||||
|
from objects.providers import Delegate
|
||||||
|
|
||||||
|
|
||||||
|
# Some callable provider and few delegates of it:
|
||||||
|
callable_provider = Callable(sys.exit)
|
||||||
|
callable_provider_delegate1 = callable_provider.delegate()
|
||||||
|
callable_provider_delegate2 = Delegate(callable_provider)
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert callable_provider_delegate1() is callable_provider
|
||||||
|
assert callable_provider_delegate2() is callable_provider
|
18
examples/providers/callable_injections.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
"""`Callable` providers example."""
|
||||||
|
|
||||||
|
from passlib.hash import sha256_crypt
|
||||||
|
|
||||||
|
from objects.providers import Callable
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
# Password hasher and verifier providers (hash function could be changed
|
||||||
|
# anytime (for example, to sha512) without any changes in client's code):
|
||||||
|
password_hasher = Callable(sha256_crypt.encrypt,
|
||||||
|
KwArg('salt_size', 16),
|
||||||
|
KwArg('rounds', 10000))
|
||||||
|
password_verifier = Callable(sha256_crypt.verify)
|
||||||
|
|
||||||
|
# Making some asserts (client's code):
|
||||||
|
hashed_password = password_hasher('super secret')
|
||||||
|
assert password_verifier('super secret', hashed_password)
|
80
examples/providers/external_dependency.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
"""`ExternalDependency` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import ExternalDependency
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.providers import Singleton
|
||||||
|
|
||||||
|
from objects.injections import KwArg
|
||||||
|
from objects.injections import Attribute
|
||||||
|
|
||||||
|
# Importing SQLITE3 and contextlib.closing for working with cursors:
|
||||||
|
import sqlite3
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
|
||||||
|
# Definition of example UserService:
|
||||||
|
class UserService(object):
|
||||||
|
|
||||||
|
"""Example class UserService.
|
||||||
|
|
||||||
|
UserService has dependency on DBAPI 2.0 database connection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, database):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
Database dependency need to be injected via init arg.
|
||||||
|
"""
|
||||||
|
self.database = database
|
||||||
|
|
||||||
|
def init_database(self):
|
||||||
|
"""Initialize database, if it has not been initialized yet."""
|
||||||
|
with closing(self.database.cursor()) as cursor:
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS users(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name VARCHAR(32)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
def create(self, name):
|
||||||
|
"""Create user with provided name and return his id."""
|
||||||
|
with closing(self.database.cursor()) as cursor:
|
||||||
|
cursor.execute('INSERT INTO users(name) VALUES (?)', (name,))
|
||||||
|
return cursor.lastrowid
|
||||||
|
|
||||||
|
def get_by_id(self, id):
|
||||||
|
"""Return user info by user id."""
|
||||||
|
with closing(self.database.cursor()) as cursor:
|
||||||
|
cursor.execute('SELECT id, name FROM users WHERE id=?', (id,))
|
||||||
|
return cursor.fetchone()
|
||||||
|
|
||||||
|
|
||||||
|
# Database and UserService providers:
|
||||||
|
database = ExternalDependency(instance_of=sqlite3.dbapi2.Connection)
|
||||||
|
users_service_factory = Factory(UserService,
|
||||||
|
KwArg('database', database))
|
||||||
|
|
||||||
|
# Out of library's scope.
|
||||||
|
#
|
||||||
|
# Setting database provider:
|
||||||
|
database.provided_by(Singleton(sqlite3.dbapi2.Connection,
|
||||||
|
KwArg('database', ':memory:'),
|
||||||
|
KwArg('timeout', 30),
|
||||||
|
KwArg('detect_types', True),
|
||||||
|
KwArg('isolation_level', 'EXCLUSIVE'),
|
||||||
|
Attribute('row_factory', sqlite3.Row)))
|
||||||
|
|
||||||
|
# Creating UserService instance:
|
||||||
|
users_service = users_service_factory()
|
||||||
|
|
||||||
|
# Initializing UserService database:
|
||||||
|
users_service.init_database()
|
||||||
|
|
||||||
|
# Creating test user and retrieving full information about him:
|
||||||
|
test_user_id = users_service.create(name='test_user')
|
||||||
|
test_user = users_service.get_by_id(test_user_id)
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert test_user['id'] == 1
|
||||||
|
assert test_user['name'] == 'test_user'
|
19
examples/providers/factory.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
"""`Factory` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
# Factory provider creates new instance of specified class on every call.
|
||||||
|
users_factory = Factory(User)
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
user2 = users_factory()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1 is not user2
|
||||||
|
assert isinstance(user1, User) and isinstance(user2, User)
|
53
examples/providers/factory_attribute_injections.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
"""`Factory` providers with attribute injections example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import Attribute
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initializer."""
|
||||||
|
self.main_photo = None
|
||||||
|
self.credit_card = None
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreditCard(object):
|
||||||
|
|
||||||
|
"""Example class CreditCard."""
|
||||||
|
|
||||||
|
# User, Photo and CreditCard factories:
|
||||||
|
credit_cards_factory = Factory(CreditCard)
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
Attribute('main_photo', photos_factory),
|
||||||
|
Attribute('credit_card', credit_cards_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
# Same as: user1 = User()
|
||||||
|
# user1.main_photo = Photo()
|
||||||
|
# user1.credit_card = CreditCard()
|
||||||
|
user2 = users_factory()
|
||||||
|
# Same as: user2 = User()
|
||||||
|
# user2.main_photo = Photo()
|
||||||
|
# user2.credit_card = CreditCard()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1 is not user2
|
||||||
|
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
assert isinstance(user1.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
assert isinstance(user2.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
||||||
|
assert user1.credit_card is not user2.credit_card
|
50
examples/providers/factory_delegation.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
"""`Factory` providers delegation example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self, photos_factory):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
:param photos_factory: objects.providers.Factory
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.photos_factory = photos_factory
|
||||||
|
self._main_photo = None
|
||||||
|
super(User, self).__init__()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def main_photo(self):
|
||||||
|
"""Return user's main photo."""
|
||||||
|
if not self._main_photo:
|
||||||
|
self._main_photo = self.photos_factory()
|
||||||
|
return self._main_photo
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
# User and Photo factories:
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
KwArg('photos_factory', photos_factory.delegate()))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
user2 = users_factory()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert isinstance(user1, User)
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
|
||||||
|
assert isinstance(user2, User)
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
|
||||||
|
assert user1 is not user2
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
42
examples/providers/factory_init_injections.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
"""`Factory` providers with init injections example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self, main_photo):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
:param main_photo: Photo
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.main_photo = main_photo
|
||||||
|
super(User, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
# User and Photo factories:
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
KwArg('main_photo', photos_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory() # Same as: user1 = User(main_photo=Photo())
|
||||||
|
user2 = users_factory() # Same as: user2 = User(main_photo=Photo())
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert isinstance(user1, User)
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
|
||||||
|
assert isinstance(user2, User)
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
|
||||||
|
assert user1 is not user2
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
80
examples/providers/factory_init_injections_and_contexts.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
"""`Factory` providers with init injections and context arguments example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User.
|
||||||
|
|
||||||
|
Class User has to be provided with user id.
|
||||||
|
|
||||||
|
Also Class User has dependencies on class Photo and class CreditCard
|
||||||
|
objects.
|
||||||
|
|
||||||
|
All of the dependencies have to be provided like __init__ arguments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, id, main_photo, credit_card):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
:param id: int
|
||||||
|
:param main_photo: Photo
|
||||||
|
:param credit_card: CreditCard
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.id = id
|
||||||
|
self.main_photo = main_photo
|
||||||
|
self.credit_card = credit_card
|
||||||
|
super(User, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreditCard(object):
|
||||||
|
|
||||||
|
"""Example class CreditCard."""
|
||||||
|
|
||||||
|
# User, Photo and CreditCard factories:
|
||||||
|
credit_cards_factory = Factory(CreditCard)
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
KwArg('main_photo', photos_factory),
|
||||||
|
KwArg('credit_card', credit_cards_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory(1)
|
||||||
|
# Same as: user1 = User(1,
|
||||||
|
# main_photo=Photo(),
|
||||||
|
# credit_card=CreditCard())
|
||||||
|
user2 = users_factory(2)
|
||||||
|
# Same as: user2 = User(2,
|
||||||
|
# main_photo=Photo(),
|
||||||
|
# credit_card=CreditCard())
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1.id == 1
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
assert isinstance(user1.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user2.id == 2
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
assert isinstance(user2.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
||||||
|
assert user1.credit_card is not user2.credit_card
|
||||||
|
|
||||||
|
# Context keyword arguments have priority on KwArg injections priority:
|
||||||
|
main_photo_mock = Photo()
|
||||||
|
credit_card_mock = CreditCard()
|
||||||
|
|
||||||
|
user3 = users_factory(3, main_photo=main_photo_mock,
|
||||||
|
credit_card=credit_card_mock)
|
||||||
|
|
||||||
|
assert user3.id == 3
|
||||||
|
assert user3.main_photo is main_photo_mock
|
||||||
|
assert user3.credit_card is credit_card_mock
|
61
examples/providers/factory_method_injections.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
"""`Factory` providers with method injections example."""
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.injections import Method
|
||||||
|
|
||||||
|
|
||||||
|
class User(object):
|
||||||
|
|
||||||
|
"""Example class User."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initializer."""
|
||||||
|
self.main_photo = None
|
||||||
|
self.credit_card = None
|
||||||
|
|
||||||
|
def set_main_photo(self, photo):
|
||||||
|
"""Set user's main photo."""
|
||||||
|
self.main_photo = photo
|
||||||
|
|
||||||
|
def set_credit_card(self, credit_card):
|
||||||
|
"""Set user's credit card."""
|
||||||
|
self.credit_card = credit_card
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(object):
|
||||||
|
|
||||||
|
"""Example class Photo."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreditCard(object):
|
||||||
|
|
||||||
|
"""Example class CreditCard."""
|
||||||
|
|
||||||
|
# User, Photo and CreditCard factories:
|
||||||
|
credit_cards_factory = Factory(CreditCard)
|
||||||
|
photos_factory = Factory(Photo)
|
||||||
|
users_factory = Factory(User,
|
||||||
|
Method('set_main_photo', photos_factory),
|
||||||
|
Method('set_credit_card', credit_cards_factory))
|
||||||
|
|
||||||
|
# Creating several User objects:
|
||||||
|
user1 = users_factory()
|
||||||
|
# Same as: user1 = User()
|
||||||
|
# user1.set_main_photo(Photo())
|
||||||
|
# user1.set_credit_card(CreditCard())
|
||||||
|
user2 = users_factory()
|
||||||
|
# Same as: user2 = User()
|
||||||
|
# user2.set_main_photo(Photo())
|
||||||
|
# user2.set_credit_card(CreditCard())
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user1 is not user2
|
||||||
|
|
||||||
|
assert isinstance(user1.main_photo, Photo)
|
||||||
|
assert isinstance(user1.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert isinstance(user2.main_photo, Photo)
|
||||||
|
assert isinstance(user2.credit_card, CreditCard)
|
||||||
|
|
||||||
|
assert user1.main_photo is not user2.main_photo
|
||||||
|
assert user1.credit_card is not user2.credit_card
|
21
examples/providers/singleton.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
"""`Singleton` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import Singleton
|
||||||
|
|
||||||
|
|
||||||
|
class UserService(object):
|
||||||
|
|
||||||
|
"""Example class UserService."""
|
||||||
|
|
||||||
|
# Singleton provider creates new instance of specified class on first call and
|
||||||
|
# returns same instance on every next call.
|
||||||
|
users_service_provider = Singleton(UserService)
|
||||||
|
|
||||||
|
# Retrieving several UserService objects:
|
||||||
|
user_service1 = users_service_provider()
|
||||||
|
user_service2 = users_service_provider()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user_service1 is user_service2
|
||||||
|
assert isinstance(user_service1, UserService)
|
||||||
|
assert isinstance(user_service2, UserService)
|
14
examples/providers/singleton_delegation.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
"""`Singleton` providers delegation example."""
|
||||||
|
|
||||||
|
from objects.providers import Singleton
|
||||||
|
from objects.providers import Delegate
|
||||||
|
|
||||||
|
|
||||||
|
# Some singleton provider and few delegates of it:
|
||||||
|
singleton_provider = Singleton(object)
|
||||||
|
singleton_provider_delegate1 = singleton_provider.delegate()
|
||||||
|
singleton_provider_delegate2 = Delegate(singleton_provider)
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert singleton_provider_delegate1() is singleton_provider
|
||||||
|
assert singleton_provider_delegate2() is singleton_provider
|
29
examples/providers/singleton_reseting.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
"""`Singleton` providers resetting example."""
|
||||||
|
|
||||||
|
from objects.providers import Singleton
|
||||||
|
|
||||||
|
|
||||||
|
class UserService(object):
|
||||||
|
|
||||||
|
"""Example class UserService."""
|
||||||
|
|
||||||
|
# Users service singleton provider:
|
||||||
|
users_service_provider = Singleton(UserService)
|
||||||
|
|
||||||
|
# Retrieving several UserService objects:
|
||||||
|
user_service1 = users_service_provider()
|
||||||
|
user_service2 = users_service_provider()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user_service1 is user_service2
|
||||||
|
assert isinstance(user_service1, UserService)
|
||||||
|
assert isinstance(user_service2, UserService)
|
||||||
|
|
||||||
|
# Resetting of memorized instance:
|
||||||
|
users_service_provider.reset()
|
||||||
|
|
||||||
|
# Retrieving one more UserService object:
|
||||||
|
user_service3 = users_service_provider()
|
||||||
|
|
||||||
|
# Making some asserts:
|
||||||
|
assert user_service3 is not user_service1
|
23
examples/providers/static.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
"""`Static` providers example."""
|
||||||
|
|
||||||
|
from objects.providers import Class
|
||||||
|
from objects.providers import Object
|
||||||
|
from objects.providers import Function
|
||||||
|
from objects.providers import Value
|
||||||
|
|
||||||
|
|
||||||
|
# Provides class - `object`:
|
||||||
|
cls_provider = Class(object)
|
||||||
|
assert cls_provider() is object
|
||||||
|
|
||||||
|
# Provides object - `object()`:
|
||||||
|
object_provider = Object(object())
|
||||||
|
assert isinstance(object_provider(), object)
|
||||||
|
|
||||||
|
# Provides function - `len`:
|
||||||
|
function_provider = Function(len)
|
||||||
|
assert function_provider() is len
|
||||||
|
|
||||||
|
# Provides value - `123`:
|
||||||
|
value_provider = Value(123)
|
||||||
|
assert value_provider() == 123
|
|
@ -1,9 +1,8 @@
|
||||||
"""`@inject` decorator example."""
|
"""`@inject` decorator example."""
|
||||||
|
|
||||||
from objects.providers import NewInstance
|
from objects.providers import NewInstance
|
||||||
|
|
||||||
from objects.injections import KwArg
|
from objects.injections import KwArg
|
||||||
from objects.injections import inject
|
from objects.decorators import inject
|
||||||
|
|
||||||
|
|
||||||
new_object = NewInstance(object)
|
new_object = NewInstance(object)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from objects.catalog import AbstractCatalog
|
from objects.catalog import AbstractCatalog
|
||||||
from objects.catalog import override
|
|
||||||
|
|
||||||
from objects.providers import Singleton
|
from objects.providers import Singleton
|
||||||
from objects.providers import NewInstance
|
from objects.providers import NewInstance
|
||||||
|
@ -11,6 +10,8 @@ from objects.providers import NewInstance
|
||||||
from objects.injections import KwArg
|
from objects.injections import KwArg
|
||||||
from objects.injections import Attribute
|
from objects.injections import Attribute
|
||||||
|
|
||||||
|
from objects.decorators import override
|
||||||
|
|
||||||
|
|
||||||
class ObjectA(object):
|
class ObjectA(object):
|
||||||
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
"""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
|
|
|
@ -1,23 +0,0 @@
|
||||||
"""`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
|
|
|
@ -1,12 +1,17 @@
|
||||||
"""`KwArg` and `Attribute` injections example."""
|
"""`@inject` decorator example.
|
||||||
|
|
||||||
|
Flask is required to make this example work.
|
||||||
|
"""
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from objects.providers import Singleton
|
from flask import Flask
|
||||||
from objects.providers import NewInstance
|
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.providers import Singleton
|
||||||
from objects.injections import KwArg
|
from objects.injections import KwArg
|
||||||
from objects.injections import Attribute
|
from objects.injections import Attribute
|
||||||
|
from objects.decorators import inject
|
||||||
|
|
||||||
|
|
||||||
class ObjectA(object):
|
class ObjectA(object):
|
||||||
|
@ -32,14 +37,25 @@ database = Singleton(sqlite3.Connection,
|
||||||
KwArg('isolation_level', 'EXCLUSIVE'),
|
KwArg('isolation_level', 'EXCLUSIVE'),
|
||||||
Attribute('row_factory', sqlite3.Row))
|
Attribute('row_factory', sqlite3.Row))
|
||||||
|
|
||||||
object_a = NewInstance(ObjectA,
|
object_a_factory = Factory(ObjectA,
|
||||||
KwArg('database', database))
|
KwArg('database', database))
|
||||||
|
|
||||||
# Creating several `ObjectA` instances.
|
|
||||||
object_a_1 = object_a()
|
|
||||||
object_a_2 = object_a()
|
|
||||||
|
|
||||||
# Making some asserts.
|
# Flask application.
|
||||||
assert object_a_1 is not object_a_2
|
app = Flask(__name__)
|
||||||
assert object_a_1.database is object_a_2.database
|
|
||||||
assert object_a_1.get_one() == object_a_2.get_one() == 1
|
|
||||||
|
# Flask view with inject decorator.
|
||||||
|
@app.route('/')
|
||||||
|
@inject(KwArg('database', database))
|
||||||
|
@inject(KwArg('object_a', object_a_factory))
|
||||||
|
def hello(database):
|
||||||
|
one = database.execute('SELECT 1').fetchone()[0]
|
||||||
|
return 'Query returned {0}, db connection {1}'.format(one, database)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run()
|
||||||
|
|
||||||
|
# Example output of "GET / HTTP/1.1" is:
|
||||||
|
# Query returned 1, db connection <sqlite3.Connection object at 0x1057e4030>
|
|
@ -4,11 +4,10 @@ Dependency management tool for Python projects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .catalog import AbstractCatalog
|
from .catalog import AbstractCatalog
|
||||||
from .catalog import override
|
|
||||||
|
|
||||||
from .providers import Provider
|
from .providers import Provider
|
||||||
from .providers import Delegate
|
from .providers import Delegate
|
||||||
from .providers import NewInstance
|
from .providers import Factory
|
||||||
from .providers import Singleton
|
from .providers import Singleton
|
||||||
from .providers import ExternalDependency
|
from .providers import ExternalDependency
|
||||||
from .providers import Class
|
from .providers import Class
|
||||||
|
@ -22,16 +21,18 @@ from .injections import KwArg
|
||||||
from .injections import Attribute
|
from .injections import Attribute
|
||||||
from .injections import Method
|
from .injections import Method
|
||||||
|
|
||||||
|
from .decorators import override
|
||||||
|
from .decorators import inject
|
||||||
|
|
||||||
from .errors import Error
|
from .errors import Error
|
||||||
|
|
||||||
|
|
||||||
__all__ = ('AbstractCatalog',
|
__all__ = ('AbstractCatalog',
|
||||||
'override',
|
|
||||||
|
|
||||||
# Providers
|
# Providers
|
||||||
'Provider',
|
'Provider',
|
||||||
'Delegate',
|
'Delegate',
|
||||||
'NewInstance',
|
'Factory',
|
||||||
'Singleton',
|
'Singleton',
|
||||||
'ExternalDependency',
|
'ExternalDependency',
|
||||||
'Class',
|
'Class',
|
||||||
|
@ -46,5 +47,9 @@ __all__ = ('AbstractCatalog',
|
||||||
'Attribute',
|
'Attribute',
|
||||||
'Method',
|
'Method',
|
||||||
|
|
||||||
|
# Decorators
|
||||||
|
'override',
|
||||||
|
'inject',
|
||||||
|
|
||||||
# Errors
|
# Errors
|
||||||
'Error')
|
'Error')
|
||||||
|
|
|
@ -1,58 +1,67 @@
|
||||||
"""Catalog module."""
|
"""Catalog module."""
|
||||||
|
|
||||||
from .providers import Provider
|
from six import iteritems
|
||||||
|
from six import add_metaclass
|
||||||
|
|
||||||
from .errors import Error
|
from .errors import Error
|
||||||
|
from .utils import is_provider
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogMetaClass(type):
|
||||||
|
|
||||||
|
"""Providers catalog meta class."""
|
||||||
|
|
||||||
|
def __new__(mcs, class_name, bases, attributes):
|
||||||
|
"""Meta class factory."""
|
||||||
|
providers = dict()
|
||||||
|
new_attributes = dict()
|
||||||
|
for name, value in iteritems(attributes):
|
||||||
|
if is_provider(value):
|
||||||
|
providers[name] = value
|
||||||
|
new_attributes[name] = value
|
||||||
|
|
||||||
|
cls = type.__new__(mcs, class_name, bases, new_attributes)
|
||||||
|
cls.providers = cls.providers.copy()
|
||||||
|
cls.providers.update(providers)
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
@add_metaclass(CatalogMetaClass)
|
||||||
class AbstractCatalog(object):
|
class AbstractCatalog(object):
|
||||||
|
|
||||||
"""Abstract object provides catalog."""
|
"""Abstract providers catalog."""
|
||||||
|
|
||||||
__slots__ = ('__used_providers__',)
|
providers = dict()
|
||||||
|
|
||||||
|
__slots__ = ('_used_providers',)
|
||||||
|
|
||||||
def __init__(self, *used_providers):
|
def __init__(self, *used_providers):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
self.__used_providers__ = set(used_providers)
|
self._used_providers = set(used_providers)
|
||||||
|
|
||||||
def __getattribute__(self, item):
|
def __getattribute__(self, item):
|
||||||
"""Return providers."""
|
"""Return providers."""
|
||||||
attribute = super(AbstractCatalog, self).__getattribute__(item)
|
attribute = super(AbstractCatalog, self).__getattribute__(item)
|
||||||
if item in ('__used_providers__',):
|
if item in ('providers', '_used_providers',):
|
||||||
return attribute
|
return attribute
|
||||||
|
|
||||||
if attribute not in self.__used_providers__:
|
if attribute not in self._used_providers:
|
||||||
raise Error('Provider \'{0}\' '.format(item) +
|
raise Error('Provider \'{0}\' '.format(item) +
|
||||||
'is not listed in dependencies')
|
'is not listed in dependencies')
|
||||||
return attribute
|
return attribute
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def all_providers(cls, provider_type=Provider):
|
def filter(cls, provider_type):
|
||||||
"""Return set of all class providers."""
|
"""Return dict of providers, that are instance of provided type."""
|
||||||
providers = set()
|
return dict([(name, provider)
|
||||||
for attr_name in set(dir(cls)) - set(dir(AbstractCatalog)):
|
for name, provider in iteritems(cls.providers)
|
||||||
provider = getattr(cls, attr_name)
|
if isinstance(provider, provider_type)])
|
||||||
if not isinstance(provider, provider_type):
|
|
||||||
continue
|
|
||||||
providers.add((attr_name, provider))
|
|
||||||
return providers
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def override(cls, overriding):
|
def override(cls, overriding):
|
||||||
|
"""Override current catalog providers by overriding catalog providers.
|
||||||
|
|
||||||
|
:type overriding: AbstractCatalog
|
||||||
"""
|
"""
|
||||||
Override current catalog providers by overriding catalog providers.
|
for name, provider in iteritems(overriding.providers):
|
||||||
|
cls.providers[name].override(provider)
|
||||||
:param overriding: AbstractCatalog
|
|
||||||
"""
|
|
||||||
overridden = overriding.all_providers() - cls.all_providers()
|
|
||||||
for name, provider in overridden:
|
|
||||||
overridden_provider = getattr(cls, name)
|
|
||||||
overridden_provider.override(provider)
|
|
||||||
|
|
||||||
|
|
||||||
def override(catalog):
|
|
||||||
"""Catalog overriding decorator."""
|
|
||||||
def decorator(overriding_catalog):
|
|
||||||
"""Overriding decorator."""
|
|
||||||
catalog.override(overriding_catalog)
|
|
||||||
return overriding_catalog
|
|
||||||
return decorator
|
|
||||||
|
|
41
objects/decorators.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"""Decorators module."""
|
||||||
|
|
||||||
|
from six import wraps
|
||||||
|
|
||||||
|
from .utils import ensure_is_injection
|
||||||
|
|
||||||
|
|
||||||
|
def override(catalog):
|
||||||
|
"""Catalog overriding decorator."""
|
||||||
|
def decorator(overriding_catalog):
|
||||||
|
"""Overriding decorator."""
|
||||||
|
catalog.override(overriding_catalog)
|
||||||
|
return overriding_catalog
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def inject(injection):
|
||||||
|
"""Dependency injection decorator.
|
||||||
|
|
||||||
|
:type injection: Injection
|
||||||
|
:return: (callable) -> (callable)
|
||||||
|
"""
|
||||||
|
injection = ensure_is_injection(injection)
|
||||||
|
|
||||||
|
def decorator(callback):
|
||||||
|
"""Dependency injection decorator."""
|
||||||
|
if hasattr(callback, '_injections'):
|
||||||
|
callback._injections += (injection,)
|
||||||
|
|
||||||
|
@wraps(callback)
|
||||||
|
def decorated(*args, **kwargs):
|
||||||
|
"""Decorated with dependency injection callback."""
|
||||||
|
for injection in getattr(decorated, '_injections'):
|
||||||
|
if injection.name not in kwargs:
|
||||||
|
kwargs[injection.name] = injection.value
|
||||||
|
return callback(*args, **kwargs)
|
||||||
|
|
||||||
|
setattr(decorated, '_injections', (injection,))
|
||||||
|
|
||||||
|
return decorated
|
||||||
|
return decorator
|
|
@ -1,16 +1,13 @@
|
||||||
"""Injections module."""
|
"""Injections module."""
|
||||||
|
|
||||||
from six import wraps
|
|
||||||
|
|
||||||
from .utils import is_provider
|
from .utils import is_provider
|
||||||
from .utils import ensure_is_injection
|
|
||||||
|
|
||||||
|
|
||||||
class Injection(object):
|
class Injection(object):
|
||||||
|
|
||||||
"""Base injection class."""
|
"""Base injection class."""
|
||||||
|
|
||||||
__IS_OBJECTS_INJECTION__ = True
|
__IS_INJECTION__ = True
|
||||||
__slots__ = ('name', 'injectable')
|
__slots__ = ('name', 'injectable')
|
||||||
|
|
||||||
def __init__(self, name, injectable):
|
def __init__(self, name, injectable):
|
||||||
|
@ -30,38 +27,18 @@ class KwArg(Injection):
|
||||||
|
|
||||||
"""Keyword argument injection."""
|
"""Keyword argument injection."""
|
||||||
|
|
||||||
__IS_OBJECTS_KWARG_INJECTION__ = True
|
__IS_KWARG_INJECTION__ = True
|
||||||
|
|
||||||
|
|
||||||
class Attribute(Injection):
|
class Attribute(Injection):
|
||||||
|
|
||||||
"""Attribute injection."""
|
"""Attribute injection."""
|
||||||
|
|
||||||
__IS_OBJECTS_ATTRIBUTE_INJECTION__ = True
|
__IS_ATTRIBUTE_INJECTION__ = True
|
||||||
|
|
||||||
|
|
||||||
class Method(Injection):
|
class Method(Injection):
|
||||||
|
|
||||||
"""Method injection."""
|
"""Method injection."""
|
||||||
|
|
||||||
__IS_OBJECTS_METHOD_INJECTION__ = True
|
__IS_METHOD_INJECTION__ = True
|
||||||
|
|
||||||
|
|
||||||
def inject(injection):
|
|
||||||
"""Inject decorator.
|
|
||||||
|
|
||||||
:type injection: Injection
|
|
||||||
:return: (callable) -> (callable)
|
|
||||||
"""
|
|
||||||
injection = ensure_is_injection(injection)
|
|
||||||
|
|
||||||
def decorator(callback):
|
|
||||||
"""Decorator."""
|
|
||||||
@wraps(callback)
|
|
||||||
def decorated(*args, **kwargs):
|
|
||||||
"""Decorated."""
|
|
||||||
if injection.name not in kwargs:
|
|
||||||
kwargs[injection.name] = injection.value
|
|
||||||
return callback(*args, **kwargs)
|
|
||||||
return decorated
|
|
||||||
return decorator
|
|
||||||
|
|
|
@ -14,15 +14,26 @@ class Provider(object):
|
||||||
|
|
||||||
"""Base provider class."""
|
"""Base provider class."""
|
||||||
|
|
||||||
__IS_OBJECTS_PROVIDER__ = True
|
__IS_PROVIDER__ = True
|
||||||
__slots__ = ('overridden',)
|
__slots__ = ('_overridden',)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
self.overridden = None
|
self._overridden = None
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
|
if self._overridden:
|
||||||
|
return self.last_overriding(*args, **kwargs)
|
||||||
|
return self._provide(*args, **kwargs)
|
||||||
|
|
||||||
|
def _provide(self, *args, **kwargs):
|
||||||
|
"""Providing strategy implementation.
|
||||||
|
|
||||||
|
Abstract protected method that implements providing strategy of
|
||||||
|
particular provider. Current method is called every time when not
|
||||||
|
overridden provider is called. Need to be overridden in subclasses.
|
||||||
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def delegate(self):
|
def delegate(self):
|
||||||
|
@ -31,112 +42,130 @@ class Provider(object):
|
||||||
|
|
||||||
def override(self, provider):
|
def override(self, provider):
|
||||||
"""Override provider with another provider."""
|
"""Override provider with another provider."""
|
||||||
if not self.overridden:
|
if not self._overridden:
|
||||||
self.overridden = (ensure_is_provider(provider),)
|
self._overridden = (ensure_is_provider(provider),)
|
||||||
else:
|
else:
|
||||||
self.overridden = self.overridden + (ensure_is_provider(provider),)
|
self._overridden += (ensure_is_provider(provider),)
|
||||||
|
|
||||||
def reset_override(self):
|
@property
|
||||||
"""Reset all overriding providers."""
|
def is_overridden(self):
|
||||||
self.overridden = None
|
"""Check if provider is overridden by another provider."""
|
||||||
|
return bool(self._overridden)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_overriding(self):
|
def last_overriding(self):
|
||||||
"""Return last overriding provider."""
|
"""Return last overriding provider."""
|
||||||
try:
|
try:
|
||||||
return self.overridden[-1]
|
return self._overridden[-1]
|
||||||
except (TypeError, IndexError):
|
except (TypeError, IndexError):
|
||||||
raise Error('Provider {0} '.format(str(self)) +
|
raise Error('Provider {0} is not overridden'.format(str(self)))
|
||||||
'is not overridden')
|
|
||||||
|
def reset_last_overriding(self):
|
||||||
|
"""Reset last overriding provider."""
|
||||||
|
if not self._overridden:
|
||||||
|
raise Error('Provider {0} is not overridden'.format(str(self)))
|
||||||
|
self._overridden = self._overridden[:-1]
|
||||||
|
|
||||||
|
def reset_override(self):
|
||||||
|
"""Reset all overriding providers."""
|
||||||
|
self._overridden = None
|
||||||
|
|
||||||
|
|
||||||
class Delegate(Provider):
|
class Delegate(Provider):
|
||||||
|
|
||||||
"""Provider's delegate."""
|
"""Provider's delegate."""
|
||||||
|
|
||||||
__slots__ = ('delegated',)
|
__slots__ = ('_delegated',)
|
||||||
|
|
||||||
def __init__(self, delegated):
|
def __init__(self, delegated):
|
||||||
"""Initializer.
|
"""Initializer.
|
||||||
|
|
||||||
:type delegated: Provider
|
:type delegated: Provider
|
||||||
"""
|
"""
|
||||||
self.delegated = ensure_is_provider(delegated)
|
self._delegated = ensure_is_provider(delegated)
|
||||||
super(Delegate, self).__init__()
|
super(Delegate, self).__init__()
|
||||||
|
|
||||||
def __call__(self):
|
def _provide(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
return self.delegated
|
return self._delegated
|
||||||
|
|
||||||
|
|
||||||
class NewInstance(Provider):
|
class Factory(Provider):
|
||||||
|
|
||||||
"""New instance provider.
|
"""Factory provider.
|
||||||
|
|
||||||
New instance providers will create and return new instance on every call.
|
Factory provider creates new instance of specified class on every call.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('provides', 'kwargs', 'attributes', 'methods')
|
__slots__ = ('_provides', '_kwargs', '_attributes', '_methods')
|
||||||
|
|
||||||
def __init__(self, provides, *injections):
|
def __init__(self, provides, *injections):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
if not isinstance(provides, class_types):
|
if not callable(provides):
|
||||||
raise Error('NewInstance provider expects to get class, ' +
|
raise Error('Factory provider expects to get callable, ' +
|
||||||
'got {0} instead'.format(str(provides)))
|
'got {0} instead'.format(str(provides)))
|
||||||
self.provides = provides
|
self._provides = provides
|
||||||
self.kwargs = tuple((injection
|
self._kwargs = tuple((injection
|
||||||
for injection in injections
|
for injection in injections
|
||||||
if is_kwarg_injection(injection)))
|
if is_kwarg_injection(injection)))
|
||||||
self.attributes = tuple((injection
|
self._attributes = tuple((injection
|
||||||
for injection in injections
|
for injection in injections
|
||||||
if is_attribute_injection(injection)))
|
if is_attribute_injection(injection)))
|
||||||
self.methods = tuple((injection
|
self._methods = tuple((injection
|
||||||
for injection in injections
|
for injection in injections
|
||||||
if is_method_injection(injection)))
|
if is_method_injection(injection)))
|
||||||
super(NewInstance, self).__init__()
|
super(Factory, self).__init__()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def _provide(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
if self.overridden:
|
|
||||||
return self.last_overriding(*args, **kwargs)
|
|
||||||
|
|
||||||
init_kwargs = dict(((injection.name, injection.value)
|
init_kwargs = dict(((injection.name, injection.value)
|
||||||
for injection in self.kwargs))
|
for injection in self._kwargs))
|
||||||
init_kwargs.update(kwargs)
|
init_kwargs.update(kwargs)
|
||||||
|
|
||||||
instance = self.provides(*args, **init_kwargs)
|
instance = self._provides(*args, **init_kwargs)
|
||||||
|
|
||||||
for attribute in self.attributes:
|
for attribute in self._attributes:
|
||||||
setattr(instance, attribute.name, attribute.value)
|
setattr(instance, attribute.name, attribute.value)
|
||||||
for method in self.methods:
|
for method in self._methods:
|
||||||
getattr(instance, method.name)(method.value)
|
getattr(instance, method.name)(method.value)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class Singleton(NewInstance):
|
class NewInstance(Factory):
|
||||||
|
|
||||||
|
"""NewInstance provider.
|
||||||
|
|
||||||
|
It is synonym of Factory provider. NewInstance provider is considered to
|
||||||
|
be deprecated, but will be able to use for further backward
|
||||||
|
compatibility.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Singleton(Provider):
|
||||||
|
|
||||||
"""Singleton provider.
|
"""Singleton provider.
|
||||||
|
|
||||||
Singleton provider will create instance once and return it on every call.
|
Singleton provider will create instance once and return it on every call.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('instance',)
|
__slots__ = ('_instance', '_factory')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, provides, *injections):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
self.instance = None
|
self._instance = None
|
||||||
super(Singleton, self).__init__(*args, **kwargs)
|
self._factory = Factory(provides, *injections)
|
||||||
|
super(Singleton, self).__init__()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def _provide(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
if not self.instance:
|
if not self._instance:
|
||||||
self.instance = super(Singleton, self).__call__(*args, **kwargs)
|
self._instance = self._factory(*args, **kwargs)
|
||||||
return self.instance
|
return self._instance
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""Reset instance."""
|
"""Reset instance."""
|
||||||
self.instance = None
|
self._instance = None
|
||||||
|
|
||||||
|
|
||||||
class ExternalDependency(Provider):
|
class ExternalDependency(Provider):
|
||||||
|
@ -147,29 +176,33 @@ class ExternalDependency(Provider):
|
||||||
the client's code, but it's interface is known.
|
the client's code, but it's interface is known.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('instance_of',)
|
__slots__ = ('_instance_of',)
|
||||||
|
|
||||||
def __init__(self, instance_of):
|
def __init__(self, instance_of):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
if not isinstance(instance_of, class_types):
|
if not isinstance(instance_of, class_types):
|
||||||
raise Error('ExternalDependency provider expects to get class, ' +
|
raise Error('ExternalDependency provider expects to get class, ' +
|
||||||
'got {0} instead'.format(str(instance_of)))
|
'got {0} instead'.format(str(instance_of)))
|
||||||
self.instance_of = instance_of
|
self._instance_of = instance_of
|
||||||
super(ExternalDependency, self).__init__()
|
super(ExternalDependency, self).__init__()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
if not self.overridden:
|
if not self._overridden:
|
||||||
raise Error('Dependency is not defined')
|
raise Error('Dependency is not defined')
|
||||||
|
|
||||||
instance = self.last_overriding(*args, **kwargs)
|
instance = self.last_overriding(*args, **kwargs)
|
||||||
|
|
||||||
if not isinstance(instance, self.instance_of):
|
if not isinstance(instance, self._instance_of):
|
||||||
raise Error('{0} is not an '.format(instance) +
|
raise Error('{0} is not an '.format(instance) +
|
||||||
'instance of {0}'.format(self.instance_of))
|
'instance of {0}'.format(self._instance_of))
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
def provided_by(self, provider):
|
||||||
|
"""Set external dependency provider."""
|
||||||
|
return self.override(provider)
|
||||||
|
|
||||||
|
|
||||||
class _StaticProvider(Provider):
|
class _StaticProvider(Provider):
|
||||||
|
|
||||||
|
@ -179,18 +212,16 @@ class _StaticProvider(Provider):
|
||||||
it got on input.
|
it got on input.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('provides',)
|
__slots__ = ('_provides',)
|
||||||
|
|
||||||
def __init__(self, provides):
|
def __init__(self, provides):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
self.provides = provides
|
self._provides = provides
|
||||||
super(_StaticProvider, self).__init__()
|
super(_StaticProvider, self).__init__()
|
||||||
|
|
||||||
def __call__(self):
|
def _provide(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
if self.overridden:
|
return self._provides
|
||||||
return self.last_overriding()
|
|
||||||
return self.provides
|
|
||||||
|
|
||||||
|
|
||||||
class Class(_StaticProvider):
|
class Class(_StaticProvider):
|
||||||
|
@ -221,28 +252,25 @@ class Callable(Provider):
|
||||||
with some predefined dependency injections.
|
with some predefined dependency injections.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('callback', 'injections')
|
__slots__ = ('_callback', '_injections')
|
||||||
|
|
||||||
def __init__(self, callback, *injections):
|
def __init__(self, callback, *injections):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
if not callable(callback):
|
if not callable(callback):
|
||||||
raise Error('Callable expected, got {0}'.format(str(callback)))
|
raise Error('Callable expected, got {0}'.format(str(callback)))
|
||||||
self.callback = callback
|
self._callback = callback
|
||||||
self.injections = tuple((injection
|
self._injections = tuple((injection
|
||||||
for injection in injections
|
for injection in injections
|
||||||
if is_kwarg_injection(injection)))
|
if is_kwarg_injection(injection)))
|
||||||
super(Callable, self).__init__()
|
super(Callable, self).__init__()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def _provide(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
if self.overridden:
|
|
||||||
return self.last_overriding()
|
|
||||||
|
|
||||||
injections = dict(((injection.name, injection.value)
|
injections = dict(((injection.name, injection.value)
|
||||||
for injection in self.injections))
|
for injection in self._injections))
|
||||||
injections.update(kwargs)
|
injections.update(kwargs)
|
||||||
|
|
||||||
return self.callback(*args, **injections)
|
return self._callback(*args, **injections)
|
||||||
|
|
||||||
|
|
||||||
class Config(Provider):
|
class Config(Provider):
|
||||||
|
@ -254,18 +282,22 @@ class Config(Provider):
|
||||||
to create deferred config value provider.
|
to create deferred config value provider.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('value',)
|
__slots__ = ('_value',)
|
||||||
|
|
||||||
def __init__(self, value=None):
|
def __init__(self, value=None):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
if not value:
|
if not value:
|
||||||
value = dict()
|
value = dict()
|
||||||
self.value = value
|
self._value = value
|
||||||
super(Config, self).__init__()
|
super(Config, self).__init__()
|
||||||
|
|
||||||
def __call__(self, paths=None):
|
def __getattr__(self, item):
|
||||||
|
"""Return instance of deferred config."""
|
||||||
|
return _ChildConfig(parents=(item,), root_config=self)
|
||||||
|
|
||||||
|
def _provide(self, paths=None):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
value = self.value
|
value = self._value
|
||||||
if paths:
|
if paths:
|
||||||
for path in paths:
|
for path in paths:
|
||||||
try:
|
try:
|
||||||
|
@ -275,13 +307,9 @@ class Config(Provider):
|
||||||
'"{0}" is undefined'.format('.'.join(paths)))
|
'"{0}" is undefined'.format('.'.join(paths)))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def __getattr__(self, item):
|
|
||||||
"""Return instance of deferred config."""
|
|
||||||
return _ChildConfig(parents=(item,), root_config=self)
|
|
||||||
|
|
||||||
def update_from(self, value):
|
def update_from(self, value):
|
||||||
"""Update current value from another one."""
|
"""Update current value from another one."""
|
||||||
self.value.update(value)
|
self._value.update(value)
|
||||||
|
|
||||||
|
|
||||||
class _ChildConfig(Provider):
|
class _ChildConfig(Provider):
|
||||||
|
@ -292,19 +320,19 @@ class _ChildConfig(Provider):
|
||||||
the current path in the config tree.
|
the current path in the config tree.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('parents', 'root_config')
|
__slots__ = ('_parents', '_root_config')
|
||||||
|
|
||||||
def __init__(self, parents, root_config):
|
def __init__(self, parents, root_config):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
self.parents = parents
|
self._parents = parents
|
||||||
self.root_config = root_config
|
self._root_config = root_config
|
||||||
super(_ChildConfig, self).__init__()
|
super(_ChildConfig, self).__init__()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
"""Return provided instance."""
|
|
||||||
return self.root_config(self.parents)
|
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
"""Return instance of deferred config."""
|
"""Return instance of deferred config."""
|
||||||
return _ChildConfig(parents=self.parents + (item,),
|
return _ChildConfig(parents=self._parents + (item,),
|
||||||
root_config=self.root_config)
|
root_config=self._root_config)
|
||||||
|
|
||||||
|
def _provide(self, *args, **kwargs):
|
||||||
|
"""Return provided instance."""
|
||||||
|
return self._root_config(self._parents)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from .errors import Error
|
||||||
def is_provider(instance):
|
def is_provider(instance):
|
||||||
"""Check if instance is provider instance."""
|
"""Check if instance is provider instance."""
|
||||||
return (not isinstance(instance, class_types) and
|
return (not isinstance(instance, class_types) and
|
||||||
hasattr(instance, '__IS_OBJECTS_PROVIDER__'))
|
getattr(instance, '__IS_PROVIDER__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
def ensure_is_provider(instance):
|
def ensure_is_provider(instance):
|
||||||
|
@ -22,7 +22,7 @@ def ensure_is_provider(instance):
|
||||||
def is_injection(instance):
|
def is_injection(instance):
|
||||||
"""Check if instance is injection instance."""
|
"""Check if instance is injection instance."""
|
||||||
return (not isinstance(instance, class_types) and
|
return (not isinstance(instance, class_types) and
|
||||||
hasattr(instance, '__IS_OBJECTS_INJECTION__'))
|
getattr(instance, '__IS_INJECTION__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
def ensure_is_injection(instance):
|
def ensure_is_injection(instance):
|
||||||
|
@ -36,16 +36,16 @@ def ensure_is_injection(instance):
|
||||||
def is_kwarg_injection(instance):
|
def is_kwarg_injection(instance):
|
||||||
"""Check if instance is keyword argument injection instance."""
|
"""Check if instance is keyword argument injection instance."""
|
||||||
return (not isinstance(instance, class_types) and
|
return (not isinstance(instance, class_types) and
|
||||||
hasattr(instance, '__IS_OBJECTS_KWARG_INJECTION__'))
|
getattr(instance, '__IS_KWARG_INJECTION__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
def is_attribute_injection(instance):
|
def is_attribute_injection(instance):
|
||||||
"""Check if instance is attribute injection instance."""
|
"""Check if instance is attribute injection instance."""
|
||||||
return (not isinstance(instance, class_types) and
|
return (not isinstance(instance, class_types) and
|
||||||
hasattr(instance, '__IS_OBJECTS_ATTRIBUTE_INJECTION__'))
|
getattr(instance, '__IS_ATTRIBUTE_INJECTION__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
def is_method_injection(instance):
|
def is_method_injection(instance):
|
||||||
"""Check if instance is method injection instance."""
|
"""Check if instance is method injection instance."""
|
||||||
return (not isinstance(instance, class_types) and
|
return (not isinstance(instance, class_types) and
|
||||||
hasattr(instance, '__IS_OBJECTS_METHOD_INJECTION__'))
|
getattr(instance, '__IS_METHOD_INJECTION__', False) is True)
|
||||||
|
|
6
setup.py
|
@ -7,7 +7,7 @@ from setuptools import setup
|
||||||
from setuptools import Command
|
from setuptools import Command
|
||||||
|
|
||||||
|
|
||||||
SHORT_DESCRIPTION = 'Dependency management tool for Python projects'
|
SHORT_DESCRIPTION = 'Dependency injection framework for Python projects'
|
||||||
|
|
||||||
|
|
||||||
# Getting description.
|
# Getting description.
|
||||||
|
@ -47,8 +47,6 @@ class PublishCommand(Command):
|
||||||
self.run_command('upload')
|
self.run_command('upload')
|
||||||
os.system('git tag -a {0} -m \'version {0}\''.format(version))
|
os.system('git tag -a {0} -m \'version {0}\''.format(version))
|
||||||
os.system('git push --tags')
|
os.system('git push --tags')
|
||||||
|
|
||||||
|
|
||||||
setup(name='Objects',
|
setup(name='Objects',
|
||||||
version=version,
|
version=version,
|
||||||
description=SHORT_DESCRIPTION,
|
description=SHORT_DESCRIPTION,
|
||||||
|
@ -69,9 +67,9 @@ setup(name='Objects',
|
||||||
'Dependency management',
|
'Dependency management',
|
||||||
'Dependency injection',
|
'Dependency injection',
|
||||||
'Dependency injection container',
|
'Dependency injection container',
|
||||||
|
'Dependency injector',
|
||||||
'DI',
|
'DI',
|
||||||
'DIC',
|
'DIC',
|
||||||
'Dependency injector',
|
|
||||||
'Inversion of Control',
|
'Inversion of Control',
|
||||||
'Inversion of Control container',
|
'Inversion of Control container',
|
||||||
'IoC',
|
'IoC',
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
from objects.catalog import AbstractCatalog
|
from objects.catalog import AbstractCatalog
|
||||||
from objects.catalog import override
|
|
||||||
|
|
||||||
from objects.providers import Object
|
from objects.providers import Object
|
||||||
from objects.providers import Value
|
from objects.providers import Value
|
||||||
|
@ -34,32 +33,38 @@ class CatalogTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_all_providers(self):
|
def test_all_providers(self):
|
||||||
"""Test getting of all catalog providers."""
|
"""Test getting of all catalog providers."""
|
||||||
all_providers = self.Catalog.all_providers()
|
self.assertTrue(len(self.Catalog.providers) == 2)
|
||||||
all_providers_dict = dict(all_providers)
|
|
||||||
|
|
||||||
self.assertIsInstance(all_providers, set)
|
self.assertIn('obj', self.Catalog.providers)
|
||||||
self.assertTrue(len(all_providers) == 2)
|
self.assertIn(self.Catalog.obj, self.Catalog.providers.values())
|
||||||
|
|
||||||
self.assertIn('obj', all_providers_dict)
|
self.assertIn('another_obj', self.Catalog.providers)
|
||||||
self.assertIn(self.Catalog.obj, all_providers_dict.values())
|
self.assertIn(self.Catalog.another_obj,
|
||||||
|
self.Catalog.providers.values())
|
||||||
self.assertIn('another_obj', all_providers_dict)
|
|
||||||
self.assertIn(self.Catalog.another_obj, all_providers_dict.values())
|
|
||||||
|
|
||||||
def test_all_providers_by_type(self):
|
def test_all_providers_by_type(self):
|
||||||
"""Test getting of all catalog providers of specific type."""
|
"""Test getting of all catalog providers of specific type."""
|
||||||
self.assertTrue(len(self.Catalog.all_providers(Object)) == 2)
|
self.assertTrue(len(self.Catalog.filter(Object)) == 2)
|
||||||
self.assertTrue(len(self.Catalog.all_providers(Value)) == 0)
|
self.assertTrue(len(self.Catalog.filter(Value)) == 0)
|
||||||
|
|
||||||
def test_overriding(self):
|
def test_metaclass_with_several_catalogs(self):
|
||||||
"""Test catalog overriding with another catalog."""
|
"""Test that metaclass work well with several catalogs."""
|
||||||
@override(self.Catalog)
|
class Catalog1(AbstractCatalog):
|
||||||
class OverridingCatalog(self.Catalog):
|
|
||||||
|
|
||||||
"""Overriding catalog."""
|
"""Catalog1."""
|
||||||
|
|
||||||
obj = Value(1)
|
provider = Object(object())
|
||||||
another_obj = Value(2)
|
|
||||||
|
|
||||||
self.assertEqual(self.Catalog.obj(), 1)
|
class Catalog2(AbstractCatalog):
|
||||||
self.assertEqual(self.Catalog.another_obj(), 2)
|
|
||||||
|
"""Catalog2."""
|
||||||
|
|
||||||
|
provider = Object(object())
|
||||||
|
|
||||||
|
self.assertTrue(len(Catalog1.providers) == 1)
|
||||||
|
self.assertIs(Catalog1.provider, Catalog1.providers['provider'])
|
||||||
|
|
||||||
|
self.assertTrue(len(Catalog2.providers) == 1)
|
||||||
|
self.assertIs(Catalog2.provider, Catalog2.providers['provider'])
|
||||||
|
|
||||||
|
self.assertIsNot(Catalog1.provider, Catalog2.provider)
|
||||||
|
|
115
tests/test_decorators.py
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
"""Objects decorators unittests."""
|
||||||
|
|
||||||
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
from objects.decorators import override
|
||||||
|
from objects.decorators import inject
|
||||||
|
|
||||||
|
from objects.catalog import AbstractCatalog
|
||||||
|
|
||||||
|
from objects.providers import Factory
|
||||||
|
from objects.providers import Object
|
||||||
|
from objects.providers import Value
|
||||||
|
|
||||||
|
from objects.injections import KwArg
|
||||||
|
|
||||||
|
from objects.errors import Error
|
||||||
|
|
||||||
|
|
||||||
|
class OverrideTests(unittest.TestCase):
|
||||||
|
|
||||||
|
"""Override decorator test cases."""
|
||||||
|
|
||||||
|
class Catalog(AbstractCatalog):
|
||||||
|
|
||||||
|
"""Test catalog."""
|
||||||
|
|
||||||
|
obj = Object(object())
|
||||||
|
another_obj = Object(object())
|
||||||
|
|
||||||
|
def test_overriding(self):
|
||||||
|
"""Test catalog overriding with another catalog."""
|
||||||
|
@override(self.Catalog)
|
||||||
|
class OverridingCatalog(self.Catalog):
|
||||||
|
|
||||||
|
"""Overriding catalog."""
|
||||||
|
|
||||||
|
obj = Value(1)
|
||||||
|
another_obj = Value(2)
|
||||||
|
|
||||||
|
self.assertEqual(self.Catalog.obj(), 1)
|
||||||
|
self.assertEqual(self.Catalog.another_obj(), 2)
|
||||||
|
|
||||||
|
|
||||||
|
class InjectTests(unittest.TestCase):
|
||||||
|
|
||||||
|
"""Inject decorator test cases."""
|
||||||
|
|
||||||
|
def test_decorated(self):
|
||||||
|
"""Test `inject()` decorated callback."""
|
||||||
|
provider1 = Factory(object)
|
||||||
|
provider2 = Factory(list)
|
||||||
|
|
||||||
|
@inject(KwArg('a', provider1))
|
||||||
|
@inject(KwArg('b', provider2))
|
||||||
|
def test(a, b):
|
||||||
|
return a, b
|
||||||
|
|
||||||
|
a1, b1 = test()
|
||||||
|
a2, b2 = test()
|
||||||
|
|
||||||
|
self.assertIsInstance(a1, object)
|
||||||
|
self.assertIsInstance(a2, object)
|
||||||
|
self.assertIsNot(a1, a2)
|
||||||
|
|
||||||
|
self.assertIsInstance(b1, list)
|
||||||
|
self.assertIsInstance(b2, list)
|
||||||
|
self.assertIsNot(b1, b2)
|
||||||
|
|
||||||
|
def test_decorated_kwargs_priority(self):
|
||||||
|
"""Test `inject()` decorated callback kwargs priority."""
|
||||||
|
provider1 = Factory(object)
|
||||||
|
provider2 = Factory(list)
|
||||||
|
object_a = object()
|
||||||
|
|
||||||
|
@inject(KwArg('a', provider1))
|
||||||
|
@inject(KwArg('b', provider2))
|
||||||
|
def test(a, b):
|
||||||
|
return a, b
|
||||||
|
|
||||||
|
a1, b1 = test(a=object_a)
|
||||||
|
a2, b2 = test(a=object_a)
|
||||||
|
|
||||||
|
self.assertIsInstance(a1, object)
|
||||||
|
self.assertIsInstance(a2, object)
|
||||||
|
self.assertIs(a1, object_a)
|
||||||
|
self.assertIs(a2, object_a)
|
||||||
|
|
||||||
|
self.assertIsInstance(b1, list)
|
||||||
|
self.assertIsInstance(b2, list)
|
||||||
|
self.assertIsNot(b1, b2)
|
||||||
|
|
||||||
|
def test_decorated_with_args(self):
|
||||||
|
"""Test `inject()` decorated callback with args."""
|
||||||
|
provider = Factory(list)
|
||||||
|
object_a = object()
|
||||||
|
|
||||||
|
@inject(KwArg('b', provider))
|
||||||
|
def test(a, b):
|
||||||
|
return a, b
|
||||||
|
|
||||||
|
a1, b1 = test(object_a)
|
||||||
|
a2, b2 = test(object_a)
|
||||||
|
|
||||||
|
self.assertIsInstance(a1, object)
|
||||||
|
self.assertIsInstance(a2, object)
|
||||||
|
self.assertIs(a1, object_a)
|
||||||
|
self.assertIs(a2, object_a)
|
||||||
|
|
||||||
|
self.assertIsInstance(b1, list)
|
||||||
|
self.assertIsInstance(b2, list)
|
||||||
|
self.assertIsNot(b1, b2)
|
||||||
|
|
||||||
|
def test_decorate_with_not_injection(self):
|
||||||
|
"""Test `inject()` decorator with not an injection instance."""
|
||||||
|
self.assertRaises(Error, inject, object)
|
|
@ -6,11 +6,8 @@ from objects.injections import Injection
|
||||||
from objects.injections import KwArg
|
from objects.injections import KwArg
|
||||||
from objects.injections import Attribute
|
from objects.injections import Attribute
|
||||||
from objects.injections import Method
|
from objects.injections import Method
|
||||||
from objects.injections import inject
|
|
||||||
|
|
||||||
from objects.providers import NewInstance
|
from objects.providers import Factory
|
||||||
|
|
||||||
from objects.providers import Error
|
|
||||||
|
|
||||||
|
|
||||||
class InjectionTests(unittest.TestCase):
|
class InjectionTests(unittest.TestCase):
|
||||||
|
@ -30,7 +27,7 @@ class InjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_value_with_provider_injectable(self):
|
def test_value_with_provider_injectable(self):
|
||||||
"""Test Injection value property with provider."""
|
"""Test Injection value property with provider."""
|
||||||
injection = Injection('some_arg_name', NewInstance(object))
|
injection = Injection('some_arg_name', Factory(object))
|
||||||
self.assertIsInstance(injection.value, object)
|
self.assertIsInstance(injection.value, object)
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,77 +62,3 @@ class MethodTests(unittest.TestCase):
|
||||||
injection = Method('some_arg_name', 'some_value')
|
injection = Method('some_arg_name', 'some_value')
|
||||||
self.assertEqual(injection.name, 'some_arg_name')
|
self.assertEqual(injection.name, 'some_arg_name')
|
||||||
self.assertEqual(injection.injectable, 'some_value')
|
self.assertEqual(injection.injectable, 'some_value')
|
||||||
|
|
||||||
|
|
||||||
class InjectTests(unittest.TestCase):
|
|
||||||
|
|
||||||
"""Inject decorator test cases."""
|
|
||||||
|
|
||||||
def test_decorated(self):
|
|
||||||
"""Test `inject()` decorated callback."""
|
|
||||||
provider1 = NewInstance(object)
|
|
||||||
provider2 = NewInstance(list)
|
|
||||||
|
|
||||||
@inject(KwArg('a', provider1))
|
|
||||||
@inject(KwArg('b', provider2))
|
|
||||||
def test(a, b):
|
|
||||||
return a, b
|
|
||||||
|
|
||||||
a1, b1 = test()
|
|
||||||
a2, b2 = test()
|
|
||||||
|
|
||||||
self.assertIsInstance(a1, object)
|
|
||||||
self.assertIsInstance(a2, object)
|
|
||||||
self.assertIsNot(a1, a2)
|
|
||||||
|
|
||||||
self.assertIsInstance(b1, list)
|
|
||||||
self.assertIsInstance(b2, list)
|
|
||||||
self.assertIsNot(b1, b2)
|
|
||||||
|
|
||||||
def test_decorated_kwargs_priority(self):
|
|
||||||
"""Test `inject()` decorated callback kwargs priority."""
|
|
||||||
provider1 = NewInstance(object)
|
|
||||||
provider2 = NewInstance(list)
|
|
||||||
object_a = object()
|
|
||||||
|
|
||||||
@inject(KwArg('a', provider1))
|
|
||||||
@inject(KwArg('b', provider2))
|
|
||||||
def test(a, b):
|
|
||||||
return a, b
|
|
||||||
|
|
||||||
a1, b1 = test(a=object_a)
|
|
||||||
a2, b2 = test(a=object_a)
|
|
||||||
|
|
||||||
self.assertIsInstance(a1, object)
|
|
||||||
self.assertIsInstance(a2, object)
|
|
||||||
self.assertIs(a1, object_a)
|
|
||||||
self.assertIs(a2, object_a)
|
|
||||||
|
|
||||||
self.assertIsInstance(b1, list)
|
|
||||||
self.assertIsInstance(b2, list)
|
|
||||||
self.assertIsNot(b1, b2)
|
|
||||||
|
|
||||||
def test_decorated_with_args(self):
|
|
||||||
"""Test `inject()` decorated callback with args."""
|
|
||||||
provider = NewInstance(list)
|
|
||||||
object_a = object()
|
|
||||||
|
|
||||||
@inject(KwArg('b', provider))
|
|
||||||
def test(a, b):
|
|
||||||
return a, b
|
|
||||||
|
|
||||||
a1, b1 = test(object_a)
|
|
||||||
a2, b2 = test(object_a)
|
|
||||||
|
|
||||||
self.assertIsInstance(a1, object)
|
|
||||||
self.assertIsInstance(a2, object)
|
|
||||||
self.assertIs(a1, object_a)
|
|
||||||
self.assertIs(a2, object_a)
|
|
||||||
|
|
||||||
self.assertIsInstance(b1, list)
|
|
||||||
self.assertIsInstance(b2, list)
|
|
||||||
self.assertIsNot(b1, b2)
|
|
||||||
|
|
||||||
def test_decorate_with_not_injection(self):
|
|
||||||
"""Test `inject()` decorator with not an injection instance."""
|
|
||||||
self.assertRaises(Error, inject, object)
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import unittest2 as unittest
|
||||||
|
|
||||||
from objects.providers import Provider
|
from objects.providers import Provider
|
||||||
from objects.providers import Delegate
|
from objects.providers import Delegate
|
||||||
from objects.providers import NewInstance
|
from objects.providers import Factory
|
||||||
from objects.providers import Singleton
|
from objects.providers import Singleton
|
||||||
from objects.providers import ExternalDependency
|
from objects.providers import ExternalDependency
|
||||||
from objects.providers import Class
|
from objects.providers import Class
|
||||||
|
@ -44,12 +44,12 @@ class ProviderTests(unittest.TestCase):
|
||||||
delegate1 = self.provider.delegate()
|
delegate1 = self.provider.delegate()
|
||||||
|
|
||||||
self.assertIsInstance(delegate1, Delegate)
|
self.assertIsInstance(delegate1, Delegate)
|
||||||
self.assertIs(delegate1.delegated, self.provider)
|
self.assertIs(delegate1(), self.provider)
|
||||||
|
|
||||||
delegate2 = self.provider.delegate()
|
delegate2 = self.provider.delegate()
|
||||||
|
|
||||||
self.assertIsInstance(delegate2, Delegate)
|
self.assertIsInstance(delegate2, Delegate)
|
||||||
self.assertIs(delegate2.delegated, self.provider)
|
self.assertIs(delegate2(), self.provider)
|
||||||
|
|
||||||
self.assertIsNot(delegate1, delegate2)
|
self.assertIsNot(delegate1, delegate2)
|
||||||
|
|
||||||
|
@ -57,31 +57,12 @@ class ProviderTests(unittest.TestCase):
|
||||||
"""Test provider overriding."""
|
"""Test provider overriding."""
|
||||||
overriding_provider = Provider()
|
overriding_provider = Provider()
|
||||||
self.provider.override(overriding_provider)
|
self.provider.override(overriding_provider)
|
||||||
self.assertTrue(self.provider.overridden)
|
self.assertTrue(self.provider.is_overridden)
|
||||||
|
|
||||||
def test_override_with_not_provider(self):
|
def test_override_with_not_provider(self):
|
||||||
"""Test provider overriding with not provider instance."""
|
"""Test provider overriding with not provider instance."""
|
||||||
self.assertRaises(Error, self.provider.override, object())
|
self.assertRaises(Error, self.provider.override, object())
|
||||||
|
|
||||||
def test_reset_override(self):
|
|
||||||
"""Test reset of provider's override."""
|
|
||||||
overriding_provider = Provider()
|
|
||||||
self.provider.override(overriding_provider)
|
|
||||||
|
|
||||||
self.assertTrue(self.provider.overridden)
|
|
||||||
self.assertIs(self.provider.last_overriding, overriding_provider)
|
|
||||||
|
|
||||||
self.provider.reset_override()
|
|
||||||
|
|
||||||
self.assertFalse(self.provider.overridden)
|
|
||||||
try:
|
|
||||||
self.provider.last_overriding
|
|
||||||
except Error:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.fail('Got en error in {}'.format(
|
|
||||||
str(self.test_last_overriding_of_not_overridden_provider)))
|
|
||||||
|
|
||||||
def test_last_overriding(self):
|
def test_last_overriding(self):
|
||||||
"""Test getting last overriding provider."""
|
"""Test getting last overriding provider."""
|
||||||
overriding_provider1 = Provider()
|
overriding_provider1 = Provider()
|
||||||
|
@ -103,6 +84,45 @@ class ProviderTests(unittest.TestCase):
|
||||||
self.fail('Got en error in {}'.format(
|
self.fail('Got en error in {}'.format(
|
||||||
str(self.test_last_overriding_of_not_overridden_provider)))
|
str(self.test_last_overriding_of_not_overridden_provider)))
|
||||||
|
|
||||||
|
def test_reset_last_overriding(self):
|
||||||
|
"""Test reseting of last overriding provider."""
|
||||||
|
overriding_provider1 = Provider()
|
||||||
|
overriding_provider2 = Provider()
|
||||||
|
|
||||||
|
self.provider.override(overriding_provider1)
|
||||||
|
self.provider.override(overriding_provider2)
|
||||||
|
|
||||||
|
self.assertIs(self.provider.last_overriding, overriding_provider2)
|
||||||
|
|
||||||
|
self.provider.reset_last_overriding()
|
||||||
|
self.assertIs(self.provider.last_overriding, overriding_provider1)
|
||||||
|
|
||||||
|
self.provider.reset_last_overriding()
|
||||||
|
self.assertFalse(self.provider.is_overridden)
|
||||||
|
|
||||||
|
def test_reset_last_overriding_of_not_overridden_provider(self):
|
||||||
|
"""Test resetting of last overriding on not overridden provier."""
|
||||||
|
self.assertRaises(Error, self.provider.reset_last_overriding)
|
||||||
|
|
||||||
|
def test_reset_override(self):
|
||||||
|
"""Test reset of provider's override."""
|
||||||
|
overriding_provider = Provider()
|
||||||
|
self.provider.override(overriding_provider)
|
||||||
|
|
||||||
|
self.assertTrue(self.provider.is_overridden)
|
||||||
|
self.assertIs(self.provider.last_overriding, overriding_provider)
|
||||||
|
|
||||||
|
self.provider.reset_override()
|
||||||
|
|
||||||
|
self.assertFalse(self.provider.is_overridden)
|
||||||
|
try:
|
||||||
|
self.provider.last_overriding
|
||||||
|
except Error:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail('Got en error in {}'.format(
|
||||||
|
str(self.test_last_overriding_of_not_overridden_provider)))
|
||||||
|
|
||||||
|
|
||||||
class DelegateTests(unittest.TestCase):
|
class DelegateTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -130,13 +150,13 @@ class DelegateTests(unittest.TestCase):
|
||||||
self.assertIs(delegated2, self.delegated)
|
self.assertIs(delegated2, self.delegated)
|
||||||
|
|
||||||
|
|
||||||
class NewInstanceTests(unittest.TestCase):
|
class FactoryTests(unittest.TestCase):
|
||||||
|
|
||||||
"""NewInstance test cases."""
|
"""Factory test cases."""
|
||||||
|
|
||||||
class Example(object):
|
class Example(object):
|
||||||
|
|
||||||
"""Example class for NewInstance provider tests."""
|
"""Example class for Factory provider tests."""
|
||||||
|
|
||||||
def __init__(self, init_arg1=None, init_arg2=None):
|
def __init__(self, init_arg1=None, init_arg2=None):
|
||||||
"""Initializer.
|
"""Initializer.
|
||||||
|
@ -164,15 +184,19 @@ class NewInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_is_provider(self):
|
def test_is_provider(self):
|
||||||
"""Test `is_provider` check."""
|
"""Test `is_provider` check."""
|
||||||
self.assertTrue(is_provider(NewInstance(self.Example)))
|
self.assertTrue(is_provider(Factory(self.Example)))
|
||||||
|
|
||||||
def test_init_with_not_class(self):
|
def test_init_with_callable(self):
|
||||||
"""Test creation of provider with not a class."""
|
"""Test creation of provider with a callable."""
|
||||||
self.assertRaises(Error, NewInstance, 123)
|
self.assertTrue(Factory(credits))
|
||||||
|
|
||||||
|
def test_init_with_not_callable(self):
|
||||||
|
"""Test creation of provider with not a callable."""
|
||||||
|
self.assertRaises(Error, Factory, 123)
|
||||||
|
|
||||||
def test_call(self):
|
def test_call(self):
|
||||||
"""Test creation of new instances."""
|
"""Test creation of new instances."""
|
||||||
provider = NewInstance(self.Example)
|
provider = Factory(self.Example)
|
||||||
instance1 = provider()
|
instance1 = provider()
|
||||||
instance2 = provider()
|
instance2 = provider()
|
||||||
|
|
||||||
|
@ -182,7 +206,7 @@ class NewInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_call_with_init_args(self):
|
def test_call_with_init_args(self):
|
||||||
"""Test creation of new instances with init args injections."""
|
"""Test creation of new instances with init args injections."""
|
||||||
provider = NewInstance(self.Example,
|
provider = Factory(self.Example,
|
||||||
KwArg('init_arg1', 'i1'),
|
KwArg('init_arg1', 'i1'),
|
||||||
KwArg('init_arg2', 'i2'))
|
KwArg('init_arg2', 'i2'))
|
||||||
|
|
||||||
|
@ -201,7 +225,7 @@ class NewInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_call_with_attributes(self):
|
def test_call_with_attributes(self):
|
||||||
"""Test creation of new instances with attribute injections."""
|
"""Test creation of new instances with attribute injections."""
|
||||||
provider = NewInstance(self.Example,
|
provider = Factory(self.Example,
|
||||||
Attribute('attribute1', 'a1'),
|
Attribute('attribute1', 'a1'),
|
||||||
Attribute('attribute2', 'a2'))
|
Attribute('attribute2', 'a2'))
|
||||||
|
|
||||||
|
@ -220,7 +244,7 @@ class NewInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_call_with_methods(self):
|
def test_call_with_methods(self):
|
||||||
"""Test creation of new instances with method injections."""
|
"""Test creation of new instances with method injections."""
|
||||||
provider = NewInstance(self.Example,
|
provider = Factory(self.Example,
|
||||||
Method('method1', 'm1'),
|
Method('method1', 'm1'),
|
||||||
Method('method2', 'm2'))
|
Method('method2', 'm2'))
|
||||||
|
|
||||||
|
@ -239,7 +263,7 @@ class NewInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_call_with_context_args(self):
|
def test_call_with_context_args(self):
|
||||||
"""Test creation of new instances with context args."""
|
"""Test creation of new instances with context args."""
|
||||||
provider = NewInstance(self.Example)
|
provider = Factory(self.Example)
|
||||||
instance = provider(11, 22)
|
instance = provider(11, 22)
|
||||||
|
|
||||||
self.assertEqual(instance.init_arg1, 11)
|
self.assertEqual(instance.init_arg1, 11)
|
||||||
|
@ -247,7 +271,7 @@ class NewInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_call_with_context_kwargs(self):
|
def test_call_with_context_kwargs(self):
|
||||||
"""Test creation of new instances with context kwargs."""
|
"""Test creation of new instances with context kwargs."""
|
||||||
provider = NewInstance(self.Example,
|
provider = Factory(self.Example,
|
||||||
KwArg('init_arg1', 1))
|
KwArg('init_arg1', 1))
|
||||||
|
|
||||||
instance1 = provider(init_arg2=22)
|
instance1 = provider(init_arg2=22)
|
||||||
|
@ -260,9 +284,9 @@ class NewInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_call_overridden(self):
|
def test_call_overridden(self):
|
||||||
"""Test creation of new instances on overridden provider."""
|
"""Test creation of new instances on overridden provider."""
|
||||||
provider = NewInstance(self.Example)
|
provider = Factory(self.Example)
|
||||||
overriding_provider1 = NewInstance(dict)
|
overriding_provider1 = Factory(dict)
|
||||||
overriding_provider2 = NewInstance(list)
|
overriding_provider2 = Factory(list)
|
||||||
|
|
||||||
provider.override(overriding_provider1)
|
provider.override(overriding_provider1)
|
||||||
provider.override(overriding_provider2)
|
provider.override(overriding_provider2)
|
||||||
|
@ -323,12 +347,12 @@ class ExternalDependencyTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_call_overridden(self):
|
def test_call_overridden(self):
|
||||||
"""Test call of overridden external dependency."""
|
"""Test call of overridden external dependency."""
|
||||||
self.provider.override(NewInstance(list))
|
self.provider.provided_by(Factory(list))
|
||||||
self.assertIsInstance(self.provider(), list)
|
self.assertIsInstance(self.provider(), list)
|
||||||
|
|
||||||
def test_call_overridden_but_not_instance_of(self):
|
def test_call_overridden_but_not_instance_of(self):
|
||||||
"""Test call of overridden external dependency, but not instance of."""
|
"""Test call of overridden external dependency, but not instance of."""
|
||||||
self.provider.override(NewInstance(dict))
|
self.provider.provided_by(Factory(dict))
|
||||||
self.assertRaises(Error, self.provider)
|
self.assertRaises(Error, self.provider)
|
||||||
|
|
||||||
def test_call_not_overridden(self):
|
def test_call_not_overridden(self):
|
||||||
|
|
|
@ -40,6 +40,26 @@ class IsProviderTests(unittest.TestCase):
|
||||||
"""Test with object."""
|
"""Test with object."""
|
||||||
self.assertFalse(is_provider(object()))
|
self.assertFalse(is_provider(object()))
|
||||||
|
|
||||||
|
def test_with_subclass_instance(self):
|
||||||
|
"""Test with subclass of provider instance."""
|
||||||
|
class SomeProvider(Provider):
|
||||||
|
|
||||||
|
"""Some provider for test."""
|
||||||
|
|
||||||
|
self.assertTrue(is_provider(SomeProvider()))
|
||||||
|
|
||||||
|
def test_with_class_with_getattr(self):
|
||||||
|
"""Test with class that has __getattr__() method implementation."""
|
||||||
|
class SomeClass(object):
|
||||||
|
|
||||||
|
"""Some test class with __getattr__() method implementation."""
|
||||||
|
|
||||||
|
def __getattr__(self, _):
|
||||||
|
"""Test implementation that just returns False."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.assertFalse(is_provider(SomeClass()))
|
||||||
|
|
||||||
|
|
||||||
class EnsureIsProviderTests(unittest.TestCase):
|
class EnsureIsProviderTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
5
tox.ini
|
@ -18,6 +18,7 @@ commands=
|
||||||
|
|
||||||
[testenv:coveralls]
|
[testenv:coveralls]
|
||||||
basepython=python2.7
|
basepython=python2.7
|
||||||
|
passenv=TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH
|
||||||
deps=
|
deps=
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
coverage
|
coverage
|
||||||
|
@ -39,14 +40,14 @@ basepython=python2.7
|
||||||
deps=
|
deps=
|
||||||
flake8
|
flake8
|
||||||
commands=
|
commands=
|
||||||
flake8 --max-complexity=8 objects
|
flake8 --max-complexity=8 objects/
|
||||||
|
|
||||||
[testenv:pep257]
|
[testenv:pep257]
|
||||||
basepython=python2.7
|
basepython=python2.7
|
||||||
deps=
|
deps=
|
||||||
pep257
|
pep257
|
||||||
commands=
|
commands=
|
||||||
pep257 objects
|
pep257 objects/
|
||||||
|
|
||||||
[testenv:py26]
|
[testenv:py26]
|
||||||
basepython=python2.6
|
basepython=python2.6
|
||||||
|
|