python-quality

v2026.09.24

Python code quality with Ruff, Black, mypy, and Pylint. Covers linting, formatting, type checking, and best practices. USE WHEN: user works with "Python", "Django", "FastAPI", "Flask", asks about "Ruff", "Black", "mypy", "Pylint", "Python linting", "Python type hints" DO NOT USE FOR: SonarQube - use `sonarqube` skill, testing - use pytest skills, security - use `python-security` skill

GitHub
安装命令
npx skhub add claude-dev-suite/python-quality
Markdown
SKILL.md

Python Quality - Quick Reference

When NOT to Use This Skill

  • SonarQube setup - Use sonarqube skill
  • pytest configuration - Use pytest skills
  • Security scanning - Use python-security skill

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: fastapi or django for framework-specific patterns.

Tool Overview

ToolFocusSpeedUse Case
RuffLinting + formattingFastestAll-in-one replacement
BlackFormatting onlyFastOpinionated formatting
mypyType checkingMediumStatic type analysis
PylintDeep analysisSlowComprehensive linting
isortImport sortingFastImport organization

Recommendation: Use Ruff (replaces Black, isort, Flake8, and many Pylint rules).

Ruff Setup (Recommended)

Installation

pip install ruff

# Or with project
pip install "ruff>=0.3.0"

pyproject.toml

[tool.ruff]
target-version = "py312"
line-length = 100
exclude = [".venv", "migrations", "__pycache__"]

[tool.ruff.lint]
select = [
    "E",      # pycodestyle errors
    "W",      # pycodestyle warnings
    "F",      # Pyflakes
    "I",      # isort
    "B",      # flake8-bugbear
    "C4",     # flake8-comprehensions
    "UP",     # pyupgrade
    "ARG",    # flake8-unused-arguments
    "SIM",    # flake8-simplify
    "TCH",    # flake8-type-checking
    "PTH",    # flake8-use-pathlib
    "ERA",    # eradicate (commented code)
    "PL",     # Pylint
    "RUF",    # Ruff-specific
]
ignore = [
    "PLR0913",  # Too many arguments (configure separately)
    "PLR2004",  # Magic value comparison
]

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101"]  # Allow assert in tests

[tool.ruff.lint.pylint]
max-args = 5
max-branches = 10
max-returns = 3

[tool.ruff.lint.isort]
known-first-party = ["myapp"]
force-single-line = true

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true

Commands

# Lint
ruff check .

# Fix auto-fixable
ruff check --fix .

# Format
ruff format .

# Check format without changing
ruff format --check .

# Watch mode
ruff check --watch .

mypy Setup

Installation

pip install mypy

pyproject.toml

