Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd5afa81

Browse files
authored
feat: MkDocs
1 parent8412a82 commitd5afa81

30 files changed

+1485
-1049
lines changed

‎.github/actions/environment/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ runs:
1616

1717
-name:Install Dependencies
1818
shell:bash
19-
run:uv sync
19+
run:uv sync --no-group bench

‎.github/workflows/cd.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313

1414
steps:
1515
-name:Run checkout
16-
uses:actions/checkout@v5
16+
uses:actions/checkout@v6
1717

1818
-name:Set up environment
1919
uses:./.github/actions/environment

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020

2121
steps:
2222
-name:Run checkout
23-
uses:actions/checkout@v5
23+
uses:actions/checkout@v6
2424

2525
-name:Set up environment
2626
uses:./.github/actions/environment

‎.github/workflows/pages.yml‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name:GitHub Pages
2+
3+
on:
4+
push:
5+
branches:
6+
-prod
7+
8+
jobs:
9+
delivery:
10+
name:Delivery
11+
runs-on:ubuntu-latest
12+
permissions:
13+
contents:write
14+
15+
steps:
16+
-name:Run checkout
17+
uses:actions/checkout@v6
18+
19+
-name:Configure Git
20+
run:|
21+
git config --global user.name "github-pages[bot]"
22+
git config --global user.email "github-pages[bot]@users.noreply.github.com"
23+
24+
-name:Set up environment
25+
uses:./.github/actions/environment
26+
27+
-name:Deploy MkDocs
28+
shell:bash
29+
run:uv run mkdocs gh-deploy --force

‎Makefile‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ pyright:
1919

2020
pytest:
2121
uv run pytest
22+
23+
mkdocs:
24+
uv run mkdocs serve

