From e3ea517d72cff21968f3640eb6fd94df36e884d4 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Thu, 18 Jun 2015 16:34:26 +0300 Subject: [PATCH] Minor ExternalDependency provider updates, Adding ExternalDependency provider docs --- docs/_providers.rst | 80 --------------- docs/images/external_dependency.png | Bin 0 -> 15072 bytes docs/providers/external_dependency.rst | 120 ++++++++++++++++++++++ docs/providers/index.rst | 1 + examples/providers/external_dependency.py | 78 ++++++++++++++ objects/providers.py | 4 + tests/test_providers.py | 4 +- 7 files changed, 205 insertions(+), 82 deletions(-) create mode 100644 docs/images/external_dependency.png create mode 100644 docs/providers/external_dependency.rst create mode 100644 examples/providers/external_dependency.py diff --git a/docs/_providers.rst b/docs/_providers.rst index f70a580f..7187b56c 100644 --- a/docs/_providers.rst +++ b/docs/_providers.rst @@ -2,86 +2,6 @@ Providers ========= -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 of they would be able to find right place / right way for doing this -in their application's architectures. - -On the other side, -you can be sure, that your external dependency will be satisfied by appropriate -instance. - -Example: - -.. code-block:: python - - """External dependency providers example.""" - - import sqlite3 - - from objects.providers import Singleton - from objects.providers import Factory - from objects.providers import ExternalDependency - - from objects.injections import KwArg - from objects.injections import Attribute - - - class ObjectA(object): - - """ObjectA has dependency on database.""" - - def __init__(self, database): - """Initializer. - - Database dependency need to be injected via init arg.""" - self.database = database - - def get_one(self): - """Select one from database and return it.""" - return self.database.execute('SELECT 1').fetchone()[0] - - - # Database and `ObjectA` providers. - database = ExternalDependency(instance_of=sqlite3.Connection) - - object_a_factory = Factory(ObjectA, - KwArg('database', database)) - - # Satisfaction of external dependency. - database.override(Singleton(sqlite3.Connection, - KwArg('database', ':memory:'), - KwArg('timeout', 30), - KwArg('detect_types', True), - KwArg('isolation_level', 'EXCLUSIVE'), - Attribute('row_factory', sqlite3.Row))) - - # 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.database is object_a_2.database is database() - - - Config providers ---------------- diff --git a/docs/images/external_dependency.png b/docs/images/external_dependency.png new file mode 100644 index 0000000000000000000000000000000000000000..831f2699376f0408cefd1029dec110467e95ce68 GIT binary patch literal 15072 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 JUfw43e*t(a#zz1E literal 0 HcmV?d00001 diff --git a/docs/providers/external_dependency.rst b/docs/providers/external_dependency.rst new file mode 100644 index 00000000..ebbdd800 --- /dev/null +++ b/docs/providers/external_dependency.rst @@ -0,0 +1,120 @@ +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 by 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`` provides external dependency, that has to + be satisfied 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.Connection) + users_service_factory = Factory(UserService, + KwArg('database', database)) + + # Out of library's scope. + # + # Setting database provider: + database.provided_by(Singleton(sqlite3.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' + diff --git a/docs/providers/index.rst b/docs/providers/index.rst index 7bb0970f..8815ba0e 100644 --- a/docs/providers/index.rst +++ b/docs/providers/index.rst @@ -12,3 +12,4 @@ All providers are callable. They describe how particular objects are provided. singleton static callable + external_dependency diff --git a/examples/providers/external_dependency.py b/examples/providers/external_dependency.py new file mode 100644 index 00000000..c4b3ef04 --- /dev/null +++ b/examples/providers/external_dependency.py @@ -0,0 +1,78 @@ +"""`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.Connection) +users_service_factory = Factory(UserService, + KwArg('database', database)) + +# Out of library's scope. +# +# Setting database provider: +database.provided_by(Singleton(sqlite3.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' diff --git a/objects/providers.py b/objects/providers.py index a8fd9ea9..b7b4229f 100644 --- a/objects/providers.py +++ b/objects/providers.py @@ -189,6 +189,10 @@ class ExternalDependency(Provider): return instance + def provided_by(self, provider): + """Set external dependency provider.""" + return self.override(provider) + class _StaticProvider(Provider): diff --git a/tests/test_providers.py b/tests/test_providers.py index 0dc37ee7..26bddfa4 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -323,12 +323,12 @@ class ExternalDependencyTests(unittest.TestCase): def test_call_overridden(self): """Test call of overridden external dependency.""" - self.provider.override(Factory(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(Factory(dict)) + self.provider.provided_by(Factory(dict)) self.assertRaises(Error, self.provider) def test_call_not_overridden(self):