3
.gitignore
vendored
|
@ -64,3 +64,6 @@ venv/
|
|||
|
||||
# JointJS Experiments
|
||||
jointjs/
|
||||
|
||||
# Vim Rope
|
||||
.ropeproject/
|
||||
|
|
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2015, Roman
|
||||
Copyright (c) 2015, Roman Mogilatov
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
81
README.rst
|
@ -1,27 +1,27 @@
|
|||
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/ |
|
||||
| | :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/ |
|
||||
| | :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/ |
|
||||
| | :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/ |
|
||||
| | :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/ |
|
||||
| | :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 |
|
||||
| | :alt: Build Status |
|
||||
| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg |
|
||||
|
@ -32,31 +32,45 @@ Dependency management tool for Python projects
|
|||
Introduction
|
||||
------------
|
||||
|
||||
Python ecosystem consists of a big amount of various classes, functions and
|
||||
objects that could be used for applications development. Each of them has its
|
||||
own role.
|
||||
Python ecosystem consists of a big amount of various libraries that contain
|
||||
different classes and functions that could be used for applications
|
||||
development. Each of them has its own role.
|
||||
|
||||
Modern Python applications are mostly the composition of well-known open
|
||||
source systems, frameworks, libraries and some turnkey functionality.
|
||||
source systems / frameworks / libraries and some turnkey functionality.
|
||||
|
||||
When application goes bigger, its amount of objects and their dependencies
|
||||
also increased extremely fast and became hard to maintain.
|
||||
When application goes bigger, its complexity and SLOC_ are also increased.
|
||||
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
|
||||
and their dependencies in formal, pretty way. Main idea of **Objects** is to
|
||||
keep dependencies under control.
|
||||
It sounds like "I have so many classes and functions! They are great, now I can
|
||||
understand each of them, but it is so hard to see the whole picture! How are
|
||||
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
|
||||
------------
|
||||
|
||||
**Objects** library is available on PyPi_::
|
||||
*Objects* library is available on PyPi_::
|
||||
|
||||
pip install objects
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
**Objects** documentation is hosted on ReadTheDocs:
|
||||
*Objects* documentation is hosted on ReadTheDocs:
|
||||
|
||||
- `Stable version`_
|
||||
- `Latest version`_
|
||||
|
@ -70,12 +84,12 @@ Examples
|
|||
|
||||
from objects.catalog import AbstractCatalog
|
||||
|
||||
from objects.providers import Factory
|
||||
from objects.providers import Singleton
|
||||
from objects.providers import NewInstance
|
||||
|
||||
from objects.injections import KwArg
|
||||
from objects.injections import Attribute
|
||||
from objects.injections import inject
|
||||
from objects.decorators import inject
|
||||
|
||||
import sqlite3
|
||||
|
||||
|
@ -108,19 +122,19 @@ Examples
|
|||
Attribute('row_factory', sqlite3.Row))
|
||||
""":type: (objects.Provider) -> sqlite3.Connection"""
|
||||
|
||||
object_a = NewInstance(ObjectA,
|
||||
KwArg('db', database))
|
||||
object_a_factory = Factory(ObjectA,
|
||||
KwArg('db', database))
|
||||
""":type: (objects.Provider) -> ObjectA"""
|
||||
|
||||
object_b = NewInstance(ObjectB,
|
||||
KwArg('a', object_a),
|
||||
KwArg('db', database))
|
||||
object_b_factory = Factory(ObjectB,
|
||||
KwArg('a', object_a_factory),
|
||||
KwArg('db', database))
|
||||
""":type: (objects.Provider) -> ObjectB"""
|
||||
|
||||
|
||||
# Catalog static provides.
|
||||
a1, a2 = Catalog.object_a(), Catalog.object_a()
|
||||
b1, b2 = Catalog.object_b(), Catalog.object_b()
|
||||
a1, a2 = Catalog.object_a_factory(), Catalog.object_a_factory()
|
||||
b1, b2 = Catalog.object_b_factory(), Catalog.object_b_factory()
|
||||
|
||||
assert a1 is not a2
|
||||
assert b1 is not b2
|
||||
|
@ -128,8 +142,8 @@ Examples
|
|||
|
||||
|
||||
# Example of inline injections.
|
||||
@inject(KwArg('a', Catalog.object_a))
|
||||
@inject(KwArg('b', Catalog.object_b))
|
||||
@inject(KwArg('a', Catalog.object_a_factory))
|
||||
@inject(KwArg('b', Catalog.object_b_factory))
|
||||
@inject(KwArg('database', Catalog.database))
|
||||
def example(a, b, database):
|
||||
assert a.db is b.db is database is Catalog.database()
|
||||
|
@ -137,7 +151,8 @@ Examples
|
|||
|
||||
example()
|
||||
|
||||
You can get more **Objects** examples in ``/examples`` directory on
|
||||
|
||||
You can get more *Objects* examples in ``/examples`` directory on
|
||||
GitHub:
|
||||
|
||||
https://github.com/rmk135/objects
|
||||
|
@ -147,7 +162,7 @@ Feedback
|
|||
--------
|
||||
|
||||
Feel free to post questions, bugs, feature requests, proposals etc. on
|
||||
**Objects** GitHub Issues:
|
||||
*Objects* GitHub Issues:
|
||||
|
||||
https://github.com/rmk135/objects/issues
|
||||
|
||||
|
@ -157,3 +172,7 @@ Your feedback is quite important!
|
|||
.. _PyPi: https://pypi.python.org/pypi/Objects
|
||||
.. _Stable version: http://objects.readthedocs.org/en/stable/
|
||||
.. _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
|
||||
==============
|
||||
|
||||
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 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.
|
||||
|
||||
.. 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
|
||||
========
|
||||
|
||||
Current section describes main **Objects** entities and their interaction.
|
||||
Current section describes main *Objects* entities and their interaction.
|
||||
|
||||
Providers
|
||||
---------
|
||||
|
@ -45,10 +45,10 @@ dependencies of objects.
|
|||
|
||||
Objects can take dependencies in various forms. Some objects take init
|
||||
arguments, other are using attributes or methods to be initialized. Injection,
|
||||
in terms of **Objects**, is an instruction how to provide dependency for the
|
||||
in terms of *Objects*, is an instruction how to provide dependency for the
|
||||
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
|
||||
injectable provider call (every time injection is done).
|
||||
|
||||
|
@ -107,7 +107,7 @@ Catalogs
|
|||
|
||||
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:
|
||||
``Resources`` and ``Models``.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Examples
|
||||
========
|
||||
|
||||
You can get more **Objects** examples in ``/examples`` directory on
|
||||
You can get more *Objects* examples in ``/examples`` directory on
|
||||
GitHub:
|
||||
|
||||
https://github.com/rmk135/objects
|
||||
|
|
|
@ -2,7 +2,7 @@ Feedback
|
|||
========
|
||||
|
||||
Feel free to post questions, bugs, feature requests, proposals etc. on
|
||||
**Objects** GitHub Issues:
|
||||
*Objects* GitHub 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
|
||||
=======
|
||||
|
||||
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/ |
|
||||
| | :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/ |
|
||||
| | :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/ |
|
||||
| | :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/ |
|
||||
| | :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/ |
|
||||
| | :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 |
|
||||
| | :alt: Build Status |
|
||||
| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg |
|
||||
|
@ -38,7 +38,9 @@ Contents
|
|||
|
||||
introduction
|
||||
installation
|
||||
entities
|
||||
advanced_usage
|
||||
providers/index
|
||||
injections
|
||||
catalogs
|
||||
decorators
|
||||
examples
|
||||
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
|
||||
============
|
||||
|
||||
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
|
||||
|
||||
Sources can be cloned from GitHub_::
|
||||
Sources can be cloned from GitHub_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
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
|
||||
.. _GitHub: https://github.com/rmk135/objects
|
||||
|
|
|
@ -1,16 +1,36 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
Python ecosystem consists of a big amount of various classes, functions and
|
||||
objects that could be used for applications development. Each of them has its
|
||||
own role.
|
||||
Python ecosystem consists of a big amount of various libraries that contain
|
||||
different classes and functions that could be used for applications
|
||||
development. Each of them has its own role.
|
||||
|
||||
Modern Python applications are mostly the composition of well-known open
|
||||
source systems, frameworks, libraries and some turnkey functionality.
|
||||
source systems / frameworks / libraries and some turnkey functionality.
|
||||
|
||||
When application goes bigger, its amount of objects and their dependencies
|
||||
also increased extremely fast and became hard to maintain.
|
||||
When application goes bigger, its complexity and SLOC_ are also increased.
|
||||
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
|
||||
and their dependencies in formal, pretty way. Main idea of **Objects** is to
|
||||
keep dependencies under control.
|
||||
It sounds like "I have so many classes and functions! They are great, now I can
|
||||
understand each of them, but it is so hard to see the whole picture! How are
|
||||
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.providers import Factory
|
||||
from objects.providers import Singleton
|
||||
from objects.providers import NewInstance
|
||||
|
||||
from objects.injections import KwArg
|
||||
from objects.injections import Attribute
|
||||
from objects.injections import inject
|
||||
from objects.decorators import inject
|
||||
|
||||
import sqlite3
|
||||
|
||||
|
@ -40,19 +40,19 @@ class Catalog(AbstractCatalog):
|
|||
Attribute('row_factory', sqlite3.Row))
|
||||
""":type: (objects.Provider) -> sqlite3.Connection"""
|
||||
|
||||
object_a = NewInstance(ObjectA,
|
||||
KwArg('db', database))
|
||||
object_a_factory = Factory(ObjectA,
|
||||
KwArg('db', database))
|
||||
""":type: (objects.Provider) -> ObjectA"""
|
||||
|
||||
object_b = NewInstance(ObjectB,
|
||||
KwArg('a', object_a),
|
||||
KwArg('db', database))
|
||||
object_b_factory = Factory(ObjectB,
|
||||
KwArg('a', object_a_factory),
|
||||
KwArg('db', database))
|
||||
""":type: (objects.Provider) -> ObjectB"""
|
||||
|
||||
|
||||
# Catalog static provides.
|
||||
a1, a2 = Catalog.object_a(), Catalog.object_a()
|
||||
b1, b2 = Catalog.object_b(), Catalog.object_b()
|
||||
a1, a2 = Catalog.object_a_factory(), Catalog.object_a_factory()
|
||||
b1, b2 = Catalog.object_b_factory(), Catalog.object_b_factory()
|
||||
|
||||
assert a1 is not a2
|
||||
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.
|
||||
@inject(KwArg('a', Catalog.object_a))
|
||||
@inject(KwArg('b', Catalog.object_b))
|
||||
@inject(KwArg('a', Catalog.object_a_factory))
|
||||
@inject(KwArg('b', Catalog.object_b_factory))
|
||||
@inject(KwArg('database', Catalog.database))
|
||||
def example(a, b, 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."""
|
||||
|
||||
from objects.catalog import AbstractCatalog
|
||||
from objects.catalog import override
|
||||
|
||||
from objects.providers import Singleton
|
||||
from objects.providers import NewInstance
|
||||
|
@ -9,6 +8,8 @@ from objects.providers import NewInstance
|
|||
from objects.injections import KwArg
|
||||
from objects.injections import Attribute
|
||||
|
||||
from objects.decorators import override
|
||||
|
||||
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."""
|
||||
|
||||
from objects.providers import NewInstance
|
||||
|
||||
from objects.injections import KwArg
|
||||
from objects.injections import inject
|
||||
from objects.decorators import inject
|
||||
|
||||
|
||||
new_object = NewInstance(object)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import sqlite3
|
||||
|
||||
from objects.catalog import AbstractCatalog
|
||||
from objects.catalog import override
|
||||
|
||||
from objects.providers import Singleton
|
||||
from objects.providers import NewInstance
|
||||
|
@ -11,6 +10,8 @@ from objects.providers import NewInstance
|
|||
from objects.injections import KwArg
|
||||
from objects.injections import Attribute
|
||||
|
||||
from objects.decorators import override
|
||||
|
||||
|
||||
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
|
||||
|
||||
from objects.providers import Singleton
|
||||
from objects.providers import NewInstance
|
||||
from flask import Flask
|
||||
|
||||
from objects.providers import Factory
|
||||
from objects.providers import Singleton
|
||||
from objects.injections import KwArg
|
||||
from objects.injections import Attribute
|
||||
from objects.decorators import inject
|
||||
|
||||
|
||||
class ObjectA(object):
|
||||
|
@ -32,14 +37,25 @@ database = Singleton(sqlite3.Connection,
|
|||
KwArg('isolation_level', 'EXCLUSIVE'),
|
||||
Attribute('row_factory', sqlite3.Row))
|
||||
|
||||
object_a = NewInstance(ObjectA,
|
||||
KwArg('database', database))
|
||||
object_a_factory = Factory(ObjectA,
|
||||
KwArg('database', database))
|
||||
|
||||
# Creating several `ObjectA` instances.
|
||||
object_a_1 = object_a()
|
||||
object_a_2 = object_a()
|
||||
|
||||
# Making some asserts.
|
||||
assert object_a_1 is not object_a_2
|
||||
assert object_a_1.database is object_a_2.database
|
||||
assert object_a_1.get_one() == object_a_2.get_one() == 1
|
||||
# Flask application.
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
# 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 override
|
||||
|
||||
from .providers import Provider
|
||||
from .providers import Delegate
|
||||
from .providers import NewInstance
|
||||
from .providers import Factory
|
||||
from .providers import Singleton
|
||||
from .providers import ExternalDependency
|
||||
from .providers import Class
|
||||
|
@ -22,16 +21,18 @@ from .injections import KwArg
|
|||
from .injections import Attribute
|
||||
from .injections import Method
|
||||
|
||||
from .decorators import override
|
||||
from .decorators import inject
|
||||
|
||||
from .errors import Error
|
||||
|
||||
|
||||
__all__ = ('AbstractCatalog',
|
||||
'override',
|
||||
|
||||
# Providers
|
||||
'Provider',
|
||||
'Delegate',
|
||||
'NewInstance',
|
||||
'Factory',
|
||||
'Singleton',
|
||||
'ExternalDependency',
|
||||
'Class',
|
||||
|
@ -46,5 +47,9 @@ __all__ = ('AbstractCatalog',
|
|||
'Attribute',
|
||||
'Method',
|
||||
|
||||
# Decorators
|
||||
'override',
|
||||
'inject',
|
||||
|
||||
# Errors
|
||||
'Error')
|
||||
|
|
|
@ -1,58 +1,67 @@
|
|||
"""Catalog module."""
|
||||
|
||||
from .providers import Provider
|
||||
from six import iteritems
|
||||
from six import add_metaclass
|
||||
|
||||
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):
|
||||
|
||||
"""Abstract object provides catalog."""
|
||||
"""Abstract providers catalog."""
|
||||
|
||||
__slots__ = ('__used_providers__',)
|
||||
providers = dict()
|
||||
|
||||
__slots__ = ('_used_providers',)
|
||||
|
||||
def __init__(self, *used_providers):
|
||||
"""Initializer."""
|
||||
self.__used_providers__ = set(used_providers)
|
||||
self._used_providers = set(used_providers)
|
||||
|
||||
def __getattribute__(self, item):
|
||||
"""Return providers."""
|
||||
attribute = super(AbstractCatalog, self).__getattribute__(item)
|
||||
if item in ('__used_providers__',):
|
||||
if item in ('providers', '_used_providers',):
|
||||
return attribute
|
||||
|
||||
if attribute not in self.__used_providers__:
|
||||
if attribute not in self._used_providers:
|
||||
raise Error('Provider \'{0}\' '.format(item) +
|
||||
'is not listed in dependencies')
|
||||
return attribute
|
||||
|
||||
@classmethod
|
||||
def all_providers(cls, provider_type=Provider):
|
||||
"""Return set of all class providers."""
|
||||
providers = set()
|
||||
for attr_name in set(dir(cls)) - set(dir(AbstractCatalog)):
|
||||
provider = getattr(cls, attr_name)
|
||||
if not isinstance(provider, provider_type):
|
||||
continue
|
||||
providers.add((attr_name, provider))
|
||||
return providers
|
||||
def filter(cls, provider_type):
|
||||
"""Return dict of providers, that are instance of provided type."""
|
||||
return dict([(name, provider)
|
||||
for name, provider in iteritems(cls.providers)
|
||||
if isinstance(provider, provider_type)])
|
||||
|
||||
@classmethod
|
||||
def override(cls, overriding):
|
||||
"""Override current catalog providers by overriding catalog providers.
|
||||
|
||||
:type overriding: AbstractCatalog
|
||||
"""
|
||||
Override current catalog providers by overriding catalog providers.
|
||||
|
||||
: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
|
||||
for name, provider in iteritems(overriding.providers):
|
||||
cls.providers[name].override(provider)
|
||||
|
|
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."""
|
||||
|
||||
from six import wraps
|
||||
|
||||
from .utils import is_provider
|
||||
from .utils import ensure_is_injection
|
||||
|
||||
|
||||
class Injection(object):
|
||||
|
||||
"""Base injection class."""
|
||||
|
||||
__IS_OBJECTS_INJECTION__ = True
|
||||
__IS_INJECTION__ = True
|
||||
__slots__ = ('name', 'injectable')
|
||||
|
||||
def __init__(self, name, injectable):
|
||||
|
@ -30,38 +27,18 @@ class KwArg(Injection):
|
|||
|
||||
"""Keyword argument injection."""
|
||||
|
||||
__IS_OBJECTS_KWARG_INJECTION__ = True
|
||||
__IS_KWARG_INJECTION__ = True
|
||||
|
||||
|
||||
class Attribute(Injection):
|
||||
|
||||
"""Attribute injection."""
|
||||
|
||||
__IS_OBJECTS_ATTRIBUTE_INJECTION__ = True
|
||||
__IS_ATTRIBUTE_INJECTION__ = True
|
||||
|
||||
|
||||
class Method(Injection):
|
||||
|
||||
"""Method injection."""
|
||||
|
||||
__IS_OBJECTS_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
|
||||
__IS_METHOD_INJECTION__ = True
|
||||
|
|
|
@ -14,15 +14,26 @@ class Provider(object):
|
|||
|
||||
"""Base provider class."""
|
||||
|
||||
__IS_OBJECTS_PROVIDER__ = True
|
||||
__slots__ = ('overridden',)
|
||||
__IS_PROVIDER__ = True
|
||||
__slots__ = ('_overridden',)
|
||||
|
||||
def __init__(self):
|
||||
"""Initializer."""
|
||||
self.overridden = None
|
||||
self._overridden = None
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""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()
|
||||
|
||||
def delegate(self):
|
||||
|
@ -31,112 +42,130 @@ class Provider(object):
|
|||
|
||||
def override(self, provider):
|
||||
"""Override provider with another provider."""
|
||||
if not self.overridden:
|
||||
self.overridden = (ensure_is_provider(provider),)
|
||||
if not self._overridden:
|
||||
self._overridden = (ensure_is_provider(provider),)
|
||||
else:
|
||||
self.overridden = self.overridden + (ensure_is_provider(provider),)
|
||||
self._overridden += (ensure_is_provider(provider),)
|
||||
|
||||
def reset_override(self):
|
||||
"""Reset all overriding providers."""
|
||||
self.overridden = None
|
||||
@property
|
||||
def is_overridden(self):
|
||||
"""Check if provider is overridden by another provider."""
|
||||
return bool(self._overridden)
|
||||
|
||||
@property
|
||||
def last_overriding(self):
|
||||
"""Return last overriding provider."""
|
||||
try:
|
||||
return self.overridden[-1]
|
||||
return self._overridden[-1]
|
||||
except (TypeError, IndexError):
|
||||
raise Error('Provider {0} '.format(str(self)) +
|
||||
'is not overridden')
|
||||
raise Error('Provider {0} is not overridden'.format(str(self)))
|
||||
|
||||
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):
|
||||
|
||||
"""Provider's delegate."""
|
||||
|
||||
__slots__ = ('delegated',)
|
||||
__slots__ = ('_delegated',)
|
||||
|
||||
def __init__(self, delegated):
|
||||
"""Initializer.
|
||||
|
||||
:type delegated: Provider
|
||||
"""
|
||||
self.delegated = ensure_is_provider(delegated)
|
||||
self._delegated = ensure_is_provider(delegated)
|
||||
super(Delegate, self).__init__()
|
||||
|
||||
def __call__(self):
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""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):
|
||||
"""Initializer."""
|
||||
if not isinstance(provides, class_types):
|
||||
raise Error('NewInstance provider expects to get class, ' +
|
||||
if not callable(provides):
|
||||
raise Error('Factory provider expects to get callable, ' +
|
||||
'got {0} instead'.format(str(provides)))
|
||||
self.provides = provides
|
||||
self.kwargs = tuple((injection
|
||||
for injection in injections
|
||||
if is_kwarg_injection(injection)))
|
||||
self.attributes = tuple((injection
|
||||
for injection in injections
|
||||
if is_attribute_injection(injection)))
|
||||
self.methods = tuple((injection
|
||||
self._provides = provides
|
||||
self._kwargs = tuple((injection
|
||||
for injection in injections
|
||||
if is_method_injection(injection)))
|
||||
super(NewInstance, self).__init__()
|
||||
if is_kwarg_injection(injection)))
|
||||
self._attributes = tuple((injection
|
||||
for injection in injections
|
||||
if is_attribute_injection(injection)))
|
||||
self._methods = tuple((injection
|
||||
for injection in injections
|
||||
if is_method_injection(injection)))
|
||||
super(Factory, self).__init__()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
if self.overridden:
|
||||
return self.last_overriding(*args, **kwargs)
|
||||
|
||||
init_kwargs = dict(((injection.name, injection.value)
|
||||
for injection in self.kwargs))
|
||||
for injection in self._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)
|
||||
for method in self.methods:
|
||||
for method in self._methods:
|
||||
getattr(instance, method.name)(method.value)
|
||||
|
||||
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 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."""
|
||||
self.instance = None
|
||||
super(Singleton, self).__init__(*args, **kwargs)
|
||||
self._instance = None
|
||||
self._factory = Factory(provides, *injections)
|
||||
super(Singleton, self).__init__()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
if not self.instance:
|
||||
self.instance = super(Singleton, self).__call__(*args, **kwargs)
|
||||
return self.instance
|
||||
if not self._instance:
|
||||
self._instance = self._factory(*args, **kwargs)
|
||||
return self._instance
|
||||
|
||||
def reset(self):
|
||||
"""Reset instance."""
|
||||
self.instance = None
|
||||
self._instance = None
|
||||
|
||||
|
||||
class ExternalDependency(Provider):
|
||||
|
@ -147,29 +176,33 @@ class ExternalDependency(Provider):
|
|||
the client's code, but it's interface is known.
|
||||
"""
|
||||
|
||||
__slots__ = ('instance_of',)
|
||||
__slots__ = ('_instance_of',)
|
||||
|
||||
def __init__(self, instance_of):
|
||||
"""Initializer."""
|
||||
if not isinstance(instance_of, class_types):
|
||||
raise Error('ExternalDependency provider expects to get class, ' +
|
||||
'got {0} instead'.format(str(instance_of)))
|
||||
self.instance_of = instance_of
|
||||
self._instance_of = instance_of
|
||||
super(ExternalDependency, self).__init__()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
if not self.overridden:
|
||||
if not self._overridden:
|
||||
raise Error('Dependency is not defined')
|
||||
|
||||
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) +
|
||||
'instance of {0}'.format(self.instance_of))
|
||||
'instance of {0}'.format(self._instance_of))
|
||||
|
||||
return instance
|
||||
|
||||
def provided_by(self, provider):
|
||||
"""Set external dependency provider."""
|
||||
return self.override(provider)
|
||||
|
||||
|
||||
class _StaticProvider(Provider):
|
||||
|
||||
|
@ -179,18 +212,16 @@ class _StaticProvider(Provider):
|
|||
it got on input.
|
||||
"""
|
||||
|
||||
__slots__ = ('provides',)
|
||||
__slots__ = ('_provides',)
|
||||
|
||||
def __init__(self, provides):
|
||||
"""Initializer."""
|
||||
self.provides = provides
|
||||
self._provides = provides
|
||||
super(_StaticProvider, self).__init__()
|
||||
|
||||
def __call__(self):
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
if self.overridden:
|
||||
return self.last_overriding()
|
||||
return self.provides
|
||||
return self._provides
|
||||
|
||||
|
||||
class Class(_StaticProvider):
|
||||
|
@ -221,28 +252,25 @@ class Callable(Provider):
|
|||
with some predefined dependency injections.
|
||||
"""
|
||||
|
||||
__slots__ = ('callback', 'injections')
|
||||
__slots__ = ('_callback', '_injections')
|
||||
|
||||
def __init__(self, callback, *injections):
|
||||
"""Initializer."""
|
||||
if not callable(callback):
|
||||
raise Error('Callable expected, got {0}'.format(str(callback)))
|
||||
self.callback = callback
|
||||
self.injections = tuple((injection
|
||||
for injection in injections
|
||||
if is_kwarg_injection(injection)))
|
||||
self._callback = callback
|
||||
self._injections = tuple((injection
|
||||
for injection in injections
|
||||
if is_kwarg_injection(injection)))
|
||||
super(Callable, self).__init__()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
if self.overridden:
|
||||
return self.last_overriding()
|
||||
|
||||
injections = dict(((injection.name, injection.value)
|
||||
for injection in self.injections))
|
||||
for injection in self._injections))
|
||||
injections.update(kwargs)
|
||||
|
||||
return self.callback(*args, **injections)
|
||||
return self._callback(*args, **injections)
|
||||
|
||||
|
||||
class Config(Provider):
|
||||
|
@ -254,18 +282,22 @@ class Config(Provider):
|
|||
to create deferred config value provider.
|
||||
"""
|
||||
|
||||
__slots__ = ('value',)
|
||||
__slots__ = ('_value',)
|
||||
|
||||
def __init__(self, value=None):
|
||||
"""Initializer."""
|
||||
if not value:
|
||||
value = dict()
|
||||
self.value = value
|
||||
self._value = value
|
||||
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."""
|
||||
value = self.value
|
||||
value = self._value
|
||||
if paths:
|
||||
for path in paths:
|
||||
try:
|
||||
|
@ -275,13 +307,9 @@ class Config(Provider):
|
|||
'"{0}" is undefined'.format('.'.join(paths)))
|
||||
return value
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Return instance of deferred config."""
|
||||
return _ChildConfig(parents=(item,), root_config=self)
|
||||
|
||||
def update_from(self, value):
|
||||
"""Update current value from another one."""
|
||||
self.value.update(value)
|
||||
self._value.update(value)
|
||||
|
||||
|
||||
class _ChildConfig(Provider):
|
||||
|
@ -292,19 +320,19 @@ class _ChildConfig(Provider):
|
|||
the current path in the config tree.
|
||||
"""
|
||||
|
||||
__slots__ = ('parents', 'root_config')
|
||||
__slots__ = ('_parents', '_root_config')
|
||||
|
||||
def __init__(self, parents, root_config):
|
||||
"""Initializer."""
|
||||
self.parents = parents
|
||||
self.root_config = root_config
|
||||
self._parents = parents
|
||||
self._root_config = root_config
|
||||
super(_ChildConfig, self).__init__()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
return self.root_config(self.parents)
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Return instance of deferred config."""
|
||||
return _ChildConfig(parents=self.parents + (item,),
|
||||
root_config=self.root_config)
|
||||
return _ChildConfig(parents=self._parents + (item,),
|
||||
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):
|
||||
"""Check if instance is provider instance."""
|
||||
return (not isinstance(instance, class_types) and
|
||||
hasattr(instance, '__IS_OBJECTS_PROVIDER__'))
|
||||
getattr(instance, '__IS_PROVIDER__', False) is True)
|
||||
|
||||
|
||||
def ensure_is_provider(instance):
|
||||
|
@ -22,7 +22,7 @@ def ensure_is_provider(instance):
|
|||
def is_injection(instance):
|
||||
"""Check if instance is injection instance."""
|
||||
return (not isinstance(instance, class_types) and
|
||||
hasattr(instance, '__IS_OBJECTS_INJECTION__'))
|
||||
getattr(instance, '__IS_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def ensure_is_injection(instance):
|
||||
|
@ -36,16 +36,16 @@ def ensure_is_injection(instance):
|
|||
def is_kwarg_injection(instance):
|
||||
"""Check if instance is keyword argument injection instance."""
|
||||
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):
|
||||
"""Check if instance is attribute injection instance."""
|
||||
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):
|
||||
"""Check if instance is method injection instance."""
|
||||
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
|
||||
|
||||
|
||||
SHORT_DESCRIPTION = 'Dependency management tool for Python projects'
|
||||
SHORT_DESCRIPTION = 'Dependency injection framework for Python projects'
|
||||
|
||||
|
||||
# Getting description.
|
||||
|
@ -47,8 +47,6 @@ class PublishCommand(Command):
|
|||
self.run_command('upload')
|
||||
os.system('git tag -a {0} -m \'version {0}\''.format(version))
|
||||
os.system('git push --tags')
|
||||
|
||||
|
||||
setup(name='Objects',
|
||||
version=version,
|
||||
description=SHORT_DESCRIPTION,
|
||||
|
@ -69,9 +67,9 @@ setup(name='Objects',
|
|||
'Dependency management',
|
||||
'Dependency injection',
|
||||
'Dependency injection container',
|
||||
'Dependency injector',
|
||||
'DI',
|
||||
'DIC',
|
||||
'Dependency injector',
|
||||
'Inversion of Control',
|
||||
'Inversion of Control container',
|
||||
'IoC',
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import unittest2 as unittest
|
||||
|
||||
from objects.catalog import AbstractCatalog
|
||||
from objects.catalog import override
|
||||
|
||||
from objects.providers import Object
|
||||
from objects.providers import Value
|
||||
|
@ -34,32 +33,38 @@ class CatalogTests(unittest.TestCase):
|
|||
|
||||
def test_all_providers(self):
|
||||
"""Test getting of all catalog providers."""
|
||||
all_providers = self.Catalog.all_providers()
|
||||
all_providers_dict = dict(all_providers)
|
||||
self.assertTrue(len(self.Catalog.providers) == 2)
|
||||
|
||||
self.assertIsInstance(all_providers, set)
|
||||
self.assertTrue(len(all_providers) == 2)
|
||||
self.assertIn('obj', self.Catalog.providers)
|
||||
self.assertIn(self.Catalog.obj, self.Catalog.providers.values())
|
||||
|
||||
self.assertIn('obj', all_providers_dict)
|
||||
self.assertIn(self.Catalog.obj, all_providers_dict.values())
|
||||
|
||||
self.assertIn('another_obj', all_providers_dict)
|
||||
self.assertIn(self.Catalog.another_obj, all_providers_dict.values())
|
||||
self.assertIn('another_obj', self.Catalog.providers)
|
||||
self.assertIn(self.Catalog.another_obj,
|
||||
self.Catalog.providers.values())
|
||||
|
||||
def test_all_providers_by_type(self):
|
||||
"""Test getting of all catalog providers of specific type."""
|
||||
self.assertTrue(len(self.Catalog.all_providers(Object)) == 2)
|
||||
self.assertTrue(len(self.Catalog.all_providers(Value)) == 0)
|
||||
self.assertTrue(len(self.Catalog.filter(Object)) == 2)
|
||||
self.assertTrue(len(self.Catalog.filter(Value)) == 0)
|
||||
|
||||
def test_overriding(self):
|
||||
"""Test catalog overriding with another catalog."""
|
||||
@override(self.Catalog)
|
||||
class OverridingCatalog(self.Catalog):
|
||||
def test_metaclass_with_several_catalogs(self):
|
||||
"""Test that metaclass work well with several catalogs."""
|
||||
class Catalog1(AbstractCatalog):
|
||||
|
||||
"""Overriding catalog."""
|
||||
"""Catalog1."""
|
||||
|
||||
obj = Value(1)
|
||||
another_obj = Value(2)
|
||||
provider = Object(object())
|
||||
|
||||
self.assertEqual(self.Catalog.obj(), 1)
|
||||
self.assertEqual(self.Catalog.another_obj(), 2)
|
||||
class Catalog2(AbstractCatalog):
|
||||
|
||||
"""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 Attribute
|
||||
from objects.injections import Method
|
||||
from objects.injections import inject
|
||||
|
||||
from objects.providers import NewInstance
|
||||
|
||||
from objects.providers import Error
|
||||
from objects.providers import Factory
|
||||
|
||||
|
||||
class InjectionTests(unittest.TestCase):
|
||||
|
@ -30,7 +27,7 @@ class InjectionTests(unittest.TestCase):
|
|||
|
||||
def test_value_with_provider_injectable(self):
|
||||
"""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)
|
||||
|
||||
|
||||
|
@ -65,77 +62,3 @@ class MethodTests(unittest.TestCase):
|
|||
injection = Method('some_arg_name', 'some_value')
|
||||
self.assertEqual(injection.name, 'some_arg_name')
|
||||
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 Delegate
|
||||
from objects.providers import NewInstance
|
||||
from objects.providers import Factory
|
||||
from objects.providers import Singleton
|
||||
from objects.providers import ExternalDependency
|
||||
from objects.providers import Class
|
||||
|
@ -44,12 +44,12 @@ class ProviderTests(unittest.TestCase):
|
|||
delegate1 = self.provider.delegate()
|
||||
|
||||
self.assertIsInstance(delegate1, Delegate)
|
||||
self.assertIs(delegate1.delegated, self.provider)
|
||||
self.assertIs(delegate1(), self.provider)
|
||||
|
||||
delegate2 = self.provider.delegate()
|
||||
|
||||
self.assertIsInstance(delegate2, Delegate)
|
||||
self.assertIs(delegate2.delegated, self.provider)
|
||||
self.assertIs(delegate2(), self.provider)
|
||||
|
||||
self.assertIsNot(delegate1, delegate2)
|
||||
|
||||
|
@ -57,31 +57,12 @@ class ProviderTests(unittest.TestCase):
|
|||
"""Test provider overriding."""
|
||||
overriding_provider = Provider()
|
||||
self.provider.override(overriding_provider)
|
||||
self.assertTrue(self.provider.overridden)
|
||||
self.assertTrue(self.provider.is_overridden)
|
||||
|
||||
def test_override_with_not_provider(self):
|
||||
"""Test provider overriding with not provider instance."""
|
||||
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):
|
||||
"""Test getting last overriding provider."""
|
||||
overriding_provider1 = Provider()
|
||||
|
@ -103,6 +84,45 @@ class ProviderTests(unittest.TestCase):
|
|||
self.fail('Got en error in {}'.format(
|
||||
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):
|
||||
|
||||
|
@ -130,13 +150,13 @@ class DelegateTests(unittest.TestCase):
|
|||
self.assertIs(delegated2, self.delegated)
|
||||
|
||||
|
||||
class NewInstanceTests(unittest.TestCase):
|
||||
class FactoryTests(unittest.TestCase):
|
||||
|
||||
"""NewInstance test cases."""
|
||||
"""Factory test cases."""
|
||||
|
||||
class Example(object):
|
||||
|
||||
"""Example class for NewInstance provider tests."""
|
||||
"""Example class for Factory provider tests."""
|
||||
|
||||
def __init__(self, init_arg1=None, init_arg2=None):
|
||||
"""Initializer.
|
||||
|
@ -164,15 +184,19 @@ class NewInstanceTests(unittest.TestCase):
|
|||
|
||||
def test_is_provider(self):
|
||||
"""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):
|
||||
"""Test creation of provider with not a class."""
|
||||
self.assertRaises(Error, NewInstance, 123)
|
||||
def test_init_with_callable(self):
|
||||
"""Test creation of provider with a callable."""
|
||||
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):
|
||||
"""Test creation of new instances."""
|
||||
provider = NewInstance(self.Example)
|
||||
provider = Factory(self.Example)
|
||||
instance1 = provider()
|
||||
instance2 = provider()
|
||||
|
||||
|
@ -182,9 +206,9 @@ class NewInstanceTests(unittest.TestCase):
|
|||
|
||||
def test_call_with_init_args(self):
|
||||
"""Test creation of new instances with init args injections."""
|
||||
provider = NewInstance(self.Example,
|
||||
KwArg('init_arg1', 'i1'),
|
||||
KwArg('init_arg2', 'i2'))
|
||||
provider = Factory(self.Example,
|
||||
KwArg('init_arg1', 'i1'),
|
||||
KwArg('init_arg2', 'i2'))
|
||||
|
||||
instance1 = provider()
|
||||
instance2 = provider()
|
||||
|
@ -201,9 +225,9 @@ class NewInstanceTests(unittest.TestCase):
|
|||
|
||||
def test_call_with_attributes(self):
|
||||
"""Test creation of new instances with attribute injections."""
|
||||
provider = NewInstance(self.Example,
|
||||
Attribute('attribute1', 'a1'),
|
||||
Attribute('attribute2', 'a2'))
|
||||
provider = Factory(self.Example,
|
||||
Attribute('attribute1', 'a1'),
|
||||
Attribute('attribute2', 'a2'))
|
||||
|
||||
instance1 = provider()
|
||||
instance2 = provider()
|
||||
|
@ -220,9 +244,9 @@ class NewInstanceTests(unittest.TestCase):
|
|||
|
||||
def test_call_with_methods(self):
|
||||
"""Test creation of new instances with method injections."""
|
||||
provider = NewInstance(self.Example,
|
||||
Method('method1', 'm1'),
|
||||
Method('method2', 'm2'))
|
||||
provider = Factory(self.Example,
|
||||
Method('method1', 'm1'),
|
||||
Method('method2', 'm2'))
|
||||
|
||||
instance1 = provider()
|
||||
instance2 = provider()
|
||||
|
@ -239,7 +263,7 @@ class NewInstanceTests(unittest.TestCase):
|
|||
|
||||
def test_call_with_context_args(self):
|
||||
"""Test creation of new instances with context args."""
|
||||
provider = NewInstance(self.Example)
|
||||
provider = Factory(self.Example)
|
||||
instance = provider(11, 22)
|
||||
|
||||
self.assertEqual(instance.init_arg1, 11)
|
||||
|
@ -247,8 +271,8 @@ class NewInstanceTests(unittest.TestCase):
|
|||
|
||||
def test_call_with_context_kwargs(self):
|
||||
"""Test creation of new instances with context kwargs."""
|
||||
provider = NewInstance(self.Example,
|
||||
KwArg('init_arg1', 1))
|
||||
provider = Factory(self.Example,
|
||||
KwArg('init_arg1', 1))
|
||||
|
||||
instance1 = provider(init_arg2=22)
|
||||
self.assertEqual(instance1.init_arg1, 1)
|
||||
|
@ -260,9 +284,9 @@ class NewInstanceTests(unittest.TestCase):
|
|||
|
||||
def test_call_overridden(self):
|
||||
"""Test creation of new instances on overridden provider."""
|
||||
provider = NewInstance(self.Example)
|
||||
overriding_provider1 = NewInstance(dict)
|
||||
overriding_provider2 = NewInstance(list)
|
||||
provider = Factory(self.Example)
|
||||
overriding_provider1 = Factory(dict)
|
||||
overriding_provider2 = Factory(list)
|
||||
|
||||
provider.override(overriding_provider1)
|
||||
provider.override(overriding_provider2)
|
||||
|
@ -323,12 +347,12 @@ class ExternalDependencyTests(unittest.TestCase):
|
|||
|
||||
def test_call_overridden(self):
|
||||
"""Test call of overridden external dependency."""
|
||||
self.provider.override(NewInstance(list))
|
||||
self.provider.provided_by(Factory(list))
|
||||
self.assertIsInstance(self.provider(), list)
|
||||
|
||||
def test_call_overridden_but_not_instance_of(self):
|
||||
"""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)
|
||||
|
||||
def test_call_not_overridden(self):
|
||||
|
|
|
@ -40,6 +40,26 @@ class IsProviderTests(unittest.TestCase):
|
|||
"""Test with 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):
|
||||
|
||||
|
|
5
tox.ini
|
@ -18,6 +18,7 @@ commands=
|
|||
|
||||
[testenv:coveralls]
|
||||
basepython=python2.7
|
||||
passenv=TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH
|
||||
deps=
|
||||
{[testenv]deps}
|
||||
coverage
|
||||
|
@ -39,14 +40,14 @@ basepython=python2.7
|
|||
deps=
|
||||
flake8
|
||||
commands=
|
||||
flake8 --max-complexity=8 objects
|
||||
flake8 --max-complexity=8 objects/
|
||||
|
||||
[testenv:pep257]
|
||||
basepython=python2.7
|
||||
deps=
|
||||
pep257
|
||||
commands=
|
||||
pep257 objects
|
||||
pep257 objects/
|
||||
|
||||
[testenv:py26]
|
||||
basepython=python2.6
|
||||
|
|