‎README.md‎

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,18 @@
55
[![PyPI - Downloads](https://img.shields.io/pypi/dm/python-injection.svg?color=blue)](https://pypistats.org/packages/python-injection)
66
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
77

8+
Documentation:https://python-injection.remimd.dev
9+
810
##Installation
911

1012
⚠️_Requires Python 3.12 or higher_
11-
1213
```bash
1314
pip install python-injection
1415
```
1516

16-
##Features
17-
18-
* Automatic dependency resolution based on type hints.
19-
* Support for multiple dependency lifetimes:`transient`,`singleton`,`constant`, and`scoped`.
20-
* Works seamlessly in both`async` and`sync` environments.
21-
* Separation of dependency sets using modules.
22-
* Runtime switching between different sets of dependencies.
23-
* Centralized setup logic using entrypoints.
24-
* Built-in type annotation for easy integration with[`FastAPI`](https://github.com/fastapi/fastapi).
25-
* Lazy dependency resolution for optimized performance.
26-
27-
##Motivations
28-
29-
1. Easy to use
30-
2. No impact on class and function definitions
31-
3. No tedious configuration
32-
3317
##Quick start
3418

3519
Simply apply the decorators and the package takes care of the rest.
36-
3720
```python
3821
from injectionimport injectable, inject, singleton
3922

@@ -61,25 +44,3 @@ def main(service: Service):
6144
if__name__=="__main__":
6245
main()
6346
```
64-
65-
##Resources
66-
67-
>⚠️ The package isn't threadsafe by default, for better performance in single-threaded applications and those using
68-
>`asyncio`.
69-
>
70-
>Non-threadsafe functions are those that resolve dependencies or define scopes. They all come with an optional
71-
>parameter`threadsafe`.
72-
>
73-
>You can set`PYTHON_INJECTION_THREADSAFE=1` in environment variables to make the package fully threadsafe. The
74-
>environment variable is resolved at the**Python module level**, so be careful if the variable is defined dynamically.
75-
76-
*[**Basic usage**](https://github.com/100nm/python-injection/tree/prod/documentation/basic-usage.md)
77-
*[**Scoped dependencies**](https://github.com/100nm/python-injection/tree/prod/documentation/scoped-dependencies.md)
78-
*[**Testing**](https://github.com/100nm/python-injection/tree/prod/documentation/testing.md)
79-
*[**Advanced usage**](https://github.com/100nm/python-injection/tree/prod/documentation/advanced-usage.md)
80-
*[**Loaders**](https://github.com/100nm/python-injection/tree/prod/documentation/loaders.md)
81-
*[**Entrypoint**](https://github.com/100nm/python-injection/tree/prod/documentation/entrypoint.md)
82-
*[**Integrations**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations)
83-
*[**FastAPI**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations/fastapi.md)
84-
*[**What if my framework isn't listed?**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations/unlisted-framework.md)
85-
*[**Concrete example**](https://github.com/100nm/python-injection-example)

‎docs/CNAME‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
python-injection.remimd.dev

‎docs/guides/imports.md‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#Auto-imports
2+
3+
When using decorators to register dependencies, some implementations may never be explicitly imported in your project. This creates a problem: if a module is never imported, its decorators never execute, and the dependencies are never registered.
4+
5+
For example, if you register an implementation with`@injectable(on=AbstractDependency)` but never import that implementation module, the dependency won't be available for injection even though it's decorated.
6+
7+
To solve this,`python-injection` provides two solutions for automatically importing modules in a package.
8+
9+
!!! tip
10+
Call auto-import functions early in your application startup,**before loading your profile** and before any dependency resolution occurs.
11+
12+
##load_packages
13+
14+
The`load_packages` function imports all modules from the specified packages. Packages can be passed as module objects or as strings:
15+
```python
16+
from injection.loadersimport load_packages
17+
from srcimport adapters, services
18+
19+
# Import all modules in adapters and services packages
20+
load_packages(adapters, services)
21+
22+
# Or using string notation
23+
load_packages("src.adapters","src.services")
24+
```
25+
26+
This is the simplest approach and works well when you want to import everything from specific packages.
27+
28+
##PythonModuleLoader
29+
30+
For more control over which modules get imported, use`PythonModuleLoader` with a custom predicate function. Like`load_packages`, it accepts both module objects and strings:
31+
```python
32+
from injection.loadersimport PythonModuleLoader
33+
from srcimport adapters, services
34+
35+
defpredicate(module_name:str) ->bool:
36+
# Only import modules containing "impl" in their name
37+
return"impl"in module_name
38+
39+
PythonModuleLoader(predicate).load(adapters, services)
40+
```
41+
42+
The predicate function receives the full module name (e.g.,`"src.adapters.dependency_impl"`) and returns`True` if the module should be imported.
43+
44+
###Factory methods
45+
46+
`PythonModuleLoader` provides three convenient factory methods for common filtering patterns:
47+
48+
**Filter by prefix:**
49+
```python
50+
# Import only modules starting with "impl_"
51+
PythonModuleLoader.startswith("impl_").load(adapters, services)
52+
```
53+
54+
**Filter by suffix:**
55+
```python
56+
# Import only modules ending with "_impl"
57+
PythonModuleLoader.endswith("_impl").load(adapters, services)
58+
```
59+
60+
**Filter by keyword marker:**
61+
```python
62+
# Import only modules containing a specific comment
63+
PythonModuleLoader.from_keywords("# auto-import").load(adapters, services)
64+
```
65+
66+
This last approach is particularly useful for explicitly marking which modules should be auto-imported:
67+
```python
68+
# auto-import
69+
70+
@injectable(on=AbstractDependency)
71+
classDependency(AbstractDependency):
72+
...
73+
```

‎docs/guides/main-functions.md‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#Set up main functions
2+
3+
As we've seen throughout this guide, there can be quite a bit of setup around a main function: loading modules, defining scopes, injecting dependencies, loading profiles, etc. This becomes repetitive when you have multiple entry points in your project (CLI commands, etc.).
4+
5+
To solve this,`python-injection` provides**entrypoints**, a way to create custom decorators that encapsulate all your setup logic using a builder pattern.
6+
7+
##Creating an entrypoint
8+
9+
Use the`@entrypointmaker` decorator to define your setup logic once:
10+
```python
11+
from injectionimport adefine_scope
12+
from injection.entrypointimport AsyncEntrypoint, Entrypoint, entrypointmaker
13+
from injection.loadersimport PythonModuleLoader
14+
15+
@entrypointmaker
16+
def entrypoint[**P, T](self: AsyncEntrypoint[P, T])-> Entrypoint[P, T]:
17+
import src
18+
19+
module_loader= PythonModuleLoader.endswith("_impl")
20+
return (
21+
self.inject()
22+
.decorate(adefine_scope("lifespan",kind="shared"))
23+
.async_to_sync()
24+
.load_modules(module_loader, src)
25+
)
26+
```
27+
28+
Now you can use your custom`@entrypoint` decorator on any function:
29+
```python
30+
@entrypoint
31+
asyncdefmain(dependency: Dependency):
32+
# All setup is automatically applied
33+
...
34+
35+
if__name__=="__main__":
36+
main()
37+
```
38+
39+
!!! info "Builder execution order"
40+
The builder instructions execute in**reverse order**. Each method call re-decorates the main function, so the last instruction in the chain is call first. In the example above, modules are loaded first, then the function is converted to sync, then the scope is defined, and finally dependencies are injected.
41+
42+
###Automatic execution
43+
44+
The entrypoint decorator accepts an optional`autocall` parameter. When set to`True`, the decorated function is automatically called:
45+
```python
46+
@entrypoint(autocall=True)
47+
asyncdefmain(dependency: Dependency):
48+
# This function runs automatically when the module is executed
49+
...
50+
```
51+
52+
This is particularly convenient for scripts and CLI commands where you want the entry point to execute immediately.
53+
54+
##Integrating with ProfileLoader
55+
56+
If you're using a[`ProfileLoader`](profiles.md#profileloader) in your project, pass it to`@entrypointmaker` using the`profile_loader` parameter:
57+
```python
58+
from injection.entrypointimport Entrypoint, entrypointmaker
59+
from injection.loadersimport ProfileLoader, PythonModuleLoader
60+
61+
profile_loader= ProfileLoader(...)
62+
63+
@entrypointmaker(profile_loader=profile_loader)
64+
def entrypoint[**P, T](self: Entrypoint[P, T])-> Entrypoint[P, T]:
65+
import src
66+
67+
module_loader= PythonModuleLoader.endswith("_impl")
68+
return (
69+
self.inject()
70+
.load_profile(Profile.DEV)# Load a specific profile
71+
.load_modules(module_loader, src)
72+
)
73+
```
74+
75+
The`load_profile` method accepts a profile name and loads it before the main function executes.
76+
77+
##Resolving dependencies in the setup
78+
79+
You can resolve dependencies from the setup function parameters. These dependencies must be registered in the default module (not in a profile-specific module) and should preferably be transient or constant. This is particularly useful for resolving configuration to determine which profile to load:
80+
```python
81+
from dataclassesimport dataclass
82+
from injectionimport constant
83+
from injection.entrypointimport Entrypoint, entrypointmaker
84+
from injection.loadersimport PythonModuleLoader
85+
from osimport getenv
86+
87+
@dataclass
88+
classConfig:
89+
profile: Profile
90+
91+
@constant
92+
def_config_factory() -> Config:
93+
profile= Profile(getenv("PROFILE","development"))
94+
return Config(profile)
95+
96+
@entrypointmaker(profile_loader=profile_loader)
97+
def entrypoint[**P, T](self: Entrypoint[P, T], config: Config)-> Entrypoint[P, T]:
98+
import src
99+
100+
profile= config.profile# Use config to determine profile
101+
suffixes=self.profile_loader.required_module_names(profile)
102+
module_loader= PythonModuleLoader.endswith(*suffixes)
103+
return (
104+
self.inject()
105+
.load_profile(profile)
106+
.load_modules(module_loader, src)
107+
)
108+
```
109+
110+
In this example,`config` is resolved from the default module and used to dynamically load the appropriate profile.
111+
112+
!!! warning
113+
Dependencies resolved in the entrypoint setup function must be registered in the default module (not in a profile-specific module) and should be transient or constant to avoid state issues.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp