pyre-injector
轻量级 Python 依赖注入框架:依据类型注解自动装配、管理作用域与延迟初始化。
核心零依赖(仅标准库),统一通过 container. 注册与获取依赖。
✨ 特点
- 🚀 轻量 — 只用标准库,无任何第三方依赖。
- 🧩 自动解析 — 依据构造函数 / 工厂函数的类型注解递归装配依赖,不用手写工厂。
- 🎯 统一 API —
container.inject()注册、container.provider()获取,装饰器写法完全等价。 - ♻️ 作用域管理 —
singleton(默认)与prototype一行切换。 - ⏳ 延迟初始化 —
lazy_init=True时首次使用才构建,避免启动期无谓开销。 - 🏭 工厂函数 — 模块级工厂与类内工厂方法都支持,参数同样参与注入。
- 🧷 手动注入实例 — 已构造好的配置、连接池、logger 可直接交给容器。
- 🧭 精确异常定位 — 报出异常发生位置与依赖定义位置,并检测循环依赖。
- 📦 零必需依赖 — 只用标准库。
📦 安装
pip install pyre-injector
💡 一个简单的例子
from pyre_injector import DependencyContainer
container = DependencyContainer()
class DatabaseConfig:
def __init__(self):
self.host = 'localhost'
class UserRepository:
def __init__(self, config: DatabaseConfig):
self.config = config
container.inject(DatabaseConfig)
container.inject(UserRepository)
container.provider(DatabaseConfig).host # 'localhost'
container.provider(UserRepository).config # DatabaseConfig 实例(自动注入)
👩💻 目录结构
pyre-injector
├── pyre_injector
│ ├── __init__.py # 对外导出
│ ├── container.py # 容器:注册、解析、作用域
│ ├── constants.py # 作用域等常量
│ └── globals.py # 激活容器
├── examples/ # 可运行示例
├── tests/ # 单元测试
└── setup.py
🚀 快速开始
注册依赖
推荐统一使用 container.inject(...):
from pyre_injector import DependencyContainer
container = DependencyContainer()
container.inject(Logger) # 按类注册
container.inject(Cache, scope='prototype') # 带作用域
container.inject(Mailer, depend_name='mailer') # 自定义名称
container.inject(LazyClient, lazy_init=True) # 延迟初始化
@container.inject # 也可以当装饰器
class Metrics:
...
@inject 是等价的模块级写法,适合在模块导入时声明:
from pyre_injector import inject
@inject
class Logger:
...
@inject(scope='prototype', lazy_init=True)
class Cache:
...
获取依赖
container.provider(Logger) # 按类型
container.provider('mailer') # 按名称
init_dependency_container() 会激活容器并立即构建所有非延迟依赖,
此后即可在任意位置用模块级 autowired(...) / provider(...) 取用:
container.init_dependency_container()
from pyre_injector import autowired, provider
autowired(Logger) # 从活动容器按类型获取
provider('mailer') # provider 是 autowired 的别名
♻️ 作用域
| 作用域 | 说明 |
|---|---|
singleton |
默认。整个容器生命周期内只有一个实例。 |
prototype |
每次 provider() 都创建新实例。 |
container.inject(Config) # singleton
container.inject(RequestContext, scope='prototype')
container.provider(Config) is container.provider(Config) # True
container.provider(RequestContext) is not container.provider(RequestContext) # True
⏳ 延迟初始化
非延迟依赖会在 init_dependency_container() 时按依赖顺序构建;
lazy_init=True 的依赖推迟到首次 provider() 时才构建。
container.inject(HeavyClient, lazy_init=True)
container.init_dependency_container() # 此时 HeavyClient 尚未构建
client = container.provider(HeavyClient) # 这一行才真正构建
🏭 工厂函数
工厂函数需要返回类型注解,用它来确定依赖类型;工厂参数同样参与注入。
from pyre_injector import inject
@inject(depend_name='connection')
def create_connection(config: DatabaseConfig) -> Connection:
return Connection(config.host, config.port)
container.provider('connection')
类内工厂方法会把方法所属的类实例作为 self 注入:
@inject
class SessionFactory:
def __init__(self):
self.counter = 0
@inject(depend_name='session')
def create_session(self) -> Session:
self.counter += 1
return Session(f'session-{self.counter}')
没有返回类型注解时可用 return_type 显式指定:
container.inject(create_thing, return_type=Thing, depend_name='thing')
🧷 手动注入实例
已经构造好的对象可以直接交给容器,供其它依赖自动注入:
container.inject(app) # 名称默认取 instance.__class__.__name__
container.inject('pyre_flask', app) # 也可以显式指定名称
container.inject('app_config', AppConfig())
传类 / 工厂函数走注册,传实例走手动注入,二者共用
container.inject(...)。
🏷 自定义名称
container.inject(UserService, depend_name='user_service')
container.provider('user_service') is container.provider(UserService) # True
🧭 错误定位
初始化失败时,异常信息会同时给出依赖定义位置和异常实际发生位置:
DependencyInitError: Failed to initialize dependency 'BrokenService'. 初始化依赖 'BrokenService' 失败。
└─ defined at: __main__.BrokenService (examples/errors.py:19)
└─ origin : examples/errors.py:21 (in __init__)
ValueError: 数据库连接失败
defined at— 依赖在哪里定义/注册(模块.类名 + 文件:行号)。origin— 异常实际抛出的位置(最内层用户代码帧),原始异常通过__cause__保留。DependencyInitError.origin与.depend_name两个属性可直接读取,便于上报与断言。
异常类型
| 异常 | 触发场景 |
|---|---|
DependencyInitError |
构造函数 / 工厂函数抛出异常(含 origin 定位)。 |
DependencyNotRegisteredError |
参数声明的类型未注册,或按名称/类型查不到定义。 |
DependencyAnnotationError |
参数缺少类型注解,且无默认值。 |
DependencyCycleError |
检测到循环依赖,消息中给出完整链路。 |
DependencyAlreadyRegisteredError |
同名依赖重复注册 / 重复注入(可用 replace=True 覆盖)。 |
DependencyNotInitializedError |
从活动容器取值但容器未激活,或名称未注册。 |
全部异常都继承自 DependencyError,而 DependencyError 继承自 RuntimeError。
from pyre_injector import DependencyContainer, DependencyInitError
try:
container.provider(BrokenService)
except DependencyInitError as error:
print(error.origin) # 'app/services.py:21 (in __init__)'
print(error.__cause__) # 原始异常
🔌 与 Web 框架集成
pyre-injector 不依赖任何 Web 框架。框架侧通常按下面三步接入:
- 创建
DependencyContainer; - 用
container.inject(...)注册服务,把 app / config / logger / db 等实例手动注入; - 调用
container.init_dependency_container()激活容器,之后框架反射注入时统一用autowired(...)取值。
container = DependencyContainer()
container.inject(app)
container.inject(app_config)
container.inject(logger)
container.inject(DatabaseConfig)
container.init_dependency_container()
框架里的服务基类也可以走等价的类方法式注册:
from pyre_injector import DependencyContainer, DependencyDefinition, autowired
class BaseService:
def __init_subclass__(cls, **kwargs):
definition = DependencyDefinition.parse_dependency(cls)
if cls.__name__ not in DependencyContainer.dependency_definitions:
DependencyContainer.register_dependency_definition(cls.__name__, definition)
return super().__init_subclass__(**kwargs)
def autowired(self, name):
return autowired(name)
📖 API 参考
| 名称 | 说明 |
|---|---|
DependencyContainer |
容器。inject / provider / inject_instance / init_dependency_container / active / clear / is_registered / register_dependency_definition / get_dependency_definition / solve_depend_init |
DependencyDefinition |
依赖定义。parse_dependency() / depend_class / depend_func / scope / lazy_init / depends_on |
inject |
模块级注册装饰器,等价于 container.inject。 |
autowired / provider |
从活动容器按类型或名称获取依赖(两者等价)。 |
DependencyError |
所有异常基类(RuntimeError 子类)。 |
DependencyInitError |
初始化失败,带 origin / depend_name。 |
DependencyNotRegisteredError / DependencyAnnotationError / DependencyCycleError / DependencyAlreadyRegisteredError / DependencyNotInitializedError |
具体错误类型。 |
container.inject
container.inject(
target=None, # 类 / 工厂函数(注册)或实例(手动注入)
depend_instance=None,
*,
name=None, # 手动注入时的名称
depend_name=None, # 注册时使用的名称,默认取类名
scope=None, # 'singleton' | 'prototype'
lazy_init=None, # 是否延迟初始化
return_type=None, # 工厂函数的返回类型
solve_init=True, # 注册后若容器已就绪是否立即构建
replace=False, # 是否覆盖同名定义
)
container.provider
container.provider(name) # name: 类 / 工厂函数 / 字符串名称
🛠 开发
pip install -e . pytest
pytest tests -q
示例位于 examples/:
python examples/basic.py # 最小可用
python examples/container_api.py # 推荐用法:container.inject / provider
python examples/scopes.py # 作用域与延迟初始化
python examples/factory.py # 工厂函数 / 工厂方法
python examples/errors.py # 异常与定位信息
📄 许可
本项目基于 MIT 许可开源。
Metadata
Release files for pyre-injector 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pyre_injector-1.0.0.tar.gz | 24.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyre_injector-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.7 kB
Release files / pyre_injector-1.0.0.tar.gz
| Download URL | pyre_injector-1.0.0.tar.gz |
|---|---|
| Size | 24.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e99d068ede8cb5e670256d4b849adbb2d5bd4de442750979baa89cde9d3b397
|
|
BLAKE2b-256 checksum How to use checksums |
b6a37e203d90a614d780725db85bc83f0f53f3858095ece8495e847d520587dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pyre_injector-1.0.0-py3-none-any.whl
| Download URL | pyre_injector-1.0.0-py3-none-any.whl |
|---|---|
| Size | 16.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8a9cc39e8d07f0f9fd7ddad3dba39aa23ec99d7f677064f02c784c727bad3960
|
|
BLAKE2b-256 checksum How to use checksums |
03040992e99cf4297513628f1842383321d36729219ddbe974bcb52ba479aac7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for pyre-injector 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.