ygo 1.0.1__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.

Potentially problematic release.


This version of ygo might be problematic. Click here for more details.

ygo-1.0.1/LICENSE ADDED
@@ -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.
ygo-1.0.1/PKG-INFO ADDED
@@ -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
+
ygo-1.0.1/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # ygo
2
+ 并发执行(加入进度条)以及延迟调用(基于joblib),以及获取对应函数的相关信息
3
+
4
+ ### 安装
5
+ ```shell
6
+ pip install -U git+https://github.com/link-yundi/ygo.git
7
+ ```
8
+
9
+ ### 示例
10
+
11
+ ```
12
+ ├── a
13
+ │   ├── __init__.py
14
+ │   └── b
15
+ │   ├── __init__.py
16
+ │   └── c.py
17
+ └── test.py
18
+
19
+ c.py 中定义了目标函数
20
+ def test_fn(a, b=2):
21
+ return a+b
22
+ ```
23
+
24
+ #### 场景1: 并发
25
+
26
+ ```python
27
+ import ygo
28
+ import ylog
29
+ from a.b.c import test_fn
30
+
31
+ with ygo.pool(job_name="test parallel", show_progress=True) as go:
32
+ for i in range(10):
33
+ go.submit(test_fn)(a=i, b=2*i)
34
+ for res in go.do():
35
+ ylog.info(res)
36
+ ```
37
+
38
+ #### 场景2: 延迟调用
39
+
40
+ ```
41
+ >>> fn = delay(test_fn)(a=1, b=2)
42
+ >>> fn()
43
+ 3
44
+ >>> # 逐步传递参数
45
+ >>> fn1 = delay(lambda a, b, c: a+b+c)(a=1)
46
+ >>> fn2 = delay(fn1)(b=2)
47
+ >>> fn2(c=3)
48
+ 6
49
+ >>> # 参数更改
50
+ >>> fn1 = delay(lambda a, b, c: a+b+c)(a=1, b=2)
51
+ >>> fn2 = delay(fn1)(c=3, b=5)
52
+ >>> fn2()
53
+ 9
54
+ ```
55
+
56
+ #### 场景3: 获取目标函数信息
57
+
58
+ ```
59
+ >>> ygo.fn_info(test_fn)
60
+ =============================================================
61
+ a.b.c.test_fn(a, b=2)
62
+ =============================================================
63
+ def test_fn(a, b=2):
64
+ return a+b
65
+ ```
66
+
67
+ #### 场景4: 通过字符串解析函数并执行
68
+
69
+ ```
70
+ >>> ygo.fn_from_str("a.b.c.test_fn")(a=1, b=5)
71
+ 6
72
+ ```
73
+
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ygo"
7
+ version = "1.0.1"
8
+ description = ""
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = [
12
+ "clickhouse-driver>=0.2.9",
13
+ "duckdb>=1.2.2",
14
+ "dynaconf>=3.2.11",
15
+ "joblib>=1.4.2",
16
+ "loguru>=0.7.3",
17
+ "pandas>=2.0.3",
18
+ "polars>=1.8.2",
19
+ "pyarrow>=17.0.0",
20
+ "pymysql>=1.1.1",
21
+ "sqlalchemy>=2.0.40",
22
+ "sqlparse>=0.5.3",
23
+ "tqdm>=4.67.1",
24
+ ]
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["."]
28
+ include = ["ygo", "ygo.*", "ylog", "ylog.*", "ycat", "ycat.*"]
29
+
30
+ [project.urls]
31
+ homepage = "https://github.com/link-yundi/ygo"
32
+ repository = "https://github.com/link-yundi/ygo"
ygo-1.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
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 HOME, CATDB, SETTINGS, sql, put, create_engine_ck, create_engine_mysql, read_mysql, read_ck
11
+
12
+ __all__ = [
13
+ "HOME",
14
+ "CATDB",
15
+ "SETTINGS",
16
+ "sql",
17
+ "put",
18
+ "create_engine_ck",
19
+ "create_engine_mysql",
20
+ "read_mysql",
21
+ "read_ck",
22
+ ]
@@ -0,0 +1,157 @@
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 typing import Optional
12
+ from .yck import connect, query_polars
13
+
14
+ import duckdb
15
+ import polars as pl
16
+ import ylog
17
+ from dynaconf import Dynaconf
18
+ from sqlalchemy import create_engine
19
+ from functools import partial
20
+
21
+ from .parse import extract_table_names_from_sql
22
+
23
+ # 配置文件在 “~/.catdb/setting.toml”
24
+ USERHOME = os.path.expanduser('~') # 用户家目录
25
+ CONFIG_PATH = os.path.join(USERHOME, ".catdb", "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, "catdb")
34
+ template_content = f"""[paths]
35
+ catdb="{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])
58
+ except:
59
+ return
60
+
61
+
62
+ HOME = USERHOME
63
+ CATDB = os.path.join(HOME, "catdb")
64
+ # 读取配置文件覆盖
65
+ SETTINGS = get_settings()
66
+ if SETTINGS is not None:
67
+ CATDB = SETTINGS.paths.catdb
68
+
69
+
70
+ # ======================== 本地数据库 catdb ========================
71
+ def tb_path(tb_name: str) -> str:
72
+ """
73
+ 返回指定表名 完整的本地路径
74
+ Parameters
75
+ ----------
76
+ tb_name: str
77
+ 表名,路径写法: a/b/c
78
+ Returns
79
+ -------
80
+ full_abs_path: str
81
+ 完整的本地绝对路径 $HOME/catdb/a/b/c
82
+ """
83
+ return os.path.join(CATDB, tb_name)
84
+
85
+ def put(df: pl.DataFrame, tb_name: str, partitions: Optional[list[str]] = None):
86
+ """
87
+ 将数据写入duck_db支持的parquet格式文件
88
+ Parameters
89
+ ----------
90
+ df: pandas.DataFrame | pandas.Series | polars.DataFrame
91
+ 写入的数据
92
+ tb_name: str
93
+ 表名,支持路径写法, a/b/c
94
+ partitions: Optional[List[str]]
95
+ 根据哪些字段进行分区,默认不分区
96
+ """
97
+ tbpath = tb_path(tb_name)
98
+ if not os.path.exists(tbpath):
99
+ try:
100
+ os.makedirs(tbpath)
101
+ except FileExistsError as e:
102
+ pass
103
+ if partitions is not None:
104
+ for field in partitions:
105
+ assert field in df.columns, f'dataframe must have Field `{field}`'
106
+ df.write_parquet(tbpath, partition_by=partitions)
107
+
108
+
109
+ def sql(query: str):
110
+ """
111
+ 从duckdb中读取数据, query语法与mysql一致,特殊语法请查duckdb官网: https://duckdb.org/docs/sql/query_syntax/select
112
+ Parameters
113
+ ----------
114
+ query: str
115
+ 查询语句
116
+ Returns
117
+ -------
118
+ result: duckdb.DuckDBPyRelation
119
+ 查询结果
120
+ """
121
+ tbs = extract_table_names_from_sql(query)
122
+ convertor = dict()
123
+ for tb in tbs:
124
+ db_path = tb_path(tb)
125
+ format_tb = f"read_parquet('{db_path}/**/*.parquet', hive_partitioning = true)"
126
+ convertor[tb] = format_tb
127
+ pattern = re.compile("|".join(re.escape(k) for k in convertor.keys()))
128
+ new_query = pattern.sub(lambda m: convertor[m.group(0)], query)
129
+ conn = duckdb.connect()
130
+ conn.execute("PRAGMA disable_progress_bar;")
131
+ conn.execute("PRAGMA threads=1;")
132
+ res = conn.execute(new_query).fetch_arrow_table()
133
+ res = pl.from_arrow(res)
134
+ conn.close()
135
+ return res
136
+
137
+ def create_engine_ck(urls: list[str], user: str, password: str):
138
+ return partial(connect, urls, user, password)
139
+
140
+ def read_ck(sql, eng)->pl.DataFrame:
141
+ with eng() as conn:
142
+ return query_polars(sql, conn)
143
+
144
+ def create_engine_mysql(url, user, password, database):
145
+ """
146
+ :param url: <host>:<port>
147
+ :param user:
148
+ :param password:
149
+ :param database:
150
+ :return:
151
+ """
152
+ engine = create_engine(f"mysql+pymysql://{user}:{password}@{url}/{database}")
153
+ return engine
154
+
155
+ def read_mysql(sql, eng) -> pl.DataFrame:
156
+ with eng.connect() as conn:
157
+ return pl.read_database(sql, conn)