# Python Dev Practices

> Python development best practices for modern Python projects. Activated when working with Python files, pyproject.toml, or discussing Python patterns, testing, linting, type hints.

- Skill: `majiayu000/python-dev-practices` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/python-dev-practices`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/python-dev-practices/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/python-dev-practices

---


# Python development practices

## Purpose

Comprehensive guide for Python development covering modern Python practices, type safety, testing strategies, and coding conventions.

## When to use

This skill activates when:

- Creating or modifying Python files
- Working with pyproject.toml configuration
- Writing property-based or fuzz tests
- Handling type hints and type safety
- Setting up development environment
- Configuring linting, formatting, or type checking

## Core principles

### Minimal dependencies philosophy

**CRITICAL: Standard library first, dependencies last.**

- ALWAYS prefer standard library solutions
- Evaluate stdlib before considering third-party code
- Implementation over dependencies

### Type safety requirements

**MANDATORY: Comprehensive type hints for ALL code**

```python
from typing import Protocol
from typing_extensions import TypeIs, ReadOnly, TypedDict

class Config(TypedDict):
    """Configuration with immutable fields."""
    name: ReadOnly[str]
    options: ReadOnly[list[str]]

def is_valid_config(value: object) -> TypeIs[Config]:
    """Type narrowing with TypeIs."""
    return isinstance(value, dict) and 'name' in value
```

### Modern Python features

Leverage Python 3.10+ capabilities:

```python
# Pattern matching (3.10+)
match argument:
    case {"type": "option", "name": str(name)}:
        return process_option(name)
    case {"type": "value", "data": list(data)}:
        return process_value(data)
    case _:
        raise ValueError(f"Unknown argument: {argument}")

# Dataclasses with slots and frozen
from dataclasses import dataclass

@dataclass(slots=True, frozen=True)
class Result:
    """Immutable result with memory efficiency."""
    success: bool
    data: dict[str, Any]
    errors: list[str]
```

## Import organization

Follow project conventions with isort/Ruff:

```python
# Standard library imports first
import sys
from collections.abc import Sequence
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Protocol

# Third-party (minimal)
from typing_extensions import Doc, ReadOnly, TypeIs

# Local imports
from yourpackage._module import Module
from yourpackage.validation import Validator
```

## Class organization standards

**MANDATORY class member ordering:**

```python
class Processor:
    # 1. Class State: Constants and class attributes
    DEFAULT_CONFIG = "default"
    RESERVED_NAMES = frozenset(["__init__", "__new__"])

    # 2. Lifecycle: __init__ and __new__
    def __init__(self, config: Config) -> None:
        self._config = config
        self._state = ProcessorState()

    # 3. Magic Methods: __repr__, __str__, __len__
    def __repr__(self) -> str:
        return f"Processor(config={self._config!r})"

    # 4. Public Interface: Properties, then public methods
    @property
    def name(self) -> str:
        return self._config.get("name", self.DEFAULT_CONFIG)

    def process(self, data: Sequence[str]) -> Result:
        """Process data."""
        return self._process_impl(list(data))

    # 5. Internals: Private/protected methods at bottom
    def _process_impl(self, data: list[str]) -> Result:
        pass
```

## Testing strategy

### Domain-centric test organization

```text
tests/
├── unit/
│   ├── processing/
│   │   ├── test_data_processing.py
│   │   └── test_transformations.py
│   └── validation/
│       └── test_input_validation.py
├── properties/
│   └── test_invariants.py
└── fuzz/
    └── test_fuzzing.py
```

## Error handling patterns

```python
class AppError(Exception):
    """Base exception for application."""
    pass

class ValidationError(AppError):
    """Raised when validation fails."""

    def __init__(self, message: str, field: str | None = None) -> None:
        super().__init__(message)
        self.field = field

def process_data(data: Sequence[str]) -> Result:
    """Process with clear error messages."""
    if not data:
        raise ValidationError("No data provided")

    try:
        return _process_impl(data)
    except ValueError as e:
        raise ValidationError(f"Invalid data format: {e}") from e
```

## Package management with uv

### Essential commands

```bash
# CRITICAL: ALWAYS use 'uv run python' for script execution
uv run python scripts/analyze.py
uv run python -m yourpackage

# Install dependencies
uv sync

# Run tools
uv run pytest
uv run ruff check .
uv run basedpyright
```

## Code quality tools

### Configuration (pyproject.toml)

```toml
[project]
requires-python = ">=3.14"
dependencies = ["typing-extensions>=4.8"]

[tool.basedpyright]
typeCheckingMode = "strict"
pythonVersion = "3.14"

[tool.ruff]
line-length = 88
target-version = "py314"

[tool.ruff.lint]
select = ["ALL"]
ignore = ["D", "ANN101", "ANN102"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--strict-markers --cov=src --cov-report=term-missing"
```

## Security practices

```python
import shlex
from pathlib import Path

def validate_input(user_input: str) -> str:
    """Validate and sanitize user input."""
    # Prevent command injection
    if any(char in user_input for char in [';', '|', '&', '`', '$']):
        raise ValueError("Invalid characters in input")
    return shlex.quote(user_input)

def safe_path_resolution(user_path: str, base: Path) -> Path:
    """Safely resolve paths to prevent traversal."""
    path = (base / user_path).resolve()
    if not path.is_relative_to(base):
        raise ValueError("Path traversal detected")
    return path
```

## Quick reference

### Testing checklist

- [ ] Unit tests with >95% coverage
- [ ] Property-based tests for invariants
- [ ] Fuzz tests for robustness
- [ ] Benchmarks for critical paths
- [ ] Type checking passes (basedpyright)
- [ ] Linting passes (ruff)
- [ ] Domain-centric test organization

---

**Remember**: Prioritize type safety, comprehensive testing, and standard library usage in all development.

