Skip to content

python: declaration reader and rule A (model decorator) #1386

Open

Description

@yujiyokoo

Created with Claude Code from the spec in #1341.

Parent

Sub-issue of #1341. Ticket 5 of 14.

What to build

A developer writes

@stackencrypt.model("users")   # decorator name is an assumption; see PR #1284
class User:
    email: Annotated[str, stackencrypt.TextEq]
    age: Annotated[int, stackencrypt.Passthrough]

on a dataclass, an attrs class, a Pydantic model, a NamedTuple or a SQLAlchemy declarative class, and the class statement either completes or raises DeclarationError at that line, with no credentials and no network. This ticket covers the reader and rule A only: every annotated field must carry exactly one marker.

Covered here: DECLARATION_NO_MARKER, DECLARATION_TWO_MARKERS, DECLARATION_NOT_OUTERMOST (the marker must be in the outermost Annotated, with the structural wrapper rule for Mapped[Annotated[...]]), Ignore fields leaving the plan. The plan the reader produces is the input to the next ticket.

Amendment: the code for a misplaced marker.

A marker in the wrong place now raises DECLARATION_MARKER_NOT_OUTERMOST, not DECLARATION_NOT_OUTERMOST. An example is Annotated[Mapped[str], TextEq].

The spec already uses DECLARATION_NOT_OUTERMOST for the decorator order. One code for two mistakes would give the developer the wrong remedy. The decorator order is checked in #1387.

PR #1433 also raises two codes from the spec early, because its review asked for them:

  • DECLARATION_HINTS_UNRESOLVED, for a hint that does not resolve.
  • DECLARATION_ALREADY_DECLARED, for a class decorated twice.

Amendment: error codes are now lower_snake_case. See the amendment on #1341. Read each code in this issue in lower case, such as declaration_no_marker for DECLARATION_NO_MARKER.

Acceptance criteria

  • Each of the five class kinds declares successfully with one TextEq field, one Passthrough field and one Ignore field.
  • The three codes above raise DeclarationError with the field name and the code in the message.
  • Mapped[Annotated[str, TextEq]] is accepted; Annotated[Mapped[str], TextEq] is refused with NOT_OUTERMOST.
  • A relationship marked Ignore does not appear in the plan.
  • Tests run on 3.9 and 3.14 in CI, with and without from __future__ import annotations.

Metadata

Metadata

Assignees

Labels

SDKready-for-agentFully specified and ready for an AFK agent

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions