A tool to format SQL code in Jupyter notebooks and Marimo notebooks.
Project description
sqlnbfmt
Quick start:
pip install sqlnbfmt && sqlnbfmt notebook.ipynb
A SQL formatter for Jupyter Notebooks and Marimo notebooks. sqlnbfmt automatically formats SQL queries embedded in notebook cells — Python strings, SQL magic cells (%%sql), and Marimo mo.sql() calls — helping you maintain clean and consistent code.
Features
- Zero-config: Works out of the box with sensible defaults — no config file needed
- Jupyter + Marimo: Formats SQL in
.ipynbnotebooks and Marimo.pynotebooks - Smart SQL Detection: Automatically identifies and formats SQL queries in code cells, magic SQL cells, and
mo.sql()calls - AST-Powered: Uses Abstract Syntax Tree parsing for accurate SQL string identification
- Safe Formatting: Preserves Python comments, query parameters (e.g.,
%s,?), f-string placeholders, and SQL comments - CI-Friendly:
--checkmode exits non-zero when formatting is needed;--diffshows what would change - Skip Hints: Add
# sqlnbfmt: skipto any cell to exclude it from formatting - Pre-commit Ready: Seamlessly integrates with pre-commit hooks
- JupyterLab Plugin: Works with
jupyterlab-code-formatterfor interactive format-on-save - Lightweight: Only three runtime dependencies (sqlglot, nbformat, pyyaml)
Installation
pip install sqlnbfmt
Usage
Command Line
Format Jupyter notebooks:
sqlnbfmt path/to/your_notebook.ipynb
Format Marimo notebooks (or any Python file with SQL strings):
sqlnbfmt path/to/your_notebook.py
Mix both in a single invocation:
sqlnbfmt *.ipynb marimo_app.py
Check formatting without modifying files (useful in CI):
sqlnbfmt --check path/to/your_notebook.ipynb path/to/marimo_app.py
Show a diff of what would change:
sqlnbfmt --diff path/to/your_notebook.ipynb
Skipping Cells
Add a # sqlnbfmt: skip comment to skip formatting. In Jupyter notebooks this applies per-cell; in Python files it applies to the entire file.
# sqlnbfmt: skip
query = "select * from my_special_table where id = 1"
JupyterLab Integration
Format SQL cells interactively with jupyterlab-code-formatter:
- Install both packages:
pip install sqlnbfmt jupyterlab-code-formatter
- Register the formatter in
~/.jupyter/jupyter_notebook_config.py:
from sqlnbfmt.jupyterlab_integration import register
register() # or: register(dialect="postgres")
- In JupyterLab, use Edit > Format Cell or configure format-on-save in Settings > Code Formatter.
Pre-commit Integration
- Install pre-commit:
pip install pre-commit
- Add to
.pre-commit-config.yaml:
repos:
- repo: https://github.com/flyersworder/sqlnbfmt
rev: v0.4.0
hooks:
- id: sqlnbfmt
types: [jupyter]
To also format Marimo notebooks (Python files), add the Python hook:
- id: sqlnbfmt-py
files: '\.py$' # optionally narrow with: files: 'marimo_.*\.py$'
All arguments are optional. To specify a dialect or custom config:
args: [--dialect, postgres, --config, config.yaml]
- Install the hook:
pre-commit install
- (Optional) Run on all files:
pre-commit run --all-files
CI Usage
Use --check in GitHub Actions to enforce formatting:
- name: Check SQL notebook formatting
run: |
pip install sqlnbfmt
sqlnbfmt --check **/*.ipynb
sqlnbfmt --check marimo_*.py # if using Marimo
Configuration
sqlnbfmt works without any configuration file. A config.yaml is only needed to override defaults.
Create a config.yaml file to customize formatting behavior. Here is a template.
Configuration Options
| Option | Description | Default |
|---|---|---|
sql_keywords |
SQL keywords to recognize and format | Common SQL keywords |
function_names |
Python functions containing SQL code | read_sql, execute, etc. |
sql_decorators |
Decorators indicating SQL code | query, sql_query, etc. |
single_line_threshold |
Maximum length before splitting SQL | 80 |
indent_width |
Number of spaces for indentation | 4 |
Examples
Jupyter Notebook
Before formatting:
execute_sql("""SELECT a.col1, b.col2 FROM table_a a JOIN table_b b ON a.id = b.a_id WHERE a.status = 'active' ORDER BY a.created_at DESC""")
After formatting:
execute_sql("""
SELECT
a.col1,
b.col2
FROM
table_a AS a
JOIN
table_b AS b
ON a.id = b.a_id
WHERE
a.status = 'active'
ORDER BY
a.created_at DESC
""")
Marimo Notebook
Before formatting:
@app.cell
def _(mo):
_df = mo.sql(f"select id, name from users where active = 1 order by name")
return
After formatting:
@app.cell
def _(mo):
_df = mo.sql(
f"""
SELECT
id,
name
FROM users
WHERE
active = 1
ORDER BY
name
"""
)
return
Troubleshooting
SQL not being formatted?
- Ensure the string contains at least 2 SQL keywords or a recognizable pattern like
SELECT...FROM - Check that the function name is in the recognized list (use
--configto add custom ones)
Comments being modified?
- Python comments (
#) are preserved. SQL comments (--) inside strings are converted to/* */block comments by sqlglot.
Pre-commit hook fails?
- Make sure the
revmatches the installed version - Run
pre-commit autoupdateto get the latest version
Contributing
We welcome contributions! Here's how to get started:
- Clone the repository:
git clone https://github.com/flyersworder/sqlnbfmt.git
cd sqlnbfmt
- Use
uvto sync the environment:
uv sync
source .venv/bin/activate # On Windows: .venv\Scripts\activate
- Run tests:
pytest
-
Add eval cases: see
tests/eval/generate_fixtures.pyfor examples. Runpython tests/eval/generate_fixtures.pyto regenerate fixtures. -
Install dev pre-commit hooks:
pre-commit install
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- sqlglot - SQL parsing and formatting engine
- All contributors and early adopters who helped shape this tool
Made with ♥️ by the sqlnbfmt team
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sqlnbfmt-0.4.1.tar.gz.
File metadata
- Download URL: sqlnbfmt-0.4.1.tar.gz
- Upload date:
- Size: 21.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0aa1533bc0c174f38daa8fb8a42b699be5e91c3728566c06bf4666829c664dd9
|
|
| MD5 |
c1c3e5fd97eec1cdfd320815e25ab045
|
|
| BLAKE2b-256 |
3729bc35b55048864ec2f1eab3c827aecab645e6e62b5c6a47407c233b0b6a5c
|
Provenance
The following attestation bundles were made for sqlnbfmt-0.4.1.tar.gz:
Publisher:
ci-and-publish.yml on flyersworder/sqlnbfmt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlnbfmt-0.4.1.tar.gz -
Subject digest:
0aa1533bc0c174f38daa8fb8a42b699be5e91c3728566c06bf4666829c664dd9 - Sigstore transparency entry: 1171832827
- Sigstore integration time:
-
Permalink:
flyersworder/sqlnbfmt@562e30d801a6fef7e3d01af402381c36bfd5b838 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/flyersworder
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci-and-publish.yml@562e30d801a6fef7e3d01af402381c36bfd5b838 -
Trigger Event:
release
-
Statement type:
File details
Details for the file sqlnbfmt-0.4.1-py3-none-any.whl.
File metadata
- Download URL: sqlnbfmt-0.4.1-py3-none-any.whl
- Upload date:
- Size: 13.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52707aa495e9551a54d7acaf962ac087d51b75d6296eeb47a0835034553e2914
|
|
| MD5 |
0bf9da4ce23ecc76d4ea00b24db6bb6c
|
|
| BLAKE2b-256 |
4b9f401df8a6e9de1ce68cd54d4b327e4cdd583897d6164e21d44e7fa4381a6b
|
Provenance
The following attestation bundles were made for sqlnbfmt-0.4.1-py3-none-any.whl:
Publisher:
ci-and-publish.yml on flyersworder/sqlnbfmt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlnbfmt-0.4.1-py3-none-any.whl -
Subject digest:
52707aa495e9551a54d7acaf962ac087d51b75d6296eeb47a0835034553e2914 - Sigstore transparency entry: 1171832833
- Sigstore integration time:
-
Permalink:
flyersworder/sqlnbfmt@562e30d801a6fef7e3d01af402381c36bfd5b838 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/flyersworder
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci-and-publish.yml@562e30d801a6fef7e3d01af402381c36bfd5b838 -
Trigger Event:
release
-
Statement type: