Skip to content

Latest commit

 

History

History
144 lines (128 loc) · 8.56 KB

File metadata and controls

144 lines (128 loc) · 8.56 KB

python312.rule

Rule for enforcing modern Python 3.12+ best practices.

Applies to all Python files (*.py) in the project.

Guidelines covered:

- Use match-case syntax instead of if/elif/else for pattern matching.

- Use the walrus operator (:=) when it simplifies assignments and tests.

- Favor a "never nester" approach by avoiding deep nesting with early returns or guard clauses.

- Employ modern type hints using built-in generics (list, dict) and the union pipe (|) operator,

rather than deprecated types from the typing module (e.g., Optional, Union, Dict, List).

- Ensure code adheres to strong static typing practices compatible with static analyzers like pyright.

- Favor pathlib.Path methods for file system operations over older os.path functions.

- Write code in a declarative and minimalist style that clearly expresses its intent.

- Additional best practices including f-string formatting, comprehensions, context managers, and overall PEP 8 compliance.

description: "Modern Python 3.12+ best practices and style guidelines for coding." files: "**/*.py"

guidelines:

  • title: "Match-Case Syntax" description: > Prefer using the match-case construct over traditional if/elif/else chains when pattern matching is applicable. This leads to clearer, more concise, and more maintainable code.

  • title: "Walrus Operator" description: > Utilize the walrus operator (:=) to streamline code where assignment and conditional testing can be combined. Use it judiciously when it improves readability and reduces redundancy.

  • title: "Never Nester" description: > Aim to keep code flat by avoiding deep nesting. Use early returns, guard clauses, and refactoring to minimize nested structures, making your code more readable and maintainable.

  • title: "Modern Type Hints" description: > Adopt modern type hinting by using built-in generics like list and dict, along with the pipe (|) operator for union types (e.g., int | None). Avoid older, deprecated constructs such as Optional, Union, Dict, and List from the typing module.

  • title: "Strong Static Typing" description: > Write code with explicit and robust type annotations that are fully compatible with static type checkers like pyright. This ensures higher code reliability and easier maintenance.

  • title: "Pydantic-First Parsing" description: > Prefer Pydantic v2's native validation over ad-hoc parsing. Use model_validate, field_validator, from_attributes, and field aliases to coerce external SDK/DTO objects. Avoid manual getattr/hasattr flows and custom constructors like from_sdk unless they are thin wrappers over model_validate. Keep normalization logic inside model validators so call sites remain declarative and typed.

  • title: "Pathlib for File Operations" description: > Favor the use of pathlib.Path methods for file system operations. This approach offers a more readable, object-oriented way to handle file paths and enhances cross-platform compatibility, reducing reliance on legacy os.path functions.

  • title: "Declarative and Minimalist Code" description: > Write code that is declarative—clearly expressing its intentions rather than focusing on implementation details. Strive to keep your code minimalist by removing unnecessary complexity and boilerplate. This approach improves readability, maintainability, and aligns with modern Python practices.

  • title: "Additional Best Practices" description: > Embrace other modern Python idioms such as:

    • Using f-strings for string formatting.
    • Favoring comprehensions for building lists and dictionaries.
    • Employing context managers (with statements) for resource management.
    • Following PEP 8 guidelines to maintain overall code style consistency.
  • title: "Exception Documentation" description: > Document exceptions accurately and minimally in docstrings:

    • Only document exceptions that are explicitly raised in the function implementation
    • Remove Raises entries for exceptions that are not directly raised
    • Include all possible exceptions from explicit raise statements
    • For public APIs, document exceptions from called functions if they are allowed to propagate
    • Avoid documenting built-in exceptions that are obvious (like TypeError from type hints) This ensures documentation stays accurate and maintainable, avoiding the common pitfall of listing every possible exception that could theoretically occur.
  • title: "Modern Enum Usage" description: > Leverage Python's enum module effectively following modern practices:

    • Use StrEnum for string-based enums that need string comparison
    • Use IntEnum/IntFlag for performance-critical integer-based enums
    • Use auto() for automatic value assignment to maintain clean code
    • Always use UPPERCASE for enum members to avoid name clashes
    • Add methods to enums when behavior needs to be associated with values
    • Use @property for computed attributes rather than storing values
    • For type mixing, ensure mix-in types appear before Enum in base class sequence
    • Consider Flag/IntFlag for bit field operations
    • Use generate_next_value for custom value generation
    • Implement bool when enum boolean evaluation should depend on value This promotes type-safe constants, self-documenting code, and maintainable value sets.
  • title: "No Inline Ignores" description: > Do not use inline suppressions like # type: ignore[...] or # noqa[...] in production code. Instead, fix types and lint warnings at the source by:

    • Refining signatures with generics (TypeVar), Protocols, or precise return types
    • Guarding with isinstance checks before attribute access
    • Using typing.cast when control flow guarantees the type
    • Extracting small helpers to create clearer, typed boundaries If a suppression is truly unavoidable at an external boundary, prefer a narrow, well-typed wrapper over in-line ignores, and document the rationale in code comments.
  • title: "Pydantic Discriminated Unions" description: > When modeling variants with a discriminated union (e.g., on a transport field), do not narrow a field type in a subclass (e.g., overriding transport: Literal['http'] with Literal['streamable-http']). This violates Liskov substitution and triggers type checker errors due to invariance of class attributes. Prefer sibling classes plus a shared mixin for common fields and helpers, and compose the union with Annotated[Union[...], Field(discriminator='transport')]. Example pattern:

    • Create a base with shared non-discriminator fields (e.g., _MCPBase).
    • Create a mixin with protocol-specific fields/methods (e.g., _MCPHttpFields), without a transport.
    • Define sibling final classes per variant (e.g., MCPHttp, MCPStreamableHttp, MCPStdio) that set transport: Literal[...] once in each final class.
    • Use match on the discriminator to narrow types at call sites.
  • title: "Use a Python venv for All Commands" description: > We use a standard Python venv (created with python3 -m venv .venv) to manage the development environment. Activate it with source .venv/bin/activate (or run binaries directly via .venv/bin/<tool>) and use pip install -e . to install the package in editable mode. Useful commands:

    • python3 -m venv .venv to create the venv
    • source .venv/bin/activate to activate it
    • pip install -e . to install albert-code and its runtime deps
    • pip install pytest pyright ruff respx pytest-asyncio pytest-xdist pytest-timeout to install dev tools
    • pytest, pyright, ruff (run directly from the venv) for tests, type checks, lint
    • The launcher scripts albert-code.sh / albert-code.bat at the repo root automate venv creation and editable install on first run.
  • title: "Imports in Cursor (no Pylance)" description: > Cursor's built-in Pyright does not offer the "Add import" quick fix (Ctrl+.). To add a missing import:

    • Use the workspace snippets: type the prefix (e.g. acpschema, acphelpers, vibetypes, vibeconfig) and accept the suggestion to insert the import line, then change the symbol name.
    • Or ask Cursor: select the undefined symbol, then Cmd+K and request "Add the missing import for ".
    • Or copy the import from an existing file in the repo (e.g. acp.schema, acp.helpers, vibe.core.*).