Skip to content

Commit

Permalink
Merge branch 'master' into annotated-support
Browse files Browse the repository at this point in the history
  • Loading branch information
ljnsn authored Jan 25, 2024
2 parents 934b8b5 + 44ac4e0 commit a440d70
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 43 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
name: CI

on: [push, pull_request]
on:
push:
branches:
- master
pull_request:

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "pypy3.7", "pypy3.8", "pypy3.9", "pypy3.10"]
python-version: [3.8, 3.9, "3.10", "3.11", "pypy3.8", "pypy3.9", "pypy3.10"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -17,7 +21,7 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install --upgrade -r requirements.txt -r requirements-dev.txt
pip install --upgrade -r requirements-dev.txt
pip install .
- name: Run tests
run: |
Expand Down
20 changes: 20 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Read the Docs configuration file for Sphinx projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2

build:
os: ubuntu-22.04
tools:
python: "3.12"

sphinx:
configuration: docs/conf.py
# TODO: Enable this when we get rid of the existing warnings
# fail_on_warning: true

python:
install:
- method: pip
path: .
- requirements: requirements-docs.txt
9 changes: 9 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Injector Change Log
===================

0.21.0
------

- Improved the documentation, thanks to jonathanmach and Jakub Wilk
- Fixed a thread-safety regression
- Improved the type annotations, thanks to David Pärsson
- Fixed singleton scope behavior with parent/child injectors, thanks to David Pärsson
- Stopped using a deprecated test function, thanks to ljnsn

0.20.1
------

Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
include *.py
include *.toml
include requirements-*.in
include *.txt
include CHANGES
include COPYING
Expand All @@ -10,3 +11,4 @@ recursive-include docs *.html
recursive-include docs *.py
recursive-include docs *.rst
recursive-include docs Makefile
exclude .readthedocs.yaml
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ The core values of Injector are:
* Documentation: https://injector.readthedocs.org
* Change log: https://injector.readthedocs.io/en/latest/changelog.html

Injector works with CPython 3.7+ and PyPy 3 implementing Python 3.7+.
Injector works with CPython 3.8+ and PyPy 3 implementing Python 3.8+.

A Quick Example
---------------
Expand Down
13 changes: 8 additions & 5 deletions docs/terminology.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,25 @@ Injection is the process of providing an instance of a type, to a method that us

Here is an example of injection on a module provider method, and on the constructor of a normal class::

from injector import inject
from typing import NewType

from injector import Binder, Module, inject, provider

Name = NewType("Name", str)
Description = NewType("Description", str)

class User:
@inject
def __init__(self, name: Name, description: Description):
self.name = name
self.description = description


class UserModule(Module):
def configure(self, binder):
def configure(self, binder: Binder):
binder.bind(User)


class UserAttributeModule(Module):
def configure(self, binder):
def configure(self, binder: Binder):
binder.bind(Name, to='Sherlock')

@provider
Expand Down
17 changes: 8 additions & 9 deletions docs/testing.rst
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
Testing with Injector
=====================

When you use unit test framework such as `unittest2` or `nose` you can also profit from `injector`. However, manually creating injectors and test classes can be quite annoying. There is, however, `with_injector` method decorator which has parameters just as `Injector` construtor and installes configured injector into class instance on the time of method call::
When you use unit test framework such as `unittest2` or `nose` you can also profit from `injector`. ::

import unittest
from injector import Module, with_injector, inject
from injector import Injector, Module


class UsernameModule(Module):
def configure(self, binder):
binder.bind(str, 'Maria')


class TestSomethingClass(unittest.TestCase):
@with_injector(UsernameModule())

def setUp(self):
pass
self.__injector = Injector(UsernameModule())

@inject
def test_username(self, username: str):
def test_username(self):
username = self.__injector.get(str)
self.assertEqual(username, 'Maria')

**Each** method call re-initializes :class:`~injector.Injector` - if you want to you can also put :func:`~injector.with_injector` decorator on class constructor.

After such call all :func:`~injector.inject`-decorated methods will work just as you'd expect them to work.
20 changes: 12 additions & 8 deletions injector/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@


__author__ = 'Alec Thomas <[email protected]>'
__version__ = '0.20.1'
__version__ = '0.21.0'
__version_tag__ = ''

log = logging.getLogger('injector')
Expand Down Expand Up @@ -531,7 +531,7 @@ def install(self, module: _InstallableModuleType) -> None:
In this context the module is one of the following:
* function taking the :class:`Binder` as it's only parameter
* function taking the :class:`Binder` as its only parameter
::
Expand All @@ -540,7 +540,7 @@ def configure(binder):
binder.install(configure)
* instance of :class:`Module` (instance of it's subclass counts)
* instance of :class:`Module` (instance of its subclass counts)
::
Expand Down Expand Up @@ -1007,7 +1007,15 @@ def create_object(self, cls: Type[T], additional_kwargs: Any = None) -> T:
def call_with_injection(
self, callable: Callable[..., T], self_: Any = None, args: Any = (), kwargs: Any = {}
) -> T:
"""Call a callable and provide it's dependencies if needed.
"""Call a callable and provide its dependencies if needed.
Dependencies are provided when the callable is decorated with :func:`@inject <inject>`
or some individual parameters are wrapped in :data:`Inject` – otherwise
``call_with_injection()`` is equivalent to just calling the callable directly.
If there is an overlap between arguments provided in ``args`` and ``kwargs``
and injectable dependencies the provided values take precedence and no dependency
injection process will take place for the corresponding parameters.
:param self_: Instance of a class callable belongs to if it's a method,
None otherwise.
Expand Down Expand Up @@ -1121,10 +1129,6 @@ def get_bindings(callable: Callable) -> Dict[str, type]:
>>> get_bindings(function3)
{'a': <class 'int'>}
>>> import sys, pytest
>>> if sys.version_info < (3, 7, 0):
... pytest.skip('Python 3.7.0 required for sufficient Annotated support')
>>> # The simple case of no @inject but injection requested with Inject[...]
>>> def function4(a: Inject[int], b: str) -> None:
... pass
Expand Down
16 changes: 4 additions & 12 deletions injector_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1549,21 +1549,13 @@ def configure(binder):
assert injector.get(UserID) == 123


@pytest.mark.skipif(sys.version_info < (3, 6), reason="Requires Python 3.6+")
def test_dataclass_integration_works():
import dataclasses

# Python 3.6+-only syntax below
exec(
"""
@inject
@dataclasses.dataclass
class Data:
name: str
""",
locals(),
globals(),
)
@inject
@dataclasses.dataclass
class Data:
name: str

def configure(binder):
binder.bind(str, to='data')
Expand Down
14 changes: 14 additions & 0 deletions requirements-dev.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Our direct dependencies used in development/CI.
#
# We generate requirements-dev.txt from this file by running
#
# pip install -r requirements-dev.in && pip freeze > requirements-dev.txt
#
# and then modifying the file manually to restrict black and mypy to CPython

pytest
pytest-cov>=2.5.1
mypy;implementation_name=="cpython"
black;implementation_name=="cpython"
check-manifest
typing_extensions>=3.7.4;python_version<"3.9"
33 changes: 28 additions & 5 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
pytest
pytest-cov>=2.5.1
mypy;implementation_name=="cpython"
black;implementation_name=="cpython"
check-manifest
aiohttp==3.9.1
aiosignal==1.3.1
async-timeout==4.0.3
attrs==23.1.0
black==23.3.0;implementation_name=="cpython"
build==1.0.3
check-manifest==0.49
click==8.1.7
coverage==7.3.2
exceptiongroup==1.2.0
frozenlist==1.4.0
idna==3.6
importlib-metadata==7.0.0
iniconfig==2.0.0
multidict==6.0.4
mypy==1.7.1;implementation_name=="cpython"
mypy-extensions==1.0.0
packaging==23.2
pathspec==0.12.1
platformdirs==4.1.0
pluggy==1.3.0
pyproject-hooks==1.0.0
pytest==7.4.3
pytest-cov==4.1.0
tomli==2.0.1
typing-extensions==4.9.0
yarl==1.9.4
zipp==3.17.0
8 changes: 8 additions & 0 deletions requirements-docs.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# The documentation-specific development dependencies.
#
# We generate requirements-dev.txt from this file by running
#
# pip install -r requirements-docs.in && pip freeze > requirements-docs.txt
#
# and then modifying the file manually to restrict black and mypy to CPython
sphinx
24 changes: 24 additions & 0 deletions requirements-docs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
alabaster==0.7.13
Babel==2.14.0
certifi==2023.11.17
charset-normalizer==3.3.2
docutils==0.20.1
idna==3.6
imagesize==1.4.1
importlib-metadata==7.0.0
Jinja2==3.1.2
MarkupSafe==2.1.3
packaging==23.2
pygments==2.17.2
pytz==2023.3.post1
requests==2.31.0
snowballstemmer==2.2.0
sphinx==7.1.2
sphinxcontrib-applehelp==1.0.4
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
urllib3==2.1.0
zipp==3.17.0

0 comments on commit a440d70

Please sign in to comment.