Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
11 KiB
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 manualgetattr/hasattrflows and custom constructors likefrom_sdkunless they are thin wrappers overmodel_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
isinstancechecks before attribute access - Using
typing.castwhen 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
transportfield), do not narrow a field type in a subclass (e.g., overridingtransport: Literal['http']withLiteral['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 withAnnotated[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 atransport. - Define sibling final classes per variant (e.g.,
MCPHttp,MCPStreamableHttp,MCPStdio) that settransport: Literal[...]once in each final class. - Use
matchon the discriminator to narrow types at call sites.
- Create a base with shared non-discriminator fields (e.g.,
-
title: "Use uv for All Commands" description: > We use uv to manage our python environment. You should never try to run bare python commands. Always run commands using
uvinstead of invokingpythonorpipdirectly. For example, useuv add packageanduv run script.pyrather thanpip install packageorpython script.py. This practice helps avoid environment drift and leverages modern Python packaging best practices. Useful uv commands are:- uv add/remove to manage dependencies
- uv sync to install dependencies declared in pyproject.toml and uv.lock
- uv run script.py to run a script within the uv environment
- uv run pytest (or any other python tool) to run the tool within the uv environment
-
title: "Safe File Reading" description: > When reading files from disk, prefer the helpers in
vibe.core.utils.ioover rawPath.read_text(),Path.read_bytes().decode(), oropen()calls:read_safe(path)— synchronous read with automatic encoding detection.read_safe_async(path)— async equivalent (anyio-based).decode_safe(raw)— decode an already-readbytesobject. These functions try UTF-8 first, then BOM detection, the locale encoding, andcharset_normalizer(lazily, only when cheaper candidates fail). They return aReadSafeResult(text, encoding)so callers always get validstroutput without having to handle encoding errors manually. Useraise_on_error=Trueonly when the caller must distinguish corrupt files from valid ones; the default (False) replaces undecodable bytes with U+FFFD.
-
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.*).
-
title: "Keep Builtin Vibe Skill Up-to-Date" description: > The file
vibe/core/skills/builtins/vibe.pyis the builtin self-awareness skill. It documents the CLI's features for the model: config.toml fields, CLI parameters, slash commands, agents, skills, tools, VIBE_HOME structure, and environment variables. When you change any of the following, updatevibe/core/skills/builtins/vibe.pyto reflect the new behavior:- CLI arguments or flags (vibe/cli/entrypoint.py)
- config.toml fields or defaults (vibe/core/config/_settings.py)
- Slash commands (vibe/cli/commands.py)
- Built-in agents (vibe/core/agents/)
- VIBE_HOME directory layout or paths (vibe/core/paths/)
- Skill, tool, or agent discovery logic
- Environment variables If in doubt, read the skill file and check whether your change makes any section stale.
-
title: "No Docstrings in Tests" description: > Do not add docstrings to test functions, test methods, or test classes. Test names should be descriptive enough to convey intent (e.g.,
test_create_user_returns_403_when_unauthorized). Docstrings in tests add noise, duplicate the function name, and can suppress pytest's default output (pytest displays the docstring instead of the node id when one is present). Use inline comments sparingly for non-obvious setup or assertions instead.