ygo 1.0.10__tar.gz → 1.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.
Potentially problematic release.
This version of ygo might be problematic. Click here for more details.
- ygo-1.1.0/PKG-INFO +160 -0
- ygo-1.1.0/README.md +147 -0
- ygo-1.1.0/pyproject.toml +23 -0
- ygo-1.1.0/tests/test_ygo.py +171 -0
- {ygo-1.0.10 → ygo-1.1.0}/ygo/__init__.py +7 -4
- ygo-1.1.0/ygo/delay.py +89 -0
- ygo-1.0.10/ygo/ygo.py → ygo-1.1.0/ygo/pool.py +41 -215
- ygo-1.1.0/ygo/utils.py +137 -0
- ygo-1.1.0/ygo.egg-info/PKG-INFO +160 -0
- ygo-1.1.0/ygo.egg-info/SOURCES.txt +16 -0
- ygo-1.1.0/ygo.egg-info/requires.txt +3 -0
- {ygo-1.0.10 → ygo-1.1.0}/ygo.egg-info/top_level.txt +0 -1
- {ygo-1.0.10 → ygo-1.1.0}/ylog/__init__.py +2 -0
- ygo-1.0.10/PKG-INFO +0 -102
- ygo-1.0.10/README.md +0 -73
- ygo-1.0.10/pyproject.toml +0 -39
- ygo-1.0.10/ycat/__init__.py +0 -33
- ygo-1.0.10/ycat/client.py +0 -172
- ygo-1.0.10/ycat/parse.py +0 -64
- ygo-1.0.10/ycat/qdf/__init__.py +0 -530
- ygo-1.0.10/ycat/qdf/errors.py +0 -65
- ygo-1.0.10/ycat/qdf/expr.py +0 -308
- ygo-1.0.10/ycat/qdf/qdf.py +0 -180
- ygo-1.0.10/ycat/qdf/udf/__init__.py +0 -14
- ygo-1.0.10/ycat/qdf/udf/base_udf.py +0 -145
- ygo-1.0.10/ycat/qdf/udf/cs_udf.py +0 -97
- ygo-1.0.10/ycat/qdf/udf/d_udf.py +0 -176
- ygo-1.0.10/ycat/qdf/udf/ind_udf.py +0 -202
- ygo-1.0.10/ycat/qdf/udf/ts_udf.py +0 -175
- ygo-1.0.10/ycat/updator.py +0 -101
- ygo-1.0.10/ygo.egg-info/PKG-INFO +0 -102
- ygo-1.0.10/ygo.egg-info/SOURCES.txt +0 -27
- ygo-1.0.10/ygo.egg-info/requires.txt +0 -19
- {ygo-1.0.10 → ygo-1.1.0}/LICENSE +0 -0
- {ygo-1.0.10 → ygo-1.1.0}/setup.cfg +0 -0
- {ygo-1.0.10 → ygo-1.1.0}/ygo/exceptions.py +0 -0
- {ygo-1.0.10 → ygo-1.1.0}/ygo.egg-info/dependency_links.txt +0 -0
- {ygo-1.0.10 → ygo-1.1.0}/ylog/core.py +0 -0
ygo-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ygo
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Project-URL: homepage, https://github.com/link-yundi/ygo
|
|
5
|
+
Project-URL: repository, https://github.com/link-yundi/ygo
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: joblib>=1.5.0
|
|
10
|
+
Requires-Dist: loguru>=0.7.3
|
|
11
|
+
Requires-Dist: tqdm>=4.67.1
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# ygo
|
|
15
|
+
一个轻量级 Python 工具包,底层基于 joblib 和 tqdm 、loguru 实现,支持
|
|
16
|
+
- 并发执行(带进度条)
|
|
17
|
+
- 延迟调用
|
|
18
|
+
- 链式绑定参数
|
|
19
|
+
- 函数信息获取
|
|
20
|
+
- 模块/函数动态加载...
|
|
21
|
+
- 并结合 ylog 提供日志记录能力
|
|
22
|
+
|
|
23
|
+
### 安装
|
|
24
|
+
```shell
|
|
25
|
+
pip install -U ygo
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 🧰 功能概览
|
|
29
|
+
|
|
30
|
+
| 模块 | 功能 |
|
|
31
|
+
| :----- | :----------------------------------------------------------- |
|
|
32
|
+
| `ygo` | 支持并发执行(带进度条)、延迟调用、函数信息获取以及模块/函数动态加载等功能 |
|
|
33
|
+
| `ylog` | 日志模块,提供统一的日志输出接口 |
|
|
34
|
+
|
|
35
|
+
### 示例
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
├── a
|
|
39
|
+
│ ├── __init__.py
|
|
40
|
+
│ └── b
|
|
41
|
+
│ ├── __init__.py
|
|
42
|
+
│ └── c.py
|
|
43
|
+
└── test.py
|
|
44
|
+
|
|
45
|
+
c.py 中定义了目标函数
|
|
46
|
+
def test_fn(a, b=2):
|
|
47
|
+
return a+b
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### 场景1: 并发执行
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import ygo
|
|
54
|
+
import ylog
|
|
55
|
+
from a.b.c import test_fn
|
|
56
|
+
|
|
57
|
+
with ygo.pool(job_name="test parallel", show_progress=True) as go:
|
|
58
|
+
for i in range(10):
|
|
59
|
+
go.submit(test_fn)(a=i, b=2*i)
|
|
60
|
+
for res in go.do():
|
|
61
|
+
ylog.info(res)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### ✅ `ygo.pool` 支持的参数
|
|
65
|
+
|
|
66
|
+
| 参数名 | 类型 | 描述 |
|
|
67
|
+
| ------------- | ---- | ------------------------------------------------------------ |
|
|
68
|
+
| n_jobs | int | 并行任务数(<=1 表示串行) |
|
|
69
|
+
| show_progress | bool | 是否显示进度条 |
|
|
70
|
+
| backend | str | 执行后端(默认 'threading',可选 'multiprocessing' 或 'loky') |
|
|
71
|
+
|
|
72
|
+
#### 场景2: 延迟调用
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
>>> fn = delay(test_fn)(a=1, b=2)
|
|
76
|
+
>>> fn()
|
|
77
|
+
3
|
|
78
|
+
>>> # 逐步传递参数
|
|
79
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1)
|
|
80
|
+
>>> fn2 = delay(fn1)(b=2)
|
|
81
|
+
>>> fn2(c=3)
|
|
82
|
+
6
|
|
83
|
+
>>> # 参数更改
|
|
84
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1, b=2)
|
|
85
|
+
>>> fn2 = delay(fn1)(c=3, b=5)
|
|
86
|
+
>>> fn2()
|
|
87
|
+
9
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### 场景3: 获取目标函数信息
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
>>> ygo.fn_info(test_fn)
|
|
94
|
+
=============================================================
|
|
95
|
+
a.b.c.test_fn(a, b=2)
|
|
96
|
+
=============================================================
|
|
97
|
+
def test_fn(a, b=2):
|
|
98
|
+
return a+b
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### 🔍 其他函数信息工具
|
|
102
|
+
|
|
103
|
+
| 方法名 | 描述 |
|
|
104
|
+
| ------------------------- | ---------------------------------------- |
|
|
105
|
+
| `fn_params(fn)` | 获取函数实参 |
|
|
106
|
+
| `fn_signature_params(fn)` | 获取函数定义的所有参数名 |
|
|
107
|
+
| `fn_code(fn)` | 获取函数源码字符串 |
|
|
108
|
+
| `fn_path(fn)` | 获取函数所属模块路径 |
|
|
109
|
+
| `fn_from_str(s)` | 根据字符串导入函数(如 "a.b.c.test_fn") |
|
|
110
|
+
| `module_from_str(s)` | 根据字符串导入模块 |
|
|
111
|
+
|
|
112
|
+
#### 场景4: 通过字符串解析函数并执行
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
>>> ygo.fn_from_str("a.b.c.test_fn")(a=1, b=5)
|
|
116
|
+
6
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 📝 日志记录(ylog)
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
import ylog
|
|
123
|
+
|
|
124
|
+
ylog.info("这是一个信息日志")
|
|
125
|
+
ylog.warning("这是一个警告日志")
|
|
126
|
+
ylog.error("这是一个错误日志", exc_info=True)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# 为不同的模块使用不同的logger
|
|
131
|
+
logger_app1 = ylog.get_logger("app1", )
|
|
132
|
+
logger_app2 = ylog.get_logger("app2", )
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### 🔧 配置管理:`update_config`
|
|
136
|
+
|
|
137
|
+
你可以通过 update_config 方法动态修改日志配置,例如设置日志级别、格式、是否启用颜色等。
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# 开启调试模式
|
|
141
|
+
ylog.update_config(debug_mode=True)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### 🧩 获取独立的 Logger 实例:`get_logger`
|
|
145
|
+
|
|
146
|
+
在大型项目中,你可能希望为不同模块或组件创建独立的 logger 实例以区分日志来源。
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
logger1 = ylog.get_logger("moduleA")
|
|
150
|
+
logger2 = ylog.get_logger("moduleB")
|
|
151
|
+
|
|
152
|
+
logger1.info("这是来自 moduleA 的日志")
|
|
153
|
+
logger2.warning("这是来自 moduleB 的警告")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### 📌 使用建议
|
|
157
|
+
|
|
158
|
+
- 生产环境建议关闭 `debug_mode`,避免产生过多调试日志。
|
|
159
|
+
- 对于复杂项目,推荐使用 `get_logger` 创建命名 logger,便于日志分类与分析。
|
|
160
|
+
- 使用 `exc_info=True` 参数时,可自动打印异常堆栈信息,适用于错误捕获场景。
|
ygo-1.1.0/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# ygo
|
|
2
|
+
一个轻量级 Python 工具包,底层基于 joblib 和 tqdm 、loguru 实现,支持
|
|
3
|
+
- 并发执行(带进度条)
|
|
4
|
+
- 延迟调用
|
|
5
|
+
- 链式绑定参数
|
|
6
|
+
- 函数信息获取
|
|
7
|
+
- 模块/函数动态加载...
|
|
8
|
+
- 并结合 ylog 提供日志记录能力
|
|
9
|
+
|
|
10
|
+
### 安装
|
|
11
|
+
```shell
|
|
12
|
+
pip install -U ygo
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### 🧰 功能概览
|
|
16
|
+
|
|
17
|
+
| 模块 | 功能 |
|
|
18
|
+
| :----- | :----------------------------------------------------------- |
|
|
19
|
+
| `ygo` | 支持并发执行(带进度条)、延迟调用、函数信息获取以及模块/函数动态加载等功能 |
|
|
20
|
+
| `ylog` | 日志模块,提供统一的日志输出接口 |
|
|
21
|
+
|
|
22
|
+
### 示例
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
├── a
|
|
26
|
+
│ ├── __init__.py
|
|
27
|
+
│ └── b
|
|
28
|
+
│ ├── __init__.py
|
|
29
|
+
│ └── c.py
|
|
30
|
+
└── test.py
|
|
31
|
+
|
|
32
|
+
c.py 中定义了目标函数
|
|
33
|
+
def test_fn(a, b=2):
|
|
34
|
+
return a+b
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### 场景1: 并发执行
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import ygo
|
|
41
|
+
import ylog
|
|
42
|
+
from a.b.c import test_fn
|
|
43
|
+
|
|
44
|
+
with ygo.pool(job_name="test parallel", show_progress=True) as go:
|
|
45
|
+
for i in range(10):
|
|
46
|
+
go.submit(test_fn)(a=i, b=2*i)
|
|
47
|
+
for res in go.do():
|
|
48
|
+
ylog.info(res)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### ✅ `ygo.pool` 支持的参数
|
|
52
|
+
|
|
53
|
+
| 参数名 | 类型 | 描述 |
|
|
54
|
+
| ------------- | ---- | ------------------------------------------------------------ |
|
|
55
|
+
| n_jobs | int | 并行任务数(<=1 表示串行) |
|
|
56
|
+
| show_progress | bool | 是否显示进度条 |
|
|
57
|
+
| backend | str | 执行后端(默认 'threading',可选 'multiprocessing' 或 'loky') |
|
|
58
|
+
|
|
59
|
+
#### 场景2: 延迟调用
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
>>> fn = delay(test_fn)(a=1, b=2)
|
|
63
|
+
>>> fn()
|
|
64
|
+
3
|
|
65
|
+
>>> # 逐步传递参数
|
|
66
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1)
|
|
67
|
+
>>> fn2 = delay(fn1)(b=2)
|
|
68
|
+
>>> fn2(c=3)
|
|
69
|
+
6
|
|
70
|
+
>>> # 参数更改
|
|
71
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1, b=2)
|
|
72
|
+
>>> fn2 = delay(fn1)(c=3, b=5)
|
|
73
|
+
>>> fn2()
|
|
74
|
+
9
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### 场景3: 获取目标函数信息
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
>>> ygo.fn_info(test_fn)
|
|
81
|
+
=============================================================
|
|
82
|
+
a.b.c.test_fn(a, b=2)
|
|
83
|
+
=============================================================
|
|
84
|
+
def test_fn(a, b=2):
|
|
85
|
+
return a+b
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### 🔍 其他函数信息工具
|
|
89
|
+
|
|
90
|
+
| 方法名 | 描述 |
|
|
91
|
+
| ------------------------- | ---------------------------------------- |
|
|
92
|
+
| `fn_params(fn)` | 获取函数实参 |
|
|
93
|
+
| `fn_signature_params(fn)` | 获取函数定义的所有参数名 |
|
|
94
|
+
| `fn_code(fn)` | 获取函数源码字符串 |
|
|
95
|
+
| `fn_path(fn)` | 获取函数所属模块路径 |
|
|
96
|
+
| `fn_from_str(s)` | 根据字符串导入函数(如 "a.b.c.test_fn") |
|
|
97
|
+
| `module_from_str(s)` | 根据字符串导入模块 |
|
|
98
|
+
|
|
99
|
+
#### 场景4: 通过字符串解析函数并执行
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
>>> ygo.fn_from_str("a.b.c.test_fn")(a=1, b=5)
|
|
103
|
+
6
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 📝 日志记录(ylog)
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import ylog
|
|
110
|
+
|
|
111
|
+
ylog.info("这是一个信息日志")
|
|
112
|
+
ylog.warning("这是一个警告日志")
|
|
113
|
+
ylog.error("这是一个错误日志", exc_info=True)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# 为不同的模块使用不同的logger
|
|
118
|
+
logger_app1 = ylog.get_logger("app1", )
|
|
119
|
+
logger_app2 = ylog.get_logger("app2", )
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### 🔧 配置管理:`update_config`
|
|
123
|
+
|
|
124
|
+
你可以通过 update_config 方法动态修改日志配置,例如设置日志级别、格式、是否启用颜色等。
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# 开启调试模式
|
|
128
|
+
ylog.update_config(debug_mode=True)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### 🧩 获取独立的 Logger 实例:`get_logger`
|
|
132
|
+
|
|
133
|
+
在大型项目中,你可能希望为不同模块或组件创建独立的 logger 实例以区分日志来源。
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
logger1 = ylog.get_logger("moduleA")
|
|
137
|
+
logger2 = ylog.get_logger("moduleB")
|
|
138
|
+
|
|
139
|
+
logger1.info("这是来自 moduleA 的日志")
|
|
140
|
+
logger2.warning("这是来自 moduleB 的警告")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### 📌 使用建议
|
|
144
|
+
|
|
145
|
+
- 生产环境建议关闭 `debug_mode`,避免产生过多调试日志。
|
|
146
|
+
- 对于复杂项目,推荐使用 `get_logger` 创建命名 logger,便于日志分类与分析。
|
|
147
|
+
- 使用 `exc_info=True` 参数时,可自动打印异常堆栈信息,适用于错误捕获场景。
|
ygo-1.1.0/pyproject.toml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ygo"
|
|
7
|
+
version = "v1.1.0"
|
|
8
|
+
description = ""
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"joblib>=1.5.0",
|
|
13
|
+
"loguru>=0.7.3",
|
|
14
|
+
"tqdm>=4.67.1",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
where = ["."]
|
|
19
|
+
include = ["ygo", "ygo.*", "ylog", "ylog.*", ]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
homepage = "https://github.com/link-yundi/ygo"
|
|
23
|
+
repository = "https://github.com/link-yundi/ygo"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Created on 2025/5/27 09:14
|
|
5
|
+
@author: ZhangYundi
|
|
6
|
+
@email: yundi.xxii@outlook.com
|
|
7
|
+
---------------------------------------------
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
import ygo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def sample_task_add(a, b):
|
|
16
|
+
time.sleep(1)
|
|
17
|
+
return a + b
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def sample_task_sub(a, b):
|
|
21
|
+
time.sleep(1)
|
|
22
|
+
return a - b
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def warn_task():
|
|
26
|
+
raise ygo.WarnException("Test warning")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def error_task():
|
|
30
|
+
raise ValueError("Test error")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_pool_submit_and_do():
|
|
34
|
+
with ygo.pool() as go:
|
|
35
|
+
go.submit(sample_task_add, "task add")(a=1, b=2)
|
|
36
|
+
go.submit(sample_task_add, "task add")(a=2, b=3)
|
|
37
|
+
go.submit(sample_task_sub, "task sub")(a=5, b=1)
|
|
38
|
+
|
|
39
|
+
results = go.do()
|
|
40
|
+
|
|
41
|
+
assert len(results) == 3
|
|
42
|
+
assert results[0] == 3
|
|
43
|
+
assert results[1] == 5
|
|
44
|
+
assert results[2] == 4
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_pool_error_handling():
|
|
48
|
+
with ygo.pool() as go:
|
|
49
|
+
go.submit(error_task, job_name="task_error")()
|
|
50
|
+
|
|
51
|
+
results = go.do()
|
|
52
|
+
|
|
53
|
+
assert len(results) == 1
|
|
54
|
+
assert results[0] is None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_pool_warn_exception_handled():
|
|
58
|
+
"""测试 WarnException 是否被记录警告但继续执行"""
|
|
59
|
+
with ygo.pool(n_jobs=1) as go:
|
|
60
|
+
go.submit(warn_task, job_name="task_warn")()
|
|
61
|
+
|
|
62
|
+
results = go.do()
|
|
63
|
+
|
|
64
|
+
assert len(results) == 1
|
|
65
|
+
assert results[0] is None # WarnException 不中断流程
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_pool_progress_bar_disabled():
|
|
69
|
+
"""测试关闭进度条时的行为"""
|
|
70
|
+
with ygo.pool(n_jobs=2, show_progress=False) as go:
|
|
71
|
+
go.submit(sample_task_add, "test add")(a=1, b=2)
|
|
72
|
+
go.submit(sample_task_add, "test add")(a=3, b=4)
|
|
73
|
+
|
|
74
|
+
results = go.do()
|
|
75
|
+
|
|
76
|
+
assert len(results) == 2
|
|
77
|
+
assert 3 == results[0]
|
|
78
|
+
assert 7 == results[1]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_pool_with_one_jobs():
|
|
82
|
+
"""测试 n_jobs=1 时是否退化为串行执行"""
|
|
83
|
+
with ygo.pool(n_jobs=1) as go:
|
|
84
|
+
go.submit(sample_task_add, "test add")(a=1, b=2)
|
|
85
|
+
go.submit(sample_task_add, "test add")(a=3, b=4)
|
|
86
|
+
|
|
87
|
+
results = go.do()
|
|
88
|
+
|
|
89
|
+
assert len(results) == 2
|
|
90
|
+
assert 3 == results[0]
|
|
91
|
+
assert 7 == results[1]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_delay_basic_usage():
|
|
95
|
+
"""场景1:基本使用"""
|
|
96
|
+
fn = ygo.delay(lambda a, b: a + b)(a=1, b=2)
|
|
97
|
+
assert fn() == 3
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_delay_partial_kwargs():
|
|
101
|
+
"""场景2:逐步传递参数"""
|
|
102
|
+
fn1 = ygo.delay(lambda a, b, c: a + b + c)(a=1)
|
|
103
|
+
fn2 = ygo.delay(fn1)(b=2)
|
|
104
|
+
assert fn2(c=3) == 6
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_delay_override_kwargs():
|
|
108
|
+
"""场景3:参数更改"""
|
|
109
|
+
fn1 = ygo.delay(lambda a, b, c: a + b + c)(a=1, b=2)
|
|
110
|
+
fn2 = ygo.delay(fn1)(c=3, b=5) # 修改 b 的值
|
|
111
|
+
assert fn2() == 9
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_delay_with_no_call():
|
|
115
|
+
"""延迟函数在未调用时不会执行"""
|
|
116
|
+
called = False
|
|
117
|
+
|
|
118
|
+
def side_effect(*args, **kwargs):
|
|
119
|
+
nonlocal called
|
|
120
|
+
called = True
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
delayed_fn = ygo.delay(side_effect)(x=1)
|
|
124
|
+
assert not called # 还未执行
|
|
125
|
+
delayed_fn()
|
|
126
|
+
assert called # 执行后标记为 True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_fn_signature_params():
|
|
130
|
+
assert ygo.fn_signature_params(sample_task_add) == ['a', 'b']
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_fn_params_with_defaults():
|
|
134
|
+
delayed = ygo.delay(sample_task_add)(a=3)
|
|
135
|
+
print(ygo.fn_params(delayed))
|
|
136
|
+
assert dict(ygo.fn_params(delayed)) == {'a': 3, }
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_fn_path_for_function():
|
|
140
|
+
fn_path = ygo.fn_path(sample_task_add)
|
|
141
|
+
assert 'tests.test_ygo' in fn_path or '__main__' in fn_path
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_fn_code():
|
|
145
|
+
code = ygo.fn_code(sample_task_add)
|
|
146
|
+
assert "def sample_task_add(a, b):" in code
|
|
147
|
+
assert "return a+b" in code
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_fn_info():
|
|
151
|
+
info = ygo.fn_info(sample_task_add)
|
|
152
|
+
assert "sample_task_add(a, b)" in info
|
|
153
|
+
assert "def sample_task_add(a, b):" in info
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_fn_from_str():
|
|
157
|
+
func = ygo.fn_from_str("ygo.utils.fn_from_str")
|
|
158
|
+
assert func.__name__ == "fn_from_str"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_module_from_str():
|
|
162
|
+
mod = ygo.module_from_str("ygo.utils")
|
|
163
|
+
assert mod.__name__ == "ygo.utils"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_fn_params_with_no_defaults():
|
|
167
|
+
def no_defaults(x, y):
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
delayed = ygo.delay(no_defaults)()
|
|
171
|
+
assert dict(ygo.fn_params(delayed)) == {}
|
|
@@ -7,9 +7,10 @@ Created on 2025/4/28 15:25
|
|
|
7
7
|
---------------------------------------------
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from .exceptions import FailTaskError
|
|
11
|
-
from .
|
|
12
|
-
|
|
10
|
+
from .exceptions import FailTaskError, WarnException
|
|
11
|
+
from .pool import pool
|
|
12
|
+
from .delay import delay
|
|
13
|
+
from .utils import (
|
|
13
14
|
fn_params,
|
|
14
15
|
fn_signature_params,
|
|
15
16
|
fn_path,
|
|
@@ -17,12 +18,14 @@ from .ygo import (
|
|
|
17
18
|
fn_info,
|
|
18
19
|
module_from_str,
|
|
19
20
|
fn_from_str,
|
|
20
|
-
pool,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
+
__version__ = "v1.1.0"
|
|
24
|
+
|
|
23
25
|
__all__ = [
|
|
24
26
|
"FailTaskError",
|
|
25
27
|
"delay",
|
|
28
|
+
"WarnException",
|
|
26
29
|
"fn_params",
|
|
27
30
|
"fn_signature_params",
|
|
28
31
|
"fn_path",
|
ygo-1.1.0/ygo/delay.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Created on 2025/5/26 20:12
|
|
5
|
+
@author: ZhangYundi
|
|
6
|
+
@email: yundi.xxii@outlook.com
|
|
7
|
+
---------------------------------------------
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import functools
|
|
11
|
+
import inspect
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DelayedFunction:
|
|
15
|
+
|
|
16
|
+
def __init__(self, func):
|
|
17
|
+
self.func = func
|
|
18
|
+
self._fn_params_k = inspect.signature(self.func).parameters.keys()
|
|
19
|
+
self.stored_kwargs = self._get_default_args(func)
|
|
20
|
+
if hasattr(func, 'stored_kwargs'):
|
|
21
|
+
self.stored_kwargs.update(func.stored_kwargs)
|
|
22
|
+
|
|
23
|
+
def _get_default_args(self, func):
|
|
24
|
+
signature = inspect.signature(func)
|
|
25
|
+
return {
|
|
26
|
+
k: v.default
|
|
27
|
+
for k, v in signature.parameters.items()
|
|
28
|
+
if v.default is not inspect.Parameter.empty
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def __call__(self, *args, **kwargs):
|
|
32
|
+
def delayed(*args, **_kwargs):
|
|
33
|
+
new_kwargs = {k: v for k, v in self.stored_kwargs.items()}
|
|
34
|
+
for k, v in _kwargs.items():
|
|
35
|
+
if k not in self._fn_params_k:
|
|
36
|
+
continue
|
|
37
|
+
new_kwargs[k] = v
|
|
38
|
+
return self.func(*args, **new_kwargs)
|
|
39
|
+
|
|
40
|
+
self._stored_kwargs(**kwargs)
|
|
41
|
+
new_fn = functools.wraps(self.func)(delayed)
|
|
42
|
+
new_fn.stored_kwargs = self.stored_kwargs
|
|
43
|
+
return new_fn
|
|
44
|
+
|
|
45
|
+
def _stored_kwargs(self, **kwargs):
|
|
46
|
+
for k, v in kwargs.items():
|
|
47
|
+
if k not in self._fn_params_k:
|
|
48
|
+
continue
|
|
49
|
+
self.stored_kwargs[k] = v
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def delay(func):
|
|
53
|
+
"""
|
|
54
|
+
延迟执行
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
func: Callable
|
|
58
|
+
需要延迟执行的对象, 必须是一个Callable对象
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
DelayedFunction
|
|
63
|
+
将预先设置好的参数包装进原始的Callable对象中
|
|
64
|
+
|
|
65
|
+
Examples
|
|
66
|
+
--------
|
|
67
|
+
|
|
68
|
+
场景1:基本使用
|
|
69
|
+
|
|
70
|
+
>>> fn = delay(lambda a, b: a+b)(a=1, b=2)
|
|
71
|
+
>>> fn()
|
|
72
|
+
3
|
|
73
|
+
|
|
74
|
+
场景2: 逐步传递参数
|
|
75
|
+
|
|
76
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1)
|
|
77
|
+
>>> fn2 = delay(fn1)(b=2)
|
|
78
|
+
>>> fn2(c=3)
|
|
79
|
+
6
|
|
80
|
+
|
|
81
|
+
场景3: 参数更改
|
|
82
|
+
|
|
83
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1, b=2)
|
|
84
|
+
>>> fn2 = delay(fn1)(c=3, b=5)
|
|
85
|
+
>>> fn2()
|
|
86
|
+
9
|
|
87
|
+
"""
|
|
88
|
+
return DelayedFunction(func)
|
|
89
|
+
|