Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions docs/getting_started/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,18 @@ Package groups
* - ``adk``
- ``google-adk``
- Google ADK storage extension.
* - ``aioodbc``
- ``aioodbc``
- Async ODBC connections.
* - ``aiomysql``
- ``aiomysql``
- Async MySQL driver.
* - ``aiosqlite``
- ``aiosqlite``
- Async SQLite driver.
* - ``alloydb``
- ``google-cloud-alloydb-connector``
- AlloyDB connector.
* - ``arrow-odbc``
- ``arrow-odbc``, ``pyarrow``
- ODBC Arrow export and ingest.
* - ``asyncmy``
- ``asyncmy``
- Async MySQL driver.
Expand All @@ -101,19 +104,16 @@ Package groups
- ``attrs``, ``cattrs``
- Result mapping with attrs models.
* - ``bigquery``
- ``google-cloud-bigquery``, ``google-cloud-storage``
- ``google-cloud-bigquery``, ``google-cloud-bigquery-storage``, ``google-cloud-storage``
- BigQuery adapter dependencies.
* - ``cli``
- ``rich-click``, ``tomli`` (Py<3.11)
- CLI enhancements.
* - ``cloud-sql``
- ``cloud-sql-python-connector``
- Google Cloud SQL connector.
* - ``cockroachdb``
- ``psycopg[binary,pool]``, ``asyncpg``
- CockroachDB drivers.
* - ``duckdb``
- ``duckdb``
- ``duckdb``, ``pytz``
- DuckDB adapter.
* - ``fastapi``
- ``fastapi``
Expand All @@ -130,9 +130,9 @@ Package groups
* - ``msgspec``
- ``msgspec``
- High-performance result mapping.
* - ``performance``
- ``librt``, ``msgspec``
- Runtime helpers for compiled hot paths and high-speed serialization.
* - ``mssql-python``
- ``mssql-python``
- Microsoft SQL Server driver.
* - ``mypyc``
- ``sqlglot[c]``
- C-compiled sqlglot internals for faster SQL parsing.
Expand All @@ -158,8 +158,8 @@ Package groups
- ``pandas``, ``pyarrow``
- Pandas data export.
* - ``performance``
- ``sqlglot[rs]``, ``msgspec``
- Rust-based SQL parsing + msgspec.
- ``librt``, ``msgspec``
- Runtime helpers for compiled hot paths and high-speed serialization.
* - ``polars``
- ``polars``, ``pyarrow``
- Polars data export.
Expand All @@ -181,9 +181,15 @@ Package groups
* - ``pymysql``
- ``pymysql``
- MySQL driver.
* - ``sanic``
- ``sanic``
- Sanic integration helpers.
* - ``spanner``
- ``google-cloud-spanner``
- Cloud Spanner adapter.
* - ``starlette``
- ``starlette``
- Starlette integration helpers.
* - ``uuid``
- ``uuid-utils``
- UUID helpers.
Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Source = "https://github.com/litestar-org/sqlspec"
adbc = ["adbc_driver_manager", "pyarrow"]
adk = ["google-adk"]
aiomysql = ["aiomysql"]
aioodbc = ["aioodbc"]
aiosqlite = ["aiosqlite"]
alloydb = ["google-cloud-alloydb-connector"]
arrow-odbc = ["arrow-odbc>=10.4", "pyarrow"]
Expand Down Expand Up @@ -442,7 +441,6 @@ markers = [
# Driver markers
"adbc: marks tests using ADBC drivers",
"aiomysql: marks tests using aiomysql",
"aioodbc: marks tests using aioodbc",
"aiosqlite: marks tests using aiosqlite",
"arrow_odbc: marks tests using the arrow_odbc adapter",
"asyncmy: marks tests using asyncmy",
Expand Down
63 changes: 63 additions & 0 deletions tests/unit/test_docs_extras_parity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Installation docs parity tests for optional extras."""

import re
import sys
from pathlib import Path

if sys.version_info >= (3, 11):
import tomllib
else: # pragma: no cover
import tomli as tomllib


PROJECT_ROOT = Path(__file__).resolve().parents[2]


def _normalize_dependency_name(requirement: str) -> str:
requirement = requirement.split(";", 1)[0].strip()
return re.split(r"[<>=!~]", requirement, maxsplit=1)[0].strip()


def _package_groups_table() -> str:
docs = (PROJECT_ROOT / "docs/getting_started/installation.rst").read_text()
_, table = docs.split("Package groups\n--------------", maxsplit=1)
return table.split("Multiple extras\n---------------", maxsplit=1)[0]


def _documented_extras() -> dict[str, set[str]]:
table = _package_groups_table()
rows: dict[str, set[str]] = {}
matches = list(re.finditer(r"^\s+\* - ``([^`]+)``\s*$", table, flags=re.MULTILINE))

for index, match in enumerate(matches):
extra_name = match.group(1)
next_start = matches[index + 1].start() if index + 1 < len(matches) else len(table)
row = table[match.end() : next_start]
includes_match = re.search(r"^\s+- (.+)$", row, flags=re.MULTILINE)
if includes_match is None:
rows[extra_name] = set()
continue
rows[extra_name] = {
_normalize_dependency_name(dependency) for dependency in re.findall(r"``([^`]+)``", includes_match.group(1))
}

return rows


def _documented_extra_names() -> list[str]:
return re.findall(r"^\s+\* - ``([^`]+)``\s*$", _package_groups_table(), flags=re.MULTILINE)


def _pyproject_extras() -> dict[str, set[str]]:
pyproject = tomllib.loads((PROJECT_ROOT / "pyproject.toml").read_text())
return {
extra_name: {_normalize_dependency_name(dependency) for dependency in dependencies}
for extra_name, dependencies in pyproject["project"]["optional-dependencies"].items()
}


def test_installation_docs_extras_match_pyproject_optional_dependencies() -> None:
"""Every public optional extra should appear exactly once in installation docs."""
documented_names = _documented_extra_names()
assert len(documented_names) == len(set(documented_names))
assert _documented_extras() == _pyproject_extras()
Loading
Loading