cli._shapes_codegen

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

chirp shapes-codegen — suggest @shapedecorators and audit drift (#172).

Two jobs, both incremental and non-destructive by design:

  1. Ingest + emit.Scan target Python modules for frozen dataclasses sitting near an explicit…

chirp shapes-codegen — suggest @shapedecorators and audit drift (#172).

Two jobs, both incremental and non-destructive by design:

  1. Ingest + emit. Scan target Python modules for frozen dataclasses sitting near an explicit named-columnSELECTliteral, pair each dataclass to the SELECTwhose output columns are a subset of the dataclass fields, and emit a@shape("SELECT ...")decorator suggestion above each matched class (view-by-view, one suggestion per pair).--dry-runprints the unified diff and writes nothing.

  2. Day-one audit (--audit). Load an app's surface_contractsregistry (set viaapp.set_contract_check_data("surface_contracts", {...})) and report every surface name with no backing Shape — REUSING the L2 registry-drift logic (_check_registry_drift()), never a second copy. Exit non-zero when drift is found (CI-friendly), 0 otherwise.

The SELECT parsing reuses_parse_select_columns() (the single conservative SELECT parser); ingest only pairs a class to a SELECT the parser can read (SELECT */ expressions / CTE / UNION are skipped), so the emit step never suggests a decorator it cannot verify.

This command is intentionally best-effort and never overwrites source unless explicitly asked: the default is a dry-run-style preview. Mirrors _makemigrations.py — lazy imports live inside run_shapes_codegen().

cli._shapes_codegen

Name Type Default Description
type
qualified_name
element_type
description
source_file
line_number
is_autodoc
autodoc_element
_autodoc_template
_autodoc_url_path
_autodoc_page_type
title
doc_content_hash

Symbols on this page

function run_shapes_codegen

Suggest@shapedecorators and/or audit surface-contract drift.

Resolves the requested mode fromargs:

  • --audit loads the app at args.pathand reports surface-contract names…
Jump to symbol
function _run_audit

Report surface-contract names with no backing Shape; return the exit code.

Loads the app atargs.path (an import string, e.g. myapp:app), reads its …

Jump to symbol
function _app_surface_contracts

Return the app'ssurface_contracts contract-check data (or {}).

The data is stored viaapp.set_contract_check_data("surface_contracts", {...}); it lives on the app's mutable state.…

Jump to symbol
function _run_ingest

Scanargs.path for dataclass/SELECT pairs and print @shapesuggestions.

--dry-runis the safe, write-nothing default (the only behavior in v1): every match prints a…

Jump to symbol
function _python_files

Return the.py files under root (or rootitself if it is one).

Skips dunder/cache and common virtual-env / build directories so a broad …

Jump to symbol
function _suggest_shapes

Pair frozen dataclasses to nearby explicit-column SELECTs; yield suggestions.

Returns a list of_Suggestion(class name, line number, the chosen SELECT, and the SELECT's…

Jump to symbol
function _collect_select_literals

Collect(sql, columns)for every explicit-column SELECT string in the module.

Walks string constants (assignments and bare expressions) and keeps only those the conservative…

Jump to symbol
function _best_select

Return the(sql, columns) whose columns best subset fields, else None.

A candidate qualifies only when EVERY parsed output column is a…

Jump to symbol
function _is_frozen_dataclass

True when aClassDef carries @dataclass(frozen=True).

Jump to symbol
function _already_shape

True when aClassDef already carries an @shape(...)decorator (skip it).

Jump to symbol
function _class_field_names

Return the annotated field names declared on a dataclassClassDef.

Jump to symbol
function _format_suggestion

Render one suggestion as a unified-diff-style@shapeinsertion preview.

Jump to symbol
class _Suggestion

One dataclass/SELECT pairing the ingest step would annotate with@shape.

Jump to symbol
run_shapes_codegen
function
def run_shapes_codegen(args: argparse.Namespace) -> None

Suggest@shapedecorators and/or audit surface-contract drift.

Resolves the requested mode fromargs:

  • --audit loads the app at args.pathand reports surface-contract names with no backing Shape; exits non-zero on drift, 0 when clean.
  • otherwise (the default) scansargs.pathfor frozen dataclasses paired with a nearby explicit-columnSELECT and prints a @shape(...) suggestion above each match (--dry-runis the safe default — nothing is written).

Parameters

Name Type Default Description
args argparse.Namespace
_run_audit
function
def _run_audit(args: argparse.Namespace) -> int

Report surface-contract names with no backing Shape; return the exit code.

Loads the app atargs.path (an import string, e.g. myapp:app), reads itssurface_contractscontract-check data, and reuses the L2 _check_registry_drift() against the liveshape_registry(). Returns 1when any drift is found (so CI fails),0when clean. The drift detection — including the closest-match suggestion — is exactly whatapp.check()runs; the audit is a focused, exit-coded view of the same logic.

Parameters

Name Type Default Description
args argparse.Namespace
_app_surface_contracts
function
def _app_surface_contracts(app: object) -> dict[str, str]

Return the app'ssurface_contracts contract-check data (or {}).

The data is stored viaapp.set_contract_check_data("surface_contracts", {...}); it lives on the app's mutable state. Only str -> strentries are kept (the same shape the drift checker consumes), so a malformed registry never crashes the audit.

Parameters

Name Type Default Description
app object
_run_ingest
function
def _run_ingest(args: argparse.Namespace) -> None

Scanargs.path for dataclass/SELECT pairs and print @shapesuggestions.

--dry-runis the safe, write-nothing default (the only behavior in v1): every match prints a unified-diff-style preview of the@shape("SELECT ...") line that would be inserted above the dataclass. Nothing on disk is modified.

Parameters

Name Type Default Description
args argparse.Namespace
_python_files
function
def _python_files(root: Path) -> list[Path]

Return the.py files under root (or rootitself if it is one).

Skips dunder/cache and common virtual-env / build directories so a broad shapes-codegen .does not walk the whole environment.

Parameters

Name Type Default Description
root Path
_suggest_shapes
function
def _suggest_shapes(source: str) -> list[_Suggestion]

Pair frozen dataclasses to nearby explicit-column SELECTs; yield suggestions.

Returns a list of_Suggestion(class name, line number, the chosen SELECT, and the SELECT's parsed columns) for every frozen dataclass that:

  • is NOT already@shape-decorated (incremental — skip done work), and
  • sits near an explicit named-columnSELECTliteral (string constant / assignment in the module) whose parsed output columns are a SUBSET of the dataclass fields.

A dataclass with no matching SELECT yields nothing (best-effort, view-by-view).

Parameters

Name Type Default Description
source str
_collect_select_literals
function
def _collect_select_literals(tree: ast.Module, parse_columns: Callable[[str], tuple[str, ...] | None]) -> list[tuple[str, tuple[str, ...]]]

Collect(sql, columns)for every explicit-column SELECT string in the module.

Walks string constants (assignments and bare expressions) and keeps only those the conservative SELECT parser can read —SELECT */ expressions / CTE / UNION returnNone and are skipped, so a suggested @shapeis always one shapecheckcan later verify.

Parameters

Name Type Default Description
tree ast.Module
parse_columns Callable[[str], tuple[str, ...] | None]
_best_select
function
def _best_select(fields: frozenset[str], selects: list[tuple[str, tuple[str, ...]]]) -> tuple[str, tuple[str, ...]] | None

Return the(sql, columns) whose columns best subset fields, else None.

A candidate qualifies only when EVERY parsed output column is a field of the dataclass (columns ⊆ fields) — the pairing the blueprint specifies. Among qualifying candidates, the one covering the most fields wins (the richest verified projection), ties broken by the SQL text for determinism.

Parameters

Name Type Default Description
fields frozenset[str]
selects list[tuple[str, tuple[str, ...]]]
_is_frozen_dataclass
function
def _is_frozen_dataclass(node: ast.ClassDef) -> bool

True when aClassDef carries @dataclass(frozen=True).

Parameters

Name Type Default Description
node ast.ClassDef
_already_shape
function
def _already_shape(node: ast.ClassDef) -> bool

True when aClassDef already carries an @shape(...)decorator (skip it).

Parameters

Name Type Default Description
node ast.ClassDef
_class_field_names
function
def _class_field_names(node: ast.ClassDef) -> frozenset[str]

Return the annotated field names declared on a dataclassClassDef.

Parameters

Name Type Default Description
node ast.ClassDef
_format_suggestion
function
def _format_suggestion(path: Path, suggestion: _Suggestion) -> str

Render one suggestion as a unified-diff-style@shapeinsertion preview.

Parameters

Name Type Default Description
path Path
suggestion _Suggestion
_Suggestion
class

One dataclass/SELECT pairing the ingest step would annotate with@shape.

View source · /home/runner/work/chirp/chirp/site/../src/chirp/cli/_shapes_codegen.py:1