sqlseed 0.1.16__tar.gz → 0.1.17__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.
- {sqlseed-0.1.16 → sqlseed-0.1.17}/CHANGELOG.md +21 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/CHANGELOG.zh-CN.md +21 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/PKG-INFO +1 -1
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_utils/progress.py +57 -10
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/orchestrator.py +62 -2
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/_base_adapter.py +12 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/_protocol.py +2 -0
- sqlseed-0.1.16/sqlseed-/344/273/243/347/240/201/350/264/250/351/207/217/351/227/256/351/242/230/346/261/207/346/200/273.md +0 -204
- {sqlseed-0.1.16 → sqlseed-0.1.17}/.gitignore +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/LICENSE +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/README.md +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/README.zh-CN.md +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/build_demo_db.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/01-quickstart.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/02-column-mapping.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/03-generators.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/04-database-advanced.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/05-dag-and-constraints.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/06-config-deep-dive.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/07-ai-plugin.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/08-mcp-server.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/09-plugin-hooks.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/10-cli-reference.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/11-utilities.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/examples/notebooks/12-testing-patterns.ipynb +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/pyproject.toml +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/__init__.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_utils/__init__.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_utils/logger.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_utils/metrics.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_utils/paths.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_utils/schema_helpers.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_utils/sql_safe.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/_version.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/cli/__init__.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/cli/main.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/config/__init__.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/config/loader.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/config/models.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/config/snapshot.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/__init__.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/column_dag.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/constraints.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/enrichment.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/expression.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/mapper.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/plugin_mediator.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/relation.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/result.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/schema.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/transform.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/core/unique_adjuster.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/__init__.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/_compat.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/_helpers.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/optimizer.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/raw_sqlite_adapter.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/database/sqlite_utils_adapter.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/__init__.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/_dispatch.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/_json_helpers.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/_protocol.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/_string_helpers.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/base_provider.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/faker_provider.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/mimesis_provider.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/registry.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/generators/stream.py +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/src/sqlseed/py.typed +0 -0
- {sqlseed-0.1.16 → sqlseed-0.1.17}/uv.lock +0 -0
|
@@ -7,6 +7,27 @@ All notable changes to this project will be documented in this file.
|
|
|
7
7
|
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
8
8
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
9
9
|
|
|
10
|
+
## [v0.1.17]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `RichProgressBackend` now accepts `ascii_only` parameter: when `True`, uses `"line"` spinner (`|/-\`) and omits `BarColumn`, avoiding `UnicodeEncodeError` on GBK/Big5/CP936 console encodings
|
|
15
|
+
- `_can_render_unicode()` cached helper probes whether stdout can encode Rich's Braille/block-element characters (U+280B, U+2588, U+2591)
|
|
16
|
+
- `create_progress()` auto-falls back to `ascii_only=True` when `_can_render_unicode()` returns `False`, with a debug log message
|
|
17
|
+
- `DataOrchestrator` adds SQL operation methods: `execute(sql, params)`, `query(sql, params)`, `fetch_one(sql, params)`, `fetch_all(sql, params)` for direct database interaction
|
|
18
|
+
- `BaseSQLiteAdapter._execute(sql, params)` method for parameterized SQL execution
|
|
19
|
+
- `DatabaseAdapter` protocol updated with `_execute` method signature
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- `RichProgressBackend` refresh rate set to 1 Hz (was default 10 Hz) to reduce terminal flicker
|
|
24
|
+
- Test suite: `TestRichProgressBackend` and `TestRichProgressBackendAsciiOnly` merged via `pytest.mark.parametrize` to eliminate code duplication
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Windows compatibility: GBK/GB2312/Big5/CP936 encoded terminals no longer crash with `UnicodeEncodeError` when displaying Rich progress bars
|
|
29
|
+
- `.gitignore`: removed `*汇总.md` pattern (no longer needed)
|
|
30
|
+
|
|
10
31
|
## [v0.1.16]
|
|
11
32
|
|
|
12
33
|
### Added
|
|
@@ -7,6 +7,27 @@
|
|
|
7
7
|
格式基于 [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
8
8
|
本项目遵循[语义化版本](https://semver.org/spec/v2.0.0.html)。
|
|
9
9
|
|
|
10
|
+
## [v0.1.17]
|
|
11
|
+
|
|
12
|
+
### 新增
|
|
13
|
+
|
|
14
|
+
- `RichProgressBackend` 新增 `ascii_only` 参数:启用时使用 `"line"` 旋转符(`|/-\`)并省略 `BarColumn`,避免 GBK/Big5/CP936 编码终端的 `UnicodeEncodeError`
|
|
15
|
+
- `_can_render_unicode()` 缓存辅助函数,检测 stdout 是否能编码 Rich 的盲文/方块字符(U+280B, U+2588, U+2591)
|
|
16
|
+
- `create_progress()` 在 `_can_render_unicode()` 返回 `False` 时自动回退到 `ascii_only=True`,并输出 debug 日志
|
|
17
|
+
- `DataOrchestrator` 新增 SQL 操作方法:`execute(sql, params)`、`query(sql, params)`、`fetch_one(sql, params)`、`fetch_all(sql, params)` 用于直接数据库交互
|
|
18
|
+
- `BaseSQLiteAdapter._execute(sql, params)` 参数化 SQL 执行方法
|
|
19
|
+
- `DatabaseAdapter` 协议新增 `_execute` 方法签名
|
|
20
|
+
|
|
21
|
+
### 变更
|
|
22
|
+
|
|
23
|
+
- `RichProgressBackend` 刷新频率设为 1 Hz(原默认 10 Hz),减少终端闪烁
|
|
24
|
+
- 测试套件:`TestRichProgressBackend` 和 `TestRichProgressBackendAsciiOnly` 通过 `pytest.mark.parametrize` 合并,消除代码重复
|
|
25
|
+
|
|
26
|
+
### 修复
|
|
27
|
+
|
|
28
|
+
- Windows 兼容性:GBK/GB2312/Big5/CP936 编码终端不再因 Rich 进度条的 Unicode 字符而崩溃
|
|
29
|
+
- `.gitignore`:移除 `*汇总.md` 规则(不再需要)
|
|
30
|
+
|
|
10
31
|
## [v0.1.16]
|
|
11
32
|
|
|
12
33
|
### 新增
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import sys
|
|
3
4
|
from abc import ABC, abstractmethod
|
|
4
5
|
from functools import lru_cache
|
|
5
6
|
from importlib.util import find_spec
|
|
@@ -64,6 +65,24 @@ def _detect_environment() -> RuntimeEnv:
|
|
|
64
65
|
return "terminal"
|
|
65
66
|
|
|
66
67
|
|
|
68
|
+
@lru_cache(maxsize=1)
|
|
69
|
+
def _can_render_unicode() -> bool:
|
|
70
|
+
"""Check whether stdout can encode characters used by Rich progress bars.
|
|
71
|
+
|
|
72
|
+
Rich's default ``SpinnerColumn`` uses Braille patterns (e.g. ``⠋`` U+280B)
|
|
73
|
+
and ``BarColumn`` uses block elements (``█`` U+2588, ``░`` U+2591). On
|
|
74
|
+
Windows consoles with GBK / GB2312 / Big5 encodings these characters cause
|
|
75
|
+
``UnicodeEncodeError``. This helper probes the actual encoding so that
|
|
76
|
+
``create_progress()`` can fall back to an ASCII-safe layout.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
|
|
80
|
+
"\u280b\u2588\u2591".encode(encoding)
|
|
81
|
+
return True
|
|
82
|
+
except (UnicodeEncodeError, LookupError, TypeError):
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
|
|
67
86
|
# ---------------------------------------------------------------------------
|
|
68
87
|
# Abstract backend
|
|
69
88
|
# ---------------------------------------------------------------------------
|
|
@@ -130,18 +149,37 @@ class RichProgressBackend(ProgressBackend):
|
|
|
130
149
|
"""Rich Progress backend for terminal environments.
|
|
131
150
|
|
|
132
151
|
Renders spinner, progress bar, percentage, speed, and ETA.
|
|
152
|
+
|
|
153
|
+
When *ascii_only* is ``True`` the layout avoids Unicode characters
|
|
154
|
+
(Braille spinners, block-element bars) that cannot be encoded by
|
|
155
|
+
limited console encodings such as GBK or Big5. The spinner falls back
|
|
156
|
+
to the ``"line"`` style (``|/-\\``) and the graphical bar is omitted.
|
|
133
157
|
"""
|
|
134
158
|
|
|
135
|
-
def __init__(self) -> None:
|
|
159
|
+
def __init__(self, *, ascii_only: bool = False) -> None:
|
|
160
|
+
if ascii_only:
|
|
161
|
+
columns: list[Any] = [
|
|
162
|
+
SpinnerColumn("line"),
|
|
163
|
+
TextColumn("[progress.description]{task.description}"),
|
|
164
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
165
|
+
TextColumn("{task.completed}/{task.total}"),
|
|
166
|
+
TransferSpeedColumn(),
|
|
167
|
+
TimeRemainingColumn(),
|
|
168
|
+
]
|
|
169
|
+
else:
|
|
170
|
+
columns = [
|
|
171
|
+
SpinnerColumn(),
|
|
172
|
+
TextColumn("[progress.description]{task.description}"),
|
|
173
|
+
BarColumn(),
|
|
174
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
175
|
+
TextColumn("{task.completed}/{task.total}"),
|
|
176
|
+
TransferSpeedColumn(),
|
|
177
|
+
TimeRemainingColumn(),
|
|
178
|
+
]
|
|
136
179
|
self._progress = Progress(
|
|
137
|
-
|
|
138
|
-
TextColumn("[progress.description]{task.description}"),
|
|
139
|
-
BarColumn(),
|
|
140
|
-
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
141
|
-
TextColumn("{task.completed}/{task.total}"),
|
|
142
|
-
TransferSpeedColumn(),
|
|
143
|
-
TimeRemainingColumn(),
|
|
180
|
+
*columns,
|
|
144
181
|
transient=False,
|
|
182
|
+
refresh_per_second=1,
|
|
145
183
|
)
|
|
146
184
|
|
|
147
185
|
def __enter__(self) -> RichProgressBackend:
|
|
@@ -258,11 +296,17 @@ def create_progress(*, disable: bool = False) -> ProgressBackend:
|
|
|
258
296
|
disable=True → NullProgressBackend (zero-cost no-op)
|
|
259
297
|
Jupyter+tqdm → TqdmNotebookBackend (native notebook widget)
|
|
260
298
|
Jupyter-tqdm → NullProgressBackend (graceful degradation)
|
|
261
|
-
Terminal
|
|
299
|
+
Terminal+UTF8 → RichProgressBackend (rich spinner + bar)
|
|
300
|
+
Terminal+GBK → RichProgressBackend(ascii_only=True) (ASCII spinner, no bar)
|
|
262
301
|
|
|
263
302
|
The Jupyter-without-tqdm path logs a one-time warning instead of raising
|
|
264
303
|
ImportError, because progress display is a UX nicety, not a correctness
|
|
265
304
|
requirement.
|
|
305
|
+
|
|
306
|
+
When the console encoding cannot represent the Unicode characters used by
|
|
307
|
+
Rich's default spinner (Braille patterns) and bar (block elements), the
|
|
308
|
+
factory automatically switches to an ASCII-safe layout so that Windows
|
|
309
|
+
consoles with GBK / Big5 encodings do not crash with ``UnicodeEncodeError``.
|
|
266
310
|
"""
|
|
267
311
|
if disable:
|
|
268
312
|
return NullProgressBackend()
|
|
@@ -277,4 +321,7 @@ def create_progress(*, disable: bool = False) -> ProgressBackend:
|
|
|
277
321
|
)
|
|
278
322
|
return NullProgressBackend()
|
|
279
323
|
|
|
280
|
-
|
|
324
|
+
ascii_only = not _can_render_unicode()
|
|
325
|
+
if ascii_only:
|
|
326
|
+
logger.debug("Console encoding does not support Unicode progress characters — using ASCII-safe layout")
|
|
327
|
+
return RichProgressBackend(ascii_only=ascii_only)
|
|
@@ -387,8 +387,6 @@ class DataOrchestrator:
|
|
|
387
387
|
|
|
388
388
|
except (ValueError, RuntimeError, OSError, sqlite3.OperationalError, sqlite3.IntegrityError) as e:
|
|
389
389
|
if isinstance(e, sqlite3.IntegrityError) and enrich:
|
|
390
|
-
# enrich mode: UNIQUE conflicts are expected when merging
|
|
391
|
-
# into tables that already contain data
|
|
392
390
|
logger.warning("Integrity constraint during enrich", table_name=table_name, error=e)
|
|
393
391
|
else:
|
|
394
392
|
logger.error("Failed to fill table", table_name=table_name, error=e)
|
|
@@ -550,6 +548,68 @@ class DataOrchestrator:
|
|
|
550
548
|
|
|
551
549
|
fill = fill_table
|
|
552
550
|
|
|
551
|
+
def execute(self, sql: str, params: tuple[Any, ...] | None = None) -> Any:
|
|
552
|
+
"""Execute a SQL statement.
|
|
553
|
+
|
|
554
|
+
Args:
|
|
555
|
+
sql: The SQL statement to execute.
|
|
556
|
+
params: Optional tuple of parameters for parameterized queries.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
A cursor object with the result.
|
|
560
|
+
"""
|
|
561
|
+
self._ensure_connected()
|
|
562
|
+
if params is None:
|
|
563
|
+
params = ()
|
|
564
|
+
return self._db._execute(sql, params)
|
|
565
|
+
|
|
566
|
+
def query(self, sql: str, params: tuple[Any, ...] | None = None) -> list[dict[str, Any]]:
|
|
567
|
+
"""Execute a SELECT query and return results as a list of dictionaries.
|
|
568
|
+
|
|
569
|
+
Args:
|
|
570
|
+
sql: The SQL SELECT query to execute.
|
|
571
|
+
params: Optional tuple of parameters for parameterized queries.
|
|
572
|
+
|
|
573
|
+
Returns:
|
|
574
|
+
A list of dictionaries, where each dictionary represents a row.
|
|
575
|
+
"""
|
|
576
|
+
self._ensure_connected()
|
|
577
|
+
cursor = self.execute(sql, params)
|
|
578
|
+
columns = [desc[0] for desc in cursor.description]
|
|
579
|
+
return [dict(zip(columns, row, strict=True)) for row in cursor.fetchall()]
|
|
580
|
+
|
|
581
|
+
def fetch_one(self, sql: str, params: tuple[Any, ...] | None = None) -> dict[str, Any] | None:
|
|
582
|
+
"""Execute a SELECT query and return a single row as a dictionary.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
sql: The SQL SELECT query to execute.
|
|
586
|
+
params: Optional tuple of parameters for parameterized queries.
|
|
587
|
+
|
|
588
|
+
Returns:
|
|
589
|
+
A dictionary representing the first row, or None if no rows found.
|
|
590
|
+
"""
|
|
591
|
+
self._ensure_connected()
|
|
592
|
+
cursor = self.execute(sql, params)
|
|
593
|
+
row = cursor.fetchone()
|
|
594
|
+
if row is None:
|
|
595
|
+
return None
|
|
596
|
+
columns = [desc[0] for desc in cursor.description]
|
|
597
|
+
return dict(zip(columns, row, strict=True))
|
|
598
|
+
|
|
599
|
+
def fetch_all(self, sql: str, params: tuple[Any, ...] | None = None) -> list[Any]:
|
|
600
|
+
"""Execute a SELECT query and return results as a list of lists.
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
sql: The SQL SELECT query to execute.
|
|
604
|
+
params: Optional tuple of parameters for parameterized queries.
|
|
605
|
+
|
|
606
|
+
Returns:
|
|
607
|
+
A list of rows, where each row is a list of values.
|
|
608
|
+
"""
|
|
609
|
+
self._ensure_connected()
|
|
610
|
+
cursor = self.execute(sql, params)
|
|
611
|
+
return cursor.fetchall() # type: ignore[no-any-return]
|
|
612
|
+
|
|
553
613
|
def close(self) -> None:
|
|
554
614
|
if self._connected:
|
|
555
615
|
self._db.close()
|
|
@@ -31,6 +31,18 @@ class BaseSQLiteAdapter:
|
|
|
31
31
|
def _get_execute_fn(self) -> Callable[..., Any]:
|
|
32
32
|
raise NotImplementedError
|
|
33
33
|
|
|
34
|
+
def _execute(self, sql: str, params: tuple[Any, ...] = ()) -> Any:
|
|
35
|
+
"""Execute a SQL statement with parameters.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
sql: The SQL statement to execute.
|
|
39
|
+
params: Tuple of parameters for parameterized queries.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
The cursor object.
|
|
43
|
+
"""
|
|
44
|
+
return self._get_execute_fn()(sql, params)
|
|
45
|
+
|
|
34
46
|
def get_column_info(self, table_name: str) -> list[ColumnInfo]:
|
|
35
47
|
raise NotImplementedError
|
|
36
48
|
|
|
@@ -67,6 +67,8 @@ class DatabaseAdapter(Protocol):
|
|
|
67
67
|
|
|
68
68
|
def restore_settings(self) -> None: ...
|
|
69
69
|
|
|
70
|
+
def _execute(self, sql: str, params: tuple[Any, ...] = ()) -> Any: ...
|
|
71
|
+
|
|
70
72
|
def __enter__(self) -> DatabaseAdapter: ...
|
|
71
73
|
|
|
72
74
|
def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: Any) -> None: ...
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
# sqlseed 项目代码质量问题汇总
|
|
2
|
-
|
|
3
|
-
> **项目**:`sunbos/sqlseed`
|
|
4
|
-
> **生成日期**:2026-05-07
|
|
5
|
-
> **Commit 链**:`91a30c2b` → `b2a367e0` → `eaab405a`
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 一、SonarCloud 安全漏洞汇总(Commit 91a30c2b 之前)
|
|
10
|
-
|
|
11
|
-
**来源**:[SonarCloud Issues](https://sonarcloud.io/project/issues?severities=BLOCKER%2CCRITICAL%2CMAJOR%2CMINOR&sinceLeakPeriod=true&issueStatuses=OPEN%2CCONFIRMED&types=VULNERABILITY&id=sunbos_sqlseed)
|
|
12
|
-
|
|
13
|
-
**统计**:共 **3 个** Vulnerability,全部为 Major 级别
|
|
14
|
-
|
|
15
|
-
| # | 文件 | 规则 | 严重级别 | 安全影响 | 状态 |
|
|
16
|
-
|---|------|------|---------|---------|------|
|
|
17
|
-
| 1 | `plugins/mcp-server-sqlseed/pyproject.toml` | S8565 | Major | Security - Medium | ✅ 已修复 |
|
|
18
|
-
| 2 | `plugins/sqlseed-ai/pyproject.toml` | S8565 | Major | Security - Medium | ✅ 已修复 |
|
|
19
|
-
| 3 | `pyproject.toml`(根目录) | S8565 | Major | Security - Medium | ✅ 已修复 |
|
|
20
|
-
|
|
21
|
-
> **修复方式**:Commit `b2a367e0` 中已为所有 3 个 `pyproject.toml` 生成了 `uv.lock` 文件。
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## 二、SonarCloud 代码质量问题汇总(最新状态)
|
|
26
|
-
|
|
27
|
-
**来源**:[SonarCloud Issues](https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&sinceLeakPeriod=true&id=sunbos_sqlseed)
|
|
28
|
-
|
|
29
|
-
**统计**:共 **11 个问题**(4 高级 + 7 中级),分布在 2 个维度
|
|
30
|
-
|
|
31
|
-
### 2.1 按维度和严重级别分布
|
|
32
|
-
|
|
33
|
-
| 维度 | 严重级别 | 数量 | 类型 |
|
|
34
|
-
|------|---------|------|------|
|
|
35
|
-
| Reliability | High (Medium impact) | 7 | **Bug** |
|
|
36
|
-
| Maintainability | High (High impact) | 4 | Code Smell |
|
|
37
|
-
|
|
38
|
-
### 2.2 按文件分布
|
|
39
|
-
|
|
40
|
-
| 文件 | Bug | Code Smell | 合计 |
|
|
41
|
-
|------|-----|------------|------|
|
|
42
|
-
| `examples/notebooks/01-quickstart.ipynb` | 5 | 0 | 5 |
|
|
43
|
-
| `examples/notebooks/05-dag-and-constraints.ipynb` | 0 | 1 | 1 |
|
|
44
|
-
| `examples/notebooks/06-config-deep-dive.ipynb` | 0 | 2 | 2 |
|
|
45
|
-
| `examples/notebooks/07-ai-plugin.ipynb` | 0 | 1 | 1 |
|
|
46
|
-
| `examples/notebooks/09-plugin-hooks.ipynb` | 2 | 0 | 2 |
|
|
47
|
-
|
|
48
|
-
### 2.3 详细问题列表
|
|
49
|
-
|
|
50
|
-
#### 高级问题(Maintainability High)— 共 4 个 Code Smell
|
|
51
|
-
|
|
52
|
-
| # | 文件 | 行号 | 描述 | 修复工作量 |
|
|
53
|
-
|---|------|------|------|-----------|
|
|
54
|
-
| 1 | `05-dag-and-constraints.ipynb` | L293 | 字符串字面量 `"PRJ-\\d{6}"` 重复 3 次,应定义为常量 | 6min |
|
|
55
|
-
| 2 | `06-config-deep-dive.ipynb` | L755 | 字符串字面量 `"ORG-\\d{4}"` 重复 4 次,应定义为常量 | 8min |
|
|
56
|
-
| 3 | `06-config-deep-dive.ipynb` | L756 | 字符串字面量 `"ORG-\\d{4}"` 重复 3 次,应定义为常量 | 6min |
|
|
57
|
-
| 4 | `07-ai-plugin.ipynb` | L330 | 字符串字面量 `"ORG-\\d{4}"` 重复 3 次,应定义为常量 | 6min |
|
|
58
|
-
|
|
59
|
-
#### 中级问题(Reliability Medium)— 共 7 个 Bug
|
|
60
|
-
|
|
61
|
-
| # | 文件 | 行号 | 描述 | 修复工作量 |
|
|
62
|
-
|---|------|------|------|-----------|
|
|
63
|
-
| 1 | `01-quickstart.ipynb` | L428 | 无效的自我赋值 `PRJ_PATTERN = PRJ_PATTERN` | 3min |
|
|
64
|
-
| 2 | `01-quickstart.ipynb` | L571 | 无效的自我赋值 `ORG_PATTERN = ORG_PATTERN` | 3min |
|
|
65
|
-
| 3 | `01-quickstart.ipynb` | L858 | 无效的自我赋值 `ORG_PATTERN = ORG_PATTERN` | 3min |
|
|
66
|
-
| 4 | `01-quickstart.ipynb` | L864 | 无效的自我赋值 `ORG_PATTERN = ORG_PATTERN` | 3min |
|
|
67
|
-
| 5 | `01-quickstart.ipynb` | L1095 | 无效的自我赋值 `COUNT_ORG_SQL = COUNT_ORG_SQL` | 3min |
|
|
68
|
-
| 6 | `09-plugin-hooks.ipynb` | L386 | 无效的自我赋值 `ORG_PATTERN = ORG_PATTERN` | 3min |
|
|
69
|
-
| 7 | `09-plugin-hooks.ipynb` | L467 | 无效的自我赋值 `ORG_PATTERN = ORG_PATTERN` | 3min |
|
|
70
|
-
|
|
71
|
-
> **根因分析**:这 7 个 Bug 是 Commit `eaab405a` 中引入的。在将重复字符串提取为常量时,notebook 的单元格中出现了 `PRJ_PATTERN = PRJ_PATTERN` 这样的自我赋值(notebook 中常量定义在之前的单元格,当前单元格重新赋值是冗余的)。**应删除这些冗余的赋值语句**。
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## 三、CodeFlow 分析汇总 — Commit eaab405a(最新)
|
|
76
|
-
|
|
77
|
-
**来源**:[CodeFlow - Commit eaab405a](https://app.getcodeflow.com/github/sunbos/sqlseed/commits/eaab405a1f7bf74e6e7aac9b74a1b7092fa5eedf)
|
|
78
|
-
|
|
79
|
-
**Commit**:`chore: additional code quality and documentation improvements`
|
|
80
|
-
|
|
81
|
-
**统计**:共 **9 个警示**(0 个错误)
|
|
82
|
-
|
|
83
|
-
### 3.1 按文件分布
|
|
84
|
-
|
|
85
|
-
| 文件 | 错误 | 警示 | 合计 |
|
|
86
|
-
|------|------|------|------|
|
|
87
|
-
| `src/sqlseed/_utils/progress.py` | 0 | 6 | 6 |
|
|
88
|
-
| `tests/test_utils/test_progress.py` | 0 | 3 | 3 |
|
|
89
|
-
|
|
90
|
-
### 3.2 按问题类型分布
|
|
91
|
-
|
|
92
|
-
| 问题类型 | 数量 | 说明 |
|
|
93
|
-
|---------|------|------|
|
|
94
|
-
| `unnecessary-pass` | 3 | 方法体中不必要的 `pass` 语句 |
|
|
95
|
-
| `wrong-import-position` | 3 | import 语句位置不符合 PEP 8 |
|
|
96
|
-
| `import-outside-toplevel` | 2 | import 不在模块顶层 |
|
|
97
|
-
| `global-statement` | 1 | 使用了 global 语句 |
|
|
98
|
-
|
|
99
|
-
### 3.3 详细问题列表
|
|
100
|
-
|
|
101
|
-
#### 文件 1:`src/sqlseed/_utils/progress.py`(0 错误 / 6 警示)
|
|
102
|
-
|
|
103
|
-
| # | 行号 | 规则 | 描述 | 代码 |
|
|
104
|
-
|---|------|------|------|------|
|
|
105
|
-
| 1 | 105 | `unnecessary-pass` | 方法体中不必要的 `pass` | `def __enter__(self) -> TqdmNotebookBackend: ... pass` |
|
|
106
|
-
| 2 | 113 | `unnecessary-pass` | 方法体中不必要的 `pass` | `def __exit__(self, *args: Any) -> None: ... pass` |
|
|
107
|
-
| 3 | 117 | `unnecessary-pass` | 方法体中不必要的 `pass` | `def add_task(...): ... pass` |
|
|
108
|
-
| 4 | 201 | `import-outside-toplevel` | import 不在模块顶层 | `from tqdm.auto import tqdm` |
|
|
109
|
-
| 5 | 246 | `global-statement` | 使用了 global 语句 | `global _HAS_TQDM` |
|
|
110
|
-
| 6 | — | `import-outside-toplevel` | import 不在模块顶层 | 延迟导入相关 |
|
|
111
|
-
|
|
112
|
-
> **说明**:Commit `eaab405a` 为 `NullProgressBackend` 的空方法添加了 docstring(如 `"""Enter the progress context."""`),但方法体仍包含 `pass` 语句。由于已有 docstring,`pass` 是多余的。
|
|
113
|
-
|
|
114
|
-
#### 文件 2:`tests/test_utils/test_progress.py`(0 错误 / 3 警示)
|
|
115
|
-
|
|
116
|
-
| # | 行号 | 规则 | 描述 | 代码 |
|
|
117
|
-
|---|------|------|------|------|
|
|
118
|
-
| 1 | 11 | `wrong-import-position` | `import pytest` 应放在模块顶部 | `import pytest` |
|
|
119
|
-
| 2 | 13 | `wrong-import-position` | `import sqlseed._utils.progress` 应放在模块顶部 | `import sqlseed._utils.progress as progress_mod` |
|
|
120
|
-
| 3 | 14 | `wrong-import-position` | `from sqlseed._utils.progress import (...)` 应放在模块顶部 | `from sqlseed._utils.progress import NullProgressBackend, ...` |
|
|
121
|
-
|
|
122
|
-
> **说明**:这些 import 被放在 `if TYPE_CHECKING:` 块之后,违反了 PEP 8 的 import 排序规范。应将它们移到 `TYPE_CHECKING` 块之前。
|
|
123
|
-
|
|
124
|
-
### 3.4 修复建议
|
|
125
|
-
|
|
126
|
-
| 优先级 | 问题 | 建议 |
|
|
127
|
-
|--------|------|------|
|
|
128
|
-
| **P1** | `unnecessary-pass` × 3 | 删除 `progress.py` 中行 105、113、117 的 `pass` 语句(已有 docstring,pass 多余) |
|
|
129
|
-
| **P1** | `wrong-import-position` × 3 | 将 `test_progress.py` 中行 11、13-22 的 import 移到 `if TYPE_CHECKING:` 块之前 |
|
|
130
|
-
| **P3** | `import-outside-toplevel` × 2 | 延迟加载是有意设计,已有 `# noqa` 注释,建议配置忽略 |
|
|
131
|
-
| **P3** | `global-statement` × 1 | 缓存模式的有意使用,建议配置忽略 |
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## 四、历史问题修复记录
|
|
136
|
-
|
|
137
|
-
### Commit b2a367e0 修复的问题
|
|
138
|
-
|
|
139
|
-
| # | 来源 | 文件 | 问题 | 修复方式 |
|
|
140
|
-
|---|------|------|------|---------|
|
|
141
|
-
| 1 | SonarCloud | 3× `pyproject.toml` | S8565 缺少锁文件 | 生成 `uv.lock` |
|
|
142
|
-
| 2 | CodeFlow | `models.py:75` | `no-member` 错误 | `cls.model_fields.keys()` → `cls.model_fields` |
|
|
143
|
-
| 3 | CodeFlow | `_model_selector.py:28` | `broad-exception-caught` | 缩窄为具体异常类型 |
|
|
144
|
-
| 4 | CodeFlow | `progress.py` | 4× `disallowed-name` "bar" | 改为 `pbar` |
|
|
145
|
-
| 5 | CodeFlow | `stream.py` | 2× `try-except-raise` | 移除冗余 except 块 |
|
|
146
|
-
| 6 | CodeFlow | `stream.py:91` | `unused-variable` | `_retry_i` → `_` |
|
|
147
|
-
| 7 | CodeFlow | `test_cli.py` | 2× `CodeDuplication` | 提取公共 fixture |
|
|
148
|
-
|
|
149
|
-
### Commit eaab405a 修复的问题
|
|
150
|
-
|
|
151
|
-
| # | 来源 | 文件 | 问题 | 修复方式 |
|
|
152
|
-
|---|------|------|------|---------|
|
|
153
|
-
| 8 | SonarCloud | `01-quickstart.ipynb` | 字符串 `"SELECT COUNT(*) FROM organizations"` 重复 | 提取为 `COUNT_ORG_SQL` 常量 |
|
|
154
|
-
| 9 | SonarCloud | `01-quickstart.ipynb` | 字符串 `"SELECT org_code FROM organizations"` 重复 | 复用 `COUNT_ORG_SQL` |
|
|
155
|
-
| 10 | SonarCloud | `progress.py:103,109` | 2× 空方法体 | 添加 docstring |
|
|
156
|
-
| 11 | SonarCloud | `cli/main.py:263` | 认知复杂度过高 | 提取外键打印逻辑 |
|
|
157
|
-
| 12 | SonarCloud | `test_progress.py:265` | yield 缺少 Generator 类型标注 | 添加类型标注 |
|
|
158
|
-
| 13 | SonarCloud | `models.py:79` | 不必要的 `list()` 调用 | 简化字典迭代 |
|
|
159
|
-
| 14 | SonarCloud | `_model_selector.py:29` | 冗余 Exception 子类 | 移除 `OSError` |
|
|
160
|
-
| 15 | SonarCloud | `04-database-advanced.ipynb` | 2× set 构造优化 | 改为集合推导式 |
|
|
161
|
-
| 16 | CodeFlow | `_model_selector.py:29` | `overlapping-except` | 移除 `OSError` |
|
|
162
|
-
|
|
163
|
-
### Commit eaab405a 新引入的问题
|
|
164
|
-
|
|
165
|
-
| # | 来源 | 文件 | 问题 | 原因 |
|
|
166
|
-
|---|------|------|------|------|
|
|
167
|
-
| 1 | SonarCloud | `01-quickstart.ipynb` | 5× useless self-assignment | 常量定义在 notebook 前一个单元格,当前单元格冗余赋值 |
|
|
168
|
-
| 2 | SonarCloud | `09-plugin-hooks.ipynb` | 2× useless self-assignment | 同上 |
|
|
169
|
-
| 3 | CodeFlow | `progress.py` | 3× `unnecessary-pass` | 添加 docstring 后未删除多余的 `pass` |
|
|
170
|
-
| 4 | CodeFlow | `test_progress.py` | 3× `wrong-import-position` | import 排在 `TYPE_CHECKING` 块之后 |
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## 五、综合修复优先级建议
|
|
175
|
-
|
|
176
|
-
### P0 — 必须修复(Bug / 影响正确性)
|
|
177
|
-
|
|
178
|
-
| # | 文件 | 问题 | 数量 | 预计工作量 |
|
|
179
|
-
|---|------|------|------|-----------|
|
|
180
|
-
| 1 | `01-quickstart.ipynb` | useless self-assignment | 5 | 15min |
|
|
181
|
-
| 2 | `09-plugin-hooks.ipynb` | useless self-assignment | 2 | 6min |
|
|
182
|
-
|
|
183
|
-
> **修复方式**:删除 notebook 单元格中冗余的 `PRJ_PATTERN = PRJ_PATTERN`、`ORG_PATTERN = ORG_PATTERN`、`COUNT_ORG_SQL = COUNT_ORG_SQL` 等自我赋值语句。
|
|
184
|
-
|
|
185
|
-
### P1 — 建议尽快修复(代码规范)
|
|
186
|
-
|
|
187
|
-
| # | 文件 | 问题 | 数量 | 预计工作量 |
|
|
188
|
-
|---|------|------|------|-----------|
|
|
189
|
-
| 3 | `progress.py` | `unnecessary-pass` | 3 | 3min |
|
|
190
|
-
| 4 | `test_progress.py` | `wrong-import-position` | 3 | 5min |
|
|
191
|
-
| 5 | 4× notebooks | 字符串字面量重复 | 4 | 26min |
|
|
192
|
-
|
|
193
|
-
### P2 — 建议修复(可维护性)
|
|
194
|
-
|
|
195
|
-
| # | 文件 | 问题 | 数量 |
|
|
196
|
-
|---|------|------|------|
|
|
197
|
-
| 6 | `progress.py` | `import-outside-toplevel` + `global-statement` | 3 |
|
|
198
|
-
|
|
199
|
-
### P3 — 可选(有意设计,建议配置忽略)
|
|
200
|
-
|
|
201
|
-
| # | 文件 | 问题 | 说明 |
|
|
202
|
-
|---|------|------|------|
|
|
203
|
-
| 7 | `progress.py` | `import-outside-toplevel` | tqdm 延迟加载 |
|
|
204
|
-
| 8 | `progress.py` | `global-statement` | `_HAS_TQDM` 缓存标记 |
|
|
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
|
|
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
|
|
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
|