funcguard 0.2.51__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.51 → funcguard-0.2.53}/PKG-INFO +6 -1
  2. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/ip_utils.py +2 -2
  3. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/convert_utils.py +14 -9
  4. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/fill_round.py +10 -0
  5. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/statistics/df_statistics.py +10 -2
  6. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/statistics/mask_utils.py +2 -2
  7. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/printer.py +3 -0
  8. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/tools.py +8 -3
  9. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard.egg-info/PKG-INFO +6 -1
  10. {funcguard-0.2.51 → funcguard-0.2.53}/setup.py +6 -1
  11. {funcguard-0.2.51 → funcguard-0.2.53}/LICENSE +0 -0
  12. {funcguard-0.2.51 → funcguard-0.2.53}/README.md +0 -0
  13. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/__init__.py +0 -0
  14. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/calculate.py +0 -0
  15. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/core.py +0 -0
  16. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/data_models/__init__.py +0 -0
  17. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/data_models/request_models.py +0 -0
  18. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/log_utils.py +0 -0
  19. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/__init__.py +0 -0
  20. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/date_utils.py +0 -0
  21. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/filter.py +0 -0
  22. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/json_utils/__init__.py +0 -0
  23. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/json_utils/json_parser.py +0 -0
  24. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/statistics/__init__.py +0 -0
  25. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/statistics/agg_utils.py +0 -0
  26. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/pd_utils/statistics/count_utils.py +0 -0
  27. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard/time_utils.py +0 -0
  28. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard.egg-info/SOURCES.txt +0 -0
  29. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard.egg-info/dependency_links.txt +0 -0
  30. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard.egg-info/not-zip-safe +0 -0
  31. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard.egg-info/requires.txt +0 -0
  32. {funcguard-0.2.51 → funcguard-0.2.53}/funcguard.egg-info/top_level.txt +0 -0
  33. {funcguard-0.2.51 → funcguard-0.2.53}/setup.cfg +0 -0
  34. {funcguard-0.2.51 → funcguard-0.2.53}/tests/__init__.py +0 -0
  35. {funcguard-0.2.51 → 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.51
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
@@ -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.51
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
@@ -9,7 +9,7 @@ except FileNotFoundError:
9
9
 
10
10
  setup(
11
11
  name='funcguard',
12
- version='0.2.51',
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