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.
Files changed (35) hide show
  1. {funcguard-0.2.50 → funcguard-0.2.53}/PKG-INFO +36 -7
  2. {funcguard-0.2.50 → funcguard-0.2.53}/README.md +30 -6
  3. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/core.py +36 -12
  4. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/ip_utils.py +2 -2
  5. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/convert_utils.py +14 -9
  6. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/fill_round.py +10 -0
  7. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/df_statistics.py +10 -2
  8. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/mask_utils.py +2 -2
  9. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/printer.py +3 -0
  10. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/tools.py +8 -3
  11. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/PKG-INFO +36 -7
  12. {funcguard-0.2.50 → funcguard-0.2.53}/setup.py +6 -1
  13. {funcguard-0.2.50 → funcguard-0.2.53}/LICENSE +0 -0
  14. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/__init__.py +0 -0
  15. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/calculate.py +0 -0
  16. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/data_models/__init__.py +0 -0
  17. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/data_models/request_models.py +0 -0
  18. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/log_utils.py +0 -0
  19. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/__init__.py +0 -0
  20. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/date_utils.py +0 -0
  21. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/filter.py +0 -0
  22. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/json_utils/__init__.py +0 -0
  23. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/json_utils/json_parser.py +0 -0
  24. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/__init__.py +0 -0
  25. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/agg_utils.py +0 -0
  26. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/pd_utils/statistics/count_utils.py +0 -0
  27. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard/time_utils.py +0 -0
  28. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/SOURCES.txt +0 -0
  29. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/dependency_links.txt +0 -0
  30. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/not-zip-safe +0 -0
  31. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/requires.txt +0 -0
  32. {funcguard-0.2.50 → funcguard-0.2.53}/funcguard.egg-info/top_level.txt +0 -0
  33. {funcguard-0.2.50 → funcguard-0.2.53}/setup.cfg +0 -0
  34. {funcguard-0.2.50 → funcguard-0.2.53}/tests/__init__.py +0 -0
  35. {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.50
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
- count = ask_select(
134
- options={10: "10条", 50: "50条", 100: "100条"},
135
- default_key=50,
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"查询 {count} 条数据")
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
- count = ask_select(
107
- options={10: "10条", 50: "50条", 100: "100条"},
108
- default_key=50,
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"查询 {count} 条数据")
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
- 例如: {True: "需要填写属性", False: "不需要填写属性", "all": "不限制"}
106
- default_key: 超时或输入无效时的默认返回值,若为None则使用最后一个选项的键
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
- 用户选择的选项键(options中的某个键),或default_key
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
- if default_key is None:
127
- default_key = list(options.keys())[-1]
138
+ # 判断是字典,还是列表
139
+ is_list_mode = isinstance(options, list)
128
140
 
129
- # 构建选项列表(保持字典顺序)
130
- items = list(options.items())
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
- default_label = options.get(default_key, default_key)
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
- # 检查列中的第一个非空值是否是Decimal类型
186
- first_non_null = df[column].first_valid_index()
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
- target_type = column_types[column]
192
- if target_type == "int":
196
+ col_target_type = column_types[column]
197
+ if col_target_type == "int":
193
198
  df[column] = df[column].astype(int)
194
- elif target_type == "float":
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 target_type == "auto":
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._length, index=self._index)
43
- self._false_mask = pd.Series([False] * self._length, index=self._index)
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
@@ -35,8 +35,8 @@ def _is_not_empty(x):
35
35
  if pd.isna(x):
36
36
  return False
37
37
  except ValueError:
38
- # pd.isna() 返回数组或产生歧义时,说明有值
39
- return True
38
+ # pd.isna() 失败说明 x 不是可空的标量值,应视为有值
39
+ pass
40
40
  return True
41
41
 
42
42
 
@@ -15,6 +15,9 @@ def print_progress(idx: int, total: int, message: str = "") -> None:
15
15
  :param message: 额外消息,默认为空字符串
16
16
  """
17
17
  # 计算当前进度百分比(0-100)
18
+ if total <= 0:
19
+ print(f"警告: total 必须大于 0(当前值: {total})")
20
+ return
18
21
  percent = int(idx / total * 100)
19
22
 
20
23
  # 构建进度条:
@@ -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
- result = response.json()
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.50
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
- count = ask_select(
134
- options={10: "10条", 50: "50条", 100: "100条"},
135
- default_key=50,
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"查询 {count} 条数据")
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.50',
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