[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_unreachable = true

# Per-module overrides
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

[[tool.mypy.overrides]]
module = "migrations.*"
ignore_errors = true

Commands

# Type check
mypy src/

# Show error codes
mypy src/ --show-error-codes

# Generate stubs for library
stubgen -p some_library

Common mypy Errors

ErrorDescriptionFix
[arg-type]Wrong argument typeFix type or add annotation
[return-value]Wrong return typeFix return or annotation
[assignment]Incompatible assignmentFix type or use cast
[no-untyped-def]Missing type annotationsAdd annotations
[union-attr]Optional access without checkAdd None check

Pylint Setup (Optional - Deep Analysis)

Installation

pip install pylint

pyproject.toml

[tool.pylint.main]
py-version = "3.12"
jobs = 0  # Auto-detect CPU count
ignore-patterns = ["migrations", "__pycache__"]

[tool.pylint.messages_control]
disable = [
    "missing-module-docstring",
    "missing-class-docstring",
    "missing-function-docstring",
    "too-few-public-methods",
]

[tool.pylint.design]
max-args = 5
max-locals = 15
max-branches = 10
max-statements = 50
max-attributes = 10
max-public-methods = 20

[tool.pylint.format]
max-line-length = 100

[tool.pylint.similarities]
min-similarity-lines = 5
ignore-imports = true

Commands

# Full check
pylint src/

# Specific file
pylint src/main.py

# Generate config
pylint --generate-rcfile > .pylintrc

Type Hints Best Practices

Basic Types

from typing import Any

# Primitives
name: str = "John"
age: int = 30
active: bool = True
score: float = 95.5

# Collections
names: list[str] = ["Alice", "Bob"]
scores: dict[str, int] = {"Alice": 95, "Bob": 87}
unique_ids: set[int] = {1, 2, 3}
coordinates: tuple[float, float] = (1.0, 2.0)

# Optional (can be None)
middle_name: str | None = None

# Union types
identifier: str | int = "abc123"

# Any (avoid when possible)
data: Any = get_dynamic_data()

Function Signatures

from collections.abc import Callable, Iterable

def greet(name: str) -> str:
    return f"Hello, {name}"

def process_items(items: Iterable[int]) -> list[int]:
    return [item * 2 for item in items]

def apply_func(func: Callable[[int], int], value: int) -> int:
    return func(value)

# Generic functions
from typing import TypeVar

T = TypeVar("T")

def first(items: list[T]) -> T | None:
    return items[0] if items else None

Classes

from dataclasses import dataclass
from typing import Self

@dataclass
class User:
    id: int
    email: str
    name: str | None = None

    def with_name(self, name: str) -> Self:
        return User(id=self.id, email=self.email, name=name)

Protocols (Structural Typing)

from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...

def render(item: Drawable) -> None:
    item.draw()

# Any class with draw() method works
class Circle:
    def draw(self) -> None:
        print("Drawing circle")

render(Circle())  # OK

Common Code Smells & Fixes

1. Mutable Default Arguments

# BAD - Mutable default
def append_to(item, target=[]):
    target.append(item)
    return target

# GOOD - Use None
def append_to(item: str, target: list[str] | None = None) -> list[str]:
    if target is None:
        target = []
    target.append(item)
    return target

2. Bare Except

# BAD
try:
    do_something()
except:
    pass

# GOOD - Specific exceptions
try:
    do_something()
except ValueError as e:
    logger.warning(f"Invalid value: {e}")
except IOError as e:
    logger.error(f"IO error: {e}")
    raise

3. God Class

# BAD
class OrderProcessor:
    def create_order(self): ...
    def send_email(self): ...
    def generate_pdf(self): ...
    def calculate_tax(self): ...
    def update_inventory(self): ...

# GOOD - Single responsibility
class OrderService:
    def __init__(
        self,
        email_service: EmailService,
        pdf_generator: PdfGenerator,
        tax_calculator: TaxCalculator,
    ):
        self._email = email_service
        self._pdf = pdf_generator
        self._tax = tax_calculator

    def create_order(self, request: OrderRequest) -> Order:
        order = self._build_order(request)
        order.tax = self._tax.calculate(order)
        return order

4. Long Functions

# BAD - 100+ line function
def process_order(order):
    # validation
    # calculation
    # database
    # notifications
    pass

# GOOD - Extracted functions
def process_order(order: Order) -> ProcessedOrder:
    validate_order(order)
    total = calculate_total(order)
    saved = save_order(order, total)
    notify_user(saved)
    return saved

5. Magic Numbers

# BAD
if user.age >= 18:
    if len(password) >= 8:
        pass

# GOOD - Named constants
MINIMUM_AGE = 18
MIN_PASSWORD_LENGTH = 8

if user.age >= MINIMUM_AGE:
    if len(password) >= MIN_PASSWORD_LENGTH:
        pass

Pre-commit Setup

.pre-commit-config.yaml

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.3.0
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.8.0
    hooks:
      - id: mypy
        additional_dependencies:
          - types-requests
          - pydantic

Commands

# Install hooks
pre-commit install

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

# Update hooks
pre-commit autoupdate

VS Code Settings

// .vscode/settings.json
{
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll.ruff": "explicit",
      "source.organizeImports.ruff": "explicit"
    }
  },
  "python.analysis.typeCheckingMode": "strict",
  "mypy-type-checker.importStrategy": "fromEnvironment"
}

Quality Metrics Targets

MetricTargetTool
Cyclomatic Complexity< 10Ruff (PLR0912)
Cognitive Complexity< 15Ruff (C901)
Function Length< 50 linesRuff (PLR0915)
Arguments< 5Ruff (PLR0913)
Returns< 3Ruff (PLR0911)
Type Coverage100%mypy

CI/CD Integration

GitHub Actions

name: Quality
on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: |
          pip install ruff mypy
          pip install -e ".[dev]"

      - name: Ruff check
        run: ruff check .

      - name: Ruff format check
        run: ruff format --check .

      - name: mypy
        run: mypy src/

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
# type: ignore everywhereDefeats type checkingFix types or be specific
Any overuseNo type safetyUse proper types or TypeVar
Mutable default argsShared state bugsUse None with check
Bare except:Catches everythingCatch specific exceptions
noqa without codeIgnores all rulesUse noqa: E501 specifically
No type hintsHard to maintainAdd progressive typing

Quick Troubleshooting

IssueLikely CauseSolution
mypy can't find moduleMissing stubsInstall types-* package
Ruff conflicts with BlackBoth formattingUse only Ruff format
Type error in library codeLibrary not typedAdd to mypy ignore list
Pre-commit too slowRunning all checksUse --files for changed only
Import order conflictsMultiple toolsUse only Ruff isort

Related Skills

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

skills/quality/python-quality

默认分支

main

最新提交

9496306

Tree SHA

fe4e2f1