tablemaster 2.1.3__tar.gz → 2.1.5__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 (37) hide show
  1. {tablemaster-2.1.3 → tablemaster-2.1.5}/PKG-INFO +2 -2
  2. {tablemaster-2.1.3 → tablemaster-2.1.5}/pyproject.toml +2 -2
  3. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/database.py +1 -1
  4. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/feishu.py +2 -2
  5. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/gspread.py +85 -4
  6. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster.egg-info/PKG-INFO +2 -2
  7. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster.egg-info/requires.txt +1 -1
  8. {tablemaster-2.1.3 → tablemaster-2.1.5}/LICENSE +0 -0
  9. {tablemaster-2.1.3 → tablemaster-2.1.5}/README.md +0 -0
  10. {tablemaster-2.1.3 → tablemaster-2.1.5}/setup.cfg +0 -0
  11. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/__init__.py +0 -0
  12. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/__main__.py +0 -0
  13. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/cli.py +0 -0
  14. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/config.py +0 -0
  15. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/local.py +0 -0
  16. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/__init__.py +0 -0
  17. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/apply.py +0 -0
  18. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/dialects/__init__.py +0 -0
  19. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/dialects/base.py +0 -0
  20. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/dialects/mysql.py +0 -0
  21. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/dialects/postgresql.py +0 -0
  22. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/dialects/tidb.py +0 -0
  23. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/diff.py +0 -0
  24. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/init.py +0 -0
  25. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/introspect.py +0 -0
  26. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/loader.py +0 -0
  27. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/models.py +0 -0
  28. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/plan.py +0 -0
  29. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/schema/pull.py +0 -0
  30. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/sync.py +0 -0
  31. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster/utils.py +0 -0
  32. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster.egg-info/SOURCES.txt +0 -0
  33. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster.egg-info/dependency_links.txt +0 -0
  34. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster.egg-info/entry_points.txt +0 -0
  35. {tablemaster-2.1.3 → tablemaster-2.1.5}/tablemaster.egg-info/top_level.txt +0 -0
  36. {tablemaster-2.1.3 → tablemaster-2.1.5}/tests/test_error_visibility.py +0 -0
  37. {tablemaster-2.1.3 → tablemaster-2.1.5}/tests/test_schema_core.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tablemaster
3
- Version: 2.1.3
3
+ Version: 2.1.5
4
4
  Summary: tablemaster is a Python toolkit for moving and managing tabular data across databases, Feishu/Lark, Google Sheets, and local files with one consistent API.
5
5
  Author-email: Livid <livid.su@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/ilivid/tablemaster
7
7
  Requires-Python: >=3.9
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
- Requires-Dist: pandas<3,>=1.5
10
+ Requires-Dist: pandas>=1.5
11
11
  Requires-Dist: pyyaml>=6
12
12
  Requires-Dist: python-dateutil>=2.8
13
13
  Requires-Dist: tqdm>=4.60
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tablemaster"
7
- version = "2.1.3"
7
+ version = "2.1.5"
8
8
  description = "tablemaster is a Python toolkit for moving and managing tabular data across databases, Feishu/Lark, Google Sheets, and local files with one consistent API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -12,7 +12,7 @@ authors = [
12
12
  { name = "Livid", email = "livid.su@gmail.com" }
13
13
  ]
