sqlnbfmt 0.4.2__tar.gz → 0.5.0__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.
- sqlnbfmt-0.5.0/.github/dependabot.yml +23 -0
- sqlnbfmt-0.5.0/.github/workflows/ci-and-publish.yml +55 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/.pre-commit-config.yaml +3 -3
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/CHANGELOG.md +23 -1
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/PKG-INFO +55 -36
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/README.md +53 -34
- sqlnbfmt-0.5.0/SECURITY.md +13 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/config.yaml +4 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/pyproject.toml +7 -2
- sqlnbfmt-0.5.0/sqlnbfmt/__init__.py +29 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/sqlnbfmt/formatter.py +276 -93
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/sqlnbfmt/jupyterlab_integration.py +29 -12
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/in_function_sql/expected.ipynb +1 -2
- {sqlnbfmt-0.4.2/tests/eval/marimo_idempotency → sqlnbfmt-0.5.0/tests/eval/marimo_basic_sql}/expected.py +11 -11
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_comment_preservation/expected.py +20 -20
- sqlnbfmt-0.5.0/tests/eval/marimo_complex_sql/expected.py +63 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_fstring_interpolation/expected.py +19 -19
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_multi_cell_mixed/expected.py +22 -22
- sqlnbfmt-0.5.0/tests/eval/marimo_multi_statement/expected.py +34 -0
- sqlnbfmt-0.5.0/tests/test_call_layout.py +151 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/test_formatter.py +22 -4
- sqlnbfmt-0.5.0/tests/test_jupyterlab_integration.py +208 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/test_marimo.py +6 -2
- sqlnbfmt-0.5.0/tests/test_pyproject_config.py +244 -0
- sqlnbfmt-0.5.0/uv.lock +497 -0
- sqlnbfmt-0.4.2/.github/workflows/ci-and-publish.yml +0 -42
- sqlnbfmt-0.4.2/sqlnbfmt/__init__.py +0 -15
- sqlnbfmt-0.4.2/tests/eval/marimo_complex_sql/expected.py +0 -63
- sqlnbfmt-0.4.2/tests/eval/marimo_multi_statement/expected.py +0 -34
- sqlnbfmt-0.4.2/tests/test_jupyterlab_integration.py +0 -76
- sqlnbfmt-0.4.2/uv.lock +0 -455
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/.gitignore +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/.pre-commit-hooks.yaml +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/.python-version +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/LICENSE +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/jupyter-config/sqlnbfmt.json +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/__init__.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/README.md +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/basic_select/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/basic_select/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/case_expression/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/case_expression/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/comment_preservation/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/comment_preservation/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/cte/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/cte/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/fstring_variables/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/fstring_variables/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/generate_fixtures.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/group_by_having/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/group_by_having/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/idempotency/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/idempotency/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/in_function_sql/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/joins/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/joins/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/magic_commands/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/magic_commands/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_basic_sql/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_comment_preservation/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_complex_sql/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_fstring_interpolation/input.py +0 -0
- {sqlnbfmt-0.4.2/tests/eval/marimo_basic_sql → sqlnbfmt-0.5.0/tests/eval/marimo_idempotency}/expected.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_idempotency/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_multi_cell_mixed/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_multi_statement/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_non_sql_unchanged/expected.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_non_sql_unchanged/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_skip_hint/expected.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/marimo_skip_hint/input.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/multi_cell_mixed/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/multi_cell_mixed/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/multi_statement/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/multi_statement/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/non_sql_unchanged/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/non_sql_unchanged/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/skip_hint/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/skip_hint/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/subquery/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/subquery/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/union/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/union/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/window_function/expected.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/eval/window_function/input.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/test_eval.py +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/test_notebook.ipynb +0 -0
- {sqlnbfmt-0.4.2 → sqlnbfmt-0.5.0}/tests/test_notebook_copy.ipynb +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "github-actions"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
day: "monday"
|
|
8
|
+
labels:
|
|
9
|
+
- "dependencies"
|
|
10
|
+
|
|
11
|
+
- package-ecosystem: "pip"
|
|
12
|
+
directory: "/"
|
|
13
|
+
schedule:
|
|
14
|
+
interval: "weekly"
|
|
15
|
+
day: "monday"
|
|
16
|
+
open-pull-requests-limit: 10
|
|
17
|
+
labels:
|
|
18
|
+
- "dependencies"
|
|
19
|
+
groups:
|
|
20
|
+
minor-and-patch:
|
|
21
|
+
update-types:
|
|
22
|
+
- "minor"
|
|
23
|
+
- "patch"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Python CI and Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
run_publish:
|
|
12
|
+
description: 'Set to true to run the publish job'
|
|
13
|
+
required: false
|
|
14
|
+
default: 'false'
|
|
15
|
+
|
|
16
|
+
permissions: {}
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
test:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
cache-dependency-glob: "pyproject.toml"
|
|
29
|
+
- run: uv python install 3.12
|
|
30
|
+
- run: uv sync --all-extras --dev --frozen
|
|
31
|
+
- run: uv run pytest
|
|
32
|
+
|
|
33
|
+
security:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
39
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
40
|
+
- run: uvx uv-secure uv.lock
|
|
41
|
+
|
|
42
|
+
publish:
|
|
43
|
+
needs: [test, security]
|
|
44
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.run_publish == 'true')
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
permissions:
|
|
47
|
+
id-token: write
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
50
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
51
|
+
with:
|
|
52
|
+
enable-cache: true
|
|
53
|
+
- run: uv python install 3.12
|
|
54
|
+
- run: uv build --no-sources
|
|
55
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
# Linting + formatting (Astral)
|
|
3
3
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
4
|
-
rev: v0.
|
|
4
|
+
rev: v0.17.1
|
|
5
5
|
hooks:
|
|
6
|
-
- id: ruff
|
|
6
|
+
- id: ruff-check
|
|
7
7
|
args: [--fix]
|
|
8
8
|
- id: ruff-format
|
|
9
9
|
|
|
@@ -24,7 +24,7 @@ repos:
|
|
|
24
24
|
|
|
25
25
|
# Validate pyproject.toml against PEP 621
|
|
26
26
|
- repo: https://github.com/abravalheri/validate-pyproject
|
|
27
|
-
rev:
|
|
27
|
+
rev: '0.26'
|
|
28
28
|
hooks:
|
|
29
29
|
- id: validate-pyproject
|
|
30
30
|
|
|
@@ -5,7 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [0.
|
|
8
|
+
## [0.5.0] - 2026-10-15
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`pyproject.toml` configuration**: set options in a `[tool.sqlnbfmt]` table instead of a separate YAML file. Without `--config`, sqlnbfmt uses the nearest `pyproject.toml` that has the table, searching up to the repository root. The JupyterLab integration uses the same discovery ([#22](https://github.com/flyersworder/sqlnbfmt/issues/22))
|
|
13
|
+
- `--config` accepts a `.toml` path as well as YAML
|
|
14
|
+
- `dialect` config option (in `[tool.sqlnbfmt]` or YAML), so the dialect no longer has to be passed as `--dialect` on every run; `--dialect` still overrides it
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Config values are now type-checked: a wrong type (e.g. a string where a list is expected) is reported as an error naming the file. Previously such values were silently mis-parsed or crashed with a traceback
|
|
19
|
+
- Unknown config options now produce a warning instead of being silently ignored
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- SQL passed to a function call is now indented relative to the line the call is on, and a closing `)` or remaining arguments no longer land at column 0. This was most visible in Marimo cells, where every `mo.sql(...)` sits inside a function. It had regressed in 0.3.0, when the `black` post-processing pass was removed. Code already formatted by an earlier version keeps its layout until its SQL changes
|
|
24
|
+
- The Jupyter server extension no longer overrides a formatter registered in `jupyter_server_config.py` (e.g. `register(dialect="postgres")`), and it now logs configuration errors instead of silently not registering
|
|
25
|
+
- An unknown dialect (e.g. `--dialect postgress`) is now rejected with a suggestion. Previously every SQL string silently failed to parse, so `--check` reported all files as properly formatted
|
|
26
|
+
- The `config.yaml` template was missing the `sql` function name (default since 0.4.0). Because config lists replace the defaults, copying the template silently disabled Marimo `mo.sql()` formatting. The README now states the replace semantics explicitly
|
|
27
|
+
- Upgraded locked dependencies, including sqlglot 29 → 30 (formatting output unchanged across the eval suite)
|
|
28
|
+
- A skipped Jupyter extension registration is now logged at debug level instead of being silently swallowed
|
|
29
|
+
|
|
30
|
+
## [0.4.2] - 2026-03-24
|
|
9
31
|
|
|
10
32
|
### Added
|
|
11
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: sqlnbfmt
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: A tool to format SQL code in Jupyter notebooks and Marimo notebooks.
|
|
5
5
|
Project-URL: Homepage, https://github.com/flyersworder/sqlnbfmt
|
|
6
6
|
Project-URL: Repository, https://github.com/flyersworder/sqlnbfmt
|
|
@@ -104,7 +104,7 @@ pip install sqlnbfmt jupyterlab-code-formatter
|
|
|
104
104
|
|
|
105
105
|
That's it — sqlnbfmt auto-registers as a Jupyter server extension. In JupyterLab, use **Edit > Format Cell** or configure format-on-save in Settings > Code Formatter, and select "Apply SQL Notebook Formatter".
|
|
106
106
|
|
|
107
|
-
To
|
|
107
|
+
The extension uses `[tool.sqlnbfmt]` (including `dialect`) from the nearest `pyproject.toml` of the directory you start Jupyter in. To set the dialect explicitly instead, add this to `~/.jupyter/jupyter_server_config.py`:
|
|
108
108
|
```python
|
|
109
109
|
from sqlnbfmt.jupyterlab_integration import register
|
|
110
110
|
register(dialect="postgres")
|
|
@@ -121,7 +121,7 @@ pip install pre-commit
|
|
|
121
121
|
```yaml
|
|
122
122
|
repos:
|
|
123
123
|
- repo: https://github.com/flyersworder/sqlnbfmt
|
|
124
|
-
rev: v0.
|
|
124
|
+
rev: v0.5.0
|
|
125
125
|
hooks:
|
|
126
126
|
- id: sqlnbfmt
|
|
127
127
|
types: [jupyter]
|
|
@@ -133,7 +133,7 @@ To also format Marimo notebooks (Python files), add the Python hook:
|
|
|
133
133
|
files: '\.py$' # optionally narrow with: files: 'marimo_.*\.py$'
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
All arguments are optional.
|
|
136
|
+
All arguments are optional. Settings in `[tool.sqlnbfmt]` in your `pyproject.toml` are picked up automatically (see [Configuration](#configuration)); alternatively pass them as arguments:
|
|
137
137
|
```yaml
|
|
138
138
|
args: [--dialect, postgres, --config, config.yaml]
|
|
139
139
|
```
|
|
@@ -162,19 +162,38 @@ Use `--check` in GitHub Actions to enforce formatting:
|
|
|
162
162
|
|
|
163
163
|
## Configuration
|
|
164
164
|
|
|
165
|
-
`sqlnbfmt` works without any configuration
|
|
165
|
+
`sqlnbfmt` works without any configuration. To override defaults, use a `[tool.sqlnbfmt]` table in `pyproject.toml` or a separate YAML file.
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
### pyproject.toml
|
|
168
|
+
|
|
169
|
+
```toml
|
|
170
|
+
[tool.sqlnbfmt]
|
|
171
|
+
dialect = "postgres"
|
|
172
|
+
|
|
173
|
+
[tool.sqlnbfmt.formatting_options]
|
|
174
|
+
indent_width = 2
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`sqlnbfmt` searches the current directory and its parents for the nearest `pyproject.toml` with a `[tool.sqlnbfmt]` table, stopping at the repository root (the directory containing `.git`). A `pyproject.toml` without that table is skipped, so a sub-package's file doesn't hide the repository-level configuration. The JupyterLab integration does the same, starting from the Jupyter server's working directory.
|
|
178
|
+
|
|
179
|
+
### YAML file
|
|
180
|
+
|
|
181
|
+
Pass a YAML file with `--config config.yaml`. [Here](https://github.com/flyersworder/sqlnbfmt/blob/main/config.yaml) is a template. `--config` also accepts a `.toml` path, which is read from its `[tool.sqlnbfmt]` table.
|
|
182
|
+
|
|
183
|
+
When `--config` is given, `pyproject.toml` discovery is skipped. Options you don't set keep their defaults, and unknown option names produce a warning.
|
|
168
184
|
|
|
169
185
|
### Configuration Options
|
|
170
186
|
|
|
171
187
|
| Option | Description | Default |
|
|
172
188
|
|--------|-------------|---------|
|
|
189
|
+
| `dialect` | SQL dialect passed to sqlglot (e.g. `postgres`, `mysql`, `bigquery`); `--dialect` overrides it | none (generic SQL) |
|
|
173
190
|
| `sql_keywords` | SQL keywords to recognize and format | Common SQL keywords |
|
|
174
|
-
| `function_names` | Python functions containing SQL code | `read_sql`, `execute`, etc. |
|
|
191
|
+
| `function_names` | Python functions containing SQL code | `read_sql`, `execute`, `sql` (Marimo's `mo.sql`), etc. |
|
|
175
192
|
| `sql_decorators` | Decorators indicating SQL code | `query`, `sql_query`, etc. |
|
|
176
|
-
| `single_line_threshold` | Maximum length before splitting SQL | 80 |
|
|
177
|
-
| `indent_width` | Number of spaces for indentation | 4 |
|
|
193
|
+
| `formatting_options.single_line_threshold` | Maximum length before splitting SQL | 80 |
|
|
194
|
+
| `formatting_options.indent_width` | Number of spaces for indentation | 4 |
|
|
195
|
+
|
|
196
|
+
`sql_keywords`, `function_names` and `sql_decorators` **replace** the built-in lists rather than extending them. Copy the defaults you still rely on from the [template](https://github.com/flyersworder/sqlnbfmt/blob/main/config.yaml); for example, keep `sql` in `function_names` or Marimo's `mo.sql()` calls stop being formatted.
|
|
178
197
|
|
|
179
198
|
## Examples
|
|
180
199
|
|
|
@@ -187,20 +206,20 @@ execute_sql("""SELECT a.col1, b.col2 FROM table_a a JOIN table_b b ON a.id = b.a
|
|
|
187
206
|
|
|
188
207
|
After formatting:
|
|
189
208
|
```python
|
|
190
|
-
execute_sql(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
JOIN
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
209
|
+
execute_sql(
|
|
210
|
+
"""
|
|
211
|
+
SELECT
|
|
212
|
+
a.col1,
|
|
213
|
+
b.col2
|
|
214
|
+
FROM table_a AS a
|
|
215
|
+
JOIN table_b AS b
|
|
216
|
+
ON a.id = b.a_id
|
|
217
|
+
WHERE
|
|
218
|
+
a.status = 'active'
|
|
219
|
+
ORDER BY
|
|
220
|
+
a.created_at DESC
|
|
221
|
+
"""
|
|
222
|
+
)
|
|
204
223
|
```
|
|
205
224
|
|
|
206
225
|
### Marimo Notebook
|
|
@@ -218,17 +237,17 @@ After formatting:
|
|
|
218
237
|
@app.cell
|
|
219
238
|
def _(mo):
|
|
220
239
|
_df = mo.sql(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
240
|
+
f"""
|
|
241
|
+
SELECT
|
|
242
|
+
id,
|
|
243
|
+
name
|
|
244
|
+
FROM users
|
|
245
|
+
WHERE
|
|
246
|
+
active = 1
|
|
247
|
+
ORDER BY
|
|
248
|
+
name
|
|
249
|
+
"""
|
|
250
|
+
)
|
|
232
251
|
return
|
|
233
252
|
```
|
|
234
253
|
|
|
@@ -236,7 +255,7 @@ def _(mo):
|
|
|
236
255
|
|
|
237
256
|
**SQL not being formatted?**
|
|
238
257
|
- Ensure the string contains at least 2 SQL keywords or a recognizable pattern like `SELECT...FROM`
|
|
239
|
-
- Check that the function name is in the recognized list (
|
|
258
|
+
- Check that the function name is in the recognized list (add custom ones via `function_names` in `[tool.sqlnbfmt]` or `--config`)
|
|
240
259
|
|
|
241
260
|
**Comments being modified?**
|
|
242
261
|
- Python comments (`#`) are preserved. SQL comments (`--`) inside strings are converted to `/* */` block comments by sqlglot.
|
|
@@ -76,7 +76,7 @@ pip install sqlnbfmt jupyterlab-code-formatter
|
|
|
76
76
|
|
|
77
77
|
That's it — sqlnbfmt auto-registers as a Jupyter server extension. In JupyterLab, use **Edit > Format Cell** or configure format-on-save in Settings > Code Formatter, and select "Apply SQL Notebook Formatter".
|
|
78
78
|
|
|
79
|
-
To
|
|
79
|
+
The extension uses `[tool.sqlnbfmt]` (including `dialect`) from the nearest `pyproject.toml` of the directory you start Jupyter in. To set the dialect explicitly instead, add this to `~/.jupyter/jupyter_server_config.py`:
|
|
80
80
|
```python
|
|
81
81
|
from sqlnbfmt.jupyterlab_integration import register
|
|
82
82
|
register(dialect="postgres")
|
|
@@ -93,7 +93,7 @@ pip install pre-commit
|
|
|
93
93
|
```yaml
|
|
94
94
|
repos:
|
|
95
95
|
- repo: https://github.com/flyersworder/sqlnbfmt
|
|
96
|
-
rev: v0.
|
|
96
|
+
rev: v0.5.0
|
|
97
97
|
hooks:
|
|
98
98
|
- id: sqlnbfmt
|
|
99
99
|
types: [jupyter]
|
|
@@ -105,7 +105,7 @@ To also format Marimo notebooks (Python files), add the Python hook:
|
|
|
105
105
|
files: '\.py$' # optionally narrow with: files: 'marimo_.*\.py$'
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
All arguments are optional.
|
|
108
|
+
All arguments are optional. Settings in `[tool.sqlnbfmt]` in your `pyproject.toml` are picked up automatically (see [Configuration](#configuration)); alternatively pass them as arguments:
|
|
109
109
|
```yaml
|
|
110
110
|
args: [--dialect, postgres, --config, config.yaml]
|
|
111
111
|
```
|
|
@@ -134,19 +134,38 @@ Use `--check` in GitHub Actions to enforce formatting:
|
|
|
134
134
|
|
|
135
135
|
## Configuration
|
|
136
136
|
|
|
137
|
-
`sqlnbfmt` works without any configuration
|
|
137
|
+
`sqlnbfmt` works without any configuration. To override defaults, use a `[tool.sqlnbfmt]` table in `pyproject.toml` or a separate YAML file.
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
### pyproject.toml
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[tool.sqlnbfmt]
|
|
143
|
+
dialect = "postgres"
|
|
144
|
+
|
|
145
|
+
[tool.sqlnbfmt.formatting_options]
|
|
146
|
+
indent_width = 2
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`sqlnbfmt` searches the current directory and its parents for the nearest `pyproject.toml` with a `[tool.sqlnbfmt]` table, stopping at the repository root (the directory containing `.git`). A `pyproject.toml` without that table is skipped, so a sub-package's file doesn't hide the repository-level configuration. The JupyterLab integration does the same, starting from the Jupyter server's working directory.
|
|
150
|
+
|
|
151
|
+
### YAML file
|
|
152
|
+
|
|
153
|
+
Pass a YAML file with `--config config.yaml`. [Here](https://github.com/flyersworder/sqlnbfmt/blob/main/config.yaml) is a template. `--config` also accepts a `.toml` path, which is read from its `[tool.sqlnbfmt]` table.
|
|
154
|
+
|
|
155
|
+
When `--config` is given, `pyproject.toml` discovery is skipped. Options you don't set keep their defaults, and unknown option names produce a warning.
|
|
140
156
|
|
|
141
157
|
### Configuration Options
|
|
142
158
|
|
|
143
159
|
| Option | Description | Default |
|
|
144
160
|
|--------|-------------|---------|
|
|
161
|
+
| `dialect` | SQL dialect passed to sqlglot (e.g. `postgres`, `mysql`, `bigquery`); `--dialect` overrides it | none (generic SQL) |
|
|
145
162
|
| `sql_keywords` | SQL keywords to recognize and format | Common SQL keywords |
|
|
146
|
-
| `function_names` | Python functions containing SQL code | `read_sql`, `execute`, etc. |
|
|
163
|
+
| `function_names` | Python functions containing SQL code | `read_sql`, `execute`, `sql` (Marimo's `mo.sql`), etc. |
|
|
147
164
|
| `sql_decorators` | Decorators indicating SQL code | `query`, `sql_query`, etc. |
|
|
148
|
-
| `single_line_threshold` | Maximum length before splitting SQL | 80 |
|
|
149
|
-
| `indent_width` | Number of spaces for indentation | 4 |
|
|
165
|
+
| `formatting_options.single_line_threshold` | Maximum length before splitting SQL | 80 |
|
|
166
|
+
| `formatting_options.indent_width` | Number of spaces for indentation | 4 |
|
|
167
|
+
|
|
168
|
+
`sql_keywords`, `function_names` and `sql_decorators` **replace** the built-in lists rather than extending them. Copy the defaults you still rely on from the [template](https://github.com/flyersworder/sqlnbfmt/blob/main/config.yaml); for example, keep `sql` in `function_names` or Marimo's `mo.sql()` calls stop being formatted.
|
|
150
169
|
|
|
151
170
|
## Examples
|
|
152
171
|
|
|
@@ -159,20 +178,20 @@ execute_sql("""SELECT a.col1, b.col2 FROM table_a a JOIN table_b b ON a.id = b.a
|
|
|
159
178
|
|
|
160
179
|
After formatting:
|
|
161
180
|
```python
|
|
162
|
-
execute_sql(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
JOIN
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
execute_sql(
|
|
182
|
+
"""
|
|
183
|
+
SELECT
|
|
184
|
+
a.col1,
|
|
185
|
+
b.col2
|
|
186
|
+
FROM table_a AS a
|
|
187
|
+
JOIN table_b AS b
|
|
188
|
+
ON a.id = b.a_id
|
|
189
|
+
WHERE
|
|
190
|
+
a.status = 'active'
|
|
191
|
+
ORDER BY
|
|
192
|
+
a.created_at DESC
|
|
193
|
+
"""
|
|
194
|
+
)
|
|
176
195
|
```
|
|
177
196
|
|
|
178
197
|
### Marimo Notebook
|
|
@@ -190,17 +209,17 @@ After formatting:
|
|
|
190
209
|
@app.cell
|
|
191
210
|
def _(mo):
|
|
192
211
|
_df = mo.sql(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
)
|
|
212
|
+
f"""
|
|
213
|
+
SELECT
|
|
214
|
+
id,
|
|
215
|
+
name
|
|
216
|
+
FROM users
|
|
217
|
+
WHERE
|
|
218
|
+
active = 1
|
|
219
|
+
ORDER BY
|
|
220
|
+
name
|
|
221
|
+
"""
|
|
222
|
+
)
|
|
204
223
|
return
|
|
205
224
|
```
|
|
206
225
|
|
|
@@ -208,7 +227,7 @@ def _(mo):
|
|
|
208
227
|
|
|
209
228
|
**SQL not being formatted?**
|
|
210
229
|
- Ensure the string contains at least 2 SQL keywords or a recognizable pattern like `SELECT...FROM`
|
|
211
|
-
- Check that the function name is in the recognized list (
|
|
230
|
+
- Check that the function name is in the recognized list (add custom ones via `function_names` in `[tool.sqlnbfmt]` or `--config`)
|
|
212
231
|
|
|
213
232
|
**Comments being modified?**
|
|
214
233
|
- Python comments (`#`) are preserved. SQL comments (`--`) inside strings are converted to `/* */` block comments by sqlglot.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Only the latest release receives security fixes.
|
|
6
|
+
|
|
7
|
+
## Reporting a Vulnerability
|
|
8
|
+
|
|
9
|
+
Please **do not** open a public GitHub issue for security vulnerabilities.
|
|
10
|
+
|
|
11
|
+
Report them privately via [GitHub Security Advisories](https://github.com/flyersworder/sqlnbfmt/security/advisories/new). Include a description of the issue, steps to reproduce, and the potential impact.
|
|
12
|
+
|
|
13
|
+
You can expect an acknowledgement within 48 hours and a patch or mitigation plan within 14 days for confirmed issues.
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
# Use this file only to override specific settings.
|
|
3
3
|
# config.yaml
|
|
4
4
|
|
|
5
|
+
# SQL dialect for sqlglot (e.g. postgres, mysql, bigquery); --dialect overrides it
|
|
6
|
+
# dialect: postgres
|
|
7
|
+
|
|
5
8
|
sql_keywords:
|
|
6
9
|
- SELECT
|
|
7
10
|
- FROM
|
|
@@ -35,6 +38,7 @@ function_names:
|
|
|
35
38
|
- fetchone
|
|
36
39
|
- fetchmany
|
|
37
40
|
- execute_query
|
|
41
|
+
- sql # Marimo mo.sql()
|
|
38
42
|
|
|
39
43
|
# Decorator names that contain SQL
|
|
40
44
|
sql_decorators:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sqlnbfmt"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
description = "A tool to format SQL code in Jupyter notebooks and Marimo notebooks."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Qing", email = "qingye779@gmail.com" }
|
|
@@ -56,4 +56,9 @@ markers = [
|
|
|
56
56
|
"jupyter-config/sqlnbfmt.json" = "etc/jupyter/jupyter_server_config.d/sqlnbfmt.json"
|
|
57
57
|
|
|
58
58
|
[tool.ruff]
|
|
59
|
-
|
|
59
|
+
# Eval cases and test notebooks are fixtures: their exact formatting is the test input.
|
|
60
|
+
extend-exclude = ["tests/eval/*/", "tests/*.ipynb"]
|
|
61
|
+
|
|
62
|
+
[tool.ruff.format]
|
|
63
|
+
# Code blocks in docs show sqlnbfmt's own output, not ruff's.
|
|
64
|
+
exclude = ["*.md"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
def _jupyter_server_extension_points():
|
|
2
|
+
return [{"module": "sqlnbfmt"}]
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _load_jupyter_server_extension(server_app):
|
|
6
|
+
try:
|
|
7
|
+
from jupyterlab_code_formatter.formatters import SERVER_FORMATTERS
|
|
8
|
+
except ImportError:
|
|
9
|
+
server_app.log.debug(
|
|
10
|
+
"sqlnbfmt: jupyterlab-code-formatter not installed; skipping registration"
|
|
11
|
+
)
|
|
12
|
+
return
|
|
13
|
+
|
|
14
|
+
# jupyter_server_config.py runs before extensions load; keep a formatter the
|
|
15
|
+
# user registered there (e.g. register(dialect="postgres")).
|
|
16
|
+
if "sqlnbfmt" in SERVER_FORMATTERS:
|
|
17
|
+
server_app.log.info("sqlnbfmt: keeping the formatter registered in config")
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from sqlnbfmt.jupyterlab_integration import register
|
|
22
|
+
|
|
23
|
+
register()
|
|
24
|
+
except Exception as e: # noqa: BLE001 - must never break Jupyter server startup
|
|
25
|
+
server_app.log.error(f"sqlnbfmt: SQL formatter not registered: {e}")
|
|
26
|
+
return
|
|
27
|
+
server_app.log.info(
|
|
28
|
+
"sqlnbfmt: registered SQL formatter with jupyterlab-code-formatter"
|
|
29
|
+
)
|