data.shapes

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

Verified SQL-to-render data shapes.

A Shape is a frozen, slotted dataclass that declares — co-located with the row model — theSELECTthat produces it. Decorate the row dataclass with @shape("SELECT ...")…

Verified SQL-to-render data shapes.

A Shape is a frozen, slotted dataclass that declares — co-located with the row model — theSELECTthat produces it. Decorate the row dataclass with @shape("SELECT ...")and the declared SQL becomes the single source of truth for what columns the row carries::

from dataclasses import dataclass
from chirp.data import Database, Shape, shape

@shape("SELECT id, title FROM boards WHERE id = :id")
@dataclass(frozen=True, slots=True)
class BoardView:
    id: int
    title: str

boards = await Shape.fetch(BoardView, db, id=42)

The compiled SQL and all execution live behind theDatabasefacade (theShape.fetch / Shape.fetch_one / Shape.streamclassmethods, which delegate toDatabase) — never in template-adjacent code and never as a SQL string threaded through a handler kwarg into a template. The author writes :name placeholders; the driver dialect (? for SQLite, $Nfor PostgreSQL) is resolved in one place (_bind_params) so parameters are never concatenated into the SQL text.

Free-threading lifecycle:

  • The decorated class is the row type;@shapeattaches a single frozen
    ``_ShapeMeta`` sidecar (``cls.__chirp_shape__``) once at decoration and
    
    never mutates it thereafter.
    
  • The module-level shape registry is shared mutable state. Both writes and
    reads are guarded by a ``threading.Lock``; ``shape_registry()`` returns a
    
    read-only ``MappingProxyType`` copy. Registration happens at decoration /
    
    import time; the registry is treated read-only after app setup.
    

data.shapes

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 _skip_inert

If a string literal or SQL comment begins atsql[i], skip past it.

The single, shared low-level "inert span" skipper consulted by EVERY…

Jump to symbol
function _paren_depth_at

Return the parenthesis depth at indextarget, or Noneif inert.

Walkssql[:target]skipping string literals and SQL comments via _skip_inert(), counting…

Jump to symbol
function _scan_placeholders

Yield(name, start, end) spans for each :name placeholder in sql.

The single, shared placeholder scanner consumed by both_bind_params() (which rewrites…

Jump to symbol
function _reject_reserved_placeholders

Fail loud whensql declares a :__chirp_...placeholder (finding F1).

The__chirp_prefix is reserved for compiler-generated placeholders (__chirp_k0 batch keys, __chirp_rnwindow…

Jump to symbol
class NestedShape

Explicit child-Shape declaration for the bounded compiler (#167).

Created vianested() and recorded in the field's metadata. The child clsMUST itself be …

Jump to symbol
class _ShapeMeta

Immutable sidecar describing a@shape-decorated dataclass.

Attached to the class ascls.__chirp_shape__once at decoration and never mutated (frozen-clean for free-threaded 3.14t).

Jump to symbol
function nested

Declare a nested child Shape on a parent@shapefield (#167).

Returns afield() with an empty-tuple default and the NestedShapemetadata, so…

Jump to symbol
function _collect_nested

Collect declared nested children from a dataclass's field metadata.

Walksfields() for the chirp_nestedmetadata planted bynested() and fails loud (…

Jump to symbol
function register_shape

Register a named Shape in the module-level registry.

Called automatically by@shapeso every shape is discoverable for drift detection (#166/#172); may also be…

Jump to symbol
function shape_registry

Return a read-only snapshot of registered named Shapes.

Consumed by theshapecheckcontract (#166) for registry-drift detection. The returned mapping is an immutable copy…

Jump to symbol
function _validate_target

RaiseShapeError unless clsis a frozen, slotted dataclass.

Jump to symbol
function shape

Declare a verified SQL row Shape on a frozen, slotted dataclass.

The decorated dataclass is the row type (identity decorator). The declared sqlis…

Jump to symbol
function _meta

Return the_ShapeMeta sidecar for a Shape, or raise ShapeError.

Jump to symbol
function _bind_params

Translate:nameplaceholders to the driver placeholder.

SQLite uses? (positional), PostgreSQL uses $N(1-based). Returns the rewritten SQL plus the params tuple ordered…

Jump to symbol
function _iter_sql_tokens

Yield(token, depth, start) for keyword/identifier tokens in sql.

A minimal left-to-right tokenizer that tracks parenthesis depth and skips quoted-string literals AND SQL…

Jump to symbol
function _next_nonspace

Return the first non-whitespace character at/afteridx, or None.

Jump to symbol
function _outer_where_target

Classify the OUTER query's WHERE analyzability for scope injection (#6).

Jump to symbol
function _scope_injectable

Return whether the scope predicate can be safely structurally injected.

Un-injectable (opaque) when the SQL is a CTE (WITH), a compound query…

Jump to symbol
function _depth0_scope_predicate

Return the RHS of a depth-0 predicate on the scope column, orNone.

Detects ANY depth-0 constraint on the scope column --<col>…

Jump to symbol
function _has_scope_predicate

Return whethersql already carries the canonical <scope> = :scope.

Used for idempotency (the compiler does not double-inject its own predicate) and as…

Jump to symbol
function _inject_scope

Structurally inject<scope> = :scope into sql's WHERE clause.

Idempotent: returnssqlunchanged when the compiler's own canonical predicate is already present. Adds …

Jump to symbol
function _first_depth0_comment

Return the start index of the first depth-0 SQL comment at/afterafter.

Walks the inert-span-aware character stream and reports where a depth-0 --…

Jump to symbol
function _skip_leading_inert

Advanceidxpast leading whitespace and inert spans (finding F3).

Returns the index of the first REAL (non-whitespace, non-inert) character at/afteridx, skipping…

Jump to symbol
function _depth0_where_position

Return the index just past the first depth-0WHEREkeyword, or 0.

Used to anchor the scope-injection tail search so a comment that appears…

Jump to symbol
function _depth0_from_target_end

Return the index just past the depth-0FROMkeyword, or 0.

Anchors the no-WHERE scope-injection tail search so a comment in the projection list…

Jump to symbol
function _depth0_tail_position

Return the index of the first depth-0 trailing clause keyword, orlen.

The scope predicate must be inserted BEFORE a depth-0 GROUP BY…

Jump to symbol
function _compiled_statement

Return the parent SELECT after scope injection (the compiler's output).

Jump to symbol
class Shape

Execution surface for@shape-decorated row models.

Not instantiated directly — the decorated dataclass isthe shape. The classmethod accessors expose the declared metadata,…

Jump to symbol
class _ChildDecomposition

The structural pieces of a child SQL needed to batch it (findings #4/A3).

Jump to symbol
function _split_top_level_and

Split a WHERE body into its top-levelANDconjuncts.

Returns(conjuncts, ok) where conjunctsis the list of top-level predicate texts (split on depth-0 …

Jump to symbol
function _is_join_equality

Return whetherconjunct is a simple {on} = :placeholderequality.

Jump to symbol
function _decompose_child

Decompose a child SQL into head + residual WHERE + ORDER BY / LIMIT.

The bounded compiler replaces the child's per-parent join EQUALITY with…

Jump to symbol
function _batched_child_sql

Build the ONE batchedIN-list query for a child level (findings #4/#5/A3).

key_namesis the ordered tuple of generated key placeholder names for…

Jump to symbol
function _splice_window_column

Insert, \{window} into the projection of innerbefore its FROM.

Locates the first depth-0FROMand inserts the window column at the end…

Jump to symbol
async function _resolve_children

Run the batchedIN-list query(ies) for childand attach to parents.

Collects the distinct parentkeyvalues, runs ONE query per chunk of …

Jump to symbol
async function _fetch_nested

Bounded nested loader: 1 parent query + batched query(ies) per child level.

Runs the parent SELECT (scope-injected when declared), then for EACH declared child…

Jump to symbol
class _CompositeMember

One field of a@compositeresolved to its member Shape.

Jump to symbol
class _CompositeMeta

Immutable sidecar describing a@composite-decorated dataclass.

Attached to the class ascls.__chirp_composite__once at decoration and never mutated (frozen-clean for free-threaded 3.14t).

Jump to symbol
function _resolve_member_shape

Resolve a composite field annotation to(shape_cls, is_sequence).

A composite field is either a single@shapeclass (single-object load) or atuple[Shape, ...]…

Jump to symbol
function _shape_or_none

Return the_ShapeMeta sidecar for cls, or Noneif not a Shape.

Jump to symbol
function _collect_members

Resolve every composite field to a member Shape (fail loud otherwise).

Usesget_type_hints() so string annotations (from __future__ import annotations) resolve…

Jump to symbol
function composite

Aggregate several Shapes for one page into a single frozen dataclass (#170).

The decorated dataclass declares the page's data ONCE: each field is a…

Jump to symbol
function _composite_meta

Return the_CompositeMeta sidecar for a Composite, or raise ShapeError.

Jump to symbol
class Composite

Load surface for@composite-decorated page models (#170, #171).

Not instantiated directly -- the decorated dataclass is the page model. load() runs the…

Jump to symbol
function _member_params

Coalesce the shared scope + params for one member Shape's load.

Only the placeholders the member's compiled SQL actually references are passed (so an…

Jump to symbol
function _placeholder_names

Return the set of:name placeholder names referenced by sql.

A thin consumer of the shared_scan_placeholders() scanner (which is ::cast-…

Jump to symbol
_skip_inert
function
def _skip_inert(sql: str, i: int) -> int | None

If a string literal or SQL comment begins atsql[i], skip past it.

The single, shared low-level "inert span" skipper consulted by EVERY scanner in this module (_scan_placeholders(), _iter_sql_tokens(), and the depth bookkeeping that consumes_iter_sql_tokens()). Routing every scanner through one skipper removes the parallel-maintenance hazard between them and -- critically -- makes them all comment-aware in lockstep (finding A2): a:nametoken, a paren, or a clause keyword that lives inside a string literal or a comment is NOT real SQL and must never drive placeholder binding, paren depth, or clause detection.

Recognized inert spans:

  • String literals ('...' / "..."), honoring doubled-quote escapes ('' / "") so a colon inside a time literal like ':30:00'stays inside the string.
  • Line comments (-- ... EOL) -- consumed through the next newline (the newline itself is left for the caller).
  • Block comments (/* ... */) -- consumed through the closing */. Per the SQL standard these do NOT nest, so the first*/closes the comment. An unterminated block comment runs to end-of-string.

Returns the index ONE PAST the inert span when one started ati, or None when sql[i]does not begin an inert span (the caller advances normally). Always returns a value> i when non-Noneso no caller can livelock.

Parameters

Name Type Default Description
sql str
i int
_paren_depth_at
function
def _paren_depth_at(sql: str, target: int) -> int | None

Return the parenthesis depth at indextarget, or Noneif inert.

Walkssql[:target]skipping string literals and SQL comments via _skip_inert(), counting only REAL ( / ). Used to confirm a regex match sits at depth 0 without a second, drift-prone hand-rolled depth loop (finding A2: the two former depth counters could desync).

ReturnsNone when targetlands INSIDE an inert span -- a string literal or a SQL comment (finding A2 leak: acommunity_id = :scope written inside a-- ...comment is NOT a real predicate and must never be treated as already-scoped, which would suppress injection and ship an unscoped query). ANoneresult means "this regex match is not real SQL; skip it." Otherwise returns the real paren depth attarget.

Parameters

Name Type Default Description
sql str
target int
_scan_placeholders
function
def _scan_placeholders(sql: str) -> Iterator[tuple[str, int, int]]

Yield(name, start, end) spans for each :name placeholder in sql.

The single, shared placeholder scanner consumed by both_bind_params() (which rewrites spans to the driver placeholder) and_placeholder_names() (which collects the distinct names). Factoring one scanner removes the parallel-maintenance hazard between the two callers (finding #8).

It is:

  • cast-aware -- a PostgreSQL::castoperator is not a placeholder and yields nothing (the scanner advances past both colons);
  • quoted-string aware -- a:name-shaped token inside a string literal ('...' or "...") is NOT a placeholder. The scanner skips string bodies, honoring doubled-quote escapes ('' / ""), so a colon inside a time literal like':30:00'or an interval string is never misparsed as a bind placeholder.
  • comment-aware (finding A2) -- a:name-shaped token inside a SQL line comment (-- ...) or block comment (/* ... */) is NOT a placeholder, so a commented note never becomes a phantom bind param.

A name is[A-Za-z_][A-Za-z0-9_]* immediately following a single :. Spans are yielded left-to-right;start is the index of the :and end is one past the last name character (so sql[start:end]is the full:nametoken).

Parameters

Name Type Default Description
sql str
_reject_reserved_placeholders
function
def _reject_reserved_placeholders(sql: str, *, shape_name: str) -> None

Fail loud whensql declares a :__chirp_...placeholder (finding F1).

The__chirp_prefix is reserved for compiler-generated placeholders (__chirp_k0 batch keys, __chirp_rnwindow rank). An author placeholder under this prefix would silently collide with a compiler-generated value at fetch time --Shape.validate passes, but Shape.fetchbinds the author's:__chirp_k0to the parent-key value seeded into the IN-list, returning wrong/empty rows. The author's declared SQL never legitimately contains a__chirp_placeholder, so this guard is precise: it scans the DECLARED SQL's placeholders via the shared_scan_placeholders() (which is comment- and string-literal-aware, so a:__chirp_token inside a comment or string literal is correctly ignored) and raises on the first reserved-prefixed author placeholder.

Parameters

Name Type Default Description
sql str
shape_name str
NestedShape
class

Explicit child-Shape declaration for the bounded compiler (#167).

Created vianested() and recorded in the field's metadata. The child cls MUST itself be @shape-decorated (it carries its own SQL). The compiler runs ONE batchedIN-list query per child level (never per parent row), groups children by theoncolumn, and attaches them to each parent viareplace().

_ShapeMeta
class

Immutable sidecar describing a@shape-decorated dataclass.

Attached to the class ascls.__chirp_shape__once at decoration and never mutated (frozen-clean for free-threaded 3.14t).

nested
function
def nested(child: type, *, on: str, key: str, optional: bool = False) -> Any

Declare a nested child Shape on a parent@shapefield (#167).

Returns afield() with an empty-tuple default and the NestedShape metadata, so the parent's row mapping (map_row's cls(**filtered), which keeps only keys present in the SQL row) does not raise for the absent nested column.@shapecollects these from fields() and the bounded compiler fills them in.

Usage::

@shape("SELECT id, title FROM boards WHERE id = :id")
@dataclass(frozen=True, slots=True)
class Board:
    id: int
    title: str
    cards: tuple[Card, ...] = nested(Card, on="board_id", key="id")

The empty-tuple default forces a field-ordering constraint: every nested()field must come AFTER all scalar (no-default) fields. @shape fails loud (ShapeError) if a no-default scalar field follows anested()field, rather than letting Python raise the opaque "non-default argument follows default argument".

Parameters

Name Type Default Description
child type The child row Shape (a ``@shape``-decorated frozen dataclass).
on str The child SQL column joining back to the parent ``key``.
key str The parent column seeding the child ``IN`` list.
optional bool False Skip the child level for a parent whose ``key`` is ``None``.
_collect_nested
function
def _collect_nested(cls: type) -> tuple[NestedShape, ...]

Collect declared nested children from a dataclass's field metadata.

Walksfields() for the chirp_nestedmetadata planted bynested() and fails loud (ShapeError) when a no-default scalar field follows a nested field (§8.2 #2) -- surfacing the field-ordering constraint clearly instead of relying on Python's opaque class-creation error (which is also pre-empted by the empty-tuple defaultnestedsets).

Parameters

Name Type Default Description
cls type
register_shape
function
def register_shape(name: str, cls: type) -> None

Register a named Shape in the module-level registry.

Called automatically by@shapeso every shape is discoverable for drift detection (#166/#172); may also be called explicitly to alias a name.

Same-name collision policy (fail-loud, not last-wins-silently):

  • registering the same class under a name is idempotent (no-op);
  • registering a different class under an already-registered name raises ShapeError.

Parameters

Name Type Default Description
name str
cls type
shape_registry
function
def shape_registry() -> Mapping[str, type]

Return a read-only snapshot of registered named Shapes.

Consumed by theshapecheckcontract (#166) for registry-drift detection. The returned mapping is an immutable copy taken under the registry lock — callers cannot mutate the live registry through it.

No parameters.

_validate_target
function
def _validate_target(cls: type) -> None

RaiseShapeError unless clsis a frozen, slotted dataclass.

Parameters

Name Type Default Description
cls type
shape
function
def shape(sql: str, *, computed: Sequence[str] = (), scope: str | None = None, name: str | None = None) -> Any

Declare a verified SQL row Shape on a frozen, slotted dataclass.

The decorated dataclass is the row type (identity decorator). The declared sqlis parsed for its output column list and stored — with the computed members and tenant scope key — in a frozen _ShapeMeta sidecar oncls.__chirp_shape__. The shape is auto-registered under name (defaults to cls.__name__).

Parameters

Name Type Default Description
sql str The declared ``SELECT``. Author writes ``:name`` placeholders; the driver dialect is resolved at fetch time (never concatenated).
computed Sequence[str] () Declared computed/derived members not present as SELECT columns (widens the verified field set for ``shapecheck``).
scope str | None None Tenant scope key (honored by the L3 compiler; stored only in L1).
name str | None None Registry name; defaults to ``cls.__name__``.
_meta
function
def _meta(cls: type) -> _ShapeMeta

Return the_ShapeMeta sidecar for a Shape, or raise ShapeError.

Parameters

Name Type Default Description
cls type
_bind_params
function
def _bind_params(sql: str, driver: str, params: Mapping[str, Any]) -> tuple[str, tuple[Any, ...]]

Translate:nameplaceholders to the driver placeholder.

SQLite uses? (positional), PostgreSQL uses $N(1-based). Returns the rewritten SQL plus the params tuple ordered to match the rewritten placeholders. Parameters are NEVER concatenated into the SQL text — only the placeholder token is rewritten, so this stays injection-safe (S608).

A:nameappearing more than once reuses the same value: SQLite repeats the value in the params tuple (one?per occurrence); PostgreSQL reuses the same$Nfor every occurrence (one value per distinct name).

Placeholder detection (including::castand quoted-string awareness) is delegated to the shared_scan_placeholders() scanner so this stays in lockstep with_placeholder_names() (finding #8).

Parameters

Name Type Default Description
sql str
driver str
params Mapping[str, Any]
_iter_sql_tokens
function
def _iter_sql_tokens(sql: str) -> Iterator[tuple[str, int, int]]

Yield(token, depth, start) for keyword/identifier tokens in sql.

A minimal left-to-right tokenizer that tracks parenthesis depth and skips quoted-string literals AND SQL comments (so a keyword, paren, or clause keyword inside a string or comment is not a token and does not move the depth counter -- finding A2). It yields one entry per word token ([A-Za-z_]\w*) with the paren depth at the token's opening position. Punctuation and parentheses are not yielded; they only adjustdepth. The single comment-aware tokenizer that ALL depth / clause / placeholder analysis routes through, so there is exactly one depth counter (no parallel hand-rolled loop that can desync). This is NOT a general SQL parser.

Parameters

Name Type Default Description
sql str
_next_nonspace
function
def _next_nonspace(sql: str, idx: int) -> str | None

Return the first non-whitespace character at/afteridx, or None.

Parameters

Name Type Default Description
sql str
idx int
_outer_where_target
function
def _outer_where_target(sql: str) -> str | None

Classify the OUTER query's WHERE analyzability for scope injection (#6).

Parameters

Name Type Default Description
sql str
_scope_injectable
function
def _scope_injectable(sql: str) -> bool

Return whether the scope predicate can be safely structurally injected.

Un-injectable (opaque) when the SQL is a CTE (WITH), a compound query (UNION/INTERSECT/EXCEPT), a SELECT */ expression projection the injector cannot reason about (_parse_select_columnsreturns None), lacks an analyzable FROM, or -- per finding #6 -- has an outer query whose WHERE/FROM is not a single analyzable target (derived table / FROM-subquery / correlated subquery / more than one depth-0 WHERE). A scoped shape that is un-injectable fails loud at startup (never a silently-unscoped query). This is the §8.1 boundary:CTE / UNION / SELECT * / dynamic / derived-tablecannot be injected, so the compiler must refuse rather than ship an unscoped query.

Parameters

Name Type Default Description
sql str
_depth0_scope_predicate
function
def _depth0_scope_predicate(sql: str, scope: str) -> str | None

Return the RHS of a depth-0 predicate on the scope column, orNone.

Detects ANY depth-0 constraint on the scope column --<col> = <rhs>, <col> IN (...), <col> = :other-- not just the canonical <col> = :scope(finding #7). Returns the matched right-hand side text (the canonical form is exactly":scope"); Nonewhen no such depth-0 predicate exists. A predicate inside a subquery (depth > 0) is ignored so a subquery predicate cannot fool the idempotency / conflict decision.

The scope-column matcher is anchored on BOTH edges (finding A1, the tenant-isolation BLOCKER). Without a left boundary,scope='community_id' substring-matched the suffix ofactor_community_idand judged a query ALREADY scoped -- so the compiler injected nothing and shipped an UNSCOPED cross-tenant query, while this same substring match made the validate() backstop pass clean. The left lookbehind (?<![\w.]) rejects a preceding word character OR .(so the column is not the tail offoo_community_idand not a qualified column whose bare suffix happens to equalscope), and the right boundary (?![\w]) rejects a trailing word character (socommunity_iddoes not match community_id_archived).

Parameters

Name Type Default Description
sql str
scope str
_has_scope_predicate
function
def _has_scope_predicate(sql: str, scope: str) -> bool

Return whethersql already carries the canonical <scope> = :scope.

Used for idempotency (the compiler does not double-inject its own predicate) and as thevalidate() output backstop. Matches ONLY the compiler's canonical depth-0 form<scope> = :scope(whitespace-tolerant, optional table qualifier) -- depth-aware so a subquery-level predicate cannot fool the backstop (finding #7). A non-canonical author predicate on the scope column is rejected loudly by_inject_scope(), not silently treated as present.

Parameters

Name Type Default Description
sql str
scope str
_inject_scope
function
def _inject_scope(sql: str, scope: str) -> str

Structurally inject<scope> = :scope into sql's WHERE clause.

Idempotent: returnssqlunchanged when the compiler's own canonical predicate is already present. AddsAND <scope> = :scopeto an existing depth-0 WHERE (before any GROUP BY / ORDER BY / LIMIT tail), or a fresh WHERE <scope> = :scopeafter the FROM target when no depth-0 WHERE exists. RaisesShapeError when sqlis un-injectable (caller should have validated first) OR when the author wrote their OWN (non-canonical) predicate on the scope column -- the scope guarantee is the compiler's, not the author's, so an ambiguous author predicate fails loud rather than being silently double-injected (finding #7).

Parameters

Name Type Default Description
sql str
scope str
_first_depth0_comment
function
def _first_depth0_comment(sql: str, after: int = 0) -> int | None

Return the start index of the first depth-0 SQL comment at/afterafter.

Walks the inert-span-aware character stream and reports where a depth-0 -- line comment or /* ... */block comment begins. The scope predicate must be inserted BEFORE any trailing comment so the injected AND <scope> = :scope lands in EXECUTABLE SQL, not after a --(which would silently comment out the tenant predicate -- finding A2). A comment inside a subquery (depth > 0) -- or one positioned BEFOREafter(e.g. a comment in the projection list, ahead of the WHERE) -- is not a boundary for the outer injection.

Parameters

Name Type Default Description
sql str
after int 0
_skip_leading_inert
function
def _skip_leading_inert(sql: str, idx: int) -> int

Advanceidxpast leading whitespace and inert spans (finding F3).

Returns the index of the first REAL (non-whitespace, non-inert) character at/afteridx, skipping whitespace and any string-literal / SQL-comment spans via the shared_skip_inert() skipper (no third hand-rolled lexer). Used to move the scope-injectionafteranchor PAST a comment that immediately follows theWHERE / FROM keyword (e.g. WHERE -- c\n a = :a or WHERE /* c */ a = :a). Without this, the tail search treats that leading comment as the WHERE clause's tail boundary and produces malformed WHERE AND <pred>SQL (the comment, not the predicate, becomes the tail). Capped atlen(sql)so the result is always a valid slice index.

Parameters

Name Type Default Description
sql str
idx int
_depth0_where_position
function
def _depth0_where_position(sql: str) -> int

Return the index just past the first depth-0WHEREkeyword, or 0.

Used to anchor the scope-injection tail search so a comment that appears BEFORE the depth-0 WHERE is not mistaken for the trailing tail (finding A2).

Parameters

Name Type Default Description
sql str
_depth0_from_target_end
function
def _depth0_from_target_end(sql: str) -> int

Return the index just past the depth-0FROMkeyword, or 0.

Anchors the no-WHERE scope-injection tail search so a comment in the projection list (before the FROM) is not mistaken for the tail (finding A2).

Parameters

Name Type Default Description
sql str
_depth0_tail_position
function
def _depth0_tail_position(sql: str, after: int = 0) -> int

Return the index of the first depth-0 trailing clause keyword, orlen.

The scope predicate must be inserted BEFORE a depth-0 GROUP BY / ORDER BY / LIMIT / etc. tail so it lands inside the WHERE clause -- but a tail keyword INSIDE a subquery (depth > 0) must be ignored (it is not the outer query's tail). Depth-aware so an ORDER BY inside an IN-subquery does not split the injection point (finding #6 robustness). A trailing depth-0 comment AT/AFTER afteris ALSO a boundary so the injected predicate never lands after a -- (finding A2); afterexcludes a comment that precedes the WHERE / FROM anchor from being treated as the tail.

Parameters

Name Type Default Description
sql str
after int 0
_compiled_statement
function
def _compiled_statement(meta: _ShapeMeta) -> str

Return the parent SELECT after scope injection (the compiler's output).

Parameters

Name Type Default Description
meta _ShapeMeta
Shape
class

Execution surface for@shape-decorated row models.

Not instantiated directly — the decorated dataclass is the shape. The classmethod accessors expose the declared metadata, and the async fetch / fetch_one / streammethods run the declared SQL behind theDatabasefacade (the repository seam).

_ChildDecomposition
class

The structural pieces of a child SQL needed to batch it (findings #4/A3).

_split_top_level_and
function
def _split_top_level_and(where_body: str) -> tuple[list[str], bool] | None

Split a WHERE body into its top-levelANDconjuncts.

Returns(conjuncts, ok) where conjunctsis the list of top-level predicate texts (split on depth-0AND within the WHERE body) and ok isFalse when a top-level ORis present (removing one conjunct from a disjunction is unsound, so the join predicate cannot be safely isolated -- finding A3). ReturnsNonewhen the body is empty. Inert-span aware so an AND/ORinside a string, comment, or sub-paren is not a split point.

Parameters

Name Type Default Description
where_body str
_is_join_equality
function
def _is_join_equality(conjunct: str, on: str) -> bool

Return whetherconjunct is a simple {on} = :placeholderequality.

Parameters

Name Type Default Description
conjunct str
on str
_decompose_child
function
def _decompose_child(sql: str, on: str) -> _ChildDecomposition

Decompose a child SQL into head + residual WHERE + ORDER BY / LIMIT.

The bounded compiler replaces the child's per-parent join EQUALITY with a single batchedWHERE {on} IN (...), so ONLY that equality is dropped -- the residual WHERE filters (e.g.deleted = 0) are PRESERVED and recombined asWHERE {on} IN (...) AND (<residual>)(finding A3). The trailingORDER BY and LIMITare also preserved (finding #4: the old _child_headsilently discarded them, turning "top 5 recent comments per card" into "all comments, arbitrary order"). All clause boundaries are found at paren depth 0 so an ORDER BY / LIMIT / AND inside a subquery is left attached. Theonargument is the child column joining back to the parent key; it is used to locate (and drop) the per-parent join equality.

Parameters

Name Type Default Description
sql str
on str
_batched_child_sql
function
def _batched_child_sql(child_meta: _ShapeMeta, on: str, key_names: Sequence[str]) -> str

Build the ONE batchedIN-list query for a child level (findings #4/#5/A3).

key_namesis the ordered tuple of generated key placeholder names for THIS chunk (__chirp_k0, __chirp_k1, ...). The generated batch-key names use the reserved__chirp_ prefix (matching __chirp_rn) so they can never collide with an author placeholder that happens to be namedk0 -- a collision would silently bind the author's residual filter to a parent-key value (finding R3-2). The child's per-parent join EQUALITY is replaced by a singleWHERE {on} IN (...), and the child's RESIDUAL WHERE filters (e.g.deleted = 0) are preserved as AND (<residual>)so the author's row exclusions survive the IN-list rewrite (finding A3). The child's declared trailingORDER BY is re-attached and a per-parent LIMITis rewritten into aROW_NUMBER() OVER (PARTITION BY {on} ORDER BY ...)window top-N (so each parent's children are limited independently rather than globally); the OUTER select orders on the PROJECTED{on}, __chirp_rnso within-parent order is deterministic and not driver-dependent without referencing a column the inner derived table does not expose (findings A4/R3-1). When the child declaresscope=, the scope predicate is structurally injected into the inner query too (every child statement scoped).

Inexpressible cases (LIMIT without ORDER BY, OFFSET, per-parent LIMIT with further nested grandchildren, an un-isolable join predicate) are rejected at decoration byvalidate(), not here.

Parameters

Name Type Default Description
child_meta _ShapeMeta
on str
key_names Sequence[str]
_splice_window_column
function
def _splice_window_column(inner: str, window: str) -> str

Insert, \{window} into the projection of innerbefore its FROM.

Locates the first depth-0FROMand inserts the window column at the end of the projection list. Used by the per-parent-LIMIT top-N rewrite (#4).

Parameters

Name Type Default Description
inner str
window str
_resolve_children
function async
async def _resolve_children(parents: list[Any], child: NestedShape, db: Database, params: Mapping[str, Any]) -> list[Any]

Run the batchedIN-list query(ies) for childand attach to parents.

Collects the distinct parentkeyvalues, runs ONE query per chunk of _MAX_IN_LIST_KEYSkeys (finding #5: a single IN-list with one placeholder per key crashes at ~32k keys with "too many SQL variables"), merges the chunk results, groups child rows by theironvalue, recurses into the child's own nested children, and rebuilds each parent via replace() (frozen-safe).

Returns the list of parents with the nested field populated. Query count for this level isceil(distinct_keys / _MAX_IN_LIST_KEYS)-- O(chunks), still independent of the child ROW count (the #167 bounded-query guarantee).

Parameters

Name Type Default Description
parents list[Any]
child NestedShape
db Database
params Mapping[str, Any]
_fetch_nested
function async
async def _fetch_nested(cls: type[T], db: Database, params: Mapping[str, Any]) -> list[T]

Bounded nested loader: 1 parent query + batched query(ies) per child level.

Runs the parent SELECT (scope-injected when declared), then for EACH declared child level runs ONE batchedIN-list query per chunk of _MAX_IN_LIST_KEYSdistinct parent keys (never per parent ROW). Query count =1 + sum(ceil(distinct_keys_at_level / _MAX_IN_LIST_KEYS))-- bounded,O(depth * chunks), independent of the parent ROW count N (the #167 no-N+1 guarantee; chunking added for finding #5 so the IN-list never exceeds the driver's bind-variable ceiling).

Parameters

Name Type Default Description
cls type[T]
db Database
params Mapping[str, Any]
_CompositeMember
class

One field of a@compositeresolved to its member Shape.

_CompositeMeta
class

Immutable sidecar describing a@composite-decorated dataclass.

Attached to the class ascls.__chirp_composite__once at decoration and never mutated (frozen-clean for free-threaded 3.14t).

_resolve_member_shape
function
def _resolve_member_shape(annotation: Any) -> tuple[type, bool] | None

Resolve a composite field annotation to(shape_cls, is_sequence).

A composite field is either a single@shapeclass (single-object load) or atuple[Shape, ...] (sequence load). Returns Nonewhen the annotation is neither (the field is not a Shape member -- fail loud at decoration incomposite()). Optionalsingle Shapes (Shape | None) are accepted and load one row (or None).

Parameters

Name Type Default Description
annotation Any
_shape_or_none
function
def _shape_or_none(cls: Any) -> _ShapeMeta | None

Return the_ShapeMeta sidecar for cls, or Noneif not a Shape.

Parameters

Name Type Default Description
cls Any
_collect_members
function
def _collect_members(cls: type) -> tuple[_CompositeMember, ...]

Resolve every composite field to a member Shape (fail loud otherwise).

Usesget_type_hints() so string annotations (from __future__ import annotations) resolve to real classes. A field whose type is neither a@shape class nor a tuple[Shape, ...] raises ShapeErrorat decoration -- a composite member must be a Shape (the page's data is declared once, in terms of Shapes).

Parameters

Name Type Default Description
cls type
composite
function
def composite(*, scope: str | None = None) -> Any

Aggregate several Shapes for one page into a single frozen dataclass (#170).

The decorated dataclass declares the page's data ONCE: each field is a member @shape class (single object) or tuple[Shape, ...](a list). Run the whole page behind the repository seam viaload(), which fans out to the member Shapes (reusing the L3 bounded compiler for nested members), coalesces the shared tenantscope+ params, and returns one frozen instance::

@composite(scope="community_id")
@dataclass(frozen=True, slots=True)
class BoardPage:
    board: Board                  # single-object member
    members: tuple[Member, ...]   # sequence member
    activity: tuple[Event, ...]

page = await Composite.load(BoardPage, db, board_id=7, scope=1)

Parameters

Name Type Default Description
scope str | None None Composite-level tenant scope key. When set, the ``:scope`` value (threaded from ``Composite.load(..., scope=...)``) is passed to every member Shape that declares a matching ``scope=`` -- the page scopes once; the members inherit it.
_composite_meta
function
def _composite_meta(cls: type) -> _CompositeMeta

Return the_CompositeMeta sidecar for a Composite, or raise ShapeError.

Parameters

Name Type Default Description
cls type
Composite
class

Load surface for@composite-decorated page models (#170, #171).

Not instantiated directly -- the decorated dataclass is the page model. load() runs the batched query set across the member Shapes behind the Databasefacade (the repository seam): SQL never leaves the@shape/@compositedeclarations, and the frozen result -- not a SQL string -- is what reaches the template.

_member_params
function
def _member_params(member_meta: _ShapeMeta, composite_scope: str | None, params: Mapping[str, Any]) -> dict[str, Any]

Coalesce the shared scope + params for one member Shape's load.

Only the placeholders the member's compiled SQL actually references are passed (so an unrelated page param does not error a member whose SQL never names it). The tenant:scopevalue is threaded when the member declares scope=-- the composite-level scope is the page's single declaration; the member inherits it.

Parameters

Name Type Default Description
member_meta _ShapeMeta
composite_scope str | None
params Mapping[str, Any]
_placeholder_names
function
def _placeholder_names(sql: str) -> set[str]

Return the set of:name placeholder names referenced by sql.

A thin consumer of the shared_scan_placeholders() scanner (which is ::cast- and quoted-string-aware), so the coalescer asks for exactly the params each member statement binds and never drifts from_bind_params() (finding #8).

Parameters

Name Type Default Description
sql str

View source · /home/runner/work/chirp/chirp/site/../src/chirp/data/shapes.py:1