diff --git a/.coveragerc b/.coveragerc index 9369b60a..46a0a2ee 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,6 +1,7 @@ [run] -include = dependency_injector/* -omit = tests/* +source = src/dependency_injector +omit = tests/unit +plugins = Cython.Coverage [html] directory=reports/unittests/ diff --git a/.gitignore b/.gitignore index d164e5eb..aed613dc 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,7 @@ venv/ .ropeproject/ # C extensions -dependency_injector/*.c -dependency_injector/*.so -dependency_injector/providers/*.c -dependency_injector/providers/*.so +src/dependency_injector/*.c +src/dependency_injector/*.so +src/dependency_injector/providers/*.c +src/dependency_injector/providers/*.so diff --git a/.pylintrc b/.pylintrc index cb6a6fb8..679c86b4 100644 --- a/.pylintrc +++ b/.pylintrc @@ -2,7 +2,7 @@ # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. -ignore=utils,test +ignore=utils,tests [MESSAGES CONTROL] diff --git a/.travis.yml b/.travis.yml index 7458db1b..806953de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,22 @@ sudo: false +install: + - pip install tox + - pip install cython + - make cythonize +script: + - tox language: python -install: pip install tox -script: tox python: - - 3.5 + - 3.5 env: - - TOXENV=coveralls - - TOXENV=pylint - - TOXENV=flake8 - - TOXENV=pydocstyle - - TOXENV=py26 - - TOXENV=py27 - - TOXENV=py33 - - TOXENV=py34 - - TOXENV=py35 - - TOXENV=pypy - - TOXENV=pypy3 + - TOXENV=coveralls DEPENDENCY_INJECTOR_DEBUG_MODE=1 + - TOXENV=pylint + - TOXENV=flake8 + - TOXENV=pydocstyle + - TOXENV=py26 + - TOXENV=py27 + - TOXENV=py33 + - TOXENV=py34 + - TOXENV=py35 + - TOXENV=pypy + - TOXENV=pypy3 diff --git a/MANIFEST.in b/MANIFEST.in index f4e704ed..bb14403d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include dependency_injector/* +recursive-include src/dependency_injector *.py *.pyx *.pxd *.c include README.rst include CONTRIBUTORS.rst include LICENSE.rst diff --git a/Makefile b/Makefile index 46b84088..4764cba1 100644 --- a/Makefile +++ b/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 sources - find dependency_injector -name '*.py[co]' -delete - find dependency_injector -name '__pycache__' -delete - find dependency_injector -name '*.c' -delete - find dependency_injector -name '*.so' -delete + find src -name '*.py[cod]' -delete + find src -name '__pycache__' -delete + find src -name '*.c' -delete + find src -name '*.so' -delete + find src -name '*.html' -delete # Clean tests find tests -name '*.py[co]' -delete find tests -name '__pycache__' -delete @@ -13,21 +24,39 @@ clean: find examples -name '*.py[co]' -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 coverage erase - coverage run --rcfile=./.coveragerc -m unittest2 discover tests + coverage run --rcfile=./.coveragerc -m unittest2 discover tests/unit coverage report --rcfile=./.coveragerc coverage html --rcfile=./.coveragerc - coverage erase + +check: # Static analysis - flake8 --max-complexity=10 dependency_injector/ + flake8 --max-complexity=10 src/dependency_injector/ flake8 --max-complexity=10 examples/ # Code style analysis - pydocstyle dependency_injector/ + pydocstyle src/dependency_injector/ pydocstyle examples/ -publish: clean +publish: cythonize # Create and upload build python setup.py sdist upload # Create and upload tag diff --git a/docs/conf.py b/docs/conf.py index 51742b96..3e2d3af9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -56,7 +56,7 @@ author = u'ETS Labs' # # The short X.Y 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) # The full version, including alpha/beta/rc tags. @@ -281,7 +281,7 @@ man_pages = [ # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'Dependency Injector', u'Dependency Injector Documentation', - author, 'Dependency Injector', 'Python dependency injection framework', + author, 'Dependency Injector', 'Dependency injection microframework for Python', 'Miscellaneous'), ] diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index f0db1f01..0b5e9807 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -9,8 +9,14 @@ follows `Semantic versioning`_ Development version ------------------- +- Add ``dependency_injector.injections`` module (C extension). - 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. diff --git a/examples/advanced_usage/inject_flask.py b/examples/advanced_usage/inject_flask.py deleted file mode 100644 index c16490bf..00000000 --- a/examples/advanced_usage/inject_flask.py +++ /dev/null @@ -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 diff --git a/examples/advanced_usage/inject_flask_class_based.py b/examples/advanced_usage/inject_flask_class_based.py deleted file mode 100644 index ae1e5767..00000000 --- a/examples/advanced_usage/inject_flask_class_based.py +++ /dev/null @@ -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 diff --git a/examples/advanced_usage/inject_simple.py b/examples/advanced_usage/inject_simple.py deleted file mode 100644 index 9923a51d..00000000 --- a/examples/advanced_usage/inject_simple.py +++ /dev/null @@ -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() diff --git a/examples/containers/declarative_provider_type.py b/examples/containers/declarative_provider_type.py index cc1b0338..2a755726 100644 --- a/examples/containers/declarative_provider_type.py +++ b/examples/containers/declarative_provider_type.py @@ -30,7 +30,7 @@ if __name__ == '__main__': class _SequenceContainer1(SequencesContainer): object_provider = providers.Factory(object) except errors.Error as exception: - print exception + print(exception) # can contain only # instances @@ -38,7 +38,7 @@ if __name__ == '__main__': class _SequenceContainer2(SequencesContainer): object_provider = SequenceProvider(object) except errors.Error as exception: - print exception + print(exception) # can provide only # instances diff --git a/examples/containers/dynamic_provider_type.py b/examples/containers/dynamic_provider_type.py index 2e0835cd..fd1c0ac9 100644 --- a/examples/containers/dynamic_provider_type.py +++ b/examples/containers/dynamic_provider_type.py @@ -24,7 +24,7 @@ if __name__ == '__main__': try: sequences_container.object_provider = providers.Factory(object) except errors.Error as exception: - print exception + print(exception) # can contain only # instances @@ -32,7 +32,7 @@ if __name__ == '__main__': try: sequences_container.object_provider = SequenceProvider(object) except errors.Error as exception: - print exception + print(exception) # can provide only # instances diff --git a/examples/miniapps/api_client/api.py b/examples/miniapps/api_client/api.py index 6d83407b..b92641e3 100644 --- a/examples/miniapps/api_client/api.py +++ b/examples/miniapps/api_client/api.py @@ -11,5 +11,5 @@ class ApiClient(object): def call(self, operation, data): """Make some network operations.""" - print 'API call [{0}:{1}], method - {2}, data - {3}'.format( - self.host, self.api_key, operation, repr(data)) + print('API call [{0}:{1}], method - {2}, data - {3}'.format( + self.host, self.api_key, operation, repr(data))) diff --git a/examples/misc/auth_system.py b/examples/misc/auth_system.py deleted file mode 100644 index be09a56a..00000000 --- a/examples/misc/auth_system.py +++ /dev/null @@ -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'} diff --git a/examples/misc/callbacks_based_container.py b/examples/misc/callbacks_based_container.py deleted file mode 100644 index 1d679858..00000000 --- a/examples/misc/callbacks_based_container.py +++ /dev/null @@ -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() diff --git a/examples/providers/callable_args.py b/examples/providers/callable_args.py index 27229a6f..a4292de4 100644 --- a/examples/providers/callable_args.py +++ b/examples/providers/callable_args.py @@ -7,9 +7,9 @@ import dependency_injector.providers as providers even_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: -even_range = even_filter(xrange(1, 10)) -odd_range = odd_filter(xrange(1, 10)) +# Creating even and odd ranges using range() and filter providers: +even_range = even_filter(range(1, 10)) +odd_range = odd_filter(range(1, 10)) # Making some asserts: assert even_range == [2, 4, 6, 8] diff --git a/examples/providers/factory_provided_type.py b/examples/providers/factory_provided_type.py index 9a282f64..4e4d645c 100644 --- a/examples/providers/factory_provided_type.py +++ b/examples/providers/factory_provided_type.py @@ -25,6 +25,6 @@ some_service_provider = ServiceProvider(SomeService) try: some_service_provider = ServiceProvider(object) except errors.Error as exception: - print exception + print(exception) # can provide only # instances diff --git a/examples/providers/singleton_provided_type.py b/examples/providers/singleton_provided_type.py index e6d4caa2..517744ca 100644 --- a/examples/providers/singleton_provided_type.py +++ b/examples/providers/singleton_provided_type.py @@ -30,6 +30,6 @@ photos_service_provider = ServiceProvider(PhotosService) try: some_service_provider = ServiceProvider(object) except errors.Error as exception: - print exception + print(exception) # can provide only # instances diff --git a/examples/providers/singleton_thread_locals.py b/examples/providers/singleton_thread_locals.py index 78434f39..a6ebeef5 100644 --- a/examples/providers/singleton_thread_locals.py +++ b/examples/providers/singleton_thread_locals.py @@ -28,7 +28,7 @@ thread_factory = providers.Factory(threading.Thread, if __name__ == '__main__': # Create 10 threads for concurrent execution of example(): threads = [] - for thread_number in xrange(10): + for thread_number in range(10): threads.append(thread_factory(name='Thread{0}'.format(thread_number))) # Start execution of all created threads: diff --git a/requirements-dev.txt b/requirements-dev.txt index 2b71e644..87d34508 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ +cython tox unittest2 sphinx diff --git a/setup.py b/setup.py index f6472771..f6ddec9c 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,14 @@ """`Dependency injector` setup script.""" +import os import re -from setuptools import setup +from setuptools import setup, Extension +# Defining setup variables: +defined_macros = list() + # Getting description: with open('README.rst') as readme_file: description = readme_file.read() @@ -14,9 +18,14 @@ with open('requirements.txt') as version: requirements = version.readlines() # 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) +# 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', version=version, @@ -27,15 +36,27 @@ setup(name='dependency-injector', maintainer='Roman Mogilatov', maintainer_email='rmogilatov@gmail.com', 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', - license='BSD New', - packages=['dependency_injector', - 'dependency_injector.providers'], - platforms=['any'], - zip_safe=True, 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=[ 'DI', 'Dependency injection', diff --git a/dependency_injector/__init__.py b/src/dependency_injector/__init__.py similarity index 100% rename from dependency_injector/__init__.py rename to src/dependency_injector/__init__.py diff --git a/dependency_injector/containers.py b/src/dependency_injector/containers.py similarity index 100% rename from dependency_injector/containers.py rename to src/dependency_injector/containers.py diff --git a/dependency_injector/errors.py b/src/dependency_injector/errors.py similarity index 100% rename from dependency_injector/errors.py rename to src/dependency_injector/errors.py diff --git a/src/dependency_injector/injections.pxd b/src/dependency_injector/injections.pxd new file mode 100644 index 00000000..5069d1ff --- /dev/null +++ b/src/dependency_injector/injections.pxd @@ -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 = 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 = inj_kwargs[index] + kwargs[kw_injection.get_name()] = kw_injection.get_value() + return kwargs diff --git a/src/dependency_injector/injections.pyx b/src/dependency_injector/injections.pyx new file mode 100644 index 00000000..b17f718f --- /dev/null +++ b/src/dependency_injector/injections.pyx @@ -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 = is_provider(value) + self.__is_delegated = 0 # TODO: use utils.is_delegated() + self.__call = 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 = is_provider(value) + self.__is_delegated = 0 # TODO: use utils.is_delegated() + self.__call = 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) diff --git a/dependency_injector/providers/__init__.py b/src/dependency_injector/providers/__init__.py similarity index 100% rename from dependency_injector/providers/__init__.py rename to src/dependency_injector/providers/__init__.py diff --git a/dependency_injector/providers/base.py b/src/dependency_injector/providers/base.py similarity index 100% rename from dependency_injector/providers/base.py rename to src/dependency_injector/providers/base.py diff --git a/dependency_injector/providers/callable.py b/src/dependency_injector/providers/callable.py similarity index 100% rename from dependency_injector/providers/callable.py rename to src/dependency_injector/providers/callable.py diff --git a/dependency_injector/providers/creational.py b/src/dependency_injector/providers/creational.py similarity index 100% rename from dependency_injector/providers/creational.py rename to src/dependency_injector/providers/creational.py diff --git a/dependency_injector/utils.py b/src/dependency_injector/utils.py similarity index 100% rename from dependency_injector/utils.py rename to src/dependency_injector/utils.py diff --git a/tests/unit/.pydocstylerc b/tests/unit/.pydocstylerc new file mode 100644 index 00000000..d55a0255 --- /dev/null +++ b/tests/unit/.pydocstylerc @@ -0,0 +1,2 @@ +[pydocstyle] +ignore = D101,D102 diff --git a/tests/__init__.py b/tests/unit/__init__.py similarity index 100% rename from tests/__init__.py rename to tests/unit/__init__.py diff --git a/tests/providers/__init__.py b/tests/unit/providers/__init__.py similarity index 100% rename from tests/providers/__init__.py rename to tests/unit/providers/__init__.py diff --git a/tests/providers/test_base.py b/tests/unit/providers/test_base.py similarity index 100% rename from tests/providers/test_base.py rename to tests/unit/providers/test_base.py diff --git a/tests/providers/test_callable.py b/tests/unit/providers/test_callable.py similarity index 100% rename from tests/providers/test_callable.py rename to tests/unit/providers/test_callable.py diff --git a/tests/providers/test_creational.py b/tests/unit/providers/test_creational.py similarity index 100% rename from tests/providers/test_creational.py rename to tests/unit/providers/test_creational.py diff --git a/tests/providers/test_static.py b/tests/unit/providers/test_static.py similarity index 100% rename from tests/providers/test_static.py rename to tests/unit/providers/test_static.py diff --git a/tests/test_common.py b/tests/unit/test_common.py similarity index 100% rename from tests/test_common.py rename to tests/unit/test_common.py diff --git a/tests/test_containers.py b/tests/unit/test_containers.py similarity index 100% rename from tests/test_containers.py rename to tests/unit/test_containers.py diff --git a/tests/unit/test_injections.py b/tests/unit/test_injections.py new file mode 100644 index 00000000..5ee6036c --- /dev/null +++ b/tests/unit/test_injections.py @@ -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) diff --git a/tests/test_utils.py b/tests/unit/test_utils.py similarity index 100% rename from tests/test_utils.py rename to tests/unit/test_utils.py diff --git a/tox.ini b/tox.ini index df81f63a..2cc5f30d 100644 --- a/tox.ini +++ b/tox.ini @@ -6,39 +6,39 @@ envlist= deps= unittest2 commands= - unit2 discover tests [] + unit2 discover tests/unit [testenv:coveralls] -basepython=python2.7 -passenv=TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH +passenv=TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH DEPENDENCY_INJECTOR_DEBUG_MODE +basepython=python3.5 +usedevelop=True deps= {[testenv]deps} + cython coverage coveralls commands= coverage erase - coverage run --rcfile=./.coveragerc -m unittest2 discover tests [] + coverage run --rcfile=./.coveragerc -m unittest2 discover tests/unit + coverage report --rcfile=./.coveragerc coveralls [testenv:pylint] -basepython=python2.7 deps= pylint commands= - - pylint -f colorized --rcfile=./.pylintrc dependency_injector + - pylint -f colorized --rcfile=./.pylintrc src/dependency_injector [testenv:flake8] -basepython=python2.7 deps= flake8 commands= - flake8 --max-complexity=10 dependency_injector/ + flake8 --max-complexity=10 src/dependency_injector/ flake8 --max-complexity=10 examples/ [testenv:pydocstyle] -basepython=python2.7 deps= pydocstyle commands= - pydocstyle dependency_injector/ + pydocstyle src/dependency_injector/ pydocstyle examples/