Sổ tay kỹ thuật

Cấu hình ruff và mypy trong pyproject.toml

how-to#python#ruff#mypy· cập nhật 2026-11-06

Dùng khi dựng chuẩn code cho project Python, gồm ruff để lint và format (thay flake8, isort, black) và mypy để kiểm tra kiểu, tất cả cấu hình trong pyproject.toml.

Sửa trang này
Mục lục

Tóm tắt: uv add --dev ruff mypy → khai báo [tool.ruff], [tool.ruff.lint], [tool.ruff.format], [tool.mypy] trong pyproject.toml → chạy ruff check --fix, ruff format, mypy → đưa vào pre-commit và CI. Đã chạy thử với ruff 0.17 và mypy 2.4. Gắn vào hook git xem pre-commit.

Luồng hoạt động

flowchart LR A["Sửa code"] --> B["ruff check --fix"] B --> C["ruff format"] C --> D["mypy"] D -->|"lỗi kiểu"| A D --> E["pre-commit / CI"] E -->|"--check, không sửa"| F["Merge"]

Cấu hình

Cài

uv add --dev ruff mypy
uv add --dev types-PyYAML types-requests   # stub cho thư viện không kèm type hint

pyproject.toml

pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py312"           # bỏ được nếu đã có requires-python trong [project]
extend-exclude = ["migrations"]
 
[tool.ruff.lint]
# E/W pycodestyle, F pyflakes, I isort, B bugbear, UP pyupgrade,
# SIM simplify, RUF luật riêng của ruff, S bandit (bảo mật)
select = ["E", "W", "F", "I", "B", "UP", "SIM", "RUF", "S"]
ignore = ["E501"]                  # để formatter lo độ dài dòng
 
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"]              # cho phép assert trong test
 
[tool.ruff.lint.isort]
known-first-party = ["netinv"]
 
[tool.ruff.format]
quote-style = "double"
docstring-code-format = true       # format cả code mẫu trong docstring
 
[tool.mypy]
python_version = "3.12"
strict = true
warn_unreachable = true
mypy_path = "src"                  # project dạng src/ layout
files = ["src", "tests"]
plugins = ["pydantic.mypy"]        # chỉ khi dùng Pydantic
 
[[tool.mypy.overrides]]
module = ["ncclient.*", "textfsm.*"]   # thư viện không có type hint
ignore_missing_imports = true
 
[[tool.mypy.overrides]]
module = ["tests.*"]               # cần tests/__init__.py để khớp tên module
disallow_untyped_defs = false

Project cũ: bắt đầu với select = ["E", "F", "I"] và mypy không strict, sửa hết lỗi rồi mới thêm nhóm luật. Bật hết một lúc sẽ ra hàng trăm lỗi và không ai sửa.

Nhóm luật ruff đáng bật

Mã Nguồn Bắt được
F pyflakes Import thừa, biến chưa định nghĩa
I isort Thứ tự import (sửa tự động)
B flake8-bugbear Default là list/dict (def f(a=[])), except quá rộng
UP pyupgrade Optional[str] → str | None, cú pháp cũ
S flake8-bandit subprocess(..., shell=True), yaml.load không an toàn, mật khẩu hard-code
SIM flake8-simplify if lồng thừa, return True/False vòng vèo
RUF ruff Mutable class default, noqa không còn dùng

Lệnh

Mục đích Lệnh
Lint toàn project uv run ruff check .
Lint và tự sửa lỗi an toàn uv run ruff check --fix .
Xem trước thay đổi của --fix uv run ruff check --fix --diff .
Áp cả fix có thể đổi hành vi (xem lại diff) uv run ruff check --fix --unsafe-fixes .
Đếm lỗi theo luật uv run ruff check --statistics .
Một dòng mỗi lỗi (dễ grep) uv run ruff check --output-format concise .
Giải thích một luật uv run ruff rule S602
Format / chỉ kiểm tra (CI) uv run ruff format . / uv run ruff format --check .
Thêm # noqa cho mọi lỗi hiện có (khi mới áp dụng) uv run ruff check --add-noqa .
Xem cấu hình ruff đang dùng uv run ruff check --show-settings .
Type check theo files trong config uv run mypy
Type check một file, strict uv run mypy --strict src/netinv/core.py
Bỏ qua một dòng ruff: # noqa: S602; mypy: # type: ignore[arg-type]

CI chỉ kiểm tra, không sửa:

uv run ruff check --output-format github .   # annotation trên PR GitHub
uv run ruff format --check .
uv run mypy

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
error: Skipping analyzing "ncclient": module is installed, but missing library stubs or py.typed marker Thư viện không có type hint Thêm override ignore_missing_imports = true cho module đó
Library stubs not installed for "yaml" Thiếu gói stub uv add --dev types-PyYAML
mypy không tìm thấy package của mình trong src/ Chưa khai báo đường dẫn mypy_path = "src" hoặc cài project vào venv (uv sync)
Override module = ["tests.*"] không có tác dụng tests/ không có __init__.py, module tên test_x chứ không phải tests.test_x Thêm tests/__init__.py
[*] N fixable with --fix nhưng --fix không sửa hết Fix bị đánh dấu unsafe (vd B006) Xem --diff rồi chạy --unsafe-fixes, hoặc sửa tay
ruff format và lint sửa qua lại Bật luật conflict với formatter (E501, ISC001, W191...) Bỏ các luật đó khỏi select, để formatter lo
Cảnh báo Decorators on top of @property are not supported @computed_field của Pydantic # type: ignore[prop-decorator], xem type hints và Pydantic
Máy dev khác CI Version ruff/mypy khác nhau Ghim trong uv.lock, CI chạy uv sync --locked

Tham khảo