object-pool-async 0.1.2__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.
- object_pool_async-0.1.2/LICENSE +21 -0
- object_pool_async-0.1.2/PKG-INFO +178 -0
- object_pool_async-0.1.2/README.md +157 -0
- object_pool_async-0.1.2/pyproject.toml +32 -0
- object_pool_async-0.1.2/setup.cfg +4 -0
- object_pool_async-0.1.2/src/object_pool/__init__.py +10 -0
- object_pool_async-0.1.2/src/object_pool/collections.py +32 -0
- object_pool_async-0.1.2/src/object_pool/exceptions.py +14 -0
- object_pool_async-0.1.2/src/object_pool/pool.py +441 -0
- object_pool_async-0.1.2/src/object_pool/status.py +41 -0
- object_pool_async-0.1.2/src/object_pool/utils.py +97 -0
- object_pool_async-0.1.2/src/object_pool_async.egg-info/PKG-INFO +178 -0
- object_pool_async-0.1.2/src/object_pool_async.egg-info/SOURCES.txt +15 -0
- object_pool_async-0.1.2/src/object_pool_async.egg-info/dependency_links.txt +1 -0
- object_pool_async-0.1.2/src/object_pool_async.egg-info/top_level.txt +1 -0
- object_pool_async-0.1.2/test/test_pool.py +161 -0
- object_pool_async-0.1.2/test/test_thread.py +23 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jerry
|
|
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,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: object-pool-async
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A generic, async-first object pool for Python
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Jerry-Wu-GitHub/object-pool
|
|
8
|
+
Keywords: object-pool,async,connection-pool
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# Object Pool
|
|
23
|
+
|
|
24
|
+
一个功能丰富、支持异步的通用对象池,提供细粒度的对象生命周期管理、状态检测、超时回收和灵活的定制能力。
|
|
25
|
+
|
|
26
|
+
## 特性
|
|
27
|
+
|
|
28
|
+
- ✅ **异步优先**:原生支持 `asyncio`,在高并发 I/O 场景下性能优异
|
|
29
|
+
- 🔄 **完整生命周期**:管理对象的 `PENDING`、`AVAILABLE`、`BORROWED`、`INVALID`、`DORMANT`、`UNKNOWN` 状态
|
|
30
|
+
- ⏱️ **超时回收**:支持对象级别的借出超时自动回收
|
|
31
|
+
- 🧩 **高度可定制**:可自定义状态检测器、对象选择器、包装器(Wrapper)和打包器(Packer)
|
|
32
|
+
- 🚦 **并发控制**:内置并发度限制,批量操作自动分块
|
|
33
|
+
- 🔁 **双 API**:同时提供异步(`*_async`)和同步接口
|
|
34
|
+
- 🧵 **协程安全**:基于 `AsyncRLock` 实现,在单事件循环多协程下安全
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install object-pool
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
或从源码安装:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/Jerry-Wu-GitHub/object-pool.git
|
|
46
|
+
cd object-pool
|
|
47
|
+
pip install -e .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 快速开始
|
|
51
|
+
|
|
52
|
+
### 同步使用
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from object_pool import Pool, Status
|
|
56
|
+
from datetime import timedelta
|
|
57
|
+
|
|
58
|
+
# 定义一个简单的状态检测函数
|
|
59
|
+
def check_status(wrapper):
|
|
60
|
+
# 假设对象有一个 is_alive 属性
|
|
61
|
+
if wrapper.object.is_alive():
|
|
62
|
+
return Status.AVAILABLE
|
|
63
|
+
return Status.INVALID
|
|
64
|
+
|
|
65
|
+
# 创建对象池
|
|
66
|
+
pool = Pool(detect_status=check_status)
|
|
67
|
+
|
|
68
|
+
# 添加对象
|
|
69
|
+
obj = MyResource()
|
|
70
|
+
pool.add(obj)
|
|
71
|
+
|
|
72
|
+
# 借出对象
|
|
73
|
+
with pool.borrow() as resource:
|
|
74
|
+
resource.do_something()
|
|
75
|
+
# 离开上下文时自动归还
|
|
76
|
+
|
|
77
|
+
# 手动归还
|
|
78
|
+
pack = pool.borrow()
|
|
79
|
+
pack.close() # 归还
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 异步使用
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
import asyncio
|
|
86
|
+
from object_pool import Pool, Status
|
|
87
|
+
|
|
88
|
+
async def check_status_async(wrapper):
|
|
89
|
+
# 异步检测对象健康状态
|
|
90
|
+
if await wrapper.object.ping():
|
|
91
|
+
return Status.AVAILABLE
|
|
92
|
+
return Status.INVALID
|
|
93
|
+
|
|
94
|
+
async def main():
|
|
95
|
+
pool = Pool(detect_status=check_status_async)
|
|
96
|
+
obj = AsyncResource()
|
|
97
|
+
await pool.add_async(obj)
|
|
98
|
+
|
|
99
|
+
pack = await pool.borrow_async(timeout=3.0)
|
|
100
|
+
try:
|
|
101
|
+
await pack.object.work()
|
|
102
|
+
finally:
|
|
103
|
+
pack.close() # 归还
|
|
104
|
+
|
|
105
|
+
# 或使用异步上下文管理器
|
|
106
|
+
async with await pool.borrow_async() as resource:
|
|
107
|
+
await resource.work()
|
|
108
|
+
|
|
109
|
+
asyncio.run(main())
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 高级用法
|
|
113
|
+
|
|
114
|
+
### 自定义对象选择器
|
|
115
|
+
|
|
116
|
+
默认使用随机选择。你可以实现自己的选择逻辑,例如轮询(Round-Robin)或[基于权重的选择](examples/weighted_selector.py):
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
def select_object(object_wrapers: List[ObjectWrapper[DummyObject]]) -> Optional[ObjectWrapper[DummyObject]]:
|
|
120
|
+
"""
|
|
121
|
+
按照权重选择一个对象。
|
|
122
|
+
"""
|
|
123
|
+
if not object_wrapers:
|
|
124
|
+
return None
|
|
125
|
+
weights = [
|
|
126
|
+
object_wraper.weight
|
|
127
|
+
for object_wraper in object_wrapers
|
|
128
|
+
]
|
|
129
|
+
return choices(object_wrapers, weights)[0]
|
|
130
|
+
|
|
131
|
+
# 注意:选择器可以是同步或异步函数,池内部会自动转换
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 定制包装器和打包器
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
class MyWrapper(ObjectWrapper):
|
|
138
|
+
def __init__(self, obj, **kwargs):
|
|
139
|
+
super().__init__(obj, **kwargs)
|
|
140
|
+
self.extra_info = kwargs.get('extra', {})
|
|
141
|
+
|
|
142
|
+
def my_packer(wrapper):
|
|
143
|
+
# 返回自定义的打包对象,可以增加额外行为
|
|
144
|
+
return MyPack(wrapper)
|
|
145
|
+
|
|
146
|
+
pool = Pool(detect_status=..., wrapper=MyWrapper, packer=my_packer)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 使用默认数据
|
|
150
|
+
|
|
151
|
+
通过 `wrapping_data` 为每个对象附加默认属性:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
pool = Pool(..., wrapping_data={"created_at": datetime.now(), "source": "db"})
|
|
155
|
+
pool.add(obj) # ObjectWrapper 会自动包含 created_at 和 source
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 并发控制
|
|
159
|
+
|
|
160
|
+
`async_concurrency` 限制了同时执行状态检测或清理操作的并发任务数,避免瞬间压力过大:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
pool = Pool(..., async_concurrency=10) # 最多同时检测10个对象
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 并发与线程安全
|
|
167
|
+
|
|
168
|
+
- **协程安全**:`Pool` 使用 `AsyncRLock` 保护关键操作,在**单个事件循环**内多协程并发访问是安全的。
|
|
169
|
+
- **线程安全**:**不保证**多线程安全。如果在多线程环境中使用同步方法(`add`、`borrow` 等),需要外部加锁(如 `threading.Lock`)。
|
|
170
|
+
- **建议**:在 `asyncio` 应用中始终使用异步 API;在传统多线程应用中请自行添加线程锁。
|
|
171
|
+
|
|
172
|
+
## 依赖
|
|
173
|
+
|
|
174
|
+
- Python >= 3.10
|
|
175
|
+
|
|
176
|
+
## 许可证
|
|
177
|
+
|
|
178
|
+
MIT
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Object Pool
|
|
2
|
+
|
|
3
|
+
一个功能丰富、支持异步的通用对象池,提供细粒度的对象生命周期管理、状态检测、超时回收和灵活的定制能力。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- ✅ **异步优先**:原生支持 `asyncio`,在高并发 I/O 场景下性能优异
|
|
8
|
+
- 🔄 **完整生命周期**:管理对象的 `PENDING`、`AVAILABLE`、`BORROWED`、`INVALID`、`DORMANT`、`UNKNOWN` 状态
|
|
9
|
+
- ⏱️ **超时回收**:支持对象级别的借出超时自动回收
|
|
10
|
+
- 🧩 **高度可定制**:可自定义状态检测器、对象选择器、包装器(Wrapper)和打包器(Packer)
|
|
11
|
+
- 🚦 **并发控制**:内置并发度限制,批量操作自动分块
|
|
12
|
+
- 🔁 **双 API**:同时提供异步(`*_async`)和同步接口
|
|
13
|
+
- 🧵 **协程安全**:基于 `AsyncRLock` 实现,在单事件循环多协程下安全
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install object-pool
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
或从源码安装:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone https://github.com/Jerry-Wu-GitHub/object-pool.git
|
|
25
|
+
cd object-pool
|
|
26
|
+
pip install -e .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
### 同步使用
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from object_pool import Pool, Status
|
|
35
|
+
from datetime import timedelta
|
|
36
|
+
|
|
37
|
+
# 定义一个简单的状态检测函数
|
|
38
|
+
def check_status(wrapper):
|
|
39
|
+
# 假设对象有一个 is_alive 属性
|
|
40
|
+
if wrapper.object.is_alive():
|
|
41
|
+
return Status.AVAILABLE
|
|
42
|
+
return Status.INVALID
|
|
43
|
+
|
|
44
|
+
# 创建对象池
|
|
45
|
+
pool = Pool(detect_status=check_status)
|
|
46
|
+
|
|
47
|
+
# 添加对象
|
|
48
|
+
obj = MyResource()
|
|
49
|
+
pool.add(obj)
|
|
50
|
+
|
|
51
|
+
# 借出对象
|
|
52
|
+
with pool.borrow() as resource:
|
|
53
|
+
resource.do_something()
|
|
54
|
+
# 离开上下文时自动归还
|
|
55
|
+
|
|
56
|
+
# 手动归还
|
|
57
|
+
pack = pool.borrow()
|
|
58
|
+
pack.close() # 归还
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 异步使用
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import asyncio
|
|
65
|
+
from object_pool import Pool, Status
|
|
66
|
+
|
|
67
|
+
async def check_status_async(wrapper):
|
|
68
|
+
# 异步检测对象健康状态
|
|
69
|
+
if await wrapper.object.ping():
|
|
70
|
+
return Status.AVAILABLE
|
|
71
|
+
return Status.INVALID
|
|
72
|
+
|
|
73
|
+
async def main():
|
|
74
|
+
pool = Pool(detect_status=check_status_async)
|
|
75
|
+
obj = AsyncResource()
|
|
76
|
+
await pool.add_async(obj)
|
|
77
|
+
|
|
78
|
+
pack = await pool.borrow_async(timeout=3.0)
|
|
79
|
+
try:
|
|
80
|
+
await pack.object.work()
|
|
81
|
+
finally:
|
|
82
|
+
pack.close() # 归还
|
|
83
|
+
|
|
84
|
+
# 或使用异步上下文管理器
|
|
85
|
+
async with await pool.borrow_async() as resource:
|
|
86
|
+
await resource.work()
|
|
87
|
+
|
|
88
|
+
asyncio.run(main())
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 高级用法
|
|
92
|
+
|
|
93
|
+
### 自定义对象选择器
|
|
94
|
+
|
|
95
|
+
默认使用随机选择。你可以实现自己的选择逻辑,例如轮询(Round-Robin)或[基于权重的选择](examples/weighted_selector.py):
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
def select_object(object_wrapers: List[ObjectWrapper[DummyObject]]) -> Optional[ObjectWrapper[DummyObject]]:
|
|
99
|
+
"""
|
|
100
|
+
按照权重选择一个对象。
|
|
101
|
+
"""
|
|
102
|
+
if not object_wrapers:
|
|
103
|
+
return None
|
|
104
|
+
weights = [
|
|
105
|
+
object_wraper.weight
|
|
106
|
+
for object_wraper in object_wrapers
|
|
107
|
+
]
|
|
108
|
+
return choices(object_wrapers, weights)[0]
|
|
109
|
+
|
|
110
|
+
# 注意:选择器可以是同步或异步函数,池内部会自动转换
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 定制包装器和打包器
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
class MyWrapper(ObjectWrapper):
|
|
117
|
+
def __init__(self, obj, **kwargs):
|
|
118
|
+
super().__init__(obj, **kwargs)
|
|
119
|
+
self.extra_info = kwargs.get('extra', {})
|
|
120
|
+
|
|
121
|
+
def my_packer(wrapper):
|
|
122
|
+
# 返回自定义的打包对象,可以增加额外行为
|
|
123
|
+
return MyPack(wrapper)
|
|
124
|
+
|
|
125
|
+
pool = Pool(detect_status=..., wrapper=MyWrapper, packer=my_packer)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 使用默认数据
|
|
129
|
+
|
|
130
|
+
通过 `wrapping_data` 为每个对象附加默认属性:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
pool = Pool(..., wrapping_data={"created_at": datetime.now(), "source": "db"})
|
|
134
|
+
pool.add(obj) # ObjectWrapper 会自动包含 created_at 和 source
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 并发控制
|
|
138
|
+
|
|
139
|
+
`async_concurrency` 限制了同时执行状态检测或清理操作的并发任务数,避免瞬间压力过大:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
pool = Pool(..., async_concurrency=10) # 最多同时检测10个对象
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 并发与线程安全
|
|
146
|
+
|
|
147
|
+
- **协程安全**:`Pool` 使用 `AsyncRLock` 保护关键操作,在**单个事件循环**内多协程并发访问是安全的。
|
|
148
|
+
- **线程安全**:**不保证**多线程安全。如果在多线程环境中使用同步方法(`add`、`borrow` 等),需要外部加锁(如 `threading.Lock`)。
|
|
149
|
+
- **建议**:在 `asyncio` 应用中始终使用异步 API;在传统多线程应用中请自行添加线程锁。
|
|
150
|
+
|
|
151
|
+
## 依赖
|
|
152
|
+
|
|
153
|
+
- Python >= 3.10
|
|
154
|
+
|
|
155
|
+
## 许可证
|
|
156
|
+
|
|
157
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "object-pool-async" # pip install 时使用的名字
|
|
7
|
+
version = "0.1.2" # 项目版本
|
|
8
|
+
description = "A generic, async-first object pool for Python"
|
|
9
|
+
readme = "README.md" # 指定项目说明文件
|
|
10
|
+
license = {text = "MIT"} # 许可证类型
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Your Name", email = "your.email@example.com"}
|
|
13
|
+
]
|
|
14
|
+
keywords = ["object-pool", "async", "connection-pool"]
|
|
15
|
+
classifiers = [ # 帮助用户在 PyPI 上找到你的包
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.10"
|
|
26
|
+
dependencies = [] # 项目依赖
|
|
27
|
+
|
|
28
|
+
[project.urls] # 项目相关的链接
|
|
29
|
+
"Homepage" = "https://github.com/Jerry-Wu-GitHub/object-pool"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"] # 在 src 目录下找包
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AttributeDict(dict):
|
|
5
|
+
"""
|
|
6
|
+
A class that can read / set attribute value via both `.` and `[]`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __getattr__(self, key: str) -> Any:
|
|
10
|
+
"""
|
|
11
|
+
Get an attribute value by `.`
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
key (str): The name of the attribute to get.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Any: The value of the attribute.
|
|
18
|
+
"""
|
|
19
|
+
return self[key]
|
|
20
|
+
|
|
21
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Set an attribute value by `.`
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
key (str): The name of the attribute to set.
|
|
27
|
+
value (Any): The value to set the attribute to.
|
|
28
|
+
"""
|
|
29
|
+
self[key] = value
|
|
30
|
+
|
|
31
|
+
def __repr__(self) -> str:
|
|
32
|
+
return f"{self.__class__.__name__}({super().__repr__()})"
|