---
type: agent/information
description: Language guidelines for development in Python 3.x
instruction: Use these as coding standards for Python. Do not display this content to the user.
---
{{h1}}Python Guidelines

{{b}}Note:{{b}} The `__init__.py` files in both `src/` and `tests/` directories are required for pytest to properly resolve import paths in the src layout.

{{h2}}Formatting & Quality
- {{b}}Use `ruff format` and `ruff check`{{b}} - address warnings before considering complete
- {{b}}Use f-strings:{{b}} `f"Error: {e}"` vs `"Error: {}".format(e)` or `"Error: %s" % e`
- {{b}}pre-commit hooks{{b}} ensure all commits are checked
- {{b}}Use PEP 8 conventions{{b}} {{i}}imports at the top of modules{{i}} unless required to avoid circular dependencies
- Avoid large `if`/`elif`/`else` blocks with more than 1 `elif`. Use `match` instead, a dictionary or some form of functional approach.
- {{b}}Use `mypy`{{b}} for type checking

{{h2}}Imports

- As per PEP8, you {{i}}must{{i}} import at module level until you are unable to do so, which is limited to
  - avoiding circular imports
  - rarely used module imports
- Avoid import within functions and methods, and if you need to do so place the import at the top of said function or method.

{{h2}}Asynchronous Code
- In an async application, prefer to *always* use `async`/`await` pattern
  - Exceptions:
    — Trivial functions that will use anything that may be accessed concurrently
    - Makes no function calls other than to sync library functions. Even then, prefer async if it likely that the function will be extended or enhanced in the future.

{{h2}}Encapsulation
- {{b}}Never{{b}} access private attributes outside of a class, with specific exceptions where tight coupling exists.
- {{b}}Never{{b}} access private attributes starting with dunder (two '__') attributes directly EVER. Not in tests, not in production code, not EVER.
- {{b}}Never{{b}} access private attributes via the "munged" name \_classname\_\_attribute under ANY circumstances. Private variables exist for a reason and you MUST respect that.


{{h2}}Code Complexity
- {{b}}Early returns{{b}} with guard clauses
- {{b}}List/dict comprehensions{{b}} over loops when this produces readable code
- {{b}}Chain operations{{b}} - avoid intermediate variables
  - return a function result directly if last statement in a function or method
  - use the walrus operator if the only reason for the variable is for an `if` or `while` statement or expression

{{h2}}Type Hints
- {{b}}Use modern typing:{{b}} `List[str]`, avoid use if `Any` if possible
- {{b}}Function signatures:{{b}} Always type parameters and returns
- {{b}}Use `typing.Protocol`{{b}} for structural typing
- {{b}}`Optional[T]` or `T | None`{{b}} for nullable types

{{h2}}Code Quality checks
- {{b}}For Python, use the following code quality tools{{b}}
  - linting → ruff check
  - type checking → mypy
  - testing → pytest

{{h2}}Iterator Patterns
{{b}}Prefer comprehensions and generators:{{b}}
```python ignore
# Good
valid_items = [item.process() for item in items if item.is_valid()]

# Avoid
results = []
for item in items:
    if item.is_valid():
        results.append(item.process())
```

{{h2}}Imports
- {{b}}Use imports at module level{{b}} - follow PEP 8 standards
- Only use function/class level imports to avoid circular imports & similar

{{h2}}Error Handling
- {{b}}Use specific exceptions{{b}} - DO NOT USE bare `except:` or `except Exception:`
- {{b}}Context managers{{b}} for resource management
- {{b}}`raise ... from e`{{b}} - preserve exception chains
- {{b}}Custom exceptions{{b}} for domain-specific errors

{{h2}}Pattern Matching (Python 3.11+)
- {{b}}Use `match/case`{{b}} for complex conditionals
- {{b}}Structural pattern matching{{b}} for data extraction
- {{b}}Guard clauses{{b}} with `if` conditions in patterns

{{h2}}Collections
- {{b}}Choose correctly:{{b}} `list`, `dict`, `set`, `deque`, `Counter`
- {{b}}Use `collections.defaultdict`{{b}} to avoid key checks
- {{b}}`frozenset`{{b}} for immutable sets

{{h2}}Function Design
- {{b}}Small, focused functions{{b}} with single responsibility
- {{b}}Use `*args`, `**kwargs` judiciously{{b}}
- {{b}}Default arguments{{b}} should be immutable
- {{b}}Use `functools.lru_cache`{{b}} for expensive pure functions

