mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-06-17 03:53:14 +03:00
Merge pull request #142 from ets-labs/138_cython_injections
Add injections extension
This commit is contained in:
commit
55cde4729d
|
@ -1,6 +1,7 @@
|
||||||
[run]
|
[run]
|
||||||
include = dependency_injector/*
|
source = src/dependency_injector
|
||||||
omit = tests/*
|
omit = tests/unit
|
||||||
|
plugins = Cython.Coverage
|
||||||
|
|
||||||
[html]
|
[html]
|
||||||
directory=reports/unittests/
|
directory=reports/unittests/
|
||||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -63,7 +63,7 @@ venv/
|
||||||
.ropeproject/
|
.ropeproject/
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
dependency_injector/*.c
|
src/dependency_injector/*.c
|
||||||
dependency_injector/*.so
|
src/dependency_injector/*.so
|
||||||
dependency_injector/providers/*.c
|
src/dependency_injector/providers/*.c
|
||||||
dependency_injector/providers/*.so
|
src/dependency_injector/providers/*.so
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Add <file or directory> to the black list. It should be a base name, not a
|
# Add <file or directory> to the black list. It should be a base name, not a
|
||||||
# path. You may set this option multiple times.
|
# path. You may set this option multiple times.
|
||||||
ignore=utils,test
|
ignore=utils,tests
|
||||||
|
|
||||||
[MESSAGES CONTROL]
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
|
10
.travis.yml
10
.travis.yml
|
@ -1,11 +1,15 @@
|
||||||
sudo: false
|
sudo: false
|
||||||
|
install:
|
||||||
|
- pip install tox
|
||||||
|
- pip install cython
|
||||||
|
- make cythonize
|
||||||
|
script:
|
||||||
|
- tox
|
||||||
language: python
|
language: python
|
||||||
install: pip install tox
|
|
||||||
script: tox
|
|
||||||
python:
|
python:
|
||||||
- 3.5
|
- 3.5
|
||||||
env:
|
env:
|
||||||
- TOXENV=coveralls
|
- TOXENV=coveralls DEPENDENCY_INJECTOR_DEBUG_MODE=1
|
||||||
- TOXENV=pylint
|
- TOXENV=pylint
|
||||||
- TOXENV=flake8
|
- TOXENV=flake8
|
||||||
- TOXENV=pydocstyle
|
- TOXENV=pydocstyle
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
include dependency_injector/*
|
recursive-include src/dependency_injector *.py *.pyx *.pxd *.c
|
||||||
include README.rst
|
include README.rst
|
||||||
include CONTRIBUTORS.rst
|
include CONTRIBUTORS.rst
|
||||||
include LICENSE.rst
|
include LICENSE.rst
|
||||||
|
|
49
Makefile
49
Makefile
|
@ -1,11 +1,22 @@
|
||||||
VERSION := $(shell python setup.py --version)
|
VERSION := $(shell python setup.py --version)
|
||||||
|
|
||||||
|
CYTHON_SRC := $(shell find src/dependency_injector -name '*.pyx')
|
||||||
|
|
||||||
|
CYTHON_DIRECTIVES =
|
||||||
|
|
||||||
|
ifdef DEPENDENCY_INJECTOR_DEBUG_MODE
|
||||||
|
CYTHON_DIRECTIVES += -Xprofile=True
|
||||||
|
CYTHON_DIRECTIVES += -Xlinetrace=True
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
# Clean sources
|
# Clean sources
|
||||||
find dependency_injector -name '*.py[co]' -delete
|
find src -name '*.py[cod]' -delete
|
||||||
find dependency_injector -name '__pycache__' -delete
|
find src -name '__pycache__' -delete
|
||||||
find dependency_injector -name '*.c' -delete
|
find src -name '*.c' -delete
|
||||||
find dependency_injector -name '*.so' -delete
|
find src -name '*.so' -delete
|
||||||
|
find src -name '*.html' -delete
|
||||||
# Clean tests
|
# Clean tests
|
||||||
find tests -name '*.py[co]' -delete
|
find tests -name '*.py[co]' -delete
|
||||||
find tests -name '__pycache__' -delete
|
find tests -name '__pycache__' -delete
|
||||||
|
@ -13,21 +24,39 @@ clean:
|
||||||
find examples -name '*.py[co]' -delete
|
find examples -name '*.py[co]' -delete
|
||||||
find examples -name '__pycache__' -delete
|
find examples -name '__pycache__' -delete
|
||||||
|
|
||||||
tests: clean
|
cythonize:
|
||||||
|
# Compile Cython to C
|
||||||
|
cython -a $(CYTHON_DIRECTIVES) $(CYTHON_SRC)
|
||||||
|
# Move all Cython html reports
|
||||||
|
mkdir -p reports/cython/
|
||||||
|
find src -name '*.html' -exec mv {} reports/cython/ \;
|
||||||
|
|
||||||
|
build: clean cythonize
|
||||||
|
# Compile C extensions
|
||||||
|
python setup.py build_ext --inplace
|
||||||
|
|
||||||
|
install: uninstall clean cythonize
|
||||||
|
pip install -ve .
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
- pip uninstall -y -q dependency-injector 2> /dev/null
|
||||||
|
|
||||||
|
test: build
|
||||||
# Unit tests with coverage report
|
# Unit tests with coverage report
|
||||||
coverage erase
|
coverage erase
|
||||||
coverage run --rcfile=./.coveragerc -m unittest2 discover tests
|
coverage run --rcfile=./.coveragerc -m unittest2 discover tests/unit
|
||||||
coverage report --rcfile=./.coveragerc
|
coverage report --rcfile=./.coveragerc
|
||||||
coverage html --rcfile=./.coveragerc
|
coverage html --rcfile=./.coveragerc
|
||||||
coverage erase
|
|
||||||
|
check:
|
||||||
# Static analysis
|
# Static analysis
|
||||||
flake8 --max-complexity=10 dependency_injector/
|
flake8 --max-complexity=10 src/dependency_injector/
|
||||||
flake8 --max-complexity=10 examples/
|
flake8 --max-complexity=10 examples/
|
||||||
# Code style analysis
|
# Code style analysis
|
||||||
pydocstyle dependency_injector/
|
pydocstyle src/dependency_injector/
|
||||||
pydocstyle examples/
|
pydocstyle examples/
|
||||||
|
|
||||||
publish: clean
|
publish: cythonize
|
||||||
# Create and upload build
|
# Create and upload build
|
||||||
python setup.py sdist upload
|
python setup.py sdist upload
|
||||||
# Create and upload tag
|
# Create and upload tag
|
||||||
|
|
|
@ -56,7 +56,7 @@ author = u'ETS Labs'
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
# Getting version:
|
# Getting version:
|
||||||
with open('../dependency_injector/__init__.py') as init_file:
|
with open('../src/dependency_injector/__init__.py') as init_file:
|
||||||
version = re.search('VERSION = \'(.*?)\'', init_file.read()).group(1)
|
version = re.search('VERSION = \'(.*?)\'', init_file.read()).group(1)
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
@ -281,7 +281,7 @@ man_pages = [
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(master_doc, 'Dependency Injector', u'Dependency Injector Documentation',
|
(master_doc, 'Dependency Injector', u'Dependency Injector Documentation',
|
||||||
author, 'Dependency Injector', 'Python dependency injection framework',
|
author, 'Dependency Injector', 'Dependency injection microframework for Python',
|
||||||
'Miscellaneous'),
|
'Miscellaneous'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,14 @@ follows `Semantic versioning`_
|
||||||
|
|
||||||
Development version
|
Development version
|
||||||
-------------------
|
-------------------
|
||||||
|
- Add ``dependency_injector.injections`` module (C extension).
|
||||||
- Remove ``@inject`` decorator.
|
- Remove ``@inject`` decorator.
|
||||||
- Add makefile (``clean``, ``tests`` & ``publish`` commands).
|
- Add makefile (``clean``, ``test``, ``build``, ``install``, ``uninstall``
|
||||||
|
& ``publish`` commands).
|
||||||
|
- Update repository structure:
|
||||||
|
|
||||||
|
- Sources are moved under ``src``.
|
||||||
|
- Tests are moved under ``tests/unit``.
|
||||||
|
|
||||||
.. - No features.
|
.. - No features.
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
"""`inject()` decorator and Flask view example."""
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
import flask
|
|
||||||
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
import dependency_injector.injections as injections
|
|
||||||
|
|
||||||
|
|
||||||
database = providers.Singleton(sqlite3.connect,
|
|
||||||
':memory:',
|
|
||||||
timeout=30,
|
|
||||||
detect_types=True,
|
|
||||||
isolation_level='EXCLUSIVE')
|
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
@injections.inject(database)
|
|
||||||
@injections.inject(flask.request)
|
|
||||||
def hello(request, database):
|
|
||||||
"""Example Flask view."""
|
|
||||||
print request
|
|
||||||
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>
|
|
|
@ -1,43 +0,0 @@
|
||||||
"""`inject()` decorator with classes example."""
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
import flask
|
|
||||||
import flask.views
|
|
||||||
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
import dependency_injector.injections as injections
|
|
||||||
|
|
||||||
|
|
||||||
database = providers.Singleton(sqlite3.Connection,
|
|
||||||
database=':memory:',
|
|
||||||
timeout=30,
|
|
||||||
detect_types=True,
|
|
||||||
isolation_level='EXCLUSIVE')
|
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@injections.inject(database=database)
|
|
||||||
@injections.inject(some_setting=777)
|
|
||||||
class HelloView(flask.views.View):
|
|
||||||
"""Example flask class-based view."""
|
|
||||||
|
|
||||||
def __init__(self, database, some_setting):
|
|
||||||
"""Initializer."""
|
|
||||||
self.database = database
|
|
||||||
self.some_setting = some_setting
|
|
||||||
|
|
||||||
def dispatch_request(self):
|
|
||||||
"""Handle example request."""
|
|
||||||
one = self.database.execute('SELECT 1').fetchone()[0]
|
|
||||||
one *= self.some_setting
|
|
||||||
return 'Query returned {0}, db connection {1}'.format(one, database)
|
|
||||||
|
|
||||||
|
|
||||||
app.add_url_rule('/', view_func=HelloView.as_view('hello_view'))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
# Example output of "GET / HTTP/1.1" is:
|
|
||||||
# Query returned 777, db connection <sqlite3.Connection object at 0x1057e4030>
|
|
|
@ -1,28 +0,0 @@
|
||||||
"""`inject()` decorator simple example."""
|
|
||||||
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
import dependency_injector.injections as injections
|
|
||||||
|
|
||||||
|
|
||||||
dependency_injector_factory = providers.Factory(object)
|
|
||||||
|
|
||||||
|
|
||||||
# Example of using `inject()` decorator keyword argument injections:
|
|
||||||
@injections.inject(new_object=dependency_injector_factory)
|
|
||||||
@injections.inject(some_setting=1334)
|
|
||||||
def example_callback1(new_object, some_setting):
|
|
||||||
"""Example callback that does some asserts for input args."""
|
|
||||||
assert isinstance(new_object, object)
|
|
||||||
assert some_setting == 1334
|
|
||||||
|
|
||||||
|
|
||||||
# Example of using `inject()` decorator with positional argument injections:
|
|
||||||
@injections.inject(dependency_injector_factory, 1334)
|
|
||||||
def example_callback2(new_object, some_setting):
|
|
||||||
"""Example callback that does some asserts for input args."""
|
|
||||||
assert isinstance(new_object, object)
|
|
||||||
assert some_setting == 1334
|
|
||||||
|
|
||||||
|
|
||||||
example_callback1()
|
|
||||||
example_callback2()
|
|
|
@ -30,7 +30,7 @@ if __name__ == '__main__':
|
||||||
class _SequenceContainer1(SequencesContainer):
|
class _SequenceContainer1(SequencesContainer):
|
||||||
object_provider = providers.Factory(object)
|
object_provider = providers.Factory(object)
|
||||||
except errors.Error as exception:
|
except errors.Error as exception:
|
||||||
print exception
|
print(exception)
|
||||||
# <class '__main__._SequenceContainer1'> can contain only
|
# <class '__main__._SequenceContainer1'> can contain only
|
||||||
# <class '__main__.SequenceProvider'> instances
|
# <class '__main__.SequenceProvider'> instances
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ if __name__ == '__main__':
|
||||||
class _SequenceContainer2(SequencesContainer):
|
class _SequenceContainer2(SequencesContainer):
|
||||||
object_provider = SequenceProvider(object)
|
object_provider = SequenceProvider(object)
|
||||||
except errors.Error as exception:
|
except errors.Error as exception:
|
||||||
print exception
|
print(exception)
|
||||||
# <class '__main__.SequenceProvider'> can provide only
|
# <class '__main__.SequenceProvider'> can provide only
|
||||||
# <class '_abcoll.Sequence'> instances
|
# <class '_abcoll.Sequence'> instances
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ if __name__ == '__main__':
|
||||||
try:
|
try:
|
||||||
sequences_container.object_provider = providers.Factory(object)
|
sequences_container.object_provider = providers.Factory(object)
|
||||||
except errors.Error as exception:
|
except errors.Error as exception:
|
||||||
print exception
|
print(exception)
|
||||||
# <dependency_injector.containers.DynamicContainer object at
|
# <dependency_injector.containers.DynamicContainer object at
|
||||||
# 0x107820ed0> can contain only <class '__main__.SequenceProvider'>
|
# 0x107820ed0> can contain only <class '__main__.SequenceProvider'>
|
||||||
# instances
|
# instances
|
||||||
|
@ -32,7 +32,7 @@ if __name__ == '__main__':
|
||||||
try:
|
try:
|
||||||
sequences_container.object_provider = SequenceProvider(object)
|
sequences_container.object_provider = SequenceProvider(object)
|
||||||
except errors.Error as exception:
|
except errors.Error as exception:
|
||||||
print exception
|
print(exception)
|
||||||
# <class '__main__.SequenceProvider'> can provide only
|
# <class '__main__.SequenceProvider'> can provide only
|
||||||
# <class '_abcoll.Sequence'> instances
|
# <class '_abcoll.Sequence'> instances
|
||||||
|
|
||||||
|
|
|
@ -11,5 +11,5 @@ class ApiClient(object):
|
||||||
|
|
||||||
def call(self, operation, data):
|
def call(self, operation, data):
|
||||||
"""Make some network operations."""
|
"""Make some network operations."""
|
||||||
print 'API call [{0}:{1}], method - {2}, data - {3}'.format(
|
print('API call [{0}:{1}], method - {2}, data - {3}'.format(
|
||||||
self.host, self.api_key, operation, repr(data))
|
self.host, self.api_key, operation, repr(data)))
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
"""Pythonic way for Dependency Injection - Auth System."""
|
|
||||||
|
|
||||||
from dependency_injector import providers
|
|
||||||
from dependency_injector import injections
|
|
||||||
|
|
||||||
|
|
||||||
@providers.DelegatedCallable
|
|
||||||
def get_user_info(user_id):
|
|
||||||
"""Return user info."""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
@providers.Factory
|
|
||||||
@injections.inject(get_user_info=get_user_info)
|
|
||||||
class AuthComponent(object):
|
|
||||||
"""Some authentication component."""
|
|
||||||
|
|
||||||
def __init__(self, get_user_info):
|
|
||||||
"""Initializer."""
|
|
||||||
self.get_user_info = get_user_info
|
|
||||||
|
|
||||||
def authenticate_user(self, token):
|
|
||||||
"""Authenticate user by token."""
|
|
||||||
user_info = self.get_user_info(user_id=token + '1')
|
|
||||||
return user_info
|
|
||||||
|
|
||||||
|
|
||||||
print AuthComponent
|
|
||||||
print get_user_info
|
|
||||||
|
|
||||||
|
|
||||||
@providers.override(get_user_info)
|
|
||||||
@providers.DelegatedCallable
|
|
||||||
def get_user_info(user_id):
|
|
||||||
"""Return user info."""
|
|
||||||
return {'user_id': user_id}
|
|
||||||
|
|
||||||
|
|
||||||
print AuthComponent().authenticate_user(token='abc')
|
|
||||||
# {'user_id': 'abc1'}
|
|
|
@ -1,66 +0,0 @@
|
||||||
"""Pythonic way for Dependency Injection - callback-based IoC container."""
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
from dependency_injector import containers
|
|
||||||
from dependency_injector import providers
|
|
||||||
from dependency_injector import injections
|
|
||||||
|
|
||||||
|
|
||||||
class UsersService(object):
|
|
||||||
"""Users service, that has dependency on database."""
|
|
||||||
|
|
||||||
def __init__(self, db):
|
|
||||||
"""Initializer."""
|
|
||||||
self.db = db
|
|
||||||
|
|
||||||
|
|
||||||
class AuthService(object):
|
|
||||||
"""Auth service, that has dependencies on users service and database."""
|
|
||||||
|
|
||||||
def __init__(self, db, users_service):
|
|
||||||
"""Initializer."""
|
|
||||||
self.db = db
|
|
||||||
self.users_service = users_service
|
|
||||||
|
|
||||||
|
|
||||||
class Services(containers.DeclarativeContainer):
|
|
||||||
"""IoC container of service providers."""
|
|
||||||
|
|
||||||
@providers.Singleton
|
|
||||||
def database():
|
|
||||||
"""Provide database connection.
|
|
||||||
|
|
||||||
:rtype: sqlite3.Connection
|
|
||||||
"""
|
|
||||||
return sqlite3.connect(':memory:')
|
|
||||||
|
|
||||||
@providers.Factory
|
|
||||||
@injections.inject(db=database)
|
|
||||||
def users(**kwargs):
|
|
||||||
"""Provide users service.
|
|
||||||
|
|
||||||
:rtype: UsersService
|
|
||||||
"""
|
|
||||||
return UsersService(**kwargs)
|
|
||||||
|
|
||||||
@providers.Factory
|
|
||||||
@injections.inject(db=database)
|
|
||||||
@injections.inject(users_service=users)
|
|
||||||
def auth(**kwargs):
|
|
||||||
"""Provide users service.
|
|
||||||
|
|
||||||
:rtype: AuthService
|
|
||||||
"""
|
|
||||||
return AuthService(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
# Retrieving services:
|
|
||||||
users_service = Services.users()
|
|
||||||
auth_service = Services.auth()
|
|
||||||
|
|
||||||
# Making some asserts:
|
|
||||||
assert users_service.db is auth_service.db is Services.database()
|
|
||||||
assert isinstance(auth_service.users_service, UsersService)
|
|
||||||
assert users_service is not Services.users()
|
|
||||||
assert auth_service is not Services.auth()
|
|
|
@ -7,9 +7,9 @@ import dependency_injector.providers as providers
|
||||||
even_filter = providers.Callable(filter, lambda x: x % 2 == 0)
|
even_filter = providers.Callable(filter, lambda x: x % 2 == 0)
|
||||||
odd_filter = providers.Callable(filter, lambda x: x % 2 != 0)
|
odd_filter = providers.Callable(filter, lambda x: x % 2 != 0)
|
||||||
|
|
||||||
# Creating even and odd ranges using xrange() and filter providers:
|
# Creating even and odd ranges using range() and filter providers:
|
||||||
even_range = even_filter(xrange(1, 10))
|
even_range = even_filter(range(1, 10))
|
||||||
odd_range = odd_filter(xrange(1, 10))
|
odd_range = odd_filter(range(1, 10))
|
||||||
|
|
||||||
# Making some asserts:
|
# Making some asserts:
|
||||||
assert even_range == [2, 4, 6, 8]
|
assert even_range == [2, 4, 6, 8]
|
||||||
|
|
|
@ -25,6 +25,6 @@ some_service_provider = ServiceProvider(SomeService)
|
||||||
try:
|
try:
|
||||||
some_service_provider = ServiceProvider(object)
|
some_service_provider = ServiceProvider(object)
|
||||||
except errors.Error as exception:
|
except errors.Error as exception:
|
||||||
print exception
|
print(exception)
|
||||||
# <class '__main__.ServiceProvider'> can provide only
|
# <class '__main__.ServiceProvider'> can provide only
|
||||||
# <class '__main__.BaseService'> instances
|
# <class '__main__.BaseService'> instances
|
||||||
|
|
|
@ -30,6 +30,6 @@ photos_service_provider = ServiceProvider(PhotosService)
|
||||||
try:
|
try:
|
||||||
some_service_provider = ServiceProvider(object)
|
some_service_provider = ServiceProvider(object)
|
||||||
except errors.Error as exception:
|
except errors.Error as exception:
|
||||||
print exception
|
print(exception)
|
||||||
# <class '__main__.ServiceProvider'> can provide only
|
# <class '__main__.ServiceProvider'> can provide only
|
||||||
# <class '__main__.BaseService'> instances
|
# <class '__main__.BaseService'> instances
|
||||||
|
|
|
@ -28,7 +28,7 @@ thread_factory = providers.Factory(threading.Thread,
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Create 10 threads for concurrent execution of example():
|
# Create 10 threads for concurrent execution of example():
|
||||||
threads = []
|
threads = []
|
||||||
for thread_number in xrange(10):
|
for thread_number in range(10):
|
||||||
threads.append(thread_factory(name='Thread{0}'.format(thread_number)))
|
threads.append(thread_factory(name='Thread{0}'.format(thread_number)))
|
||||||
|
|
||||||
# Start execution of all created threads:
|
# Start execution of all created threads:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
cython
|
||||||
tox
|
tox
|
||||||
unittest2
|
unittest2
|
||||||
sphinx
|
sphinx
|
||||||
|
|
39
setup.py
39
setup.py
|
@ -1,10 +1,14 @@
|
||||||
"""`Dependency injector` setup script."""
|
"""`Dependency injector` setup script."""
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup, Extension
|
||||||
|
|
||||||
|
|
||||||
|
# Defining setup variables:
|
||||||
|
defined_macros = list()
|
||||||
|
|
||||||
# Getting description:
|
# Getting description:
|
||||||
with open('README.rst') as readme_file:
|
with open('README.rst') as readme_file:
|
||||||
description = readme_file.read()
|
description = readme_file.read()
|
||||||
|
@ -14,9 +18,14 @@ with open('requirements.txt') as version:
|
||||||
requirements = version.readlines()
|
requirements = version.readlines()
|
||||||
|
|
||||||
# Getting version:
|
# Getting version:
|
||||||
with open('dependency_injector/__init__.py') as init_file:
|
with open('src/dependency_injector/__init__.py') as init_file:
|
||||||
version = re.search('VERSION = \'(.*?)\'', init_file.read()).group(1)
|
version = re.search('VERSION = \'(.*?)\'', init_file.read()).group(1)
|
||||||
|
|
||||||
|
# Adding debug options:
|
||||||
|
if os.environ.get('DEPENDENCY_INJECTOR_DEBUG_MODE') == '1':
|
||||||
|
defined_macros.append(('CYTHON_TRACE', 1))
|
||||||
|
defined_macros.append(('CYTHON_TRACE_NOGIL', 1))
|
||||||
|
|
||||||
|
|
||||||
setup(name='dependency-injector',
|
setup(name='dependency-injector',
|
||||||
version=version,
|
version=version,
|
||||||
|
@ -27,15 +36,27 @@ setup(name='dependency-injector',
|
||||||
maintainer='Roman Mogilatov',
|
maintainer='Roman Mogilatov',
|
||||||
maintainer_email='rmogilatov@gmail.com',
|
maintainer_email='rmogilatov@gmail.com',
|
||||||
url='https://github.com/ets-labs/python-dependency-injector',
|
url='https://github.com/ets-labs/python-dependency-injector',
|
||||||
bugtrack_url='https://github.com/ets-labs/python-dependency-injector' +
|
|
||||||
'/issues',
|
|
||||||
download_url='https://pypi.python.org/pypi/dependency_injector',
|
download_url='https://pypi.python.org/pypi/dependency_injector',
|
||||||
license='BSD New',
|
|
||||||
packages=['dependency_injector',
|
|
||||||
'dependency_injector.providers'],
|
|
||||||
platforms=['any'],
|
|
||||||
zip_safe=True,
|
|
||||||
install_requires=requirements,
|
install_requires=requirements,
|
||||||
|
packages=[
|
||||||
|
'dependency_injector',
|
||||||
|
'dependency_injector.providers',
|
||||||
|
],
|
||||||
|
package_dir={
|
||||||
|
'': 'src',
|
||||||
|
},
|
||||||
|
ext_modules=[
|
||||||
|
Extension('dependency_injector.injections',
|
||||||
|
['src/dependency_injector/injections.c'],
|
||||||
|
define_macros=defined_macros,
|
||||||
|
extra_compile_args=['-O2']),
|
||||||
|
],
|
||||||
|
package_data={
|
||||||
|
'dependency_injector': ['*.pxd'],
|
||||||
|
},
|
||||||
|
zip_safe=True,
|
||||||
|
license='BSD New',
|
||||||
|
platforms=['any'],
|
||||||
keywords=[
|
keywords=[
|
||||||
'DI',
|
'DI',
|
||||||
'Dependency injection',
|
'Dependency injection',
|
||||||
|
|
63
src/dependency_injector/injections.pxd
Normal file
63
src/dependency_injector/injections.pxd
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"""Dependency injector injections.
|
||||||
|
|
||||||
|
Cython optimized code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cpdef tuple parse_positional_injections(tuple args)
|
||||||
|
cpdef tuple parse_named_injections(dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Injection:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
cdef class PositionalInjection(Injection):
|
||||||
|
cdef object __value
|
||||||
|
cdef int __is_provider
|
||||||
|
cdef int __is_delegated
|
||||||
|
cdef int __call
|
||||||
|
|
||||||
|
cdef inline object __get_value(self):
|
||||||
|
if self.__call == 0:
|
||||||
|
return self.__value
|
||||||
|
return self.__value()
|
||||||
|
|
||||||
|
|
||||||
|
cdef class NamedInjection(Injection):
|
||||||
|
cdef object __name
|
||||||
|
cdef object __value
|
||||||
|
cdef int __is_provider
|
||||||
|
cdef int __is_delegated
|
||||||
|
cdef int __call
|
||||||
|
|
||||||
|
cdef inline object __get_name(self):
|
||||||
|
return self.__name
|
||||||
|
|
||||||
|
cdef inline object __get_value(self):
|
||||||
|
if self.__call == 0:
|
||||||
|
return self.__value
|
||||||
|
return self.__value()
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline tuple __provide_positional_args(tuple inj_args,
|
||||||
|
int inj_args_len,
|
||||||
|
tuple args):
|
||||||
|
cdef PositionalInjection injection
|
||||||
|
if inj_args_len > 0:
|
||||||
|
positional_args = list()
|
||||||
|
for index in range(inj_args_len):
|
||||||
|
injection = <PositionalInjection>inj_args[index]
|
||||||
|
positional_args.append(injection.get_value())
|
||||||
|
positional_args.extend(args)
|
||||||
|
args = positional_args
|
||||||
|
return args
|
||||||
|
|
||||||
|
cdef inline dict __provide_keyword_args(tuple inj_kwargs,
|
||||||
|
int inj_kwargs_len,
|
||||||
|
dict kwargs):
|
||||||
|
cdef NamedInjection kw_injection
|
||||||
|
if inj_kwargs_len > 0:
|
||||||
|
for index in range(inj_kwargs_len):
|
||||||
|
kw_injection = <NamedInjection>inj_kwargs[index]
|
||||||
|
kwargs[kw_injection.get_name()] = kw_injection.get_value()
|
||||||
|
return kwargs
|
78
src/dependency_injector/injections.pyx
Normal file
78
src/dependency_injector/injections.pyx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
"""Dependency injector injections.
|
||||||
|
|
||||||
|
Cython optimized code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: replace to cimport
|
||||||
|
from .utils import is_provider
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Injection:
|
||||||
|
"""Abstract injection class."""
|
||||||
|
|
||||||
|
|
||||||
|
cdef class PositionalInjection(Injection):
|
||||||
|
"""Positional injection class."""
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
"""Initializer."""
|
||||||
|
self.__value = value
|
||||||
|
self.__is_provider = <int>is_provider(value)
|
||||||
|
self.__is_delegated = 0 # TODO: use utils.is_delegated()
|
||||||
|
self.__call = <int>self.__is_provider == 1 and self.__is_delegated == 0
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
"""Return injection value."""
|
||||||
|
return self.__get_value()
|
||||||
|
|
||||||
|
|
||||||
|
cdef class NamedInjection(Injection):
|
||||||
|
"""Keyword injection class."""
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
"""Initializer."""
|
||||||
|
self.__name = name
|
||||||
|
self.__value = value
|
||||||
|
self.__is_provider = <int>is_provider(value)
|
||||||
|
self.__is_delegated = 0 # TODO: use utils.is_delegated()
|
||||||
|
self.__call = <int>self.__is_provider == 1 and self.__is_delegated == 0
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""Return injection value."""
|
||||||
|
return self.__get_name()
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
"""Return injection value."""
|
||||||
|
return self.__get_value()
|
||||||
|
|
||||||
|
|
||||||
|
cpdef tuple parse_positional_injections(tuple args):
|
||||||
|
"""Parse positional injections."""
|
||||||
|
cdef list injections = list()
|
||||||
|
cdef int args_len = len(args)
|
||||||
|
|
||||||
|
cdef object arg
|
||||||
|
cdef int index
|
||||||
|
cdef PositionalInjection injection
|
||||||
|
|
||||||
|
for index in range(args_len):
|
||||||
|
arg = args[index]
|
||||||
|
injection = PositionalInjection(arg)
|
||||||
|
injections.append(injection)
|
||||||
|
|
||||||
|
return tuple(injections)
|
||||||
|
|
||||||
|
|
||||||
|
cpdef tuple parse_named_injections(dict kwargs):
|
||||||
|
"""Parse named injections."""
|
||||||
|
cdef list injections = list()
|
||||||
|
|
||||||
|
cdef object name
|
||||||
|
cdef object arg
|
||||||
|
cdef NamedInjection injection
|
||||||
|
|
||||||
|
for name, arg in kwargs.items():
|
||||||
|
injection = NamedInjection(name, arg)
|
||||||
|
injections.append(injection)
|
||||||
|
|
||||||
|
return tuple(injections)
|
2
tests/unit/.pydocstylerc
Normal file
2
tests/unit/.pydocstylerc
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[pydocstyle]
|
||||||
|
ignore = D101,D102
|
53
tests/unit/test_injections.py
Normal file
53
tests/unit/test_injections.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
"""Dependency injector injections unit tests."""
|
||||||
|
|
||||||
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
from dependency_injector import injections
|
||||||
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
|
class PositionalInjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_isinstance(self):
|
||||||
|
injection = injections.PositionalInjection(1)
|
||||||
|
self.assertIsInstance(injection, injections.Injection)
|
||||||
|
|
||||||
|
def test_get_value_with_not_provider(self):
|
||||||
|
injection = injections.PositionalInjection(123)
|
||||||
|
self.assertEquals(injection.get_value(), 123)
|
||||||
|
|
||||||
|
def test_get_value_with_factory(self):
|
||||||
|
injection = injections.PositionalInjection(providers.Factory(object))
|
||||||
|
|
||||||
|
obj1 = injection.get_value()
|
||||||
|
obj2 = injection.get_value()
|
||||||
|
|
||||||
|
self.assertIs(type(obj1), object)
|
||||||
|
self.assertIs(type(obj2), object)
|
||||||
|
self.assertIsNot(obj1, obj2)
|
||||||
|
|
||||||
|
|
||||||
|
class NamedInjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_isinstance(self):
|
||||||
|
injection = injections.NamedInjection('name', 1)
|
||||||
|
self.assertIsInstance(injection, injections.Injection)
|
||||||
|
|
||||||
|
def test_get_name(self):
|
||||||
|
injection = injections.NamedInjection('name', 123)
|
||||||
|
self.assertEquals(injection.get_name(), 'name')
|
||||||
|
|
||||||
|
def test_get_value_with_not_provider(self):
|
||||||
|
injection = injections.NamedInjection('name', 123)
|
||||||
|
self.assertEquals(injection.get_value(), 123)
|
||||||
|
|
||||||
|
def test_get_value_with_factory(self):
|
||||||
|
injection = injections.NamedInjection('name',
|
||||||
|
providers.Factory(object))
|
||||||
|
|
||||||
|
obj1 = injection.get_value()
|
||||||
|
obj2 = injection.get_value()
|
||||||
|
|
||||||
|
self.assertIs(type(obj1), object)
|
||||||
|
self.assertIs(type(obj2), object)
|
||||||
|
self.assertIsNot(obj1, obj2)
|
20
tox.ini
20
tox.ini
|
@ -6,39 +6,39 @@ envlist=
|
||||||
deps=
|
deps=
|
||||||
unittest2
|
unittest2
|
||||||
commands=
|
commands=
|
||||||
unit2 discover tests []
|
unit2 discover tests/unit
|
||||||
|
|
||||||
[testenv:coveralls]
|
[testenv:coveralls]
|
||||||
basepython=python2.7
|
passenv=TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH DEPENDENCY_INJECTOR_DEBUG_MODE
|
||||||
passenv=TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH
|
basepython=python3.5
|
||||||
|
usedevelop=True
|
||||||
deps=
|
deps=
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
|
cython
|
||||||
coverage
|
coverage
|
||||||
coveralls
|
coveralls
|
||||||
commands=
|
commands=
|
||||||
coverage erase
|
coverage erase
|
||||||
coverage run --rcfile=./.coveragerc -m unittest2 discover tests []
|
coverage run --rcfile=./.coveragerc -m unittest2 discover tests/unit
|
||||||
|
coverage report --rcfile=./.coveragerc
|
||||||
coveralls
|
coveralls
|
||||||
|
|
||||||
[testenv:pylint]
|
[testenv:pylint]
|
||||||
basepython=python2.7
|
|
||||||
deps=
|
deps=
|
||||||
pylint
|
pylint
|
||||||
commands=
|
commands=
|
||||||
- pylint -f colorized --rcfile=./.pylintrc dependency_injector
|
- pylint -f colorized --rcfile=./.pylintrc src/dependency_injector
|
||||||
|
|
||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
basepython=python2.7
|
|
||||||
deps=
|
deps=
|
||||||
flake8
|
flake8
|
||||||
commands=
|
commands=
|
||||||
flake8 --max-complexity=10 dependency_injector/
|
flake8 --max-complexity=10 src/dependency_injector/
|
||||||
flake8 --max-complexity=10 examples/
|
flake8 --max-complexity=10 examples/
|
||||||
|
|
||||||
[testenv:pydocstyle]
|
[testenv:pydocstyle]
|
||||||
basepython=python2.7
|
|
||||||
deps=
|
deps=
|
||||||
pydocstyle
|
pydocstyle
|
||||||
commands=
|
commands=
|
||||||
pydocstyle dependency_injector/
|
pydocstyle src/dependency_injector/
|
||||||
pydocstyle examples/
|
pydocstyle examples/
|
||||||
|
|
Loading…
Reference in New Issue
Block a user