ygo 1.0.11__py3-none-any.whl → 1.1.1__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 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 .ygo import (
12
- delay,
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.1"
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='loky', ):
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 > 0 else None
272
- self._default_task_name = "TaskDefault"
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, *args, **kwargs):
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
- manager = multiprocessing.Manager()
321
- queue = manager.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
- pass
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