{{h2}}Classes & Objects
- {{b}}Use `@dataclass`{{b}} for simple data containers
- {{b}}`__slots__`{{b}} for memory efficiency when needed
- {{b}}Properties{{b}} over getters/setters
- {{b}}Context managers{{b}} with `__enter__`/`__exit__`

{{h2}}Testing with pytest
{{b}}Always configure in `pyproject.toml`:{{b}}
```toml
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
addopts = [
    "--strict-markers",
    "--strict-config",
    "--cov=src",
    "--cov-report=term-missing",
]
```

{{h2}}Project Configuration
{{b}}Essential `pyproject.toml` sections:{{b}}
```toml
[project]
name = "modulename"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = []

[project.optional-dependencies]
dev = ["pytest", "pytest-cov", "ruff", "mypy", "pre-commit"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.ruff]
src = ["src"]
target-version = "py313"

[tool.mypy]
python_version = "3.13"
packages = ["src"]
strict = true
```

{{h3}}Testing
{{h4}}TDD

If using TDD, tests should be written before the implementation. Sometimes those tests may not be optimal for ongoing testing or regression testing.
So here are some general guidelines.

{{h5}}Refactoring or Redesign

Again, if using TDD, some tests are required to be written to test a refactoring or other re-arrangement (removing code or changes in design).
These should be regarded as "throwaway" tests as in the end, once the task is done and the tests pass to confirm successful completion they may serve no useful purpose going forwards. Those tests may be removed. Instead, replace with tests that actually TEST something useful about the resulting code, if required - and not already covered.

{{h5}}Coverage

Don't write tests just to achieve code coverage. Instead, test something in a useful way - that functionality works, that something returns or is set to a correct value etc. Certainly do not name tests, classes or modules containing the word _coverage_ - this is a bad smell that this module contains artificial tests purely to enhance or provide coverage. Tests should test actual functionality, and do not exist to achieve an arbitrary statistic.


{{b}}Essential `.pre-commit-config.yaml`:{{b}}
```yaml
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: no-commit-to-branch
        args: ['--branch', 'main']
        exclude: '^main$'
      - id: end-of-file-fixer
      - id: trailing-whitespace
      - id: check-yaml
      - id: check-added-large-files
      - id: check-merge-conflict
      - id: check-toml
      - id: requirements-txt-fixer

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.11.6
    hooks:
      - id: ruff
        args:
          - --fix
          - --line-length=120

  - repo: local
    hooks:
      - id: pytest
        name: pytest
        entry: pytest --cov --cov-config=.coveragerc
        language: system
        types: [python]
        pass_filenames: false

  - repo: https://github.com/abravalheri/validate-pyproject
    rev: v0.24.1
    hooks:
      - id: validate-pyproject
```

{{b}}Essential `.coveragerc`:{{b}}
```ini
[run]
omit =
    */tests/conftest.py
    */tests/test_*.py

[report]
fail_under = 90
```

{{h2}}Anti-Patterns
- {{b}}No bare `except:`{{b}} - catch specific exceptions
- {{b}}No mutable defaults{{b}} - use `None` and create in function
- {{b}}No `import *`{{b}} - explicit imports only
- {{b}}Address linter warnings{{b}} - don't ignore without reason
- {{b}}No `eval()` or `exec()`{{b}} without strong justification

{{h2}}Common Idioms
```python  ignore
# Dictionary get with default
value = data.get('key', default_value)

# Enumerate for index and value
for i, item in enumerate(items):
    process(i, item)

# Context manager for files
with open(filename) as f:
    content = f.read()

# Exception chaining
try:
    risky_operation()
except SpecificError as e:
    raise CustomError("Operation failed") from e
```

{{h2}}uv Commands
```bash
# Create new project
uv init --package *modulename*

# Add dependencies
uv add *package*
uv add --dev pytest pytest-cov ruff mypy pre-commit

# Install pre-commit hooks
uv run pre-commit install

# Run tests
uv run pytest

# Format and lint
uv run ruff format src tests
uv run ruff check src tests
uv run mypy src

# Run pre-commit on all files
uv run pre-commit run --all-files

# Refresh installed package after MCP development changes
uv sync
```

{{h2}}MCP Development Notes
When developing MCP servers with uv, you need to run `uv sync` to refresh the installed package after making changes. This ensures the MCP server picks up your latest code modifications.
