Files
asepharyana-hub-guide/skills/python/SKILL.md
T
asepharyana e513cddd68 feat(hub-guide): expand plugin with 26 best-practice skills, hooks, and references
Transform hub-guide from a single-skill Hub monorepo guide into a
comprehensive programming best-practice plugin covering all situations.

Skills (26):
- Core: engineering-principles, clean-code, clean-architecture,
  design-patterns, testing, error-handling, security, api-design,
  git-workflow, documentation, logging-observability, performance
- Languages: typescript, python, rust, go
- Frameworks: react-frontend, elysiajs, hono-backend, drizzle-database, nextjs
- Infrastructure: docker, ci-cd, monitoring
- Monorepo: monorepo, hub-guide (existing)

Hooks:
- SessionStart: auto-detect project type and activate relevant skills
- PreToolUse (Write|Edit): inject language-specific rules per file type

Reference files for deep dives:
- clean-architecture/references/solid.md (SOLID + component principles)
- design-patterns/references/catalog.md (full GoF catalog with examples)
- testing/references/mocks.md (test double taxonomy)

Restructure plugin to modern skills/ directory format.
2026-07-25 11:35:16 +07:00

5.2 KiB

name, description
name description
python Python best practices — typing, project structure, packaging, FastAPI patterns, async, testing, and idiomatic Python. Use when writing Python code, structuring a Python project, or whenever the user mentions "Python," "FastAPI," "Django," "pytest," "PEP 8," "type hints," "Pydantic," "asyncio," "pip," "poetry," or "uv."

Python Best Practices

Type Hints

Python 3.10+ type hints are the standard. Enable via pyproject.toml.

[tool.pyright]
typeCheckingMode = "strict"
from collections.abc import Sequence
from dataclasses import dataclass
from typing import assert_never

@dataclass
class User:
    id: str
    email: str
    name: str | None  # Optional[str] in 3.8-3.9

def create_user(
    email: str,
    name: str | None = None,
    tags: Sequence[str] = (),
) -> User: ...

Rules:

  • Annotate all function signatures (params + return). No def f(x, y):.
  • Use | union syntax (3.10+) over Optional[Union[...]].
  • Use Sequence over List for parameters (accepts tuples/lists/sets).
  • Use TypeVar for generics.
  • Avoid Any. Use object or Unknown (pyright) if truly untyped.

Project Structure

project/
├── src/
│   └── app/
│       ├── __init__.py
│       ├── domain/           # Pure business logic
│       ├── application/      # Use cases
│       ├── infrastructure/   # DB, external APIs
│       └── presentation/     # API routes, CLI
├── tests/
│   ├── unit/
│   └── integration/
├── pyproject.toml
├── uv.lock                   # or poetry.lock
└── README.md

Packaging: Always use src/ layout — prevents importing from project root by accident.

Modern Tooling

Tool Purpose Overrides
uv Package manager, venv, runner pip, poetry, pipenv
pytest Testing unittest
ruff Linter + formatter flake8, black, isort
pyright Type checker mypy
Pydantic Validation + serialization dataclasses (for complex validation)
[tool.ruff]
target-version = "py312"
line-length = 100
select = ["E", "F", "I", "N", "W", "UP", "B", "SIM", "ARG", "RUF"]

[tool.pytest.ini_options]
testpaths = ["tests"]

FastAPI Patterns

from pydantic import BaseModel, EmailStr
from fastapi import FastAPI, HTTPException

app = FastAPI()

# Pydantic models at the boundary
class CreateUserRequest(BaseModel):
    email: EmailStr
    name: str | None = None

class UserResponse(BaseModel):
    id: str
    email: str
    name: str | None

@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(body: CreateUserRequest):
    # Use case or service layer — not raw ORM here
    user = await user_service.create(body.email, body.name)
    if not user:
        raise HTTPException(409, "Email already exists")
    return UserResponse.model_validate(user)
  • Dependency injection — FastAPI Depends() for shared deps (DB session, auth).
  • Pydantic v2model_validate() not from_orm().
  • Path operations — thin controllers. Business logic in use cases.

Async Best Practices

  • Only async when you need IO — DB, HTTP, file, network. CPU work should stay sync.
  • Use asyncio.run() for entry point, asyncio.gather() for concurrent IO.
  • Never mix blocking with async — no time.sleep(), no requests in async code.
  • Prefer httpx.AsyncClient over requests for async APIs.
  • Database: asyncpg (Postgres), redis.asyncio, motor (Mongo).
import asyncio
import httpx

async def fetch_all(urls: list[str]) -> list[dict]:
    async with httpx.AsyncClient() as client:
        tasks = [client.get(url) for url in urls]
        results = await asyncio.gather(*tasks, return_exceptions=True)
    return [r.json() for r in results if isinstance(r, httpx.Response)]

Testing (pytest)

# tests/unit/test_order.py
from app.domain.order import Order, OrderItem
from datetime import datetime

def test_order_total_calculates_correctly():
    order = Order(items=[
        OrderItem(price=10.0, quantity=2),
        OrderItem(price=5.0, quantity=1),
    ])
    assert order.total == 25.0  # 10*2 + 5*1

def test_order_rejects_empty_items():
    with pytest.raises(ValueError, match="at least one item"):
        Order(items=[])

@pytest.mark.asyncio
async def test_create_user_duplicate_email():
    repo = InMemoryUserRepo()  # fake
    service = UserService(repo)
    await service.create("a@x.com")
    with pytest.raises(DuplicateEmailError):
        await service.create("a@x.com")
  • Fixtures over setup/teardown.
  • Parametrize for multiple cases — @pytest.mark.parametrize.
  • Fakes over mocks — in-memory DB, fake HTTP client.

Anti-patterns

  • from module import * — pollutes namespace
  • Mutable default args — def f(x=[]): — shared across calls
  • Bare except: — catches KeyboardInterrupt, SystemExit, everything
  • Type hints at wrong level — only annotate public API, not every internal variable
  • print() for debugging — use logging or loguru
  • requirements.txt without lock — use uv.lock / poetry.lock
  • Try-except-pass — swallows errors silently