funcguard 0.2.42__tar.gz → 0.2.44__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.
- {funcguard-0.2.42 → funcguard-0.2.44}/PKG-INFO +39 -3
- {funcguard-0.2.42 → funcguard-0.2.44}/README.md +38 -2
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/__init__.py +6 -3
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/core.py +89 -3
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/__init__.py +3 -1
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/convert_utils.py +42 -1
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/statistics/agg_utils.py +10 -12
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/statistics/count_utils.py +9 -10
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/statistics/df_statistics.py +18 -18
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard.egg-info/PKG-INFO +39 -3
- {funcguard-0.2.42 → funcguard-0.2.44}/setup.py +1 -1
- {funcguard-0.2.42 → funcguard-0.2.44}/LICENSE +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/calculate.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/data_models/__init__.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/data_models/request_models.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/ip_utils.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/log_utils.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/date_utils.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/fill_round.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/filter.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/json_utils/__init__.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/json_utils/json_parser.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/statistics/__init__.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/pd_utils/statistics/mask_utils.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/printer.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/time_utils.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard/tools.py +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard.egg-info/SOURCES.txt +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard.egg-info/dependency_links.txt +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard.egg-info/not-zip-safe +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard.egg-info/requires.txt +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/funcguard.egg-info/top_level.txt +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/setup.cfg +0 -0
- {funcguard-0.2.42 → funcguard-0.2.44}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: funcguard
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.44
|
|
4
4
|
Summary: FuncGuard是一个Python库,提供函数执行超时控制、重试机制、HTTP请求封装和格式化打印工具。
|
|
5
5
|
Home-page: https://github.com/tinycen/funcguard
|
|
6
6
|
Author: tinycen
|
|
@@ -34,7 +34,7 @@ FuncGuard是一个Python库,提供了函数执行超时控制和重试机制
|
|
|
34
34
|
|
|
35
35
|
| 分类 | 功能描述 | 文档 |
|
|
36
36
|
|------|----------|------|
|
|
37
|
-
| **核心功能** |
|
|
37
|
+
| **核心功能** | 函数执行超时控制、函数执行失败自动重试、交互式选择菜单 | - |
|
|
38
38
|
| **网络请求** | HTTP请求封装(支持自动重试)、MD5哈希、Basic Auth编码 | [查看](./docs/network.md) |
|
|
39
39
|
| **时间工具** | 时间日志记录、耗时统计、执行时间监控和警告、时间等待(带倒计时) | [查看](./docs/time_utils.md) |
|
|
40
40
|
| **打印工具** | 格式化分隔线、块打印、标题打印、进度条显示 | - |
|
|
@@ -95,6 +95,41 @@ except Exception as e:
|
|
|
95
95
|
print(f"重试后仍然失败: {e}")
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
### 交互式选择菜单
|
|
99
|
+
|
|
100
|
+
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单,支持超时自动选择:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from funcguard import ask_select
|
|
104
|
+
|
|
105
|
+
# 基础用法 - 选择属性填写模式
|
|
106
|
+
result = ask_select(
|
|
107
|
+
options={True: "需要填写属性", False: "不需要填写属性", "all": "不限制"},
|
|
108
|
+
default_key="all",
|
|
109
|
+
prompt="请选择属性填写模式",
|
|
110
|
+
timeout=5
|
|
111
|
+
)
|
|
112
|
+
print(f"用户选择: {result}") # 返回 True, False 或 "all"
|
|
113
|
+
|
|
114
|
+
# 选择部署环境
|
|
115
|
+
env = ask_select(
|
|
116
|
+
options={"dev": "开发环境", "test": "测试环境", "prod": "生产环境"},
|
|
117
|
+
default_key="dev",
|
|
118
|
+
prompt="请选择部署环境",
|
|
119
|
+
timeout=3
|
|
120
|
+
)
|
|
121
|
+
print(f"当前环境: {env}")
|
|
122
|
+
|
|
123
|
+
# 选择数量(使用整数作为键)
|
|
124
|
+
count = ask_select(
|
|
125
|
+
options={10: "10条", 50: "50条", 100: "100条"},
|
|
126
|
+
default_key=50,
|
|
127
|
+
prompt="查询数量",
|
|
128
|
+
timeout=5
|
|
129
|
+
)
|
|
130
|
+
print(f"查询 {count} 条数据")
|
|
131
|
+
```
|
|
132
|
+
|
|
98
133
|
### HTTP请求
|
|
99
134
|
|
|
100
135
|
FuncGuard 提供了强大的 HTTP 请求功能,支持自动重试、请求日志记录、以及 curl_cffi 兜底(用于绕过反爬虫检测)。详细文档请参考 [network.md](docs/network.md)。
|
|
@@ -496,6 +531,7 @@ print(f"当前价格: {current_price}, 变化: {price_change}") # 输出: 当
|
|
|
496
531
|
|-----------|----------|
|
|
497
532
|
| `timeout_handler` | 函数执行超时控制 |
|
|
498
533
|
| `retry_function` | 函数执行失败自动重试 |
|
|
534
|
+
| `ask_select` | 交互式数字选择菜单(支持超时自动选择) |
|
|
499
535
|
|
|
500
536
|
### 网络请求
|
|
501
537
|
|
|
@@ -542,7 +578,7 @@ print(f"当前价格: {current_price}, 变化: {price_change}") # 输出: 当
|
|
|
542
578
|
| 函数/类名 | 功能说明 | 文档 |
|
|
543
579
|
|-----------|----------|------|
|
|
544
580
|
| `pd_fill_na` / `pd_fill_nat` | 数据填充 | [查看](docs/pandas/fill.md) |
|
|
545
|
-
| `pd_convert_columns` / `pd_convert_decimal` / `pd_convert_numeric_series` / `pd_convert_str_datetime` / `pd_convert_datetime_str` | 类型转换 | [查看](docs/pandas/convert.md) |
|
|
581
|
+
| `pd_convert_columns` / `pd_convert_decimal` / `pd_convert_numeric_series` / `pd_convert_str_datetime` / `pd_convert_datetime_str` / `pd_convert_series` | 类型转换 | [查看](docs/pandas/convert.md) |
|
|
546
582
|
| `pd_load_json` | JSON解析 | [查看](docs/pandas/json.md) |
|
|
547
583
|
| `pd_filter` | 数据筛选 | [查看](docs/pandas/filter.md) |
|
|
548
584
|
| `pd_count` / `pd_value_counts` | 条件计数统计 | [查看](docs/pandas/count.md) |
|
|
@@ -7,7 +7,7 @@ FuncGuard是一个Python库,提供了函数执行超时控制和重试机制
|
|
|
7
7
|
|
|
8
8
|
| 分类 | 功能描述 | 文档 |
|
|
9
9
|
|------|----------|------|
|
|
10
|
-
| **核心功能** |
|
|
10
|
+
| **核心功能** | 函数执行超时控制、函数执行失败自动重试、交互式选择菜单 | - |
|
|
11
11
|
| **网络请求** | HTTP请求封装(支持自动重试)、MD5哈希、Basic Auth编码 | [查看](./docs/network.md) |
|
|
12
12
|
| **时间工具** | 时间日志记录、耗时统计、执行时间监控和警告、时间等待(带倒计时) | [查看](./docs/time_utils.md) |
|
|
13
13
|
| **打印工具** | 格式化分隔线、块打印、标题打印、进度条显示 | - |
|
|
@@ -68,6 +68,41 @@ except Exception as e:
|
|
|
68
68
|
print(f"重试后仍然失败: {e}")
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
### 交互式选择菜单
|
|
72
|
+
|
|
73
|
+
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单,支持超时自动选择:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from funcguard import ask_select
|
|
77
|
+
|
|
78
|
+
# 基础用法 - 选择属性填写模式
|
|
79
|
+
result = ask_select(
|
|
80
|
+
options={True: "需要填写属性", False: "不需要填写属性", "all": "不限制"},
|
|
81
|
+
default_key="all",
|
|
82
|
+
prompt="请选择属性填写模式",
|
|
83
|
+
timeout=5
|
|
84
|
+
)
|
|
85
|
+
print(f"用户选择: {result}") # 返回 True, False 或 "all"
|
|
86
|
+
|
|
87
|
+
# 选择部署环境
|
|
88
|
+
env = ask_select(
|
|
89
|
+
options={"dev": "开发环境", "test": "测试环境", "prod": "生产环境"},
|
|
90
|
+
default_key="dev",
|
|
91
|
+
prompt="请选择部署环境",
|
|
92
|
+
timeout=3
|
|
93
|
+
)
|
|
94
|
+
print(f"当前环境: {env}")
|
|
95
|
+
|
|
96
|
+
# 选择数量(使用整数作为键)
|
|
97
|
+
count = ask_select(
|
|
98
|
+
options={10: "10条", 50: "50条", 100: "100条"},
|
|
99
|
+
default_key=50,
|
|
100
|
+
prompt="查询数量",
|
|
101
|
+
timeout=5
|
|
102
|
+
)
|
|
103
|
+
print(f"查询 {count} 条数据")
|
|
104
|
+
```
|
|
105
|
+
|
|
71
106
|
### HTTP请求
|
|
72
107
|
|
|
73
108
|
FuncGuard 提供了强大的 HTTP 请求功能,支持自动重试、请求日志记录、以及 curl_cffi 兜底(用于绕过反爬虫检测)。详细文档请参考 [network.md](docs/network.md)。
|
|
@@ -469,6 +504,7 @@ print(f"当前价格: {current_price}, 变化: {price_change}") # 输出: 当
|
|
|
469
504
|
|-----------|----------|
|
|
470
505
|
| `timeout_handler` | 函数执行超时控制 |
|
|
471
506
|
| `retry_function` | 函数执行失败自动重试 |
|
|
507
|
+
| `ask_select` | 交互式数字选择菜单(支持超时自动选择) |
|
|
472
508
|
|
|
473
509
|
### 网络请求
|
|
474
510
|
|
|
@@ -515,7 +551,7 @@ print(f"当前价格: {current_price}, 变化: {price_change}") # 输出: 当
|
|
|
515
551
|
| 函数/类名 | 功能说明 | 文档 |
|
|
516
552
|
|-----------|----------|------|
|
|
517
553
|
| `pd_fill_na` / `pd_fill_nat` | 数据填充 | [查看](docs/pandas/fill.md) |
|
|
518
|
-
| `pd_convert_columns` / `pd_convert_decimal` / `pd_convert_numeric_series` / `pd_convert_str_datetime` / `pd_convert_datetime_str` | 类型转换 | [查看](docs/pandas/convert.md) |
|
|
554
|
+
| `pd_convert_columns` / `pd_convert_decimal` / `pd_convert_numeric_series` / `pd_convert_str_datetime` / `pd_convert_datetime_str` / `pd_convert_series` | 类型转换 | [查看](docs/pandas/convert.md) |
|
|
519
555
|
| `pd_load_json` | JSON解析 | [查看](docs/pandas/json.md) |
|
|
520
556
|
| `pd_filter` | 数据筛选 | [查看](docs/pandas/filter.md) |
|
|
521
557
|
| `pd_count` / `pd_value_counts` | 条件计数统计 | [查看](docs/pandas/count.md) |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .core import timeout_handler, retry_function
|
|
1
|
+
from .core import timeout_handler, retry_function, ask_select
|
|
2
2
|
from .tools import send_request, curl_cffi_request, check_url_valid, encode_basic_auth, md5_hash
|
|
3
3
|
from .time_utils import (
|
|
4
4
|
time_log, time_diff, time_monitor, time_wait, color_logger,
|
|
@@ -14,7 +14,8 @@ from .pd_utils import (
|
|
|
14
14
|
round_columns as pd_round_columns,
|
|
15
15
|
cal_date_diff as pd_cal_date_diff,
|
|
16
16
|
|
|
17
|
-
#
|
|
17
|
+
# 数据转换类
|
|
18
|
+
convert_series as pd_convert_series,
|
|
18
19
|
convert_columns as pd_convert_columns,
|
|
19
20
|
convert_decimal as pd_convert_decimal,
|
|
20
21
|
convert_numeric_series as pd_convert_numeric_series,
|
|
@@ -49,6 +50,7 @@ __all__ = [
|
|
|
49
50
|
# 核心功能
|
|
50
51
|
"timeout_handler",
|
|
51
52
|
"retry_function",
|
|
53
|
+
"ask_select",
|
|
52
54
|
|
|
53
55
|
# 网络请求工具
|
|
54
56
|
"md5_hash",
|
|
@@ -86,7 +88,8 @@ __all__ = [
|
|
|
86
88
|
"pd_fill_nat",
|
|
87
89
|
"pd_round_columns",
|
|
88
90
|
|
|
89
|
-
#
|
|
91
|
+
# 数据转换类
|
|
92
|
+
"pd_convert_series",
|
|
90
93
|
"pd_cal_date_diff",
|
|
91
94
|
"pd_convert_columns",
|
|
92
95
|
"pd_convert_decimal",
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import sys
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor , TimeoutError
|
|
4
|
+
|
|
5
|
+
|
|
1
6
|
class FuncguardTimeoutError(Exception):
|
|
2
7
|
"""
|
|
3
8
|
funcguard库专用的超时异常类。
|
|
4
|
-
|
|
9
|
+
|
|
5
10
|
为了避免与concurrent.futures.TimeoutError和Python内置TimeoutError的命名冲突,
|
|
6
11
|
特定义此异常类来明确表示这是funcguard库抛出的函数执行超时异常。
|
|
7
12
|
这样用户可以清晰地区分异常来源,并进行针对性的异常处理。
|
|
8
13
|
"""
|
|
9
14
|
pass
|
|
10
15
|
|
|
11
|
-
import time
|
|
12
|
-
from concurrent.futures import ThreadPoolExecutor , TimeoutError
|
|
13
16
|
|
|
14
17
|
# 计算函数运行时间
|
|
15
18
|
def timeout_handler( func, args = (), kwargs = None, execution_timeout = 90 ):
|
|
@@ -86,3 +89,86 @@ def retry_function( func , max_retries = 5 , execute_timeout = 90 , task_name =
|
|
|
86
89
|
if last_exception:
|
|
87
90
|
raise last_exception # 重新抛出最后一个异常
|
|
88
91
|
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# 交互式选择菜单
|
|
95
|
+
def ask_select(
|
|
96
|
+
options: dict,
|
|
97
|
+
default_key = None,
|
|
98
|
+
prompt: str = "请选择",
|
|
99
|
+
timeout: int = 5,
|
|
100
|
+
):
|
|
101
|
+
"""
|
|
102
|
+
显示一个数字选择菜单,接受用户输入,超时自动返回默认值。
|
|
103
|
+
|
|
104
|
+
参数:
|
|
105
|
+
options: 字典,键为选项标识(任意类型),值为显示文本
|
|
106
|
+
例如: {True: "需要填写属性", False: "不需要填写属性", "all": "不限制"}
|
|
107
|
+
default_key: 超时或输入无效时的默认返回值,若为None则使用最后一个选项的键
|
|
108
|
+
prompt: 提示语前缀
|
|
109
|
+
timeout: 超时时间(秒),默认5秒
|
|
110
|
+
|
|
111
|
+
返回:
|
|
112
|
+
用户选择的选项键(options中的某个键),或default_key
|
|
113
|
+
|
|
114
|
+
示例:
|
|
115
|
+
>>> result = ask_select(
|
|
116
|
+
... {True: "需要填写属性", False: "不需要填写属性", "all": "不限制"},
|
|
117
|
+
... default_key="all",
|
|
118
|
+
... prompt="请选择属性填写模式",
|
|
119
|
+
... timeout=5,
|
|
120
|
+
... )
|
|
121
|
+
"""
|
|
122
|
+
# 确保选项非空
|
|
123
|
+
if not options:
|
|
124
|
+
raise ValueError("options字典不能为空")
|
|
125
|
+
|
|
126
|
+
# 设置默认值
|
|
127
|
+
if default_key is None:
|
|
128
|
+
default_key = list(options.keys())[-1]
|
|
129
|
+
|
|
130
|
+
# 构建选项列表(保持字典顺序)
|
|
131
|
+
items = list(options.items())
|
|
132
|
+
|
|
133
|
+
# 显示选项
|
|
134
|
+
option_lines = ", ".join([f"{i+1}-{label}" for i, (_, label) in enumerate(items)])
|
|
135
|
+
default_label = options.get(default_key, default_key)
|
|
136
|
+
print(f"{prompt} ({option_lines}),{timeout} 秒后自动选择[{default_label}]: ", end="", flush=True)
|
|
137
|
+
|
|
138
|
+
# 使用线程实现跨平台超时输入
|
|
139
|
+
from threading import Thread
|
|
140
|
+
from queue import Queue, Empty
|
|
141
|
+
|
|
142
|
+
result_queue = Queue()
|
|
143
|
+
|
|
144
|
+
def input_thread():
|
|
145
|
+
try:
|
|
146
|
+
user_input = input().strip()
|
|
147
|
+
result_queue.put(user_input)
|
|
148
|
+
except EOFError:
|
|
149
|
+
result_queue.put(None)
|
|
150
|
+
|
|
151
|
+
thread = Thread(target=input_thread, daemon=True)
|
|
152
|
+
thread.start()
|
|
153
|
+
thread.join(timeout)
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
user_input = result_queue.get_nowait()
|
|
157
|
+
except Empty:
|
|
158
|
+
user_input = None
|
|
159
|
+
|
|
160
|
+
if user_input is None:
|
|
161
|
+
print(f"\n超时,自动选择[{default_label}]")
|
|
162
|
+
return default_key
|
|
163
|
+
|
|
164
|
+
# 验证输入
|
|
165
|
+
try:
|
|
166
|
+
choice = int(user_input)
|
|
167
|
+
if 1 <= choice <= len(items):
|
|
168
|
+
return items[choice - 1][0]
|
|
169
|
+
except ValueError:
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
# 输入无效,返回默认值
|
|
173
|
+
print(f"输入无效,使用默认选项[{default_label}]")
|
|
174
|
+
return default_key
|
|
@@ -2,6 +2,7 @@ import pandas as pd
|
|
|
2
2
|
from .fill_round import fill_na, round_columns
|
|
3
3
|
from .date_utils import fill_nat, cal_date_diff
|
|
4
4
|
from .convert_utils import (
|
|
5
|
+
convert_series,
|
|
5
6
|
convert_columns,
|
|
6
7
|
convert_decimal,
|
|
7
8
|
convert_numeric_series,
|
|
@@ -31,7 +32,8 @@ __all__ = [
|
|
|
31
32
|
'round_columns',
|
|
32
33
|
'cal_date_diff',
|
|
33
34
|
|
|
34
|
-
#
|
|
35
|
+
# 数据转换类
|
|
36
|
+
'convert_series',
|
|
35
37
|
'convert_columns',
|
|
36
38
|
'convert_decimal',
|
|
37
39
|
'convert_numeric_series',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
from decimal import Decimal
|
|
3
|
-
from typing import Union, List, Dict, Any, Optional
|
|
3
|
+
from typing import Union, List, Dict, Any, Optional, Literal
|
|
4
4
|
from pandas import (
|
|
5
5
|
Int64Dtype,
|
|
6
6
|
Float64Dtype,
|
|
@@ -10,6 +10,47 @@ from pandas import (
|
|
|
10
10
|
from .json_utils import json_loads
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def convert_series(
|
|
14
|
+
data: pd.Series,
|
|
15
|
+
return_type: Literal["dict", "df", "series"] = "dict"
|
|
16
|
+
) -> Union[Dict[Any, Union[int, float]], pd.DataFrame, pd.Series]:
|
|
17
|
+
"""
|
|
18
|
+
将 pandas Series 格式化为指定的返回类型。
|
|
19
|
+
|
|
20
|
+
参数:
|
|
21
|
+
- data (pd.Series):输入的 pandas Series 数据。
|
|
22
|
+
- return_type (str):返回类型,支持:
|
|
23
|
+
- "dict":返回字典(默认)。
|
|
24
|
+
- "df":返回 DataFrame。
|
|
25
|
+
- "series":返回 pandas Series。
|
|
26
|
+
|
|
27
|
+
返回:
|
|
28
|
+
- Union[Dict[Any, Union[int, float]], pd.DataFrame, pd.Series]:
|
|
29
|
+
根据 return_type 返回格式化后的结果。
|
|
30
|
+
|
|
31
|
+
示例:
|
|
32
|
+
>>> series = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
|
|
33
|
+
>>> convert_series(series, "dict")
|
|
34
|
+
{'a': 1, 'b': 2, 'c': 3}
|
|
35
|
+
>>> convert_series(series, "df")
|
|
36
|
+
0
|
|
37
|
+
a 1
|
|
38
|
+
b 2
|
|
39
|
+
c 3
|
|
40
|
+
>>> convert_series(series, "series")
|
|
41
|
+
a 1
|
|
42
|
+
b 2
|
|
43
|
+
c 3
|
|
44
|
+
dtype: int64
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if return_type == "dict":
|
|
48
|
+
return data.to_dict()
|
|
49
|
+
elif return_type == "df":
|
|
50
|
+
return data.to_frame()
|
|
51
|
+
else: # "series"
|
|
52
|
+
return data
|
|
53
|
+
|
|
13
54
|
|
|
14
55
|
# 数据类型映射常量
|
|
15
56
|
TYPE_MAPPING = {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
"""
|
|
3
2
|
聚合(aggregation)操作工具模块。
|
|
4
3
|
|
|
@@ -7,8 +6,9 @@ sum、mean、max、min、count、median、std、var等聚合计算。
|
|
|
7
6
|
"""
|
|
8
7
|
|
|
9
8
|
import pandas as pd
|
|
10
|
-
from typing import Any, Dict, Optional, Union, List, Tuple
|
|
9
|
+
from typing import Any, Dict, Optional, Union, List, Tuple, Literal
|
|
11
10
|
from .mask_utils import build_single_mask, build_base_mask
|
|
11
|
+
from ..convert_utils import convert_series
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def group_agg(
|
|
@@ -21,8 +21,8 @@ def group_agg(
|
|
|
21
21
|
logic: str = "and",
|
|
22
22
|
true_mask: Optional[pd.Series] = None,
|
|
23
23
|
false_mask: Optional[pd.Series] = None,
|
|
24
|
-
|
|
25
|
-
) -> Dict[Any, Union[int, float]]:
|
|
24
|
+
return_type: Literal["dict", "df", "series"] = "dict"
|
|
25
|
+
) -> Union[Dict[Any, Union[int, float]], pd.DataFrame, pd.Series]:
|
|
26
26
|
"""
|
|
27
27
|
按指定列分组,对另一列进行聚合统计。
|
|
28
28
|
|
|
@@ -39,10 +39,11 @@ def group_agg(
|
|
|
39
39
|
- logic (str):逻辑操作类型,"and" 或 "or",默认为 "and"。
|
|
40
40
|
- true_mask (pd.Series):初始True掩码,默认为None。
|
|
41
41
|
- false_mask (pd.Series):初始False掩码,默认为None。
|
|
42
|
-
-
|
|
42
|
+
- return_type (str):返回类型,支持 "dict"(字典)、"df"(DataFrame)和 "series"(Series),
|
|
43
|
+
默认为 "dict"。
|
|
43
44
|
|
|
44
45
|
返回:
|
|
45
|
-
- Dict[Any, Union[int, float]]
|
|
46
|
+
- Union[Dict[Any, Union[int, float]], pd.Series]:以分组值为键,聚合结果为值的字典或Series。
|
|
46
47
|
|
|
47
48
|
示例:
|
|
48
49
|
>>> group_agg(df, "category", "amount", "sum")
|
|
@@ -80,9 +81,6 @@ def group_agg(
|
|
|
80
81
|
result_series = result_series.sort_values(ascending=True)
|
|
81
82
|
elif sort == "desc":
|
|
82
83
|
result_series = result_series.sort_values(ascending=False)
|
|
83
|
-
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
return result_series.to_dict()
|
|
87
|
-
else:
|
|
88
|
-
return result_series
|
|
84
|
+
|
|
85
|
+
# 格式化并返回结果
|
|
86
|
+
return convert_series(result_series, return_type)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
|
-
from typing import List, Tuple, Union, Optional, Dict, Any, Mapping
|
|
2
|
+
from typing import List, Tuple, Union, Optional, Dict, Any, Mapping, Literal
|
|
3
3
|
from .mask_utils import build_single_mask, build_base_mask
|
|
4
|
+
from ..convert_utils import convert_series
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def value_counts(
|
|
@@ -13,8 +14,8 @@ def value_counts(
|
|
|
13
14
|
logic: str = "and",
|
|
14
15
|
true_mask: Optional[pd.Series] = None,
|
|
15
16
|
false_mask: Optional[pd.Series] = None,
|
|
16
|
-
|
|
17
|
-
) -> Mapping[Any, Union[int, float]]:
|
|
17
|
+
return_type: Literal["dict", "df", "series"] = "dict"
|
|
18
|
+
) -> Union[Mapping[Any, Union[int, float]], pd.DataFrame, pd.Series]:
|
|
18
19
|
"""
|
|
19
20
|
统计DataFrame指定列中不同值的计数数据。
|
|
20
21
|
|
|
@@ -30,10 +31,11 @@ def value_counts(
|
|
|
30
31
|
- logic (str):逻辑操作类型,"and" 或 "or",默认为 "and"。
|
|
31
32
|
- true_mask (pd.Series):初始True掩码,默认为None。
|
|
32
33
|
- false_mask (pd.Series):初始False掩码,默认为None。
|
|
33
|
-
-
|
|
34
|
+
- return_type (str):返回类型,支持 "dict"(字典)、"df"(DataFrame)和 "series"(Series),
|
|
35
|
+
默认为 "dict"。
|
|
34
36
|
|
|
35
37
|
返回:
|
|
36
|
-
- Mapping[Any, Union[int, float]]
|
|
38
|
+
- Union[Mapping[Any, Union[int, float]], pd.Series]:以值为键,计数/百分比为值的字典或Series。
|
|
37
39
|
|
|
38
40
|
示例:
|
|
39
41
|
>>> value_counts(df, "status")
|
|
@@ -76,11 +78,8 @@ def value_counts(
|
|
|
76
78
|
dropna=dropna
|
|
77
79
|
)
|
|
78
80
|
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
return result_series.to_dict()
|
|
82
|
-
else:
|
|
83
|
-
return result_series
|
|
81
|
+
# 格式化并返回结果
|
|
82
|
+
return convert_series(result_series, return_type)
|
|
84
83
|
|
|
85
84
|
|
|
86
85
|
def count(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
|
-
from typing import Any, Dict, List, Tuple, Union, Optional, Mapping
|
|
2
|
+
from typing import Any, Dict, List, Tuple, Union, Optional, Mapping, Literal
|
|
3
3
|
from .mask_utils import build_single_mask as _original_build_single_mask, build_base_mask as _original_build_base_mask, combine_masks
|
|
4
4
|
from .count_utils import count as _original_count, value_counts as _original_value_counts
|
|
5
5
|
from .agg_utils import group_agg as _original_group_agg
|
|
@@ -43,8 +43,8 @@ class DataFrameStatistics:
|
|
|
43
43
|
self._false_mask = pd.Series([False] * self._length, index=self._index)
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def build_base_mask(self, conditions: List[Tuple], logic: str = "and",
|
|
47
|
-
true_mask: Optional[pd.Series] = None,
|
|
46
|
+
def build_base_mask(self, conditions: List[Tuple], logic: str = "and",
|
|
47
|
+
true_mask: Optional[pd.Series] = None,
|
|
48
48
|
false_mask: Optional[pd.Series] = None) -> pd.Series:
|
|
49
49
|
"""
|
|
50
50
|
构建基础查询条件掩码,自动使用内部掩码参数
|
|
@@ -54,7 +54,7 @@ class DataFrameStatistics:
|
|
|
54
54
|
- logic (str):逻辑操作类型,"and" 或 "or",默认为 "and"
|
|
55
55
|
- true_mask (pd.Series):初始True掩码,默认为None(使用内部缓存)
|
|
56
56
|
- false_mask (pd.Series):初始False掩码,默认为None(使用内部缓存)
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
返回:
|
|
59
59
|
- pd.Series:布尔掩码,True表示符合条件的行
|
|
60
60
|
"""
|
|
@@ -69,10 +69,10 @@ class DataFrameStatistics:
|
|
|
69
69
|
def build_single_mask(self, condition: Tuple) -> pd.Series:
|
|
70
70
|
"""
|
|
71
71
|
构建单个掩码,用于简单条件判断,自动使用内部DataFrame
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
参数:
|
|
74
74
|
- condition (Tuple):条件元组,包含(列名, 运算符, 值)
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
返回:
|
|
77
77
|
- pd.Series:布尔掩码,True表示符合条件的行
|
|
78
78
|
**注意**:此Series是通过原生pandas表达式(df[col] > value)直接返回,
|
|
@@ -114,8 +114,8 @@ class DataFrameStatistics:
|
|
|
114
114
|
logic: str = "and",
|
|
115
115
|
true_mask: Optional[pd.Series] = None,
|
|
116
116
|
false_mask: Optional[pd.Series] = None,
|
|
117
|
-
|
|
118
|
-
) -> Mapping[Any, Union[int, float]]:
|
|
117
|
+
return_type: Literal["dict", "df", "series"] = "dict"
|
|
118
|
+
) -> Union[Mapping[Any, Union[int, float]], pd.DataFrame, pd.Series]:
|
|
119
119
|
"""
|
|
120
120
|
统计指定列中不同值的计数数据,自动使用内部掩码参数
|
|
121
121
|
|
|
@@ -129,11 +129,11 @@ class DataFrameStatistics:
|
|
|
129
129
|
- logic (str):逻辑操作类型,"and" 或 "or",默认为 "and"
|
|
130
130
|
- true_mask (pd.Series):初始True掩码,默认为None(使用内部缓存)
|
|
131
131
|
- false_mask (pd.Series):初始False掩码,默认为None(使用内部缓存)
|
|
132
|
-
-
|
|
132
|
+
- return_type (str):返回类型,支持 "dict"(字典)、"df"(DataFrame)和 "series"(Series),
|
|
133
|
+
默认为 "dict"。
|
|
133
134
|
|
|
134
|
-
|
|
135
135
|
返回:
|
|
136
|
-
- Mapping[Any, Union[int, float]]
|
|
136
|
+
- Union[Mapping[Any, Union[int, float]], pd.DataFrame, pd.Series]:以值为键,计数/百分比为值的字典、DataFrame或Series
|
|
137
137
|
|
|
138
138
|
示例:
|
|
139
139
|
>>> stats.value_counts("status")
|
|
@@ -154,7 +154,7 @@ class DataFrameStatistics:
|
|
|
154
154
|
false_mask = self._false_mask
|
|
155
155
|
return _original_value_counts(
|
|
156
156
|
self._df, column, mode, sort, dropna,
|
|
157
|
-
conditions, logic, true_mask, false_mask,
|
|
157
|
+
conditions, logic, true_mask, false_mask, return_type
|
|
158
158
|
)
|
|
159
159
|
|
|
160
160
|
|
|
@@ -168,8 +168,8 @@ class DataFrameStatistics:
|
|
|
168
168
|
logic: str = "and",
|
|
169
169
|
true_mask: Optional[pd.Series] = None,
|
|
170
170
|
false_mask: Optional[pd.Series] = None,
|
|
171
|
-
|
|
172
|
-
) -> Dict[Any, Union[int, float]]:
|
|
171
|
+
return_type: Literal["dict", "df", "series"] = "dict"
|
|
172
|
+
) -> Union[Dict[Any, Union[int, float]], pd.DataFrame, pd.Series]:
|
|
173
173
|
"""
|
|
174
174
|
按指定列分组,对另一列进行聚合统计,自动使用内部掩码参数
|
|
175
175
|
|
|
@@ -184,11 +184,11 @@ class DataFrameStatistics:
|
|
|
184
184
|
- logic (str):逻辑操作类型,"and" 或 "or",默认为 "and"
|
|
185
185
|
- true_mask (pd.Series):初始True掩码,默认为None(使用内部缓存)
|
|
186
186
|
- false_mask (pd.Series):初始False掩码,默认为None(使用内部缓存)
|
|
187
|
-
-
|
|
187
|
+
- return_type (str):返回类型,支持 "dict"(字典)、"df"(DataFrame)和 "series"(Series),
|
|
188
|
+
默认为 "dict"。
|
|
188
189
|
|
|
189
|
-
|
|
190
190
|
返回:
|
|
191
|
-
- Dict[Any, Union[int, float]]
|
|
191
|
+
- Union[Dict[Any, Union[int, float]], pd.DataFrame, pd.Series]:以分组值为键,聚合结果为值的字典、DataFrame或Series
|
|
192
192
|
|
|
193
193
|
示例:
|
|
194
194
|
>>> stats.group_agg("category", "amount", "sum")
|
|
@@ -205,7 +205,7 @@ class DataFrameStatistics:
|
|
|
205
205
|
false_mask = self._false_mask
|
|
206
206
|
return _original_group_agg(
|
|
207
207
|
self._df, group_col, agg_col, agg_func, sort,
|
|
208
|
-
conditions, logic, true_mask, false_mask,
|
|
208
|
+
conditions, logic, true_mask, false_mask, return_type
|
|
209
209
|
)
|
|
210
210
|
|
|
211
211
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: funcguard
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.44
|
|
4
4
|
Summary: FuncGuard是一个Python库,提供函数执行超时控制、重试机制、HTTP请求封装和格式化打印工具。
|
|
5
5
|
Home-page: https://github.com/tinycen/funcguard
|
|
6
6
|
Author: tinycen
|
|
@@ -34,7 +34,7 @@ FuncGuard是一个Python库,提供了函数执行超时控制和重试机制
|
|
|
34
34
|
|
|
35
35
|
| 分类 | 功能描述 | 文档 |
|
|
36
36
|
|------|----------|------|
|
|
37
|
-
| **核心功能** |
|
|
37
|
+
| **核心功能** | 函数执行超时控制、函数执行失败自动重试、交互式选择菜单 | - |
|
|
38
38
|
| **网络请求** | HTTP请求封装(支持自动重试)、MD5哈希、Basic Auth编码 | [查看](./docs/network.md) |
|
|
39
39
|
| **时间工具** | 时间日志记录、耗时统计、执行时间监控和警告、时间等待(带倒计时) | [查看](./docs/time_utils.md) |
|
|
40
40
|
| **打印工具** | 格式化分隔线、块打印、标题打印、进度条显示 | - |
|
|
@@ -95,6 +95,41 @@ except Exception as e:
|
|
|
95
95
|
print(f"重试后仍然失败: {e}")
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
### 交互式选择菜单
|
|
99
|
+
|
|
100
|
+
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单,支持超时自动选择:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from funcguard import ask_select
|
|
104
|
+
|
|
105
|
+
# 基础用法 - 选择属性填写模式
|
|
106
|
+
result = ask_select(
|
|
107
|
+
options={True: "需要填写属性", False: "不需要填写属性", "all": "不限制"},
|
|
108
|
+
default_key="all",
|
|
109
|
+
prompt="请选择属性填写模式",
|
|
110
|
+
timeout=5
|
|
111
|
+
)
|
|
112
|
+
print(f"用户选择: {result}") # 返回 True, False 或 "all"
|
|
113
|
+
|
|
114
|
+
# 选择部署环境
|
|
115
|
+
env = ask_select(
|
|
116
|
+
options={"dev": "开发环境", "test": "测试环境", "prod": "生产环境"},
|
|
117
|
+
default_key="dev",
|
|
118
|
+
prompt="请选择部署环境",
|
|
119
|
+
timeout=3
|
|
120
|
+
)
|
|
121
|
+
print(f"当前环境: {env}")
|
|
122
|
+
|
|
123
|
+
# 选择数量(使用整数作为键)
|
|
124
|
+
count = ask_select(
|
|
125
|
+
options={10: "10条", 50: "50条", 100: "100条"},
|
|
126
|
+
default_key=50,
|
|
127
|
+
prompt="查询数量",
|
|
128
|
+
timeout=5
|
|
129
|
+
)
|
|
130
|
+
print(f"查询 {count} 条数据")
|
|
131
|
+
```
|
|
132
|
+
|
|
98
133
|
### HTTP请求
|
|
99
134
|
|
|
100
135
|
FuncGuard 提供了强大的 HTTP 请求功能,支持自动重试、请求日志记录、以及 curl_cffi 兜底(用于绕过反爬虫检测)。详细文档请参考 [network.md](docs/network.md)。
|
|
@@ -496,6 +531,7 @@ print(f"当前价格: {current_price}, 变化: {price_change}") # 输出: 当
|
|
|
496
531
|
|-----------|----------|
|
|
497
532
|
| `timeout_handler` | 函数执行超时控制 |
|
|
498
533
|
| `retry_function` | 函数执行失败自动重试 |
|
|
534
|
+
| `ask_select` | 交互式数字选择菜单(支持超时自动选择) |
|
|
499
535
|
|
|
500
536
|
### 网络请求
|
|
501
537
|
|
|
@@ -542,7 +578,7 @@ print(f"当前价格: {current_price}, 变化: {price_change}") # 输出: 当
|
|
|
542
578
|
| 函数/类名 | 功能说明 | 文档 |
|
|
543
579
|
|-----------|----------|------|
|
|
544
580
|
| `pd_fill_na` / `pd_fill_nat` | 数据填充 | [查看](docs/pandas/fill.md) |
|
|
545
|
-
| `pd_convert_columns` / `pd_convert_decimal` / `pd_convert_numeric_series` / `pd_convert_str_datetime` / `pd_convert_datetime_str` | 类型转换 | [查看](docs/pandas/convert.md) |
|
|
581
|
+
| `pd_convert_columns` / `pd_convert_decimal` / `pd_convert_numeric_series` / `pd_convert_str_datetime` / `pd_convert_datetime_str` / `pd_convert_series` | 类型转换 | [查看](docs/pandas/convert.md) |
|
|
546
582
|
| `pd_load_json` | JSON解析 | [查看](docs/pandas/json.md) |
|
|
547
583
|
| `pd_filter` | 数据筛选 | [查看](docs/pandas/filter.md) |
|
|
548
584
|
| `pd_count` / `pd_value_counts` | 条件计数统计 | [查看](docs/pandas/count.md) |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|