ygo 1.0.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.
- ycat/__init__.py +22 -0
- ycat/client.py +157 -0
- ycat/dtype.py +389 -0
- ycat/parse.py +66 -0
- ycat/yck.py +87 -0
- ygo/__init__.py +10 -0
- ygo/exceptions.py +13 -0
- ygo/ygo.py +372 -0
- ygo-1.0.1.dist-info/METADATA +95 -0
- ygo-1.0.1.dist-info/RECORD +15 -0
- ygo-1.0.1.dist-info/WHEEL +5 -0
- ygo-1.0.1.dist-info/licenses/LICENSE +21 -0
- ygo-1.0.1.dist-info/top_level.txt +3 -0
- ylog/__init__.py +20 -0
- ylog/core.py +226 -0
ycat/yck.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Created on 2024/11/4 上午9:01
|
|
5
|
+
@author: ZhangYundi
|
|
6
|
+
@email: yundi.xxii@outlook.com
|
|
7
|
+
---------------------------------------------
|
|
8
|
+
"""
|
|
9
|
+
from random import randint
|
|
10
|
+
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import polars
|
|
13
|
+
import polars as pl
|
|
14
|
+
import pyarrow as pa
|
|
15
|
+
from clickhouse_driver import Client
|
|
16
|
+
|
|
17
|
+
from . import dtype
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def connect(urls: list[str], user: str, password: str) -> Client:
|
|
21
|
+
"""
|
|
22
|
+
连接clickhouse服务器, 支持集群
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
urls: List[str]
|
|
26
|
+
["host1:port1", "host2:port2", "host3:port3"...]
|
|
27
|
+
user: str
|
|
28
|
+
用户名
|
|
29
|
+
password: str
|
|
30
|
+
密码
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
client: Client
|
|
34
|
+
ClickHouse 数据库连接客户端,必须是一个有效的 `clickhouse_driver.Client` 实例
|
|
35
|
+
"""
|
|
36
|
+
i = randint(0, len(urls) - 1)
|
|
37
|
+
url_ini = urls[i]
|
|
38
|
+
[host, port] = url_ini.split(":")
|
|
39
|
+
return Client(host, port=port, round_robin=True, alt_hosts=",".join(urls), user=user, password=password)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def query_pandas(sql, conn) -> pd.DataFrame:
|
|
43
|
+
"""
|
|
44
|
+
请求ck,返回 pandas.DataFrame
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
sql: str
|
|
48
|
+
查询语句
|
|
49
|
+
conn: Client
|
|
50
|
+
ClickHouse 数据库连接客户端,必须是一个有效的 `clickhouse_driver.Client` 实例
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
pandas.DataFrame
|
|
54
|
+
包含查询结果的 Pandas DataFrame。如果查询没有返回任何数据,则
|
|
55
|
+
返回一个空的 DataFrame 或者 None
|
|
56
|
+
"""
|
|
57
|
+
return conn.query_dataframe(sql)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def query_polars(sql, conn) -> pl.DataFrame:
|
|
61
|
+
"""
|
|
62
|
+
请求ck,返回 polars.DataFrame
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
sql: str
|
|
66
|
+
查询语句
|
|
67
|
+
conn: Client
|
|
68
|
+
ClickHouse 数据库连接客户端,必须是一个有效的 `clickhouse_driver.Client` 实例。
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
polars.DataFrame
|
|
72
|
+
包含查询结果的 Polars DataFrame。如果查询没有返回任何数据,则
|
|
73
|
+
返回一个空的 DataFrame 或者 None
|
|
74
|
+
"""
|
|
75
|
+
data, columns = conn.execute(sql, columnar=True, with_column_types=True)
|
|
76
|
+
# columns = {name: dtype.infer_dtype_from_database_typename(type_) for name, type_ in columns}
|
|
77
|
+
if len(data) < 1:
|
|
78
|
+
columns = {name: dtype.infer_dtype_from_database_typename(type_) for name, type_ in columns}
|
|
79
|
+
return pl.DataFrame(schema=columns)
|
|
80
|
+
columns = {name: dtype.map_clickhouse_to_arrow(type_) for name, type_ in columns}
|
|
81
|
+
# 构造 Arrow 表(逐列传递数据和类型)
|
|
82
|
+
arrow_table = pa.Table.from_arrays(
|
|
83
|
+
[pa.array(col, type=col_type) for col, col_type in zip(data, columns.values())],
|
|
84
|
+
schema=pa.schema(columns))
|
|
85
|
+
|
|
86
|
+
# 从 Arrow 表构造 Polars DataFrame
|
|
87
|
+
return pl.from_arrow(arrow_table)
|
ygo/__init__.py
ADDED
ygo/exceptions.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Created on 2024/12/18 下午7:01
|
|
5
|
+
@author: ZhangYundi
|
|
6
|
+
@email: yundi.xxii@outlook.com
|
|
7
|
+
---------------------------------------------
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
class WarnException(Exception):
|
|
11
|
+
"""自定义异常类,仅用于警告"""
|
|
12
|
+
def __init__(self, message):
|
|
13
|
+
super().__init__(message) # 调用父类的构造函数
|
ygo/ygo.py
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Created on 2024/11/4 下午2:10
|
|
5
|
+
@author: ZhangYundi
|
|
6
|
+
@email: yundi.xxii@outlook.com
|
|
7
|
+
---------------------------------------------
|
|
8
|
+
"""
|
|
9
|
+
import functools
|
|
10
|
+
import importlib
|
|
11
|
+
import inspect
|
|
12
|
+
import multiprocessing
|
|
13
|
+
import os
|
|
14
|
+
import threading
|
|
15
|
+
import warnings
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from joblib import Parallel, delayed
|
|
19
|
+
|
|
20
|
+
import ylog
|
|
21
|
+
from .exceptions import WarnException
|
|
22
|
+
|
|
23
|
+
with warnings.catch_warnings():
|
|
24
|
+
warnings.simplefilter("ignore")
|
|
25
|
+
from tqdm.auto import tqdm
|
|
26
|
+
|
|
27
|
+
|
|
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
|
+
def run_job(job, task_id, queue):
|
|
227
|
+
"""执行任务并更新队列"""
|
|
228
|
+
try:
|
|
229
|
+
result = job()
|
|
230
|
+
except WarnException as e:
|
|
231
|
+
warn_msg = f"""
|
|
232
|
+
=============================================================
|
|
233
|
+
{job.task_name}: {job.task_id}
|
|
234
|
+
{e}
|
|
235
|
+
=============================================================
|
|
236
|
+
"""
|
|
237
|
+
ylog.warning(warn_msg)
|
|
238
|
+
result = None
|
|
239
|
+
except Exception as e:
|
|
240
|
+
error_msg = f"""
|
|
241
|
+
=============================================================
|
|
242
|
+
{job.task_name}: {job.task_id}
|
|
243
|
+
{e}
|
|
244
|
+
=============================================================
|
|
245
|
+
"""
|
|
246
|
+
ylog.error(error_msg)
|
|
247
|
+
result = None
|
|
248
|
+
queue.put((task_id, 1))
|
|
249
|
+
return result
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def update_progress_bars(tqdm_objects: list[tqdm],
|
|
253
|
+
task_ids,
|
|
254
|
+
queue: multiprocessing.Queue,
|
|
255
|
+
num_tasks: int,
|
|
256
|
+
task_counts: dict):
|
|
257
|
+
"""根据队列中的消息更新 tqdm 进度条"""
|
|
258
|
+
completed_tasks = 0
|
|
259
|
+
completed_task_jobs = {id_: 0 for id_ in task_ids}
|
|
260
|
+
while completed_tasks < num_tasks:
|
|
261
|
+
task_id, progress_value = queue.get() # 从队列获取进度更新
|
|
262
|
+
completed_task_jobs[task_id] += 1
|
|
263
|
+
if completed_task_jobs[task_id] >= task_counts[task_id]:
|
|
264
|
+
completed_tasks += 1
|
|
265
|
+
tqdm_objects[task_id].update(progress_value)
|
|
266
|
+
[tqdm_object.close() for tqdm_object in tqdm_objects]
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class pool:
|
|
270
|
+
"""
|
|
271
|
+
每个fn运行一次算一个job,每个job需要指定job_name, 如果没有job_name, 则默认分配 TaskDefault
|
|
272
|
+
相同 job_name 的fn归到同一个task, 同时该task命名为job_name
|
|
273
|
+
即一个task中包含了多个需要运行的 job fn
|
|
274
|
+
task1 <job_fn1, job_fn2, ...>
|
|
275
|
+
task2 <job_fn3, job_fn4, ...>
|
|
276
|
+
所有的job_fn都会通过joblib并行运行
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
def __init__(self, n_jobs=5, show_progress=True, backend='loky', ):
|
|
280
|
+
"""backend: loky/threading/multiprocessing"""
|
|
281
|
+
self.show_progress = show_progress
|
|
282
|
+
self._n_jobs = n_jobs
|
|
283
|
+
self._parallel = Parallel(n_jobs=self._n_jobs, verbose=0, backend=backend) if self._n_jobs > 0 else None
|
|
284
|
+
self._default_task_name = "TaskDefault"
|
|
285
|
+
self._all_jobs = list() # list[job]
|
|
286
|
+
self._all_tasks = list() # list[task_name]
|
|
287
|
+
self._task_ids = dict() # {task_name1: 0, task_name2: 1, ...}
|
|
288
|
+
self._task_counts = dict()
|
|
289
|
+
self._id_counts = dict()
|
|
290
|
+
|
|
291
|
+
def submit(self, fn, job_name=""):
|
|
292
|
+
"""
|
|
293
|
+
提交并行任务
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
fn: callable
|
|
297
|
+
需要并行的callable对象
|
|
298
|
+
job_name: str
|
|
299
|
+
提交到的任务名, 不同的任务对应不同的进度条
|
|
300
|
+
Returns
|
|
301
|
+
-------
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
# 提交任务,对任务进行分类,提交到对应的task id中,并且封装新的功能:使其在运行完毕后将任务进度更新放入队列
|
|
305
|
+
@functools.wraps(fn)
|
|
306
|
+
def collect(**kwargs):
|
|
307
|
+
"""归集所有的job到对应的task"""
|
|
308
|
+
with warnings.catch_warnings():
|
|
309
|
+
warnings.simplefilter('ignore')
|
|
310
|
+
job = delay(fn)(**kwargs)
|
|
311
|
+
task_name = self._default_task_name if not job_name else job_name
|
|
312
|
+
if task_name not in self._task_ids:
|
|
313
|
+
self._task_ids[task_name] = len(self._all_tasks)
|
|
314
|
+
self._task_counts[task_name] = 0
|
|
315
|
+
self._all_tasks.append(task_name)
|
|
316
|
+
self._id_counts[self._task_ids[task_name]] = 0
|
|
317
|
+
self._task_counts[task_name] += 1
|
|
318
|
+
self._id_counts[self._task_ids[task_name]] += 1
|
|
319
|
+
job.task_id = self._task_ids[task_name]
|
|
320
|
+
job.job_id = self._task_counts[task_name]
|
|
321
|
+
job.task_name = task_name
|
|
322
|
+
self._all_jobs.append(job)
|
|
323
|
+
return job
|
|
324
|
+
|
|
325
|
+
return collect
|
|
326
|
+
|
|
327
|
+
def do(self, *args, **kwargs):
|
|
328
|
+
if self.show_progress:
|
|
329
|
+
# if job_name is not None:
|
|
330
|
+
# ylog.info(f"{job_name} Start")
|
|
331
|
+
# 消息队列进行通信
|
|
332
|
+
manager = multiprocessing.Manager()
|
|
333
|
+
queue = manager.Queue()
|
|
334
|
+
tqdm_bars = [tqdm(total=self._task_counts[task_name],
|
|
335
|
+
desc=f"{str(task_name)}", leave=False) for task_name in
|
|
336
|
+
self._all_tasks]
|
|
337
|
+
# 初始化多个任务的进度条,每个任务一个
|
|
338
|
+
task_ids = [task_id for task_id in range(len(self._all_tasks))]
|
|
339
|
+
# 创建并启动用于更新进度条的线程
|
|
340
|
+
progress_thread = threading.Thread(target=update_progress_bars, args=(
|
|
341
|
+
tqdm_bars, task_ids, queue, len(self._all_tasks), self._id_counts))
|
|
342
|
+
progress_thread.start()
|
|
343
|
+
if self._parallel is not None:
|
|
344
|
+
# 执行并行任务
|
|
345
|
+
result = self._parallel(delayed(run_job)(job=job,
|
|
346
|
+
task_id=job.task_id,
|
|
347
|
+
queue=queue) for job in self._all_jobs)
|
|
348
|
+
else:
|
|
349
|
+
result = [run_job(job=job, task_id=job.task_id, queue=queue) for job in self._all_jobs]
|
|
350
|
+
# time.sleep(.3)
|
|
351
|
+
# 等待进度更新线程执行完毕
|
|
352
|
+
progress_thread.join()
|
|
353
|
+
# if job_name is not None:
|
|
354
|
+
# ylog.info(f"{job_name} Done")
|
|
355
|
+
else:
|
|
356
|
+
if self._parallel is not None:
|
|
357
|
+
result = self._parallel(delayed(job)() for job in self._all_jobs)
|
|
358
|
+
else:
|
|
359
|
+
result = [job() for job in self._all_jobs]
|
|
360
|
+
self._all_jobs = list() # list[job]
|
|
361
|
+
self._all_tasks = list() # list[task_name]
|
|
362
|
+
self._task_ids = dict() # {task_name1: 0, task_name2: 1, ...}
|
|
363
|
+
self._task_counts = dict()
|
|
364
|
+
self._id_counts = dict()
|
|
365
|
+
return result
|
|
366
|
+
|
|
367
|
+
def __enter__(self):
|
|
368
|
+
return self
|
|
369
|
+
|
|
370
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
371
|
+
# 释放进程
|
|
372
|
+
pass
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ygo
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Project-URL: homepage, https://github.com/link-yundi/ygo
|
|
5
|
+
Project-URL: repository, https://github.com/link-yundi/ygo
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: clickhouse-driver>=0.2.9
|
|
10
|
+
Requires-Dist: duckdb>=1.2.2
|
|
11
|
+
Requires-Dist: dynaconf>=3.2.11
|
|
12
|
+
Requires-Dist: joblib>=1.4.2
|
|
13
|
+
Requires-Dist: loguru>=0.7.3
|
|
14
|
+
Requires-Dist: pandas>=2.0.3
|
|
15
|
+
Requires-Dist: polars>=1.8.2
|
|
16
|
+
Requires-Dist: pyarrow>=17.0.0
|
|
17
|
+
Requires-Dist: pymysql>=1.1.1
|
|
18
|
+
Requires-Dist: sqlalchemy>=2.0.40
|
|
19
|
+
Requires-Dist: sqlparse>=0.5.3
|
|
20
|
+
Requires-Dist: tqdm>=4.67.1
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# ygo
|
|
24
|
+
并发执行(加入进度条)以及延迟调用(基于joblib),以及获取对应函数的相关信息
|
|
25
|
+
|
|
26
|
+
### 安装
|
|
27
|
+
```shell
|
|
28
|
+
pip install -U git+https://github.com/link-yundi/ygo.git
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 示例
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
├── a
|
|
35
|
+
│ ├── __init__.py
|
|
36
|
+
│ └── b
|
|
37
|
+
│ ├── __init__.py
|
|
38
|
+
│ └── c.py
|
|
39
|
+
└── test.py
|
|
40
|
+
|
|
41
|
+
c.py 中定义了目标函数
|
|
42
|
+
def test_fn(a, b=2):
|
|
43
|
+
return a+b
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### 场景1: 并发
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import ygo
|
|
50
|
+
import ylog
|
|
51
|
+
from a.b.c import test_fn
|
|
52
|
+
|
|
53
|
+
with ygo.pool(job_name="test parallel", show_progress=True) as go:
|
|
54
|
+
for i in range(10):
|
|
55
|
+
go.submit(test_fn)(a=i, b=2*i)
|
|
56
|
+
for res in go.do():
|
|
57
|
+
ylog.info(res)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### 场景2: 延迟调用
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
>>> fn = delay(test_fn)(a=1, b=2)
|
|
64
|
+
>>> fn()
|
|
65
|
+
3
|
|
66
|
+
>>> # 逐步传递参数
|
|
67
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1)
|
|
68
|
+
>>> fn2 = delay(fn1)(b=2)
|
|
69
|
+
>>> fn2(c=3)
|
|
70
|
+
6
|
|
71
|
+
>>> # 参数更改
|
|
72
|
+
>>> fn1 = delay(lambda a, b, c: a+b+c)(a=1, b=2)
|
|
73
|
+
>>> fn2 = delay(fn1)(c=3, b=5)
|
|
74
|
+
>>> fn2()
|
|
75
|
+
9
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### 场景3: 获取目标函数信息
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
>>> ygo.fn_info(test_fn)
|
|
82
|
+
=============================================================
|
|
83
|
+
a.b.c.test_fn(a, b=2)
|
|
84
|
+
=============================================================
|
|
85
|
+
def test_fn(a, b=2):
|
|
86
|
+
return a+b
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### 场景4: 通过字符串解析函数并执行
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
>>> ygo.fn_from_str("a.b.c.test_fn")(a=1, b=5)
|
|
93
|
+
6
|
|
94
|
+
```
|
|
95
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ycat/__init__.py,sha256=R5sHDLS6E2Rw7m6nWny-NibmfPu_DeFabpA_AnIA3IQ,477
|
|
2
|
+
ycat/client.py,sha256=q4Hdbat1q78S9YwrKGTtYNT6fiatgE_8-NXZktM1FS0,4377
|
|
3
|
+
ycat/dtype.py,sha256=mRGLDe_Ho6-tDsoj5wwrAzozEoIYCAHGKdpRqgBfUcI,12577
|
|
4
|
+
ycat/parse.py,sha256=D9vhz-qLR-UJzFoEVWlQ44M-AMykHT8k3Lf7BwnaTHY,2287
|
|
5
|
+
ycat/yck.py,sha256=FlGMBuKEngB4TwFXMp4P3dLg9IfFmUg3eDqXzQ0kQoI,2738
|
|
6
|
+
ygo/__init__.py,sha256=FMN06Tfa8_oV26eklBZCtGTyHZ6MghHxHj4PS_FSXCA,222
|
|
7
|
+
ygo/exceptions.py,sha256=4Kd92kpwpsXHJJkSv4OqcN--PEEvIGGvDDgOOsk68gg,385
|
|
8
|
+
ygo/ygo.py,sha256=vCMUur_41yY0QB4gj8K5wBZHql_cbmANhI8QwPRCTmo,11613
|
|
9
|
+
ygo-1.0.1.dist-info/licenses/LICENSE,sha256=6AKUWQ1xe-jwPSFv_H6FMQLNNWb7AYqzuEUTwlP2S8M,1067
|
|
10
|
+
ylog/__init__.py,sha256=2sIp4PHNoQMCi0QtIarTI4raACd7SdRHNY7fY5hKYwc,397
|
|
11
|
+
ylog/core.py,sha256=uO_r5wDrBN5edV_TDHUNzcqRs6DMDDrrjfyqYZhak4w,7716
|
|
12
|
+
ygo-1.0.1.dist-info/METADATA,sha256=zGBxxHMOrJU474buyCQU745rfjkdnctVOWDXPGL4XEc,2009
|
|
13
|
+
ygo-1.0.1.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
|
14
|
+
ygo-1.0.1.dist-info/top_level.txt,sha256=jEbfiz5fX4iSzDg8_Npdv5SIC_Kphmb1m3vuyD9ZC1E,14
|
|
15
|
+
ygo-1.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 link-yundi
|
|
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.
|
ylog/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
---------------------------------------------
|
|
4
|
+
Created on 2025/5/14 15:37
|
|
5
|
+
@author: ZhangYundi
|
|
6
|
+
@email: yundi.xxii@outlook.com
|
|
7
|
+
---------------------------------------------
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .core import trace, debug, info, warning, error, critical, update_config
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"trace",
|
|
14
|
+
"debug",
|
|
15
|
+
"info",
|
|
16
|
+
"warning",
|
|
17
|
+
"error",
|
|
18
|
+
"critical",
|
|
19
|
+
"update_config"
|
|
20
|
+
]
|