ygo 1.0.2__py3-none-any.whl → 1.2.12__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.
- ygo/__init__.py +30 -9
- ygo/delay.py +89 -0
- ygo/exceptions.py +17 -1
- ygo/lazy.py +50 -0
- ygo/pool.py +286 -0
- ygo/utils.py +230 -0
- ygo-1.2.12.dist-info/METADATA +119 -0
- ygo-1.2.12.dist-info/RECORD +11 -0
- {ygo-1.0.2.dist-info → ygo-1.2.12.dist-info}/WHEEL +1 -1
- ygo-1.2.12.dist-info/top_level.txt +1 -0
- ycat/__init__.py +0 -34
- ycat/client.py +0 -142
- ycat/dtype.py +0 -389
- ycat/parse.py +0 -66
- ycat/yck.py +0 -87
- ygo/ygo.py +0 -372
- ygo-1.0.2.dist-info/METADATA +0 -94
- ygo-1.0.2.dist-info/RECORD +0 -15
- ygo-1.0.2.dist-info/top_level.txt +0 -3
- ylog/__init__.py +0 -20
- ylog/core.py +0 -226
- {ygo-1.0.2.dist-info → ygo-1.2.12.dist-info}/licenses/LICENSE +0 -0
ygo/utils.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
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 functools import wraps
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import warnings
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from .delay import delay
|
|
18
|
+
|
|
19
|
+
def deprecated(use_instead: str = None):
|
|
20
|
+
"""
|
|
21
|
+
标记方法为弃用
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
use_instead: str
|
|
26
|
+
推荐替代使用的方法或者类名称
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def decorator(func):
|
|
33
|
+
"""
|
|
34
|
+
装饰器
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
func: callable
|
|
38
|
+
被装饰的函数
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
@wraps(func)
|
|
44
|
+
def wrapper(*args, **kwargs):
|
|
45
|
+
msg = f"`{func.__name__}` is deprecated. "
|
|
46
|
+
if use_instead:
|
|
47
|
+
msg += f"Please use `{use_instead}` instead."
|
|
48
|
+
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
|
|
49
|
+
return func(*args, **kwargs)
|
|
50
|
+
return wrapper
|
|
51
|
+
return decorator
|
|
52
|
+
|
|
53
|
+
def fn_params(func: callable):
|
|
54
|
+
"""
|
|
55
|
+
获取fn的参数
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
func: callable
|
|
59
|
+
需要获取参数的callable对象
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
list[tuple]
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
stored = delay(func)().stored_kwargs.items()
|
|
66
|
+
return sorted(stored)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def fn_signature_params(func: callable):
|
|
70
|
+
"""获取fn所有定义的参数"""
|
|
71
|
+
return sorted(list(inspect.signature(func).parameters.keys()))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def fn_path(fn: callable) -> str:
|
|
75
|
+
"""
|
|
76
|
+
获取func所在的模块层级结构
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
fn: callable
|
|
80
|
+
需要获取结构的callable对象
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
str
|
|
84
|
+
用 `.` 连接各级层级
|
|
85
|
+
"""
|
|
86
|
+
module = fn.__module__
|
|
87
|
+
# 检查模块是否有 __file__ 属性
|
|
88
|
+
if module.startswith('__main__'):
|
|
89
|
+
if hasattr(module, '__file__'):
|
|
90
|
+
module = module.__file__
|
|
91
|
+
else:
|
|
92
|
+
# 如果在交互式环境中,返回 None 或者一个默认值
|
|
93
|
+
module = "<interactive environment>"
|
|
94
|
+
if module.endswith('.py'):
|
|
95
|
+
module = module.split('.py')[0].split(str(Path(__file__).parent.parent.absolute()))[-1]
|
|
96
|
+
module = '.'.join(module.strip(os.sep).split(os.sep))
|
|
97
|
+
return module
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def fn_code(fn: callable) -> str:
|
|
101
|
+
"""
|
|
102
|
+
返回fn具体的定义代码
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
fn: callable
|
|
107
|
+
需要获取具体定义代码的callable对象
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
str
|
|
112
|
+
以字符串封装定义代码
|
|
113
|
+
|
|
114
|
+
Examples
|
|
115
|
+
--------
|
|
116
|
+
|
|
117
|
+
>>> def test_fn(a, b=2):
|
|
118
|
+
>>> return a+b
|
|
119
|
+
>>> print(fn_code())
|
|
120
|
+
def test_fn(a, b=2):
|
|
121
|
+
return a+b
|
|
122
|
+
"""
|
|
123
|
+
return inspect.getsource(fn)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def fn_info(fn: callable) -> str:
|
|
127
|
+
"""获取函数的fn_mod, params, code"""
|
|
128
|
+
# mod = fn_path(fn)
|
|
129
|
+
params = fn_params(fn)
|
|
130
|
+
code = fn_code(fn)
|
|
131
|
+
all_define_params = sorted(list(inspect.signature(fn).parameters.keys()))
|
|
132
|
+
|
|
133
|
+
default_params = {k: v for k, v in params}
|
|
134
|
+
params_infos = list()
|
|
135
|
+
for p in all_define_params:
|
|
136
|
+
if p in default_params:
|
|
137
|
+
params_infos.append(f'{p}={default_params[p]}')
|
|
138
|
+
else:
|
|
139
|
+
params_infos.append(p)
|
|
140
|
+
params_infos = ', '.join(params_infos)
|
|
141
|
+
|
|
142
|
+
s = f"""
|
|
143
|
+
=============================================================
|
|
144
|
+
{fn.__name__}({params_infos})
|
|
145
|
+
=============================================================
|
|
146
|
+
{code}
|
|
147
|
+
"""
|
|
148
|
+
return s
|
|
149
|
+
|
|
150
|
+
@deprecated("lazy_import")
|
|
151
|
+
def fn_from_str(s):
|
|
152
|
+
"""
|
|
153
|
+
字符串导入对应fn
|
|
154
|
+
s: a.b.c.func
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
s: str
|
|
158
|
+
模块的路径,分隔符 `.`
|
|
159
|
+
"""
|
|
160
|
+
import importlib
|
|
161
|
+
*m_path, func = s.split(".")
|
|
162
|
+
m_path = ".".join(m_path)
|
|
163
|
+
mod = importlib.import_module(m_path)
|
|
164
|
+
_callable = getattr(mod, func)
|
|
165
|
+
return _callable
|
|
166
|
+
|
|
167
|
+
@deprecated("lazy_import")
|
|
168
|
+
def module_from_str(s):
|
|
169
|
+
"""字符串导入模块"""
|
|
170
|
+
import importlib
|
|
171
|
+
m_path = ".".join(s.split('.'))
|
|
172
|
+
mod = importlib.import_module(m_path)
|
|
173
|
+
return mod
|
|
174
|
+
|
|
175
|
+
def locate(path: str) -> Any:
|
|
176
|
+
"""
|
|
177
|
+
Notes
|
|
178
|
+
-----
|
|
179
|
+
Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
|
180
|
+
|
|
181
|
+
Locate an object by name or dotted path, importing as necessary.
|
|
182
|
+
This is similar to the pydoc function `locate`, except that it checks for
|
|
183
|
+
the module from the given path from back to front.
|
|
184
|
+
"""
|
|
185
|
+
if path == "":
|
|
186
|
+
raise ImportError("Empty path")
|
|
187
|
+
from importlib import import_module
|
|
188
|
+
from types import ModuleType
|
|
189
|
+
|
|
190
|
+
parts = [part for part in path.split(".")]
|
|
191
|
+
for part in parts:
|
|
192
|
+
if not len(part):
|
|
193
|
+
raise ValueError(
|
|
194
|
+
f"Error loading '{path}': invalid dotstring."
|
|
195
|
+
+ "\nRelative imports are not supported."
|
|
196
|
+
)
|
|
197
|
+
assert len(parts) > 0
|
|
198
|
+
part0 = parts[0]
|
|
199
|
+
try:
|
|
200
|
+
obj = import_module(part0)
|
|
201
|
+
except Exception as exc_import:
|
|
202
|
+
raise ImportError(
|
|
203
|
+
f"Error loading '{path}':\n{repr(exc_import)}"
|
|
204
|
+
+ f"\nAre you sure that module '{part0}' is installed?"
|
|
205
|
+
) from exc_import
|
|
206
|
+
for m in range(1, len(parts)):
|
|
207
|
+
part = parts[m]
|
|
208
|
+
try:
|
|
209
|
+
obj = getattr(obj, part)
|
|
210
|
+
except AttributeError as exc_attr:
|
|
211
|
+
parent_dotpath = ".".join(parts[:m])
|
|
212
|
+
if isinstance(obj, ModuleType):
|
|
213
|
+
mod = ".".join(parts[: m + 1])
|
|
214
|
+
try:
|
|
215
|
+
obj = import_module(mod)
|
|
216
|
+
continue
|
|
217
|
+
except ModuleNotFoundError as exc_import:
|
|
218
|
+
raise ImportError(
|
|
219
|
+
f"Error loading '{path}':\n{repr(exc_import)}"
|
|
220
|
+
+ f"\nAre you sure that '{part}' is importable from module '{parent_dotpath}'?"
|
|
221
|
+
) from exc_import
|
|
222
|
+
except Exception as exc_import:
|
|
223
|
+
raise ImportError(
|
|
224
|
+
f"Error loading '{path}':\n{repr(exc_import)}"
|
|
225
|
+
) from exc_import
|
|
226
|
+
raise ImportError(
|
|
227
|
+
f"Error loading '{path}':\n{repr(exc_attr)}"
|
|
228
|
+
+ f"\nAre you sure that '{part}' is an attribute of '{parent_dotpath}'?"
|
|
229
|
+
) from exc_attr
|
|
230
|
+
return obj
|
|
@@ -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
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
ygo/__init__.py,sha256=vP3Vs9LarNsn2TV8QlpuRP94WWK7m3q-gIWw6vLRED0,537
|
|
2
|
+
ygo/delay.py,sha256=66xtPXqyD630FL7LWL5qJKAIZvyGDwZyM4qPfk8Czlg,2206
|
|
3
|
+
ygo/exceptions.py,sha256=0OYDYt_9KKo8mF2XBG5QkCMr3-ASp69VDSPOEwlIsrI,660
|
|
4
|
+
ygo/lazy.py,sha256=NRE38vWKfA19fTRIbgCypdoxXhJulDtHMd7jw6CzhUc,1303
|
|
5
|
+
ygo/pool.py,sha256=hKvUttBhK2gU4WHocvg-2bjS9uVJ9jITbRb1tL2JYY4,10699
|
|
6
|
+
ygo/utils.py,sha256=45ZM7FUraDA69jwf1arbBuuS-a0wMVf9sT7ma5bGd4E,6224
|
|
7
|
+
ygo-1.2.12.dist-info/licenses/LICENSE,sha256=6AKUWQ1xe-jwPSFv_H6FMQLNNWb7AYqzuEUTwlP2S8M,1067
|
|
8
|
+
ygo-1.2.12.dist-info/METADATA,sha256=awbNtAfd34cw1Rjyc2WHerxgw4U8kSoozKgtUNNCQWw,3612
|
|
9
|
+
ygo-1.2.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
ygo-1.2.12.dist-info/top_level.txt,sha256=4E07GOD3KS5--r4rP13upXfTEgk3JGJpQmMkdkJ4t74,4
|
|
11
|
+
ygo-1.2.12.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ygo
|
ycat/__init__.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
---------------------------------------------
|
|
4
|
-
Created on 2025/5/14 18:29
|
|
5
|
-
@author: ZhangYundi
|
|
6
|
-
@email: yundi.xxii@outlook.com
|
|
7
|
-
---------------------------------------------
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from .client import (
|
|
11
|
-
HOME,
|
|
12
|
-
CATDB,
|
|
13
|
-
get_settings,
|
|
14
|
-
sql,
|
|
15
|
-
put,
|
|
16
|
-
create_engine_ck,
|
|
17
|
-
create_engine_mysql,
|
|
18
|
-
read_mysql,
|
|
19
|
-
read_ck,
|
|
20
|
-
tb_path,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
__all__ = [
|
|
24
|
-
"HOME",
|
|
25
|
-
"CATDB",
|
|
26
|
-
"get_settings",
|
|
27
|
-
"sql",
|
|
28
|
-
"put",
|
|
29
|
-
"create_engine_ck",
|
|
30
|
-
"create_engine_mysql",
|
|
31
|
-
"read_mysql",
|
|
32
|
-
"read_ck",
|
|
33
|
-
"tb_path",
|
|
34
|
-
]
|
ycat/client.py
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
---------------------------------------------
|
|
4
|
-
Created on 2024/7/1 09:44
|
|
5
|
-
@author: ZhangYundi
|
|
6
|
-
@email: yundi.xxii@outlook.com
|
|
7
|
-
---------------------------------------------
|
|
8
|
-
"""
|
|
9
|
-
import os
|
|
10
|
-
import re
|
|
11
|
-
from functools import partial
|
|
12
|
-
from typing import Optional
|
|
13
|
-
|
|
14
|
-
import polars as pl
|
|
15
|
-
from dynaconf import Dynaconf
|
|
16
|
-
from sqlalchemy import create_engine
|
|
17
|
-
|
|
18
|
-
import ylog
|
|
19
|
-
from .parse import extract_table_names_from_sql
|
|
20
|
-
from .yck import connect, query_polars
|
|
21
|
-
|
|
22
|
-
# 配置文件在 “~/.catdb/setting.toml”
|
|
23
|
-
USERHOME = os.path.expanduser('~') # 用户家目录
|
|
24
|
-
NAME = "catdb"
|
|
25
|
-
CONFIG_PATH = os.path.join(USERHOME, f".{NAME}", "settings.toml")
|
|
26
|
-
if not os.path.exists(CONFIG_PATH):
|
|
27
|
-
try:
|
|
28
|
-
os.makedirs(os.path.dirname(CONFIG_PATH))
|
|
29
|
-
except FileExistsError as e:
|
|
30
|
-
...
|
|
31
|
-
except Exception as e:
|
|
32
|
-
ylog.error(f"配置文件生成失败: {e}")
|
|
33
|
-
catdb_path = os.path.join(USERHOME, NAME)
|
|
34
|
-
template_content = f"""[paths]
|
|
35
|
-
{NAME}="{catdb_path}" # 本地数据库,默认家目录
|
|
36
|
-
|
|
37
|
-
## 数据库配置:
|
|
38
|
-
[database]
|
|
39
|
-
[database.ck]
|
|
40
|
-
# urls=["<host1>:<port1>", "<host2>:<port2>",]
|
|
41
|
-
# user="xxx"
|
|
42
|
-
# password="xxxxxx"
|
|
43
|
-
[database.jy]
|
|
44
|
-
# url="<host>:<port>"
|
|
45
|
-
# user="xxxx"
|
|
46
|
-
# password="xxxxxx"
|
|
47
|
-
|
|
48
|
-
## 视情况自由增加其他配置
|
|
49
|
-
"""
|
|
50
|
-
with open(CONFIG_PATH, "w") as f:
|
|
51
|
-
f.write(template_content)
|
|
52
|
-
ylog.info(f"生成配置文件: {CONFIG_PATH}")
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def get_settings():
|
|
56
|
-
try:
|
|
57
|
-
return Dynaconf(settings_files=[CONFIG_PATH]).as_dict()
|
|
58
|
-
except:
|
|
59
|
-
return {}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
HOME = USERHOME
|
|
63
|
-
CATDB = os.path.join(HOME, NAME)
|
|
64
|
-
# 读取配置文件覆盖
|
|
65
|
-
SETTINGS = get_settings()
|
|
66
|
-
if SETTINGS is not None:
|
|
67
|
-
CATDB = SETTINGS["PATHS"][NAME]
|
|
68
|
-
if not CATDB.endswith(NAME):
|
|
69
|
-
CATDB = os.path.join(CATDB, NAME)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# ======================== 本地数据库 catdb ========================
|
|
73
|
-
def tb_path(tb_name: str) -> str:
|
|
74
|
-
"""
|
|
75
|
-
返回指定表名 完整的本地路径
|
|
76
|
-
Parameters
|
|
77
|
-
----------
|
|
78
|
-
tb_name: str
|
|
79
|
-
表名,路径写法: a/b/c
|
|
80
|
-
Returns
|
|
81
|
-
-------
|
|
82
|
-
full_abs_path: str
|
|
83
|
-
完整的本地绝对路径 $HOME/catdb/a/b/c
|
|
84
|
-
"""
|
|
85
|
-
return os.path.join(CATDB, tb_name)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def put(df: pl.DataFrame, tb_name: str, partitions: Optional[list[str]] = None, abs_path: bool = False):
|
|
89
|
-
if not abs_path:
|
|
90
|
-
tbpath = tb_path(tb_name)
|
|
91
|
-
else:
|
|
92
|
-
tbpath = tb_name
|
|
93
|
-
if not os.path.exists(tbpath):
|
|
94
|
-
try:
|
|
95
|
-
os.makedirs(tbpath)
|
|
96
|
-
except FileExistsError as e:
|
|
97
|
-
pass
|
|
98
|
-
if partitions is not None:
|
|
99
|
-
for field in partitions:
|
|
100
|
-
assert field in df.columns, f'dataframe must have Field `{field}`'
|
|
101
|
-
df.write_parquet(tbpath, partition_by=partitions)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def sql(query: str, abs_path: bool = False):
|
|
105
|
-
tbs = extract_table_names_from_sql(query)
|
|
106
|
-
convertor = dict()
|
|
107
|
-
for tb in tbs:
|
|
108
|
-
if not abs_path:
|
|
109
|
-
db_path = tb_path(tb)
|
|
110
|
-
else:
|
|
111
|
-
db_path = tb
|
|
112
|
-
format_tb = f"read_parquet('{db_path}/**/*.parquet')"
|
|
113
|
-
convertor[tb] = format_tb
|
|
114
|
-
pattern = re.compile("|".join(re.escape(k) for k in convertor.keys()))
|
|
115
|
-
new_query = pattern.sub(lambda m: convertor[m.group(0)], query)
|
|
116
|
-
return pl.sql(new_query).collect()
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def create_engine_ck(urls: list[str], user: str, password: str):
|
|
120
|
-
return partial(connect, urls, user, password)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def read_ck(sql, eng) -> pl.DataFrame:
|
|
124
|
-
with eng() as conn:
|
|
125
|
-
return query_polars(sql, conn)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def create_engine_mysql(url, user, password, database):
|
|
129
|
-
"""
|
|
130
|
-
:param url: <host>:<port>
|
|
131
|
-
:param user:
|
|
132
|
-
:param password:
|
|
133
|
-
:param database:
|
|
134
|
-
:return:
|
|
135
|
-
"""
|
|
136
|
-
engine = create_engine(f"mysql+pymysql://{user}:{password}@{url}/{database}")
|
|
137
|
-
return engine
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def read_mysql(sql, eng) -> pl.DataFrame:
|
|
141
|
-
with eng.connect() as conn:
|
|
142
|
-
return pl.read_database(sql, conn)
|