14
14
  dependencies = [
15
- "pandas>=1.5,<3",
15
+ "pandas>=1.5",
16
16
  "pyyaml>=6",
17
17
  "python-dateutil>=2.8",
18
18
  "tqdm>=4.60",
@@ -409,7 +409,7 @@ class ManageTable:
409
409
  VALUES ({value_placeholders})
410
410
  """
411
411
 
412
- data = chunk.where(pd.notna(chunk), None).to_dict(orient='records')
412
+ data = chunk.astype(object).where(pd.notna(chunk), None).to_dict(orient='records')
413
413
  connection.execute(text(insert_sql), data)
414
414
  pbar.update(1)
415
415
  except Exception as e:
@@ -192,7 +192,7 @@ def fs_write_df(sheet_address, df, feishu_cfg, loc='A1', clear_sheet=True):
192
192
  df_copy[col] = df_copy[col].astype(str)
193
193
 
194
194
  # 处理 NaN 值,转换为空字符串
195
- df_copy = df_copy.fillna('')
195
+ df_copy = df_copy.astype(object).fillna('')
196
196
 
197
197
  # 替换 'nan' 字符串为空字符串
198
198
  df_copy = df_copy.replace('nan', '')
@@ -365,7 +365,7 @@ def fs_write_base(sheet_address, df, feishu_cfg, clear_table=False):
365
365
 
366
366
  # 处理 DataFrame - 只保留有效字段
367
367
  df_copy = df[list(valid_fields)].copy()
368
- df_copy = df_copy.fillna('')
368
+ df_copy = df_copy.astype(object).fillna('')
369
369
  df_copy = df_copy.replace('nan', '')
370
370
 
371
371
  # 将 DataFrame 转换为 records 格式
@@ -1,8 +1,12 @@
1
1
  import gspread
2
+ import json
3
+ import math
2
4
  import pandas as pd
5
+ import numpy as np
3
6
  import re
4
7
  import warnings
5
8
  import logging
9
+ from datetime import date, datetime, time
6
10
  from functools import lru_cache
7
11
 
8
12
  logger = logging.getLogger(__name__)
@@ -19,6 +23,85 @@ def _warn_deprecated(message):
19
23
  warnings.warn(f'{message} This usage will be removed in a future release.', FutureWarning, stacklevel=3)
20
24
 
21
25
 
26
+ def _json_safe(value):
27
+ if value is None or value is pd.NA or value is pd.NaT:
28
+ return None
29
+
30
+ if isinstance(value, pd.Timestamp):
31
+ return None if pd.isna(value) else value.isoformat()
32
+ if isinstance(value, pd.Timedelta):
33
+ return None if pd.isna(value) else str(value)
34
+ if isinstance(value, np.datetime64):
35
+ return None if np.isnat(value) else pd.Timestamp(value).isoformat()
36
+ if isinstance(value, np.timedelta64):
37
+ try:
38
+ if np.isnat(value):
39
+ return None
40
+ except TypeError:
41
+ pass
42
+ return str(pd.Timedelta(value))
43
+ if isinstance(value, (datetime, date, time)):
44
+ return value.isoformat()
45
+
46
+ if isinstance(value, (float, np.floating)):
47
+ if np.isnan(value) or np.isinf(value):
48
+ return None
49
+ return float(value)
50
+ if isinstance(value, np.integer):
51
+ return int(value)
52
+ if isinstance(value, np.bool_):
53
+ return bool(value)
54
+
55
+ if isinstance(value, dict):
56
+ return {k: _json_safe(v) for k, v in value.items()}
57
+ if isinstance(value, (list, tuple, set)):
58
+ return [_json_safe(v) for v in value]
59
+ if isinstance(value, np.ndarray):
60
+ return [_json_safe(v) for v in value.tolist()]
61
+
62
+ try:
63
+ if math.isnan(value) or math.isinf(value):
64
+ return None
65
+ except Exception:
66
+ pass
67
+
68
+ return value
69
+
70
+
71
+ def _build_values(df):
72
+ safe_cols = [_json_safe(col) for col in df.columns.tolist()]
73
+ safe_cols = [("" if col is None else col) for col in safe_cols]
74
+ for col_index, col_value in enumerate(safe_cols, start=1):
75
+ try:
76
+ json.dumps(col_value, allow_nan=False)
77
+ except (TypeError, ValueError) as exc:
78
+ raise ValueError(f'column header at col {col_index} is not JSON serializable: {exc}') from exc
79
+
80
+ rows = df.values.tolist()
81
+ safe_rows = []
82
+ for row_index, row in enumerate(rows, start=2):
83
+ safe_row = []
84
+ for col_index, cell in enumerate(row, start=1):
85
+ safe_cell = _json_safe(cell)
86
+ safe_cell = '' if safe_cell is None else safe_cell
87
+ try:
88
+ json.dumps(safe_cell, allow_nan=False)
89
+ except (TypeError, ValueError) as exc:
90
+ column_name = str(df.columns[col_index - 1])
91
+ raise ValueError(
92
+ f'cell at row {row_index}, col {col_index} ({column_name}) is not JSON serializable: {exc}'
93
+ ) from exc
94
+ safe_row.append(safe_cell)
95
+ safe_rows.append(safe_row)
96
+
97
+ values = [safe_cols] + safe_rows
98
+ try:
99
+ json.dumps({'values': values}, allow_nan=False)
100
+ except (TypeError, ValueError) as exc:
101
+ raise ValueError(f'worksheet payload contains non-JSON-safe data: {exc}') from exc
102
+ return values
103
+
104
+
22
105
  def _resolve_service_account_path(cfg, service_account_path):
23
106
  if service_account_path:
24
107
  _warn_deprecated('service_account_path argument is deprecated; pass a cfg object instead.')
@@ -123,10 +206,8 @@ def gs_write_df(address, df, cfg=None, loc='A1', service_account_path=None):
123
206
  try:
124
207
  wks.clear()
125
208
  df_copy = df.copy()
126
- non_float_int_columns = df_copy.select_dtypes(exclude=['float64', 'int64']).columns
127
- for col in non_float_int_columns:
128
- df_copy[col] = df_copy[col].astype(str)
129
- wks.update(loc, ([df_copy.columns.values.tolist()] + df_copy.values.tolist()))
209
+ values = _build_values(df_copy)
210
+ wks.update(loc, values)
130
211
 
131
212
  logger.info('data is written')
132
213
  except Exception as e:
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tablemaster
3
- Version: 2.1.3
3
+ Version: 2.1.5
4
4
  Summary: tablemaster is a Python toolkit for moving and managing tabular data across databases, Feishu/Lark, Google Sheets, and local files with one consistent API.
5
5
  Author-email: Livid <livid.su@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/ilivid/tablemaster
7
7
  Requires-Python: >=3.9
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
- Requires-Dist: pandas<3,>=1.5
10
+ Requires-Dist: pandas>=1.5
11
11
  Requires-Dist: pyyaml>=6
12
12
  Requires-Dist: python-dateutil>=2.8
13
13
  Requires-Dist: tqdm>=4.60
@@ -1,4 +1,4 @@
1
- pandas<3,>=1.5
1
+ pandas>=1.5
2
2
  pyyaml>=6
3
3
  python-dateutil>=2.8
4
4
  tqdm>=4.60
File without changes
File without changes
File without changes