diff --git a/.github/workflows/publishing.yml b/.github/workflows/publishing.yml index 24d39b46..5dc5c257 100644 --- a/.github/workflows/publishing.yml +++ b/.github/workflows/publishing.yml @@ -1,6 +1,7 @@ name: Publishing on: + workflow_dispatch: push: tags: - '*' @@ -9,28 +10,28 @@ jobs: tests: name: Run tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: 3.11 - run: pip install tox - run: tox env: - TOXENV: "3.10" + TOXENV: 3.11 linters: name: Run linters - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: toxenv: [flake8, pydocstyle, mypy, pylint] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: 3.11 - run: pip install tox - run: tox env: @@ -39,14 +40,14 @@ jobs: build-sdist: name: Build source tarball needs: [tests, linters] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: 3.11 - run: python setup.py sdist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: ./dist/* @@ -56,63 +57,60 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, windows-2019, macOS-10.15] + os: [ubuntu-22.04, windows-2019, macos-11] env: CIBW_SKIP: cp27-win* steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - run: pip install cibuildwheel==2.1.3 - - run: cibuildwheel --output-dir wheelhouse - - uses: actions/upload-artifact@v2 + - uses: actions/checkout@v3 + - name: Build wheels + uses: pypa/cibuildwheel@v2.11.3 + - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl build-wheels-linux-aarch64: - name: Build wheels (ubuntu-latest-aarch64) + name: Build wheels (ubuntu-22.04-aarch64) needs: [tests, linters] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - run: pip install cibuildwheel==2.1.3 - - run: cibuildwheel --archs aarch64 --output-dir wheelhouse - - uses: actions/upload-artifact@v2 + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v2 + - name: Build wheels + uses: pypa/cibuildwheel@v2.11.3 + env: + CIBW_ARCHS_LINUX: aarch64 + - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl publish: name: Publish on PyPI needs: [build-sdist, build-wheels, build-wheels-linux-aarch64] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@master + - uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} - # For publishing to Test PyPI, uncomment next two lines: - # password: ${{ secrets.TEST_PYPI_API_TOKEN }} - # repository_url: https://test.pypi.org/legacy/ +# For publishing to Test PyPI, uncomment next two lines: +# password: ${{ secrets.TEST_PYPI_API_TOKEN }} +# repository_url: https://test.pypi.org/legacy/ publish-docs: name: Publish docs needs: [publish] - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: 3.11 - run: pip install -r requirements-doc.txt - run: pip install awscli - run: pip install -e . diff --git a/.github/workflows/tests-and-linters.yml b/.github/workflows/tests-and-linters.yml index 213fb543..a983d1e6 100644 --- a/.github/workflows/tests-and-linters.yml +++ b/.github/workflows/tests-and-linters.yml @@ -4,15 +4,31 @@ on: [push, pull_request, workflow_dispatch] jobs: + tests-on-legacy-versions: + name: Run tests on legacy versions + runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, pypy2.7, pypy3.9] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - run: pip install tox + - run: tox + env: + TOXENV: ${{ matrix.python-version }} + test-on-different-versions: name: Run tests runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, "3.10", pypy2, pypy3] + python-version: [3.8, 3.9, "3.10", 3.11] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - run: pip install tox @@ -28,10 +44,10 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: 3.11 - run: pip install tox cython - run: make cythonize - run: tox @@ -45,10 +61,10 @@ jobs: matrix: toxenv: [flake8, pydocstyle, mypy, pylint] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: 3.11 - run: pip install tox - run: tox env: diff --git a/README.rst b/README.rst index 562d3ec1..546b6f50 100644 --- a/README.rst +++ b/README.rst @@ -35,7 +35,7 @@ :target: https://pypi.org/project/dependency-injector/ :alt: Wheel -.. image:: https://img.shields.io/github/workflow/status/ets-labs/python-dependency-injector/Tests%20and%20linters/master +.. image:: https://img.shields.io/github/actions/workflow/status/ets-labs/python-dependency-injector/tests-and-linters.yml?branch=master :target: https://github.com/ets-labs/python-dependency-injector/actions :alt: Build Status diff --git a/docs/index.rst b/docs/index.rst index 290a0f1f..93203043 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -50,7 +50,7 @@ Dependency Injector --- Dependency injection framework for Python :target: https://pypi.org/project/dependency-injector/ :alt: Wheel -.. image:: https://img.shields.io/github/workflow/status/ets-labs/python-dependency-injector/Tests%20and%20linters/master +.. image:: https://img.shields.io/github/actions/workflow/status/ets-labs/python-dependency-injector/tests-and-linters.yml?branch=master :target: https://github.com/ets-labs/python-dependency-injector/actions :alt: Build Status diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index c6ceb629..c7cc3306 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,22 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +4.41.0 +------ +- Add support of Python 3.11. +- Allow Closing to detect dependent resources `#633 `_, + `#636 `_. Thanks `Jamie Stumme @StummeJ `_ + for the contribution. +- Update CI/CD to use Ubuntu 22.04. +- Update CI/CD to ``actions/checkout@v3``, ``actions/setup-python@v4``, ``actions/upload-artifact@v3``, ``pypa/cibuildwheel@v2.11.3``, + and ``actions/download-artifact@v3``. +- Fix install crash on non-utf8 systems `#644 `_. +- Fix a bug in Windows build with default charset `#635 `_. +- Update FastAPI Redis example to use ``aioredis`` version 2 `#613 `_. +- Update documentation on creating custom providers `#598 `_. +- Regenerate C sources using Cython 0.29.32. +- Fix builds badge. + 4.40.0 ------ - Add ``Configuration.from_json()`` method to load configuration from a json file. diff --git a/docs/providers/custom.rst b/docs/providers/custom.rst index 7838529c..7c6e475a 100644 --- a/docs/providers/custom.rst +++ b/docs/providers/custom.rst @@ -16,10 +16,11 @@ To create a custom provider you need to follow these rules: 1. New provider class should inherit :py:class:`Provider`. 2. You need to implement the ``Provider._provide()`` method. 3. You need to implement the ``Provider.__deepcopy__()`` method. It should return an - equivalent copy of a provider. All providers must be copied with a ``deepcopy()`` function - from the ``providers`` module. After the a new provider object is created use - ``Provider._copy_overriding()`` method to copy all overriding providers. See the example - below. + equivalent copy of a provider. All providers must be copied with the ``deepcopy()`` function + from the ``providers`` module. It's essential to pass ``memo`` into ``deepcopy`` in order to keep + the preconfigured ``args`` and ``kwargs`` of stored providers. After the a new provider object + is created, use ``Provider._copy_overriding()`` method to copy all overriding providers. See the + example below. 4. If new provider has a ``__init__()`` method, it should call the parent ``Provider.__init__()``. 5. If new provider stores any other providers, these providers should be listed in @@ -33,7 +34,7 @@ To create a custom provider you need to follow these rules: .. note:: 1. Prefer delegation over inheritance. If you choose between inheriting a ``Factory`` or inheriting a ``Provider`` and use ``Factory`` internally - the last is better. - 2. When create a new provider follow the ``Factory``-like injections style. Consistency matters. + 2. When creating a new provider follow the ``Factory``-like injections style. Consistency matters. 3. Use the ``__slots__`` attribute to make sure nothing could be attached to your provider. You will also save some memory. diff --git a/examples/miniapps/fastapi-redis/Dockerfile b/examples/miniapps/fastapi-redis/Dockerfile index 932c3d6c..8b7ce3bc 100644 --- a/examples/miniapps/fastapi-redis/Dockerfile +++ b/examples/miniapps/fastapi-redis/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9-buster +FROM python:3.10-buster ENV PYTHONUNBUFFERED=1 diff --git a/examples/miniapps/fastapi-redis/README.rst b/examples/miniapps/fastapi-redis/README.rst index 931cfbed..1ef75b31 100644 --- a/examples/miniapps/fastapi-redis/README.rst +++ b/examples/miniapps/fastapi-redis/README.rst @@ -24,35 +24,26 @@ The output should be something like: .. code-block:: - redis_1 | 1:C 04 Jan 2022 02:42:14.115 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo - redis_1 | 1:C 04 Jan 2022 02:42:14.115 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=1, just started - redis_1 | 1:C 04 Jan 2022 02:42:14.115 # Configuration loaded - redis_1 | 1:M 04 Jan 2022 02:42:14.116 * Running mode=standalone, port=6379. - redis_1 | 1:M 04 Jan 2022 02:42:14.116 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. - redis_1 | 1:M 04 Jan 2022 02:42:14.116 # Server initialized - redis_1 | 1:M 04 Jan 2022 02:42:14.117 * Loading RDB produced by version 6.0.9 - redis_1 | 1:M 04 Jan 2022 02:42:14.117 * RDB age 1 seconds - redis_1 | 1:M 04 Jan 2022 02:42:14.117 * RDB memory usage when created 0.77 Mb - redis_1 | 1:M 04 Jan 2022 02:42:14.117 * DB loaded from disk: 0.000 seconds - redis_1 | 1:M 04 Jan 2022 02:42:14.117 * Ready to accept connections - redis_1 | 1:signal-handler (1609728137) Received SIGTERM scheduling shutdown... - redis_1 | 1:M 04 Jan 2022 02:42:17.984 # User requested shutdown... - redis_1 | 1:M 04 Jan 2022 02:42:17.984 # Redis is now ready to exit, bye bye... - redis_1 | 1:C 04 Jan 2022 02:42:22.035 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo - redis_1 | 1:C 04 Jan 2022 02:42:22.035 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=1, just started - redis_1 | 1:C 04 Jan 2022 02:42:22.035 # Configuration loaded - redis_1 | 1:M 04 Jan 2022 02:42:22.037 * Running mode=standalone, port=6379. - redis_1 | 1:M 04 Jan 2022 02:42:22.037 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. - redis_1 | 1:M 04 Jan 2022 02:42:22.037 # Server initialized - redis_1 | 1:M 04 Jan 2022 02:42:22.037 * Loading RDB produced by version 6.0.9 - redis_1 | 1:M 04 Jan 2022 02:42:22.037 * RDB age 9 seconds - redis_1 | 1:M 04 Jan 2022 02:42:22.037 * RDB memory usage when created 0.77 Mb - redis_1 | 1:M 04 Jan 2022 02:42:22.037 * DB loaded from disk: 0.000 seconds - redis_1 | 1:M 04 Jan 2022 02:42:22.037 * Ready to accept connections - example_1 | INFO: Started server process [1] - example_1 | INFO: Waiting for application startup. - example_1 | INFO: Application startup complete. - example_1 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) + fastapi-redis-redis-1 | 1:C 19 Dec 2022 02:33:02.484 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo + fastapi-redis-redis-1 | 1:C 19 Dec 2022 02:33:02.484 # Redis version=7.0.5, bits=64, commit=00000000, modified=0, pid=1, just started + fastapi-redis-redis-1 | 1:C 19 Dec 2022 02:33:02.484 # Configuration loaded + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.485 * monotonic clock: POSIX clock_gettime + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.485 * Running mode=standalone, port=6379. + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.485 # Server initialized + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.487 * Loading RDB produced by version 7.0.5 + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.487 * RDB age 58 seconds + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.487 * RDB memory usage when created 0.85 Mb + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.487 * Done loading RDB, keys loaded: 0, keys expired: 0. + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.487 * DB loaded from disk: 0.000 seconds + fastapi-redis-redis-1 | 1:M 19 Dec 2022 02:33:02.488 * Ready to accept connections + fastapi-redis-example-1 | INFO: Started server process [1] + fastapi-redis-example-1 | INFO: Waiting for application startup. + fastapi-redis-example-1 | INFO: Application startup complete. + fastapi-redis-example-1 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) + fastapi-redis-example-1 | INFO: 172.18.0.1:63998 - "GET / HTTP/1.1" 200 OK + fastapi-redis-example-1 | INFO: 172.18.0.1:63998 - "GET /favicon.ico HTTP/1.1" 404 Not Found + fastapi-redis-example-1 | INFO: 172.18.0.1:63998 - "GET / HTTP/1.1" 200 OK + fastapi-redis-example-1 | INFO: 172.18.0.1:63998 - "GET / HTTP/1.1" 200 OK Test ---- @@ -69,9 +60,9 @@ The output should be something like: .. code-block:: - platform linux -- Python 3.9, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 + platform linux -- Python 3.10.9, pytest-7.2.0, pluggy-1.0.0 rootdir: /code - plugins: cov-2.10.1, asyncio-0.14.0 + plugins: cov-4.0.0, asyncio-0.20.3 collected 1 item fastapiredis/tests.py . [100%] @@ -80,10 +71,10 @@ The output should be something like: Name Stmts Miss Cover ------------------------------------------------- fastapiredis/__init__.py 0 0 100% - fastapiredis/application.py 15 0 100% + fastapiredis/application.py 14 0 100% fastapiredis/containers.py 6 0 100% fastapiredis/redis.py 7 4 43% fastapiredis/services.py 7 3 57% fastapiredis/tests.py 18 0 100% ------------------------------------------------- - TOTAL 53 7 87% + TOTAL 52 7 87% \ No newline at end of file diff --git a/examples/miniapps/fastapi-redis/fastapiredis/redis.py b/examples/miniapps/fastapi-redis/fastapiredis/redis.py index 3c5f17f9..e770906c 100644 --- a/examples/miniapps/fastapi-redis/fastapiredis/redis.py +++ b/examples/miniapps/fastapi-redis/fastapiredis/redis.py @@ -1,12 +1,10 @@ -"""Redis client module.""" - from typing import AsyncIterator -from aioredis import create_redis_pool, Redis +from aioredis import from_url, Redis async def init_redis_pool(host: str, password: str) -> AsyncIterator[Redis]: - pool = await create_redis_pool(f"redis://{host}", password=password) - yield pool - pool.close() - await pool.wait_closed() + session = from_url(f"redis://{host}", password=password, encoding="utf-8", decode_responses=True) + yield session + session.close() + await session.wait_closed() diff --git a/examples/miniapps/fastapi-redis/fastapiredis/services.py b/examples/miniapps/fastapi-redis/fastapiredis/services.py index 9a202c74..0cae0731 100644 --- a/examples/miniapps/fastapi-redis/fastapiredis/services.py +++ b/examples/miniapps/fastapi-redis/fastapiredis/services.py @@ -9,4 +9,4 @@ class Service: async def process(self) -> str: await self._redis.set("my-key", "value") - return await self._redis.get("my-key", encoding="utf-8") + return await self._redis.get("my-key") diff --git a/examples/miniapps/fastapi-redis/requirements.txt b/examples/miniapps/fastapi-redis/requirements.txt index 9a144a8d..c217324a 100644 --- a/examples/miniapps/fastapi-redis/requirements.txt +++ b/examples/miniapps/fastapi-redis/requirements.txt @@ -1,7 +1,7 @@ dependency-injector fastapi uvicorn -aioredis<2 # TODO: Update example to work with aioredis >= 2.0 +aioredis # For testing: pytest diff --git a/requirements-dev.txt b/requirements-dev.txt index a26319c2..2c101e8c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -cython==0.29.30 +cython==0.29.32 pytest pytest-asyncio tox diff --git a/setup.cfg b/setup.cfg index 768a8321..cb18dacf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [flake8] -max_line_length = 100 +max_line_length = 120 max_complexity = 10 exclude = types.py per-file-ignores = diff --git a/setup.py b/setup.py index 82c8e8ff..3edd5c08 100644 --- a/setup.py +++ b/setup.py @@ -2,24 +2,31 @@ import os import re +import sys from setuptools import setup, Extension +def _open(filename): + if sys.version_info[0] == 2: + return open(filename) + return open(filename, encoding="utf-8") + + # Defining setup variables: defined_macros = dict() defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 0 # Getting description: -with open("README.rst") as readme_file: +with _open("README.rst") as readme_file: description = readme_file.read() # Getting requirements: -with open("requirements.txt") as requirements_file: +with _open("requirements.txt") as requirements_file: requirements = requirements_file.readlines() # Getting version: -with open("src/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: @@ -106,6 +113,7 @@ setup(name="dependency-injector", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: AsyncIO", diff --git a/src/dependency_injector/__init__.py b/src/dependency_injector/__init__.py index 6f8c1f80..235b6f80 100644 --- a/src/dependency_injector/__init__.py +++ b/src/dependency_injector/__init__.py @@ -1,6 +1,6 @@ """Top-level package.""" -__version__ = "4.40.0" +__version__ = "4.41.0" """Version number. :type: str diff --git a/src/dependency_injector/_cwiring.c b/src/dependency_injector/_cwiring.c index b1e425c2..b6bd2238 100644 --- a/src/dependency_injector/_cwiring.c +++ b/src/dependency_injector/_cwiring.c @@ -1,4 +1,4 @@ -/* Generated by Cython 0.29.30 */ +/* Generated by Cython 0.29.32 */ #ifndef PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN @@ -9,8 +9,8 @@ #elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000) #error Cython requires Python 2.6+ or Python 3.3+. #else -#define CYTHON_ABI "0_29_30" -#define CYTHON_HEX_VERSION 0x001D1EF0 +#define CYTHON_ABI "0_29_32" +#define CYTHON_HEX_VERSION 0x001D20F0 #define CYTHON_FUTURE_DIVISION 0 #include #ifndef offsetof @@ -49,6 +49,7 @@ #define CYTHON_COMPILING_IN_PYPY 1 #define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 0 #undef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 0 #undef CYTHON_USE_PYTYPE_LOOKUP @@ -92,6 +93,7 @@ #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYSTON 1 #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 0 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif @@ -132,10 +134,56 @@ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif +#elif defined(PY_NOGIL) + #define CYTHON_COMPILING_IN_PYPY 0 + #define CYTHON_COMPILING_IN_PYSTON 0 + #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 1 + #ifndef CYTHON_USE_TYPE_SLOTS + #define CYTHON_USE_TYPE_SLOTS 1 + #endif + #undef CYTHON_USE_PYTYPE_LOOKUP + #define CYTHON_USE_PYTYPE_LOOKUP 0 + #ifndef CYTHON_USE_ASYNC_SLOTS + #define CYTHON_USE_ASYNC_SLOTS 1 + #endif + #undef CYTHON_USE_PYLIST_INTERNALS + #define CYTHON_USE_PYLIST_INTERNALS 0 + #ifndef CYTHON_USE_UNICODE_INTERNALS + #define CYTHON_USE_UNICODE_INTERNALS 1 + #endif + #undef CYTHON_USE_UNICODE_WRITER + #define CYTHON_USE_UNICODE_WRITER 0 + #undef CYTHON_USE_PYLONG_INTERNALS + #define CYTHON_USE_PYLONG_INTERNALS 0 + #ifndef CYTHON_AVOID_BORROWED_REFS + #define CYTHON_AVOID_BORROWED_REFS 0 + #endif + #ifndef CYTHON_ASSUME_SAFE_MACROS + #define CYTHON_ASSUME_SAFE_MACROS 1 + #endif + #ifndef CYTHON_UNPACK_METHODS + #define CYTHON_UNPACK_METHODS 1 + #endif + #undef CYTHON_FAST_THREAD_STATE + #define CYTHON_FAST_THREAD_STATE 0 + #undef CYTHON_FAST_PYCALL + #define CYTHON_FAST_PYCALL 0 + #ifndef CYTHON_PEP489_MULTI_PHASE_INIT + #define CYTHON_PEP489_MULTI_PHASE_INIT 1 + #endif + #ifndef CYTHON_USE_TP_FINALIZE + #define CYTHON_USE_TP_FINALIZE 1 + #endif + #undef CYTHON_USE_DICT_VERSIONS + #define CYTHON_USE_DICT_VERSIONS 0 + #undef CYTHON_USE_EXC_INFO_STACK + #define CYTHON_USE_EXC_INFO_STACK 0 #else #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_CPYTHON 1 + #define CYTHON_COMPILING_IN_NOGIL 0 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif diff --git a/src/dependency_injector/containers.c b/src/dependency_injector/containers.c index 82286d59..6a3d9ec8 100644 --- a/src/dependency_injector/containers.c +++ b/src/dependency_injector/containers.c @@ -1,4 +1,4 @@ -/* Generated by Cython 0.29.30 */ +/* Generated by Cython 0.29.32 */ #ifndef PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN @@ -9,8 +9,8 @@ #elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000) #error Cython requires Python 2.6+ or Python 3.3+. #else -#define CYTHON_ABI "0_29_30" -#define CYTHON_HEX_VERSION 0x001D1EF0 +#define CYTHON_ABI "0_29_32" +#define CYTHON_HEX_VERSION 0x001D20F0 #define CYTHON_FUTURE_DIVISION 0 #include #ifndef offsetof @@ -49,6 +49,7 @@ #define CYTHON_COMPILING_IN_PYPY 1 #define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 0 #undef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 0 #undef CYTHON_USE_PYTYPE_LOOKUP @@ -92,6 +93,7 @@ #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYSTON 1 #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 0 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif @@ -132,10 +134,56 @@ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif +#elif defined(PY_NOGIL) + #define CYTHON_COMPILING_IN_PYPY 0 + #define CYTHON_COMPILING_IN_PYSTON 0 + #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 1 + #ifndef CYTHON_USE_TYPE_SLOTS + #define CYTHON_USE_TYPE_SLOTS 1 + #endif + #undef CYTHON_USE_PYTYPE_LOOKUP + #define CYTHON_USE_PYTYPE_LOOKUP 0 + #ifndef CYTHON_USE_ASYNC_SLOTS + #define CYTHON_USE_ASYNC_SLOTS 1 + #endif + #undef CYTHON_USE_PYLIST_INTERNALS + #define CYTHON_USE_PYLIST_INTERNALS 0 + #ifndef CYTHON_USE_UNICODE_INTERNALS + #define CYTHON_USE_UNICODE_INTERNALS 1 + #endif + #undef CYTHON_USE_UNICODE_WRITER + #define CYTHON_USE_UNICODE_WRITER 0 + #undef CYTHON_USE_PYLONG_INTERNALS + #define CYTHON_USE_PYLONG_INTERNALS 0 + #ifndef CYTHON_AVOID_BORROWED_REFS + #define CYTHON_AVOID_BORROWED_REFS 0 + #endif + #ifndef CYTHON_ASSUME_SAFE_MACROS + #define CYTHON_ASSUME_SAFE_MACROS 1 + #endif + #ifndef CYTHON_UNPACK_METHODS + #define CYTHON_UNPACK_METHODS 1 + #endif + #undef CYTHON_FAST_THREAD_STATE + #define CYTHON_FAST_THREAD_STATE 0 + #undef CYTHON_FAST_PYCALL + #define CYTHON_FAST_PYCALL 0 + #ifndef CYTHON_PEP489_MULTI_PHASE_INIT + #define CYTHON_PEP489_MULTI_PHASE_INIT 1 + #endif + #ifndef CYTHON_USE_TP_FINALIZE + #define CYTHON_USE_TP_FINALIZE 1 + #endif + #undef CYTHON_USE_DICT_VERSIONS + #define CYTHON_USE_DICT_VERSIONS 0 + #undef CYTHON_USE_EXC_INFO_STACK + #define CYTHON_USE_EXC_INFO_STACK 0 #else #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_CPYTHON 1 + #define CYTHON_COMPILING_IN_NOGIL 0 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif diff --git a/src/dependency_injector/providers.c b/src/dependency_injector/providers.c index cfb99f95..13536b62 100644 --- a/src/dependency_injector/providers.c +++ b/src/dependency_injector/providers.c @@ -1,4 +1,4 @@ -/* Generated by Cython 0.29.30 */ +/* Generated by Cython 0.29.32 */ #ifndef PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN @@ -9,8 +9,8 @@ #elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000) #error Cython requires Python 2.6+ or Python 3.3+. #else -#define CYTHON_ABI "0_29_30" -#define CYTHON_HEX_VERSION 0x001D1EF0 +#define CYTHON_ABI "0_29_32" +#define CYTHON_HEX_VERSION 0x001D20F0 #define CYTHON_FUTURE_DIVISION 0 #include #ifndef offsetof @@ -49,6 +49,7 @@ #define CYTHON_COMPILING_IN_PYPY 1 #define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 0 #undef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 0 #undef CYTHON_USE_PYTYPE_LOOKUP @@ -92,6 +93,7 @@ #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYSTON 1 #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 0 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif @@ -132,10 +134,56 @@ #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 #endif +#elif defined(PY_NOGIL) + #define CYTHON_COMPILING_IN_PYPY 0 + #define CYTHON_COMPILING_IN_PYSTON 0 + #define CYTHON_COMPILING_IN_CPYTHON 0 + #define CYTHON_COMPILING_IN_NOGIL 1 + #ifndef CYTHON_USE_TYPE_SLOTS + #define CYTHON_USE_TYPE_SLOTS 1 + #endif + #undef CYTHON_USE_PYTYPE_LOOKUP + #define CYTHON_USE_PYTYPE_LOOKUP 0 + #ifndef CYTHON_USE_ASYNC_SLOTS + #define CYTHON_USE_ASYNC_SLOTS 1 + #endif + #undef CYTHON_USE_PYLIST_INTERNALS + #define CYTHON_USE_PYLIST_INTERNALS 0 + #ifndef CYTHON_USE_UNICODE_INTERNALS + #define CYTHON_USE_UNICODE_INTERNALS 1 + #endif + #undef CYTHON_USE_UNICODE_WRITER + #define CYTHON_USE_UNICODE_WRITER 0 + #undef CYTHON_USE_PYLONG_INTERNALS + #define CYTHON_USE_PYLONG_INTERNALS 0 + #ifndef CYTHON_AVOID_BORROWED_REFS + #define CYTHON_AVOID_BORROWED_REFS 0 + #endif + #ifndef CYTHON_ASSUME_SAFE_MACROS + #define CYTHON_ASSUME_SAFE_MACROS 1 + #endif + #ifndef CYTHON_UNPACK_METHODS + #define CYTHON_UNPACK_METHODS 1 + #endif + #undef CYTHON_FAST_THREAD_STATE + #define CYTHON_FAST_THREAD_STATE 0 + #undef CYTHON_FAST_PYCALL + #define CYTHON_FAST_PYCALL 0 + #ifndef CYTHON_PEP489_MULTI_PHASE_INIT + #define CYTHON_PEP489_MULTI_PHASE_INIT 1 + #endif + #ifndef CYTHON_USE_TP_FINALIZE + #define CYTHON_USE_TP_FINALIZE 1 + #endif + #undef CYTHON_USE_DICT_VERSIONS + #define CYTHON_USE_DICT_VERSIONS 0 + #undef CYTHON_USE_EXC_INFO_STACK + #define CYTHON_USE_EXC_INFO_STACK 0 #else #define CYTHON_COMPILING_IN_PYPY 0 #define CYTHON_COMPILING_IN_PYSTON 0 #define CYTHON_COMPILING_IN_CPYTHON 1 + #define CYTHON_COMPILING_IN_NOGIL 0 #ifndef CYTHON_USE_TYPE_SLOTS #define CYTHON_USE_TYPE_SLOTS 1 #endif diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index 49f9edb3..b1f01622 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -593,6 +593,22 @@ def _fetch_reference_injections( # noqa: C901 return injections, closing +def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, providers.Provider]: + if not hasattr(provider, "args"): + return {} + + closing_deps = {} + for arg in provider.args: + if not isinstance(arg, providers.Provider) or not hasattr(arg, "args"): + continue + + if not arg.args and isinstance(arg, providers.Resource): + return {str(id(arg)): arg} + else: + closing_deps += _locate_dependent_closing_args(arg) + return closing_deps + + def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None: patched_callable = _patched_registry.get_callable(fn) if patched_callable is None: @@ -614,6 +630,9 @@ def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> Non if injection in patched_callable.reference_closing: patched_callable.add_closing(injection, provider) + deps = _locate_dependent_closing_args(provider) + for key, dep in deps.items(): + patched_callable.add_closing(key, dep) def _unbind_injections(fn: Callable[..., Any]) -> None: diff --git a/tests/typing/resource.py b/tests/typing/resource.py index e70ae67a..113ced5a 100644 --- a/tests/typing/resource.py +++ b/tests/typing/resource.py @@ -45,7 +45,7 @@ var4: List[int] = provider4() # Test 5: to check the return type with async function async def init5() -> List[int]: - ... + return [] provider5 = providers.Resource(init5) diff --git a/tests/unit/providers/coroutines/test_abstract_coroutine_py35.py b/tests/unit/providers/coroutines/test_abstract_coroutine_py35.py index 4f098442..d1a760f8 100644 --- a/tests/unit/providers/coroutines/test_abstract_coroutine_py35.py +++ b/tests/unit/providers/coroutines/test_abstract_coroutine_py35.py @@ -1,6 +1,7 @@ """AbstractCoroutine provider tests.""" import asyncio +import sys from dependency_injector import providers, errors from pytest import mark, raises @@ -12,6 +13,7 @@ def test_inheritance(): assert isinstance(providers.AbstractCoroutine(example), providers.Coroutine) +@mark.skipif(sys.version_info >= (3, 11), reason="asyncio.coroutine removed in Python 3.11") @mark.asyncio @mark.filterwarnings("ignore") async def test_call_overridden_by_coroutine(): @@ -26,6 +28,7 @@ async def test_call_overridden_by_coroutine(): assert result == (1, 2, 3, 4) +@mark.skipif(sys.version_info >= (3, 11), reason="asyncio.coroutine removed in Python 3.11") @mark.asyncio @mark.filterwarnings("ignore") async def test_call_overridden_by_delegated_coroutine(): diff --git a/tests/unit/samples/wiringstringids/resourceclosing.py b/tests/unit/samples/wiringstringids/resourceclosing.py index 2faf0efd..6360e15c 100644 --- a/tests/unit/samples/wiringstringids/resourceclosing.py +++ b/tests/unit/samples/wiringstringids/resourceclosing.py @@ -20,6 +20,11 @@ class Service: cls.shutdown_counter += 1 +class FactoryService: + def __init__(self, service: Service): + self.service = service + + def init_service(): service = Service() service.init() @@ -30,8 +35,14 @@ def init_service(): class Container(containers.DeclarativeContainer): service = providers.Resource(init_service) + factory_service = providers.Factory(FactoryService, service) @inject def test_function(service: Service = Closing[Provide["service"]]): return service + + +@inject +def test_function_dependency(factory: FactoryService = Closing[Provide["factory_service"]]): + return factory diff --git a/tests/unit/wiring/string_ids/test_main_py36.py b/tests/unit/wiring/string_ids/test_main_py36.py index 4c8f2e55..d4c49fe8 100644 --- a/tests/unit/wiring/string_ids/test_main_py36.py +++ b/tests/unit/wiring/string_ids/test_main_py36.py @@ -289,6 +289,23 @@ def test_closing_resource(): assert result_1 is not result_2 +@mark.usefixtures("resourceclosing_container") +def test_closing_dependency_resource(): + resourceclosing.Service.reset_counter() + + result_1 = resourceclosing.test_function_dependency() + assert isinstance(result_1, resourceclosing.FactoryService) + assert result_1.service.init_counter == 1 + assert result_1.service.shutdown_counter == 1 + + result_2 = resourceclosing.test_function_dependency() + assert isinstance(result_2, resourceclosing.FactoryService) + assert result_2.service.init_counter == 2 + assert result_2.service.shutdown_counter == 2 + + assert result_1 is not result_2 + + @mark.usefixtures("resourceclosing_container") def test_closing_resource_bypass_marker_injection(): resourceclosing.Service.reset_counter() diff --git a/tox.ini b/tox.ini index 726e77bc..f436345c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist= - coveralls, pylint, flake8, pydocstyle, 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, pypy2, pypy3 + coveralls, pylint, flake8, pydocstyle, 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, pypy2.7, pypy3.9 [testenv] deps= @@ -23,8 +23,8 @@ commands = pytest -c tests/.configs/pytest.ini python_files = test_*_py3*.py [testenv:coveralls] -passenv = GITHUB_* COVERALLS_* -basepython=python3.10 +passenv = GITHUB_*, COVERALLS_* +basepython=python3.11 usedevelop=True deps= {[testenv]deps} @@ -55,7 +55,7 @@ extras= flask commands = pytest -c tests/.configs/pytest-py35.ini -[testenv:pypy2] +[testenv:pypy2.7] deps= pytest extras= @@ -63,7 +63,7 @@ extras= flask commands = pytest -c tests/.configs/pytest-py27.ini -[testenv:pypy3] +[testenv:pypy3.9] deps= pytest pytest-asyncio