fastapi-injected 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "03:00"
8
+ open-pull-requests-limit: 10
9
+ assignees:
10
+ - "uriyyo"
11
+ - package-ecosystem: github-actions
12
+ directory: "/"
13
+ schedule:
14
+ interval: daily
@@ -0,0 +1,20 @@
1
+ name: Dependabot auto-merge
2
+
3
+ on:
4
+ pull_request:
5
+ workflow_dispatch:
6
+
7
+ permissions:
8
+ contents: write
9
+ pull-requests: write
10
+
11
+ jobs:
12
+ dependabot:
13
+ runs-on: ubuntu-latest
14
+ if: ${{ github.actor == 'dependabot[bot]' }}
15
+ steps:
16
+ - name: Enable auto-merge for Dependabot PRs
17
+ run: gh pr merge --auto --merge "$PR_URL"
18
+ env:
19
+ PR_URL: ${{github.event.pull_request.html_url}}
20
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,35 @@
1
+ name: Lint
2
+
3
+ on:
4
+ pull_request:
5
+ types:
6
+ - "opened"
7
+ - "synchronize"
8
+ push:
9
+ branches:
10
+ - "main"
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.12"]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v7
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ enable-cache: true
27
+ cache-dependency-glob: "uv.lock"
28
+
29
+ - name: Install dependencies
30
+ run: |
31
+ uv sync --all-extras --dev
32
+
33
+ - name: Lint
34
+ run: |
35
+ uv run pre-commit run --all-files --show-diff-on-failure
@@ -0,0 +1,33 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.12"]
13
+
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v7
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ enable-cache: true
22
+ cache-dependency-glob: "uv.lock"
23
+
24
+ - name: Build
25
+ run: |
26
+ uv build
27
+
28
+ - name: Publish
29
+ env:
30
+ PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
31
+ PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
32
+ run: |
33
+ uv publish -u "${{ env.PYPI_USERNAME }}" -p "${{ env.PYPI_PASSWORD }}"
@@ -0,0 +1,41 @@
1
+ name: Test
2
+
3
+ on:
4
+ pull_request:
5
+ types:
6
+ - "opened"
7
+ - "synchronize"
8
+ push:
9
+ branches:
10
+ - "main"
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.12", "3.13", "3.14"]
19
+
20
+ steps:
21
+ - uses: actions/checkout@v7
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v7
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+ enable-cache: true
28
+ cache-dependency-glob: "uv.lock"
29
+
30
+ - name: Install dependencies
31
+ run: |
32
+ uv sync --all-extras --dev
33
+
34
+ - name: Unit tests
35
+ run: |
36
+ uv run pytest tests --cov=fastapi_filters --cov-report=term-missing --cov-report=xml
37
+
38
+ - name: Upload coverage to Codecov
39
+ uses: codecov/codecov-action@v7
40
+ with:
41
+ file: ./coverage.xml
@@ -0,0 +1,139 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # test dbs
65
+ *.db
66
+ test_db.sqlite
67
+
68
+ # Flask stuff:
69
+ instance/
70
+ .webassets-cache
71
+
72
+ # Scrapy stuff:
73
+ .scrapy
74
+
75
+ # Sphinx documentation
76
+ docs/_build/
77
+
78
+ # PyBuilder
79
+ target/
80
+
81
+ # Jupyter Notebook
82
+ .ipynb_checkpoints
83
+
84
+ # IPython
85
+ profile_default/
86
+ ipython_config.py
87
+
88
+ # pyenv
89
+ .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
99
+ __pypackages__/
100
+
101
+ # Celery stuff
102
+ celerybeat-schedule
103
+ celerybeat.pid
104
+
105
+ # SageMath parsed files
106
+ *.sage.py
107
+
108
+ # Environments
109
+ .env
110
+ .venv
111
+ env/
112
+ venv/
113
+ ENV/
114
+ env.bak/
115
+ venv.bak/
116
+
117
+ # Spyder project settings
118
+ .spyderproject
119
+ .spyproject
120
+
121
+ # Rope project settings
122
+ .ropeproject
123
+
124
+ # mkdocs documentation
125
+ /site
126
+
127
+ # mypy
128
+ .mypy_cache/
129
+ .dmypy.json
130
+ dmypy.json
131
+
132
+ # Pyre type checker
133
+ .pyre/
134
+
135
+ # IDEA
136
+ .idea/
137
+
138
+ # ruff
139
+ .ruff_cache/
@@ -0,0 +1,26 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-format
5
+ language: python
6
+ name: ruff-format
7
+ pass_filenames: false
8
+ language_version: python3.12
9
+ entry: uv run ruff format fastapi_injected tests
10
+
11
+ - repo: local
12
+ hooks:
13
+ - id: ruff
14
+ language: python
15
+ name: ruff
16
+ pass_filenames: false
17
+ language_version: python3.12
18
+ entry: uv run ruff check --fix --exit-non-zero-on-fix --show-fixes fastapi_injected tests
19
+
20
+ - repo: local
21
+ hooks:
22
+ - id: ty
23
+ language: python
24
+ name: ty
25
+ pass_filenames: false
26
+ entry: uv run ty check fastapi_injected tests/test_typing.py --error-on-warning
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yurii Karabas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-injected
3
+ Version: 0.1.0
4
+ Summary: Yet another library to reuse fastapi dependency injection
5
+ Project-URL: Repository, https://github.com/uriyyo/fastapi-injected
6
+ Author-email: Yurii Karabas <1998uriyyo@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: fastapi>=0.139.2
18
+ Description-Content-Type: text/markdown
19
+
20
+ # fastapi-injected
21
+
22
+ Yet another attempt to reuse FastAPI's dependency injection outside of request handlers.
23
+
24
+ This is an opinionated library: it takes the DI machinery you already know from FastAPI (`Depends`, generator dependencies with teardown, dependency caching) and makes it usable in plain async functions — background jobs, CLI commands, workers, scripts — without a `Request` in sight.
25
+
26
+ ## Installation
27
+
28
+ ```sh
29
+ pip install fastapi-injected
30
+ ```
31
+
32
+ Requires Python 3.12+.
33
+
34
+ ## Usage
35
+
36
+ Declare dependencies as regular classes and annotate fields with `Dep[...]`:
37
+
38
+ ```python
39
+ from dataclasses import dataclass
40
+ from typing import AsyncIterator
41
+
42
+ from fastapi_injected import Dep, DepFactory, Inejected, inject
43
+
44
+
45
+ @dataclass
46
+ class Session:
47
+ closed: bool = False
48
+
49
+
50
+ async def session_dep() -> AsyncIterator[Session]:
51
+ session = Session()
52
+ try:
53
+ yield session
54
+ finally:
55
+ session.closed = True
56
+
57
+
58
+ @dataclass
59
+ class Repository:
60
+ session: DepFactory[Session, session_dep]
61
+
62
+
63
+ @dataclass
64
+ class Service:
65
+ repo: Dep[Repository]
66
+
67
+
68
+ @inject
69
+ async def handler(*, service: Dep[Service] = Inejected) -> None:
70
+ ... # service is built and injected, session is closed on exit
71
+
72
+
73
+ await handler()
74
+ ```
75
+
76
+ - `Dep[T]` — resolve `T` by calling it, same as FastAPI's `Annotated[T, Depends()]`.
77
+ - `DepFactory[T, factory]` — resolve `T` via a factory, same as `Annotated[T, Depends(factory)]`. Generator factories get proper teardown.
78
+ - `Inejected` — a sentinel default that exists purely to make type checkers happy: without it they would complain about a missing argument at call sites. At runtime the parameter is always filled in by `@inject`.
79
+
80
+ Injected parameters mix freely with regular ones — pass your own arguments as usual and the rest is injected:
81
+
82
+ ```python
83
+ @inject
84
+ async def add(a: int, b: int, *, service: Dep[Service] = Inejected) -> int:
85
+ ...
86
+
87
+
88
+ result = await add(1, 2)
89
+ ```
90
+
91
+ ### Solving a type directly
92
+
93
+ No decorator needed — resolve a dependency graph on demand:
94
+
95
+ ```python
96
+ from fastapi_injected import solve
97
+
98
+ service = await solve(Service)
99
+ ```
100
+
101
+ ### Scopes and caching
102
+
103
+ By default every call to an injected function gets its own scope: dependencies are built, cached within the call, and torn down when it returns. Wrap several calls in `push_inject_scope()` to share one cache (and defer teardown to the end of the scope):
104
+
105
+ ```python
106
+ from fastapi_injected import push_inject_scope
107
+
108
+ async with push_inject_scope():
109
+ a = await handler() # dependencies built here
110
+ b = await handler() # same instances reused
111
+ # generator dependencies are torn down here
112
+ ```
113
+
114
+ Use `@inject(new_scope=True)` to opt a function out of the surrounding scope and always get fresh dependencies.
115
+
116
+ ## License
117
+
118
+ MIT
@@ -0,0 +1,99 @@
1
+ # fastapi-injected
2
+
3
+ Yet another attempt to reuse FastAPI's dependency injection outside of request handlers.
4
+
5
+ This is an opinionated library: it takes the DI machinery you already know from FastAPI (`Depends`, generator dependencies with teardown, dependency caching) and makes it usable in plain async functions — background jobs, CLI commands, workers, scripts — without a `Request` in sight.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pip install fastapi-injected
11
+ ```
12
+
13
+ Requires Python 3.12+.
14
+
15
+ ## Usage
16
+
17
+ Declare dependencies as regular classes and annotate fields with `Dep[...]`:
18
+
19
+ ```python
20
+ from dataclasses import dataclass
21
+ from typing import AsyncIterator
22
+
23
+ from fastapi_injected import Dep, DepFactory, Inejected, inject
24
+
25
+
26
+ @dataclass
27
+ class Session:
28
+ closed: bool = False
29
+
30
+
31
+ async def session_dep() -> AsyncIterator[Session]:
32
+ session = Session()
33
+ try:
34
+ yield session
35
+ finally:
36
+ session.closed = True
37
+
38
+
39
+ @dataclass
40
+ class Repository:
41
+ session: DepFactory[Session, session_dep]
42
+
43
+
44
+ @dataclass
45
+ class Service:
46
+ repo: Dep[Repository]
47
+
48
+
49
+ @inject
50
+ async def handler(*, service: Dep[Service] = Inejected) -> None:
51
+ ... # service is built and injected, session is closed on exit
52
+
53
+
54
+ await handler()
55
+ ```
56
+
57
+ - `Dep[T]` — resolve `T` by calling it, same as FastAPI's `Annotated[T, Depends()]`.
58
+ - `DepFactory[T, factory]` — resolve `T` via a factory, same as `Annotated[T, Depends(factory)]`. Generator factories get proper teardown.
59
+ - `Inejected` — a sentinel default that exists purely to make type checkers happy: without it they would complain about a missing argument at call sites. At runtime the parameter is always filled in by `@inject`.
60
+
61
+ Injected parameters mix freely with regular ones — pass your own arguments as usual and the rest is injected:
62
+
63
+ ```python
64
+ @inject
65
+ async def add(a: int, b: int, *, service: Dep[Service] = Inejected) -> int:
66
+ ...
67
+
68
+
69
+ result = await add(1, 2)
70
+ ```
71
+
72
+ ### Solving a type directly
73
+
74
+ No decorator needed — resolve a dependency graph on demand:
75
+
76
+ ```python
77
+ from fastapi_injected import solve
78
+
79
+ service = await solve(Service)
80
+ ```
81
+
82
+ ### Scopes and caching
83
+
84
+ By default every call to an injected function gets its own scope: dependencies are built, cached within the call, and torn down when it returns. Wrap several calls in `push_inject_scope()` to share one cache (and defer teardown to the end of the scope):
85
+
86
+ ```python
87
+ from fastapi_injected import push_inject_scope
88
+
89
+ async with push_inject_scope():
90
+ a = await handler() # dependencies built here
91
+ b = await handler() # same instances reused
92
+ # generator dependencies are torn down here
93
+ ```
94
+
95
+ Use `@inject(new_scope=True)` to opt a function out of the surrounding scope and always get fresh dependencies.
96
+
97
+ ## License
98
+
99
+ MIT
@@ -0,0 +1,13 @@
1
+ from .inject import inject
2
+ from .scope import push_inject_scope
3
+ from .solve import solve
4
+ from .types import Dep, DepFactory, Inejected
5
+
6
+ __all__ = [
7
+ "Dep",
8
+ "DepFactory",
9
+ "Inejected",
10
+ "inject",
11
+ "push_inject_scope",
12
+ "solve",
13
+ ]