ygo 1.0.11__py3-none-any.whl → 1.1.0__py3-none-any.whl
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/__init__.py +7 -4
- ygo/delay.py +89 -0
- ygo/{ygo.py → pool.py} +41 -215
- ygo/utils.py +137 -0
- ygo-1.1.0.dist-info/METADATA +160 -0
- ygo-1.1.0.dist-info/RECORD +12 -0
- {ygo-1.0.11.dist-info → ygo-1.1.0.dist-info}/WHEEL +1 -1
- {ygo-1.0.11.dist-info → ygo-1.1.0.dist-info}/top_level.txt +0 -1
- ylog/__init__.py +2 -0
- ycat/__init__.py +0 -33
- ycat/client.py +0 -172
- ycat/parse.py +0 -64
- ycat/provider.py +0 -101
- ycat/qdf/__init__.py +0 -530
- ycat/qdf/errors.py +0 -65
- ycat/qdf/expr.py +0 -308
- ycat/qdf/qdf.py +0 -180
- ycat/qdf/udf/__init__.py +0 -14
- ycat/qdf/udf/base_udf.py +0 -145
- ycat/qdf/udf/cs_udf.py +0 -97
- ycat/qdf/udf/d_udf.py +0 -176
- ycat/qdf/udf/ind_udf.py +0 -202
- ycat/qdf/udf/ts_udf.py +0 -175
- ygo-1.0.11.dist-info/METADATA +0 -102
- ygo-1.0.11.dist-info/RECORD +0 -24
- {ygo-1.0.11.dist-info → ygo-1.1.0.dist-info}/licenses/LICENSE +0 -0
ygo/__init__.py
CHANGED
|
@@ -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/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
|
+
|
ygo/{ygo.py → pool.py}
RENAMED
|
@@ -7,222 +7,21 @@ Created on 2024/11/4 下午2:10
|
|
|
7
7
|
---------------------------------------------
|
|
8
8
|
"""
|
|
9
9
|
import functools
|
|
10
|
-
import importlib
|
|
11
|
-
import inspect
|
|
12
10
|
import multiprocessing
|
|
13
|
-
import os
|
|
14
11
|
import threading
|
|
15
12
|
import warnings
|
|
16
|
-
from pathlib import Path
|
|
17
13
|
|
|
18
14
|
from joblib import Parallel, delayed
|
|
19
15
|
|
|
20
16
|
import ylog
|
|
21
17
|
from .exceptions import WarnException, FailTaskError
|
|
18
|
+
from .delay import delay
|
|
22
19
|
|
|
23
20
|
with warnings.catch_warnings():
|
|
24
21
|
warnings.simplefilter("ignore")
|
|
25
22
|
from tqdm.auto import tqdm
|
|
26
23
|
|
|
27
24
|
|
|
28
|
-
class DelayedFunction:
|
|
29
|
-
|
|
30
|
-
def __init__(self, func):
|
|
31
|
-
self.func = func
|
|
32
|
-
self._fn_params_k = inspect.signature(self.func).parameters.keys()
|
|
33
|
-
self.stored_kwargs = self._get_default_args(func)
|
|
34
|
-
if hasattr(func, 'stored_kwargs'):
|
|
35
|
-
self.stored_kwargs.update(func.stored_kwargs)
|
|
36
|
-
|
|
37
|
-
def _get_default_args(self, func):
|
|
38
|
-
signature = inspect.signature(func)
|
|
39
|
-
return {
|
|
40
|
-
k: v.default
|
|
41
|
-
for k, v in signature.parameters.items()
|
|
42
|
-
if v.default is not inspect.Parameter.empty
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
def __call__(self, *args, **kwargs):
|
|
46
|
-
def delayed(*args, **_kwargs):
|
|
47
|
-
new_kwargs = {k: v for k, v in self.stored_kwargs.items()}
|
|
48
|
-
for k, v in _kwargs.items():
|
|
49
|
-
if k not in self._fn_params_k:
|
|
50
|
-
continue
|
|
51
|
-
new_kwargs[k] = v
|
|
52
|
-
return self.func(*args, **new_kwargs)
|
|
53
|
-
|
|
54
|
-
self._stored_kwargs(**kwargs)
|
|
55
|
-
new_fn = functools.wraps(self.func)(delayed)
|
|
56
|
-
new_fn.stored_kwargs = self.stored_kwargs
|
|
57
|
-
return new_fn
|
|
58
|
-
|
|
59
|
-
def _stored_kwargs(self, **kwargs):
|
|
60
|
-
for k, v in kwargs.items():
|
|
61
|
-
if k not in self._fn_params_k:
|
|
62
|
-
continue
|
|
63
|
-
self.stored_kwargs[k] = v
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def delay(func):
|
|
67
|
-
"""
|
|
68
|
-
延迟执行
|
|
69
|
-
Parameters
|
|
70
|
-
----------
|
|
71
|
-
func: Callable
|
|
72
|
-
需要延迟执行的对象, 必须是一个Callable对象
|
|
73
|
-
|
|
74
|
-
Returns
|
|
75
|
-
-------
|
|
76
|
-
DelayedFunction
|
|
77
|
-
将预先设置好的参数包装进原始的Callable对象中
|
|
78
|
-
|
|
79
|
-
Examples
|
|
80
|
-
--------
|
|
81
|
-
|
|
82
|
-
场景1:基本使用
|
|
83
|
-
|
|
84
|
-
>>> fn = delay(lambda a, b: a+b)(a=1, b=2)
|
|
85
|
-
>>> fn()
|
|
86
|
-
3
|
|
87
|
-
|
|
88
|
-
场景2: 逐步传递参数
|
|
89
|
-
|
|
90
|
-
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1)
|
|
91
|
-
>>> fn2 = delay(fn1)(b=2)
|
|
92
|
-
>>> fn2(c=3)
|
|
93
|
-
6
|
|
94
|
-
|
|
95
|
-
场景3: 参数更改
|
|
96
|
-
|
|
97
|
-
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1, b=2)
|
|
98
|
-
>>> fn2 = delay(fn1)(c=3, b=5)
|
|
99
|
-
>>> fn2()
|
|
100
|
-
9
|
|
101
|
-
"""
|
|
102
|
-
return DelayedFunction(func)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def fn_params(func: callable):
|
|
106
|
-
"""
|
|
107
|
-
获取fn的参数
|
|
108
|
-
Parameters
|
|
109
|
-
----------
|
|
110
|
-
func: callable
|
|
111
|
-
需要获取参数的callable对象
|
|
112
|
-
Returns
|
|
113
|
-
-------
|
|
114
|
-
list[tuple]
|
|
115
|
-
|
|
116
|
-
"""
|
|
117
|
-
# signatured = sorted(list(inspect.signature(func).parameters.keys()))
|
|
118
|
-
# stored = delay(func)().stored_kwargs
|
|
119
|
-
# return [(param, stored.get(param)) for param in signatured]
|
|
120
|
-
stored = delay(func)().stored_kwargs.items()
|
|
121
|
-
return sorted(stored)
|
|
122
|
-
|
|
123
|
-
def fn_signature_params(func: callable):
|
|
124
|
-
"""获取fn所有定义的参数"""
|
|
125
|
-
return sorted(list(inspect.signature(func).parameters.keys()))
|
|
126
|
-
|
|
127
|
-
def fn_path(fn: callable) -> str:
|
|
128
|
-
"""
|
|
129
|
-
获取func所在的模块层级结构
|
|
130
|
-
Parameters
|
|
131
|
-
----------
|
|
132
|
-
fn: callable
|
|
133
|
-
需要获取结构的callable对象
|
|
134
|
-
Returns
|
|
135
|
-
-------
|
|
136
|
-
str
|
|
137
|
-
用 `.` 连接各级层级
|
|
138
|
-
"""
|
|
139
|
-
module = fn.__module__
|
|
140
|
-
# 检查模块是否有 __file__ 属性
|
|
141
|
-
if module.startswith('__main__'):
|
|
142
|
-
if hasattr(module, '__file__'):
|
|
143
|
-
module = module.__file__
|
|
144
|
-
else:
|
|
145
|
-
# 如果在交互式环境中,返回 None 或者一个默认值
|
|
146
|
-
module = "<interactive environment>"
|
|
147
|
-
if module.endswith('.py'):
|
|
148
|
-
module = module.split('.py')[0].split(str(Path(__file__).parent.parent.absolute()))[-1]
|
|
149
|
-
module = '.'.join(module.strip(os.sep).split(os.sep))
|
|
150
|
-
return module
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def fn_code(fn: callable) -> str:
|
|
154
|
-
"""
|
|
155
|
-
返回fn具体的定义代码
|
|
156
|
-
|
|
157
|
-
Parameters
|
|
158
|
-
----------
|
|
159
|
-
fn: callable
|
|
160
|
-
需要获取具体定义代码的callable对象
|
|
161
|
-
|
|
162
|
-
Returns
|
|
163
|
-
-------
|
|
164
|
-
str
|
|
165
|
-
以字符串封装定义代码
|
|
166
|
-
|
|
167
|
-
Examples
|
|
168
|
-
--------
|
|
169
|
-
|
|
170
|
-
>>> def test_fn(a, b=2):
|
|
171
|
-
>>> return a+b
|
|
172
|
-
>>> print(fn_code())
|
|
173
|
-
def test_fn(a, b=2):
|
|
174
|
-
return a+b
|
|
175
|
-
"""
|
|
176
|
-
return inspect.getsource(fn)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def fn_info(fn: callable) -> str:
|
|
180
|
-
"""获取函数的fn_mod, params, code"""
|
|
181
|
-
# mod = fn_path(fn)
|
|
182
|
-
params = fn_params(fn)
|
|
183
|
-
code = fn_code(fn)
|
|
184
|
-
all_define_params = sorted(list(inspect.signature(fn).parameters.keys()))
|
|
185
|
-
|
|
186
|
-
default_params = {k: v for k, v in params}
|
|
187
|
-
params_infos = list()
|
|
188
|
-
for p in all_define_params:
|
|
189
|
-
if p in default_params:
|
|
190
|
-
params_infos.append(f'{p}={default_params[p]}')
|
|
191
|
-
else:
|
|
192
|
-
params_infos.append(p)
|
|
193
|
-
params_infos = ', '.join(params_infos)
|
|
194
|
-
|
|
195
|
-
s = f"""
|
|
196
|
-
=============================================================
|
|
197
|
-
{fn.__name__}({params_infos})
|
|
198
|
-
=============================================================
|
|
199
|
-
{code}
|
|
200
|
-
"""
|
|
201
|
-
return s
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def fn_from_str(s):
|
|
205
|
-
"""
|
|
206
|
-
字符串导入对应fn
|
|
207
|
-
s: a.b.c.func
|
|
208
|
-
Parameters
|
|
209
|
-
----------
|
|
210
|
-
s: str
|
|
211
|
-
模块的路径,分隔符 `.`
|
|
212
|
-
"""
|
|
213
|
-
*m_path, func = s.split(".")
|
|
214
|
-
m_path = ".".join(m_path)
|
|
215
|
-
mod = importlib.import_module(m_path)
|
|
216
|
-
_callable = getattr(mod, func)
|
|
217
|
-
return _callable
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
def module_from_str(s):
|
|
221
|
-
"""字符串导入模块"""
|
|
222
|
-
m_path = ".".join(s.split('.'))
|
|
223
|
-
mod = importlib.import_module(m_path)
|
|
224
|
-
return mod
|
|
225
|
-
|
|
226
25
|
def run_job(job, task_id, queue):
|
|
227
26
|
"""执行任务并更新队列"""
|
|
228
27
|
try:
|
|
@@ -231,7 +30,7 @@ def run_job(job, task_id, queue):
|
|
|
231
30
|
ylog.warning(FailTaskError(task_name=job.task_name, error=e))
|
|
232
31
|
result = None
|
|
233
32
|
except Exception as e:
|
|
234
|
-
ylog.error(FailTaskError(task_name=job.task_name, error=e))
|
|
33
|
+
ylog.error(FailTaskError(task_name=job.task_name, error=e), exc_info=e)
|
|
235
34
|
result = None
|
|
236
35
|
queue.put((task_id, 1))
|
|
237
36
|
return result
|
|
@@ -264,17 +63,18 @@ class pool:
|
|
|
264
63
|
所有的job_fn都会通过joblib并行运行
|
|
265
64
|
"""
|
|
266
65
|
|
|
267
|
-
def __init__(self, n_jobs=5, show_progress=True, backend='
|
|
66
|
+
def __init__(self, n_jobs=5, show_progress=True, backend='threading', ):
|
|
268
67
|
"""backend: loky/threading/multiprocessing"""
|
|
269
68
|
self.show_progress = show_progress
|
|
270
69
|
self._n_jobs = n_jobs
|
|
271
|
-
self._parallel = Parallel(n_jobs=self._n_jobs, verbose=0, backend=backend) if self._n_jobs >
|
|
272
|
-
self._default_task_name = "
|
|
70
|
+
self._parallel = Parallel(n_jobs=self._n_jobs, verbose=0, backend=backend) if self._n_jobs > 1 else None
|
|
71
|
+
self._default_task_name = "GO-JOB"
|
|
273
72
|
self._all_jobs = list() # list[job]
|
|
274
73
|
self._all_tasks = list() # list[task_name]
|
|
275
74
|
self._task_ids = dict() # {task_name1: 0, task_name2: 1, ...}
|
|
276
75
|
self._task_counts = dict()
|
|
277
76
|
self._id_counts = dict()
|
|
77
|
+
self._manager = None
|
|
278
78
|
|
|
279
79
|
def submit(self, fn, job_name=""):
|
|
280
80
|
"""
|
|
@@ -287,6 +87,22 @@ class pool:
|
|
|
287
87
|
提交到的任务名, 不同的任务对应不同的进度条
|
|
288
88
|
Returns
|
|
289
89
|
-------
|
|
90
|
+
|
|
91
|
+
Examples
|
|
92
|
+
--------
|
|
93
|
+
import time
|
|
94
|
+
import ygo
|
|
95
|
+
>>> def task_fn1(a, b):
|
|
96
|
+
time.sleep(3)
|
|
97
|
+
return a+b
|
|
98
|
+
>>> def task_fn2():
|
|
99
|
+
time.sleep(5)
|
|
100
|
+
return 0
|
|
101
|
+
>>> with ygo.pool() as go:
|
|
102
|
+
go.submit(task_fn1, job_name="task1")(a=1, b=2)
|
|
103
|
+
go.submit(task_fn2, job_name="task2")()
|
|
104
|
+
|
|
105
|
+
go.do()
|
|
290
106
|
"""
|
|
291
107
|
|
|
292
108
|
# 提交任务,对任务进行分类,提交到对应的task id中,并且封装新的功能:使其在运行完毕后将任务进度更新放入队列
|
|
@@ -312,13 +128,11 @@ class pool:
|
|
|
312
128
|
|
|
313
129
|
return collect
|
|
314
130
|
|
|
315
|
-
def do(self
|
|
131
|
+
def do(self):
|
|
316
132
|
if self.show_progress:
|
|
317
|
-
# if job_name is not None:
|
|
318
|
-
# ylog.info(f"{job_name} Start")
|
|
319
133
|
# 消息队列进行通信
|
|
320
|
-
|
|
321
|
-
queue =
|
|
134
|
+
self._manager = multiprocessing.Manager()
|
|
135
|
+
queue = self._manager.Queue()
|
|
322
136
|
tqdm_bars = [tqdm(total=self._task_counts[task_name],
|
|
323
137
|
desc=f"{str(task_name)}", leave=False) for task_name in
|
|
324
138
|
self._all_tasks]
|
|
@@ -335,11 +149,8 @@ class pool:
|
|
|
335
149
|
queue=queue) for job in self._all_jobs)
|
|
336
150
|
else:
|
|
337
151
|
result = [run_job(job=job, task_id=job.task_id, queue=queue) for job in self._all_jobs]
|
|
338
|
-
# time.sleep(.3)
|
|
339
152
|
# 等待进度更新线程执行完毕
|
|
340
153
|
progress_thread.join()
|
|
341
|
-
# if job_name is not None:
|
|
342
|
-
# ylog.info(f"{job_name} Done")
|
|
343
154
|
else:
|
|
344
155
|
if self._parallel is not None:
|
|
345
156
|
result = self._parallel(delayed(job)() for job in self._all_jobs)
|
|
@@ -350,11 +161,26 @@ class pool:
|
|
|
350
161
|
self._task_ids = dict() # {task_name1: 0, task_name2: 1, ...}
|
|
351
162
|
self._task_counts = dict()
|
|
352
163
|
self._id_counts = dict()
|
|
164
|
+
self.close()
|
|
353
165
|
return result
|
|
354
166
|
|
|
167
|
+
def close(self):
|
|
168
|
+
"""释放所有资源"""
|
|
169
|
+
if hasattr(self, '_parallel') and self._parallel is not None:
|
|
170
|
+
try:
|
|
171
|
+
self._parallel.__exit__(None, None, None)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
ylog.warning(f"Failed to close Parallel: {e}")
|
|
174
|
+
|
|
175
|
+
if hasattr(self, '_manager') and self._manager is not None:
|
|
176
|
+
try:
|
|
177
|
+
self._manager.shutdown()
|
|
178
|
+
except Exception as e:
|
|
179
|
+
ylog.warning(f"Failed to shutdown Manager: {e}")
|
|
180
|
+
|
|
355
181
|
def __enter__(self):
|
|
356
182
|
return self
|
|
357
183
|
|
|
358
184
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
359
185
|
# 释放进程
|
|
360
|
-
|
|
186
|
+
self.close()
|
ygo/utils.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Created on 2025/5/26 23:31
|
|
5
|
+
@author: ZhangYundi
|
|
6
|
+
@email: yundi.xxii@outlook.com
|
|
7
|
+
---------------------------------------------
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import inspect
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from .delay import delay
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def fn_params(func: callable):
|
|
18
|
+
"""
|
|
19
|
+
获取fn的参数
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
func: callable
|
|
23
|
+
需要获取参数的callable对象
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
list[tuple]
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
stored = delay(func)().stored_kwargs.items()
|
|
30
|
+
return sorted(stored)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def fn_signature_params(func: callable):
|
|
34
|
+
"""获取fn所有定义的参数"""
|
|
35
|
+
return sorted(list(inspect.signature(func).parameters.keys()))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def fn_path(fn: callable) -> str:
|
|
39
|
+
"""
|
|
40
|
+
获取func所在的模块层级结构
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
fn: callable
|
|
44
|
+
需要获取结构的callable对象
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
str
|
|
48
|
+
用 `.` 连接各级层级
|
|
49
|
+
"""
|
|
50
|
+
module = fn.__module__
|
|
51
|
+
# 检查模块是否有 __file__ 属性
|
|
52
|
+
if module.startswith('__main__'):
|
|
53
|
+
if hasattr(module, '__file__'):
|
|
54
|
+
module = module.__file__
|
|
55
|
+
else:
|
|
56
|
+
# 如果在交互式环境中,返回 None 或者一个默认值
|
|
57
|
+
module = "<interactive environment>"
|
|
58
|
+
if module.endswith('.py'):
|
|
59
|
+
module = module.split('.py')[0].split(str(Path(__file__).parent.parent.absolute()))[-1]
|
|
60
|
+
module = '.'.join(module.strip(os.sep).split(os.sep))
|
|
61
|
+
return module
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def fn_code(fn: callable) -> str:
|
|
65
|
+
"""
|
|
66
|
+
返回fn具体的定义代码
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
fn: callable
|
|
71
|
+
需要获取具体定义代码的callable对象
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
str
|
|
76
|
+
以字符串封装定义代码
|
|
77
|
+
|
|
78
|
+
Examples
|
|
79
|
+
--------
|
|
80
|
+
|
|
81
|
+
>>> def test_fn(a, b=2):
|
|
82
|
+
>>> return a+b
|
|
83
|
+
>>> print(fn_code())
|
|
84
|
+
def test_fn(a, b=2):
|
|
85
|
+
return a+b
|
|
86
|
+
"""
|
|
87
|
+
return inspect.getsource(fn)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def fn_info(fn: callable) -> str:
|
|
91
|
+
"""获取函数的fn_mod, params, code"""
|
|
92
|
+
# mod = fn_path(fn)
|
|
93
|
+
params = fn_params(fn)
|
|
94
|
+
code = fn_code(fn)
|
|
95
|
+
all_define_params = sorted(list(inspect.signature(fn).parameters.keys()))
|
|
96
|
+
|
|
97
|
+
default_params = {k: v for k, v in params}
|
|
98
|
+
params_infos = list()
|
|
99
|
+
for p in all_define_params:
|
|
100
|
+
if p in default_params:
|
|
101
|
+
params_infos.append(f'{p}={default_params[p]}')
|
|
102
|
+
else:
|
|
103
|
+
params_infos.append(p)
|
|
104
|
+
params_infos = ', '.join(params_infos)
|
|
105
|
+
|
|
106
|
+
s = f"""
|
|
107
|
+
=============================================================
|
|
108
|
+
{fn.__name__}({params_infos})
|
|
109
|
+
=============================================================
|
|
110
|
+
{code}
|
|
111
|
+
"""
|
|
112
|
+
return s
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def fn_from_str(s):
|
|
116
|
+
"""
|
|
117
|
+
字符串导入对应fn
|
|
118
|
+
s: a.b.c.func
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
s: str
|
|
122
|
+
模块的路径,分隔符 `.`
|
|
123
|
+
"""
|
|
124
|
+
import importlib
|
|
125
|
+
*m_path, func = s.split(".")
|
|
126
|
+
m_path = ".".join(m_path)
|
|
127
|
+
mod = importlib.import_module(m_path)
|
|
128
|
+
_callable = getattr(mod, func)
|
|
129
|
+
return _callable
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def module_from_str(s):
|
|
133
|
+
"""字符串导入模块"""
|
|
134
|
+
import importlib
|
|
135
|
+
m_path = ".".join(s.split('.'))
|
|
136
|
+
mod = importlib.import_module(m_path)
|
|
137
|
+
return mod
|