ygo 1.0.8__tar.gz → 1.2.12__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.
- ygo-1.2.12/PKG-INFO +119 -0
- ygo-1.2.12/README.md +104 -0
- ygo-1.2.12/pyproject.toml +24 -0
- ygo-1.2.12/tests/test_lazy.py +21 -0
- ygo-1.2.12/tests/test_utils.py +19 -0
- ygo-1.2.12/tests/test_ygo.py +171 -0
- ygo-1.2.12/ygo/__init__.py +31 -0
- ygo-1.2.12/ygo/delay.py +89 -0
- {ygo-1.0.8 → ygo-1.2.12}/ygo/exceptions.py +17 -1
- ygo-1.2.12/ygo/lazy.py +50 -0
- ygo-1.2.12/ygo/pool.py +286 -0
- ygo-1.2.12/ygo/utils.py +230 -0
- ygo-1.2.12/ygo.egg-info/PKG-INFO +119 -0
- ygo-1.2.12/ygo.egg-info/SOURCES.txt +17 -0
- ygo-1.2.12/ygo.egg-info/requires.txt +4 -0
- ygo-1.2.12/ygo.egg-info/top_level.txt +1 -0
- ygo-1.0.8/PKG-INFO +0 -97
- ygo-1.0.8/README.md +0 -73
- ygo-1.0.8/pyproject.toml +0 -34
- ygo-1.0.8/qdf/__init__.py +0 -529
- ygo-1.0.8/qdf/errors.py +0 -65
- ygo-1.0.8/qdf/expr.py +0 -308
- ygo-1.0.8/qdf/qdf.py +0 -182
- ygo-1.0.8/qdf/udf/__init__.py +0 -14
- ygo-1.0.8/qdf/udf/base_udf.py +0 -145
- ygo-1.0.8/qdf/udf/cs_udf.py +0 -97
- ygo-1.0.8/qdf/udf/d_udf.py +0 -176
- ygo-1.0.8/qdf/udf/ind_udf.py +0 -202
- ygo-1.0.8/qdf/udf/ts_udf.py +0 -175
- ygo-1.0.8/ycat/__init__.py +0 -34
- ygo-1.0.8/ycat/client.py +0 -145
- ygo-1.0.8/ycat/dtype.py +0 -389
- ygo-1.0.8/ycat/parse.py +0 -66
- ygo-1.0.8/ycat/yck.py +0 -87
- ygo-1.0.8/ygo/__init__.py +0 -10
- ygo-1.0.8/ygo/ygo.py +0 -372
- ygo-1.0.8/ygo.egg-info/PKG-INFO +0 -97
- ygo-1.0.8/ygo.egg-info/SOURCES.txt +0 -28
- ygo-1.0.8/ygo.egg-info/requires.txt +0 -14
- ygo-1.0.8/ygo.egg-info/top_level.txt +0 -4
- ygo-1.0.8/ylog/__init__.py +0 -20
- ygo-1.0.8/ylog/core.py +0 -229
- {ygo-1.0.8 → ygo-1.2.12}/LICENSE +0 -0
- {ygo-1.0.8 → ygo-1.2.12}/setup.cfg +0 -0
- {ygo-1.0.8 → ygo-1.2.12}/ygo.egg-info/dependency_links.txt +0 -0
ygo-1.2.12/PKG-INFO
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ygo
|
|
3
|
+
Version: 1.2.12
|
|
4
|
+
Summary: 一个轻量级 Python 工具包,支持 并发执行(带进度条)、延迟调用、链式绑定参数、函数信息获取、模块/函数动态加载。
|
|
5
|
+
Project-URL: homepage, https://github.com/link-yundi/ygo
|
|
6
|
+
Project-URL: repository, https://github.com/link-yundi/ygo
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: joblib>=1.5.0
|
|
11
|
+
Requires-Dist: logair>=1.0.6
|
|
12
|
+
Requires-Dist: psutil>=7.2.1
|
|
13
|
+
Requires-Dist: tqdm>=4.67.1
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# ygo
|
|
17
|
+
一个轻量级 Python 工具包,底层基于 joblib 和 tqdm 、loguru 实现,支持
|
|
18
|
+
- 并发执行(带进度条)
|
|
19
|
+
- 延迟调用
|
|
20
|
+
- 链式绑定参数
|
|
21
|
+
- 函数信息获取
|
|
22
|
+
- 模块/函数动态加载...
|
|
23
|
+
- 并结合 ylog 提供日志记录能力
|
|
24
|
+
|
|
25
|
+
### 安装
|
|
26
|
+
```shell
|
|
27
|
+
pip install -U ygo
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 🧰 功能概览
|
|
31
|
+
|
|
32
|
+
| 模块 | 功能 |
|
|
33
|
+
| :----- | :----------------------------------------------------------- |
|
|
34
|
+
| `ygo` | 支持并发执行(带进度条)、延迟调用、函数信息获取以及模块/函数动态加载等功能 |
|
|
35
|
+
| `ylog` | 日志模块,提供统一的日志输出接口 |
|
|
36
|
+
|
|
37
|
+
### 示例
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
├── a
|
|
41
|
+
│ ├── __init__.py
|
|
42
|
+
│ └── b
|
|
43
|
+
│ ├── __init__.py
|
|
44
|
+
│ └── c.py
|
|
45
|
+
└── test.py
|
|
46
|
+
|
|
47
|
+
c.py 中定义了目标函数
|
|
48
|
+
def test_fn(a, b=2):
|
|
49
|
+
return a+b
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### 场景1: 并发执行
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import ygo
|
|
56
|
+
import ylog
|
|
57
|
+
from a.b.c import test_fn
|
|
58
|
+
|
|
59
|
+
with ygo.pool(n_jobs=5, show_progress=True) as go:
|
|
60
|
+
for i in range(10):
|
|
61
|
+
go.submit(test_fn)(a=i, b=2*i)
|
|
62
|
+
for res in go.do():
|
|
63
|
+
ylog.info(res)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### ✅ `ygo.pool` 支持的参数
|
|
67
|
+
|
|
68
|
+
| 参数名 | 类型 | 描述 |
|
|
69
|
+
| ------------- | ---- | ------------------------------------------------------------ |
|
|
70
|
+
| n_jobs | int | 并行任务数(<=1 表示串行) |
|
|
71
|
+
| show_progress | bool | 是否显示进度条 |
|
|
72
|
+
| backend | str | 执行后端(默认 'threading',可选 'multiprocessing' 或 'loky') |
|
|
73
|
+
|
|
74
|
+
#### 场景2: 延迟调用
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
>>> fn = delay(test_fn)(a=1, b=2)
|
|
78
|
+
>>> fn()
|
|
79
|
+
3
|
|
80
|
+
>>> # 逐步传递参数
|
|
81
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1)
|
|
82
|
+
>>> fn2 = delay(fn1)(b=2)
|
|
83
|
+
>>> fn2(c=3)
|
|
84
|
+
6
|
|
85
|
+
>>> # 参数更改
|
|
86
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1, b=2)
|
|
87
|
+
>>> fn2 = delay(fn1)(c=3, b=5)
|
|
88
|
+
>>> fn2()
|
|
89
|
+
9
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### 场景3: 获取目标函数信息
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
>>> ygo.fn_info(test_fn)
|
|
96
|
+
=============================================================
|
|
97
|
+
a.b.c.test_fn(a, b=2)
|
|
98
|
+
=============================================================
|
|
99
|
+
def test_fn(a, b=2):
|
|
100
|
+
return a+b
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### 🔍 其他函数信息工具
|
|
104
|
+
|
|
105
|
+
| 方法名 | 描述 |
|
|
106
|
+
| ------------------------- | ---------------------------------------- |
|
|
107
|
+
| `fn_params(fn)` | 获取函数实参 |
|
|
108
|
+
| `fn_signature_params(fn)` | 获取函数定义的所有参数名 |
|
|
109
|
+
| `fn_code(fn)` | 获取函数源码字符串 |
|
|
110
|
+
| `fn_path(fn)` | 获取函数所属模块路径 |
|
|
111
|
+
| `fn_from_str(s)` | 根据字符串导入函数(如 "a.b.c.test_fn") |
|
|
112
|
+
| `module_from_str(s)` | 根据字符串导入模块 |
|
|
113
|
+
|
|
114
|
+
#### 场景4: 通过字符串解析函数并执行
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
>>> ygo.fn_from_str("a.b.c.test_fn")(a=1, b=5)
|
|
118
|
+
6
|
|
119
|
+
```
|
ygo-1.2.12/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
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(n_jobs=5, 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
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ygo"
|
|
7
|
+
version = "1.2.12"
|
|
8
|
+
description = "一个轻量级 Python 工具包,支持 并发执行(带进度条)、延迟调用、链式绑定参数、函数信息获取、模块/函数动态加载。"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"joblib>=1.5.0",
|
|
13
|
+
"logair>=1.0.6",
|
|
14
|
+
"psutil>=7.2.1",
|
|
15
|
+
"tqdm>=4.67.1",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
where = ["."]
|
|
20
|
+
include = ["ygo", "ygo.*",]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
homepage = "https://github.com/link-yundi/ygo"
|
|
24
|
+
repository = "https://github.com/link-yundi/ygo"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Copyright (c) 2025 ZhangYundi
|
|
5
|
+
Licensed under the MIT License.
|
|
6
|
+
Created on 2025/6/29 10:49
|
|
7
|
+
Email: yundi.xxii@outlook.com
|
|
8
|
+
Description:
|
|
9
|
+
---------------------------------------------
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import ygo
|
|
13
|
+
|
|
14
|
+
def test_import_module():
|
|
15
|
+
import os
|
|
16
|
+
os_path = ygo.lazy_import("os.path")
|
|
17
|
+
assert os_path.join("a", "b") == os.path.join("a", "b")
|
|
18
|
+
|
|
19
|
+
def test_import_function():
|
|
20
|
+
from os.path import join
|
|
21
|
+
assert ygo.lazy_import("os.path.join")("a", "b") == join("a", "b")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copyright (c) ZhangYundi.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
# Created on 2025/7/20 16:33
|
|
4
|
+
# Description:
|
|
5
|
+
import queue
|
|
6
|
+
|
|
7
|
+
# from ygo.utils import locate
|
|
8
|
+
from ygo.pool import run_job
|
|
9
|
+
|
|
10
|
+
# def test_locate():
|
|
11
|
+
# fn = locate("functools.partial")
|
|
12
|
+
# test_fn = lambda a, b: a+b
|
|
13
|
+
# assert fn(test_fn, 1, 2)() == 3
|
|
14
|
+
|
|
15
|
+
def foo():
|
|
16
|
+
run_job(1, 1, queue.Queue())
|
|
17
|
+
|
|
18
|
+
if __name__ == '__main__':
|
|
19
|
+
foo()
|
|
@@ -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)) == {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from .exceptions import FailTaskError, WarnException
|
|
2
|
+
from .pool import pool
|
|
3
|
+
from .delay import delay
|
|
4
|
+
from .utils import (
|
|
5
|
+
fn_params,
|
|
6
|
+
fn_signature_params,
|
|
7
|
+
fn_path,
|
|
8
|
+
fn_code,
|
|
9
|
+
fn_info,
|
|
10
|
+
module_from_str,
|
|
11
|
+
fn_from_str,
|
|
12
|
+
locate,
|
|
13
|
+
)
|
|
14
|
+
from .lazy import lazy_import
|
|
15
|
+
|
|
16
|
+
__version__ = "1.2.12"
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"FailTaskError",
|
|
20
|
+
"delay",
|
|
21
|
+
"WarnException",
|
|
22
|
+
"fn_params",
|
|
23
|
+
"fn_signature_params",
|
|
24
|
+
"fn_path",
|
|
25
|
+
"fn_code",
|
|
26
|
+
"fn_info",
|
|
27
|
+
"fn_from_str",
|
|
28
|
+
"module_from_str",
|
|
29
|
+
"pool",
|
|
30
|
+
"lazy_import"
|
|
31
|
+
]
|
ygo-1.2.12/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
|
+
|
|
@@ -7,7 +7,23 @@ Created on 2024/12/18 下午7:01
|
|
|
7
7
|
---------------------------------------------
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
10
12
|
class WarnException(Exception):
|
|
11
13
|
"""自定义异常类,仅用于警告"""
|
|
12
14
|
def __init__(self, message):
|
|
13
|
-
super().__init__(message) # 调用父类的构造函数
|
|
15
|
+
super().__init__(message) # 调用父类的构造函数
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class FailTaskError:
|
|
19
|
+
task_name: str
|
|
20
|
+
error: Exception
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return f"""
|
|
24
|
+
[失败任务]: {self.task_name}
|
|
25
|
+
[错误信息]: \n{self.error}
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __repr__(self):
|
|
29
|
+
return self.__str__()
|
ygo-1.2.12/ygo/lazy.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Copyright (c) 2025 ZhangYundi
|
|
5
|
+
Licensed under the MIT License.
|
|
6
|
+
Created on 2025/6/29 10:36
|
|
7
|
+
Email: yundi.xxii@outlook.com
|
|
8
|
+
Description:
|
|
9
|
+
---------------------------------------------
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
from .utils import locate
|
|
14
|
+
|
|
15
|
+
class LazyImport:
|
|
16
|
+
def __init__(self, full_name: str):
|
|
17
|
+
self._full_name = full_name
|
|
18
|
+
self._obj = None
|
|
19
|
+
|
|
20
|
+
def _load(self) -> Any:
|
|
21
|
+
if self._obj is None:
|
|
22
|
+
self._obj = locate(self._full_name)
|
|
23
|
+
return self._obj
|
|
24
|
+
|
|
25
|
+
def __getattr__(self, attr: str) -> Any:
|
|
26
|
+
obj = self._load()
|
|
27
|
+
return getattr(obj, attr)
|
|
28
|
+
|
|
29
|
+
def __dir__(self) -> list[str]:
|
|
30
|
+
obj = self._load()
|
|
31
|
+
return dir(obj)
|
|
32
|
+
|
|
33
|
+
def __call__(self, *args, **kwargs) -> Any:
|
|
34
|
+
obj = self._load()
|
|
35
|
+
if isinstance(obj, type):
|
|
36
|
+
return obj(*args, **kwargs)
|
|
37
|
+
elif callable(obj):
|
|
38
|
+
return obj(*args, **kwargs)
|
|
39
|
+
else:
|
|
40
|
+
raise TypeError(f"The target `{self._full_name}` is not callable or instantiable.")
|
|
41
|
+
|
|
42
|
+
def __str__(self):
|
|
43
|
+
return self._full_name
|
|
44
|
+
|
|
45
|
+
def __repr__(self):
|
|
46
|
+
return self._full_name
|
|
47
|
+
|
|
48
|
+
def lazy_import(full_name: str):
|
|
49
|
+
"""实现模块和方法的懒加载"""
|
|
50
|
+
return LazyImport(full_name)
|