funcguard 0.2.50__tar.gz → 0.2.53__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.50 → funcguard-0.2.53}/PKG-INFO +36 -7
- {funcguard-0.2.50 → funcguard-0.2.53}/README.md +30 -6
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/core.py +36 -12
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/ip_utils.py +2 -2
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/convert_utils.py +14 -9
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/fill_round.py +10 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/df_statistics.py +10 -2
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/mask_utils.py +2 -2
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/printer.py +3 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/tools.py +8 -3
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/PKG-INFO +36 -7
- {funcguard-0.2.50 → funcguard-0.2.53}/setup.py +6 -1
- {funcguard-0.2.50 → funcguard-0.2.53}/LICENSE +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/__init__.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/calculate.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/data_models/__init__.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/data_models/request_models.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/log_utils.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/__init__.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/date_utils.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/filter.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/json_utils/__init__.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/json_utils/json_parser.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/__init__.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/agg_utils.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/count_utils.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/time_utils.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/SOURCES.txt +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/dependency_links.txt +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/not-zip-safe +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/requires.txt +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/top_level.txt +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/setup.cfg +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/tests/__init__.py +0 -0
- {funcguard-0.2.50 → funcguard-0.2.53}/tests/test_pd_filter_empty.py +0 -0
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: funcguard
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.53
|
|
4
4
|
Summary: FuncGuard是一个Python库,提供函数执行超时控制、重试机制、HTTP请求封装和格式化打印工具。
|
|
5
5
|
Home-page: https://github.com/tinycen/funcguard
|
|
6
6
|
Author: tinycen
|
|
7
7
|
Author-email: sky_ruocen@qq.com
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
9
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
15
|
Classifier: Operating System :: OS Independent
|
|
11
16
|
Requires-Python: >=3.10
|
|
@@ -97,7 +102,10 @@ except Exception as e:
|
|
|
97
102
|
|
|
98
103
|
### 交互式选择菜单
|
|
99
104
|
|
|
100
|
-
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单(编号从0
|
|
105
|
+
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单(编号从0开始),支持超时自动选择和倒计时动态显示。`options` 参数支持**字典**或**列表**两种模式:
|
|
106
|
+
|
|
107
|
+
#### 字典模式
|
|
108
|
+
字典模式下,键为选项标识(任意类型),值为显示文本,返回用户选择的键:
|
|
101
109
|
|
|
102
110
|
```python
|
|
103
111
|
from funcguard import ask_select
|
|
@@ -128,15 +136,36 @@ count = ask_select(
|
|
|
128
136
|
timeout=10
|
|
129
137
|
)
|
|
130
138
|
print(f"查询 {count} 条数据")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### 列表模式
|
|
142
|
+
列表模式下,元素即为选项值,返回用户选中的元素:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from funcguard import ask_select
|
|
146
|
+
|
|
147
|
+
# 使用列表 - 返回选中的元素值
|
|
148
|
+
env = ask_select(
|
|
149
|
+
options=["开发环境", "测试环境", "生产环境"],
|
|
150
|
+
default_key="开发环境",
|
|
151
|
+
prompt="请选择部署环境",
|
|
152
|
+
timeout=10
|
|
153
|
+
)
|
|
154
|
+
print(f"当前环境: {env}") # 直接返回 "开发环境"、"测试环境" 或 "生产环境"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### 无超时模式
|
|
158
|
+
设置 `timeout=None` 可以禁用超时,一直等待用户输入:
|
|
131
159
|
|
|
160
|
+
```python
|
|
132
161
|
# 无超时模式 - 一直等待用户输入
|
|
133
|
-
|
|
134
|
-
options={
|
|
135
|
-
default_key=
|
|
136
|
-
prompt="
|
|
162
|
+
result = ask_select(
|
|
163
|
+
options={"save": "保存", "discard": "放弃", "cancel": "取消"},
|
|
164
|
+
default_key="cancel",
|
|
165
|
+
prompt="文件已修改,请选择操作",
|
|
137
166
|
timeout=None
|
|
138
167
|
)
|
|
139
|
-
print(f"
|
|
168
|
+
print(f"用户选择: {result}")
|
|
140
169
|
```
|
|
141
170
|
|
|
142
171
|
### HTTP请求
|
|
@@ -70,7 +70,10 @@ except Exception as e:
|
|
|
70
70
|
|
|
71
71
|
### 交互式选择菜单
|
|
72
72
|
|
|
73
|
-
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单(编号从0
|
|
73
|
+
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单(编号从0开始),支持超时自动选择和倒计时动态显示。`options` 参数支持**字典**或**列表**两种模式:
|
|
74
|
+
|
|
75
|
+
#### 字典模式
|
|
76
|
+
字典模式下,键为选项标识(任意类型),值为显示文本,返回用户选择的键:
|
|
74
77
|
|
|
75
78
|
```python
|
|
76
79
|
from funcguard import ask_select
|
|
@@ -101,15 +104,36 @@ count = ask_select(
|
|
|
101
104
|
timeout=10
|
|
102
105
|
)
|
|
103
106
|
print(f"查询 {count} 条数据")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### 列表模式
|
|
110
|
+
列表模式下,元素即为选项值,返回用户选中的元素:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from funcguard import ask_select
|
|
114
|
+
|
|
115
|
+
# 使用列表 - 返回选中的元素值
|
|
116
|
+
env = ask_select(
|
|
117
|
+
options=["开发环境", "测试环境", "生产环境"],
|
|
118
|
+
default_key="开发环境",
|
|
119
|
+
prompt="请选择部署环境",
|
|
120
|
+
timeout=10
|
|
121
|
+
)
|
|
122
|
+
print(f"当前环境: {env}") # 直接返回 "开发环境"、"测试环境" 或 "生产环境"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### 无超时模式
|
|
126
|
+
设置 `timeout=None` 可以禁用超时,一直等待用户输入:
|
|
104
127
|
|
|
128
|
+
```python
|
|
105
129
|
# 无超时模式 - 一直等待用户输入
|
|
106
|
-
|
|
107
|
-
options={
|
|
108
|
-
default_key=
|
|
109
|
-
prompt="
|
|
130
|
+
result = ask_select(
|
|
131
|
+
options={"save": "保存", "discard": "放弃", "cancel": "取消"},
|
|
132
|
+
default_key="cancel",
|
|
133
|
+
prompt="文件已修改,请选择操作",
|
|
110
134
|
timeout=None
|
|
111
135
|
)
|
|
112
|
-
print(f"
|
|
136
|
+
print(f"用户选择: {result}")
|
|
113
137
|
```
|
|
114
138
|
|
|
115
139
|
### HTTP请求
|
|
@@ -92,7 +92,7 @@ def retry_function( func , max_retries = 5 , execute_timeout = 90 , task_name =
|
|
|
92
92
|
|
|
93
93
|
# 交互式选择菜单
|
|
94
94
|
def ask_select(
|
|
95
|
-
options: dict,
|
|
95
|
+
options: dict | list,
|
|
96
96
|
default_key = None,
|
|
97
97
|
prompt: str = "请选择",
|
|
98
98
|
timeout: int | None = 10,
|
|
@@ -101,37 +101,61 @@ def ask_select(
|
|
|
101
101
|
显示一个数字选择菜单,接受用户输入,超时自动返回默认值。
|
|
102
102
|
|
|
103
103
|
参数:
|
|
104
|
-
options:
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
options: 字典或列表。
|
|
105
|
+
字典模式: 键为选项标识,值为显示文本,返回选中的键
|
|
106
|
+
列表模式: 元素为显示文本,返回选中的元素值
|
|
107
|
+
例如: {True: "需要填写属性", False: "不需要填写属性"} 或 ["选项A", "选项B", "选项C"]
|
|
108
|
+
default_key: 超时或输入无效时的默认返回值
|
|
109
|
+
字典模式下为某个键,若为None则使用最后一个选项的键
|
|
110
|
+
列表模式下为某个元素值,若为None则使用最后一个元素
|
|
107
111
|
prompt: 提示语前缀
|
|
108
112
|
timeout: 超时时间(秒),默认10秒;若为None则不设置超时,一直等待用户输入
|
|
109
113
|
|
|
110
114
|
返回:
|
|
111
|
-
|
|
115
|
+
字典模式: 返回选项键(options中的某个键),或default_key
|
|
116
|
+
列表模式: 返回选中的列表元素值,或default_key
|
|
112
117
|
|
|
113
118
|
示例:
|
|
119
|
+
>>> # 字典模式
|
|
114
120
|
>>> result = ask_select(
|
|
115
121
|
... {True: "需要填写属性", False: "不需要填写属性", "all": "不限制"},
|
|
116
122
|
... default_key="all",
|
|
117
123
|
... prompt="请选择属性填写模式",
|
|
118
124
|
... timeout=10,
|
|
119
125
|
... )
|
|
126
|
+
>>> # 列表模式
|
|
127
|
+
>>> value = ask_select(
|
|
128
|
+
... ["选项A", "选项B", "选项C"],
|
|
129
|
+
... default_key="选项A",
|
|
130
|
+
... prompt="请选择一个选项",
|
|
131
|
+
... timeout=10,
|
|
132
|
+
... )
|
|
120
133
|
"""
|
|
121
134
|
# 确保选项非空
|
|
122
135
|
if not options:
|
|
123
|
-
raise ValueError("options
|
|
136
|
+
raise ValueError("options不能为空")
|
|
124
137
|
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
default_key = list(options.keys())[-1]
|
|
138
|
+
# 判断是字典,还是列表
|
|
139
|
+
is_list_mode = isinstance(options, list)
|
|
128
140
|
|
|
129
|
-
#
|
|
130
|
-
|
|
141
|
+
# 设置默认值并构建选项列表
|
|
142
|
+
if is_list_mode:
|
|
143
|
+
# 列表模式: 将列表转为 {值: 显示文本} 的字典格式
|
|
144
|
+
if default_key is None:
|
|
145
|
+
default_key = options[-1]
|
|
146
|
+
items = [(value, value) for value in options]
|
|
147
|
+
else:
|
|
148
|
+
# 字典模式: 保持原有逻辑
|
|
149
|
+
if default_key is None:
|
|
150
|
+
default_key = list(options.keys())[-1]
|
|
151
|
+
items = list(options.items())
|
|
131
152
|
|
|
132
153
|
# 显示选项
|
|
133
154
|
option_lines = ", ".join([f"{i}-{label}" for i, (_, label) in enumerate(items)])
|
|
134
|
-
|
|
155
|
+
if is_list_mode:
|
|
156
|
+
default_label = default_key
|
|
157
|
+
else:
|
|
158
|
+
default_label = options.get(default_key, default_key)
|
|
135
159
|
|
|
136
160
|
# 使用线程实现跨平台超时输入
|
|
137
161
|
from threading import Thread
|
|
@@ -51,7 +51,7 @@ def get_public_ip():
|
|
|
51
51
|
# 验证是否为有效的IP地址格式
|
|
52
52
|
if is_valid_ip(ip):
|
|
53
53
|
return ip
|
|
54
|
-
except:
|
|
54
|
+
except Exception:
|
|
55
55
|
continue
|
|
56
56
|
|
|
57
57
|
return None
|
|
@@ -80,7 +80,7 @@ def is_valid_ip(ip_string):
|
|
|
80
80
|
return False
|
|
81
81
|
|
|
82
82
|
return True
|
|
83
|
-
except:
|
|
83
|
+
except Exception:
|
|
84
84
|
return False
|
|
85
85
|
|
|
86
86
|
|
|
@@ -134,6 +134,14 @@ def convert_columns(df: pd.DataFrame, columns: Dict[str, str], decimal_places: O
|
|
|
134
134
|
return df
|
|
135
135
|
|
|
136
136
|
|
|
137
|
+
def _has_decimal(df: pd.DataFrame, column: str) -> bool:
|
|
138
|
+
"""检查列中是否存在 Decimal 类型值"""
|
|
139
|
+
for val in df[column]:
|
|
140
|
+
if pd.notna(val) and isinstance(val, Decimal):
|
|
141
|
+
return True
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
|
|
137
145
|
def convert_decimal(
|
|
138
146
|
df: pd.DataFrame,
|
|
139
147
|
columns: Union[List[str], Dict[str, str], None] = None,
|
|
@@ -182,20 +190,17 @@ def convert_decimal(
|
|
|
182
190
|
# 检查列是否存在且为object类型 (只有object类型列才可能包含Decimal)
|
|
183
191
|
if column not in df.columns or df[column].dtype != object:
|
|
184
192
|
continue
|
|
185
|
-
#
|
|
186
|
-
|
|
187
|
-
if first_non_null is None:
|
|
188
|
-
continue
|
|
189
|
-
if isinstance( df.at[first_non_null, column], Decimal ): # pyright: ignore[reportArgumentType]
|
|
193
|
+
# 检查列中是否存在 Decimal 类型值
|
|
194
|
+
if _has_decimal(df, column):
|
|
190
195
|
# 根据指定的类型进行转换
|
|
191
|
-
|
|
192
|
-
if
|
|
196
|
+
col_target_type = column_types[column]
|
|
197
|
+
if col_target_type == "int":
|
|
193
198
|
df[column] = df[column].astype(int)
|
|
194
|
-
elif
|
|
199
|
+
elif col_target_type == "float":
|
|
195
200
|
df[column] = df[column].astype(float)
|
|
196
201
|
if decimal_places is not None:
|
|
197
202
|
df[column] = df[column].round(decimal_places)
|
|
198
|
-
elif
|
|
203
|
+
elif col_target_type == "auto":
|
|
199
204
|
df[column] = convert_numeric_series(df[column], decimal_places)
|
|
200
205
|
|
|
201
206
|
return df
|
|
@@ -24,6 +24,9 @@ def fill_na(
|
|
|
24
24
|
|
|
25
25
|
返回:
|
|
26
26
|
- pd.DataFrame:替换空值后的DataFrame。
|
|
27
|
+
|
|
28
|
+
注意:本函数直接修改传入的 DataFrame,不返回副本。如需保留原数据,
|
|
29
|
+
请在调用前使用 df.copy()。
|
|
27
30
|
"""
|
|
28
31
|
_is_numeric_fill = isinstance(fill_value, (int, float))
|
|
29
32
|
|
|
@@ -76,10 +79,17 @@ def round_columns(
|
|
|
76
79
|
|
|
77
80
|
返回:
|
|
78
81
|
- pd.DataFrame:四舍五入后的DataFrame。
|
|
82
|
+
|
|
83
|
+
注意:
|
|
84
|
+
- 当 decimal_places 为 0 时,结果列会自动转换为 Int64(可空整数)类型。
|
|
85
|
+
- decimal.Decimal 类型在 pandas 中以 object dtype 存储,不被视为数值类型,
|
|
86
|
+
调用本函数会抛出 TypeError。需先手动将其转换为 float 类型再调用本函数。
|
|
79
87
|
"""
|
|
80
88
|
for column in columns:
|
|
81
89
|
if column in df.columns:
|
|
82
90
|
if not is_numeric_dtype(df[column]):
|
|
83
91
|
raise TypeError(f"列 '{column}' 不是数值类型,无法执行四舍五入操作")
|
|
84
92
|
df[column] = df[column].round(decimal_places)
|
|
93
|
+
if decimal_places == 0:
|
|
94
|
+
df[column] = df[column].astype('Int64')
|
|
85
95
|
return df
|
|
@@ -39,9 +39,13 @@ class DataFrameStatistics:
|
|
|
39
39
|
# 重置 true_mask 和 false_mask
|
|
40
40
|
def _reset_base_masks(self):
|
|
41
41
|
"""重置基础掩码为全True和全False"""
|
|
42
|
-
self._true_mask = pd.Series([True] * self.
|
|
43
|
-
self._false_mask = pd.Series([False] * self.
|
|
42
|
+
self._true_mask = pd.Series([True] * len(self._df), index=self._df.index)
|
|
43
|
+
self._false_mask = pd.Series([False] * len(self._df), index=self._df.index)
|
|
44
44
|
|
|
45
|
+
def _ensure_masks_valid(self):
|
|
46
|
+
"""如果 DataFrame 发生变化,重新生成基础掩码"""
|
|
47
|
+
if self._true_mask is not None and len(self._true_mask) != len(self._df):
|
|
48
|
+
self._reset_base_masks()
|
|
45
49
|
|
|
46
50
|
def build_base_mask(self, conditions: List[Tuple], logic: str = "and",
|
|
47
51
|
true_mask: Optional[pd.Series] = None,
|
|
@@ -58,6 +62,7 @@ class DataFrameStatistics:
|
|
|
58
62
|
返回:
|
|
59
63
|
- pd.Series:布尔掩码,True表示符合条件的行
|
|
60
64
|
"""
|
|
65
|
+
self._ensure_masks_valid()
|
|
61
66
|
# 如果没有提供外部掩码,使用内部缓存的掩码
|
|
62
67
|
if true_mask is None:
|
|
63
68
|
true_mask = self._true_mask
|
|
@@ -96,6 +101,7 @@ class DataFrameStatistics:
|
|
|
96
101
|
返回:
|
|
97
102
|
- int:符合条件的数量
|
|
98
103
|
"""
|
|
104
|
+
self._ensure_masks_valid()
|
|
99
105
|
# 如果没有提供外部掩码,使用内部缓存的掩码
|
|
100
106
|
if true_mask is None:
|
|
101
107
|
true_mask = self._true_mask
|
|
@@ -147,6 +153,7 @@ class DataFrameStatistics:
|
|
|
147
153
|
>>> stats.value_counts("status", conditions=[("age", ">", 18)])
|
|
148
154
|
{'active': 120, 'inactive': 30}
|
|
149
155
|
"""
|
|
156
|
+
self._ensure_masks_valid()
|
|
150
157
|
# 如果没有提供外部掩码,使用内部缓存的掩码
|
|
151
158
|
if true_mask is None:
|
|
152
159
|
true_mask = self._true_mask
|
|
@@ -198,6 +205,7 @@ class DataFrameStatistics:
|
|
|
198
205
|
>>> stats.group_agg("category", "amount", "sum", sort="desc")
|
|
199
206
|
{'B': 2000, 'C': 1500, 'A': 1000}
|
|
200
207
|
"""
|
|
208
|
+
self._ensure_masks_valid()
|
|
201
209
|
# 如果没有提供外部掩码,使用内部缓存的掩码
|
|
202
210
|
if true_mask is None:
|
|
203
211
|
true_mask = self._true_mask
|
|
@@ -42,7 +42,7 @@ def md5_hash(*texts: str, encoding: str = "utf-8") -> str:
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
# 生成 base64 格式的 Basic Auth 字符串
|
|
45
|
-
def encode_basic_auth(username, password):
|
|
45
|
+
def encode_basic_auth(username: str, password: str) -> str:
|
|
46
46
|
# 将 Username 和 Password 拼接成字符串
|
|
47
47
|
auth_str = f"{username}:{password}"
|
|
48
48
|
|
|
@@ -189,7 +189,12 @@ def send_request(
|
|
|
189
189
|
|
|
190
190
|
# ---------- 结果处理 ----------
|
|
191
191
|
if return_type == "json":
|
|
192
|
-
|
|
192
|
+
try:
|
|
193
|
+
result = response.json()
|
|
194
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
f"响应内容不是有效的 JSON 格式 (status={response.status_code}): {e}"
|
|
197
|
+
) from e
|
|
193
198
|
if request_log.save_path:
|
|
194
199
|
res_log = {}
|
|
195
200
|
save_fields = [
|
|
@@ -247,7 +252,7 @@ def check_url_valid(
|
|
|
247
252
|
response = curl_cffi_request(
|
|
248
253
|
"HEAD", url, req_kwargs, curl_fallback_impersonate, auto_retry
|
|
249
254
|
)
|
|
250
|
-
if response.status_code == 200: # type: ignore
|
|
255
|
+
if response is not None and response.status_code == 200: # type: ignore
|
|
251
256
|
return True
|
|
252
257
|
except Exception:
|
|
253
258
|
pass
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: funcguard
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.53
|
|
4
4
|
Summary: FuncGuard是一个Python库,提供函数执行超时控制、重试机制、HTTP请求封装和格式化打印工具。
|
|
5
5
|
Home-page: https://github.com/tinycen/funcguard
|
|
6
6
|
Author: tinycen
|
|
7
7
|
Author-email: sky_ruocen@qq.com
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
9
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
15
|
Classifier: Operating System :: OS Independent
|
|
11
16
|
Requires-Python: >=3.10
|
|
@@ -97,7 +102,10 @@ except Exception as e:
|
|
|
97
102
|
|
|
98
103
|
### 交互式选择菜单
|
|
99
104
|
|
|
100
|
-
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单(编号从0
|
|
105
|
+
使用 `ask_select` 函数创建一个带数字编号的交互式选择菜单(编号从0开始),支持超时自动选择和倒计时动态显示。`options` 参数支持**字典**或**列表**两种模式:
|
|
106
|
+
|
|
107
|
+
#### 字典模式
|
|
108
|
+
字典模式下,键为选项标识(任意类型),值为显示文本,返回用户选择的键:
|
|
101
109
|
|
|
102
110
|
```python
|
|
103
111
|
from funcguard import ask_select
|
|
@@ -128,15 +136,36 @@ count = ask_select(
|
|
|
128
136
|
timeout=10
|
|
129
137
|
)
|
|
130
138
|
print(f"查询 {count} 条数据")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### 列表模式
|
|
142
|
+
列表模式下,元素即为选项值,返回用户选中的元素:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from funcguard import ask_select
|
|
146
|
+
|
|
147
|
+
# 使用列表 - 返回选中的元素值
|
|
148
|
+
env = ask_select(
|
|
149
|
+
options=["开发环境", "测试环境", "生产环境"],
|
|
150
|
+
default_key="开发环境",
|
|
151
|
+
prompt="请选择部署环境",
|
|
152
|
+
timeout=10
|
|
153
|
+
)
|
|
154
|
+
print(f"当前环境: {env}") # 直接返回 "开发环境"、"测试环境" 或 "生产环境"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### 无超时模式
|
|
158
|
+
设置 `timeout=None` 可以禁用超时,一直等待用户输入:
|
|
131
159
|
|
|
160
|
+
```python
|
|
132
161
|
# 无超时模式 - 一直等待用户输入
|
|
133
|
-
|
|
134
|
-
options={
|
|
135
|
-
default_key=
|
|
136
|
-
prompt="
|
|
162
|
+
result = ask_select(
|
|
163
|
+
options={"save": "保存", "discard": "放弃", "cancel": "取消"},
|
|
164
|
+
default_key="cancel",
|
|
165
|
+
prompt="文件已修改,请选择操作",
|
|
137
166
|
timeout=None
|
|
138
167
|
)
|
|
139
|
-
print(f"
|
|
168
|
+
print(f"用户选择: {result}")
|
|
140
169
|
```
|
|
141
170
|
|
|
142
171
|
### HTTP请求
|
|
@@ -9,7 +9,7 @@ except FileNotFoundError:
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name='funcguard',
|
|
12
|
-
version='0.2.
|
|
12
|
+
version='0.2.53',
|
|
13
13
|
packages=find_packages(),
|
|
14
14
|
install_requires=[
|
|
15
15
|
'requests',
|
|
@@ -24,6 +24,11 @@ setup(
|
|
|
24
24
|
url='https://github.com/tinycen/funcguard',
|
|
25
25
|
classifiers=[
|
|
26
26
|
'Programming Language :: Python :: 3',
|
|
27
|
+
'Programming Language :: Python :: 3.10',
|
|
28
|
+
'Programming Language :: Python :: 3.11',
|
|
29
|
+
'Programming Language :: Python :: 3.12',
|
|
30
|
+
'Programming Language :: Python :: 3.13',
|
|
31
|
+
'Programming Language :: Python :: 3.14',
|
|
27
32
|
'License :: OSI Approved :: MIT License',
|
|
28
33
|
'Operating System :: OS Independent',
|
|
29
34
|
],
|
|
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
|