← Назад к вопросам

Какими линтерами пользуешься?

1.0 Junior🔥 111 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Линтеры для Python: полный обзор опыта

Линтеры - критичны для качества кода. За 10+ лет использовал разные инструменты. Вот мой текущий stack и рекомендации.

1. Ruff - современный выбор (суперфастный)

Реши использовать Ruff (Rust + Python) вместо всех остальных:

# pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py311"
include = ["*.py", "*.pyi"]
exclude = [".git", "__pycache__", "migrations"]

[tool.ruff.lint]
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # Pyflakes
    "I",    # isort
    "N",    # pep8-naming
    "UP",   # pyupgrade
    "B",    # flake8-bugbear
    "D",    # pydocstyle
    "C90",  # mccabe
    "RUF",  # Ruff-specific
]
ignore = [
    "E501",  # line too long (обрабатываем в formatter)
    "D100",  # Missing module docstring
    "D104",  # Missing package docstring
]

[tool.ruff.lint.isort]
known-first-party = ["myapp"]
known-django = ["django"]
lines-after-imports = 2
lines-between-types = 1
single-line-exclusions = ["typing"]

[tool.ruff.lint.mccabe]
max-complexity = 10

[tool.ruff.lint.pydocstyle]
convention = "google"

# pyproject.toml - конец

# CLI команды
# ruff check .              # Проверить весь код
# ruff check --fix .        # Исправить автоматически
# ruff check --select E501  # Проверить только линию длиной
# ruff format .             # Форматировать (вместо Black)

Плюсы Ruff:

  • Суперфастный (написан на Rust)
  • Заменяет 10+ инструментов
  • Автофиксы для большинства ошибок
  • Отличная документация
  • Активно развивается

Минусы:

  • Относительно новый (но уже используют гиганты)

2. MyPy - type checking

Обязателен для production кода:

# pyproject.toml
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_calls = true
no_implicit_optional = true
warn_no_return = true
warn_unreachable = true
strict_equality = true

# Per-module настройки
[[tool.mypy.overrides]]
module = "tests.*"
ignore_errors = true

# CLI команды
# mypy myapp/          # Проверить папку
# mypy myapp/file.py   # Проверить файл
# mypy --strict .      # Максимум строгости

Пример кода:

from typing import Optional, List, Dict

def process_users(users: List[Dict[str, str]]) -> Optional[int]:
    """Обработать пользователей.
    
    Args:
        users: Список пользователей
    
    Returns:
        Количество обработанных пользователей или None
    """
    if not users:
        return None
    
    for user in users:
        name: str = user["name"]  # Type hints
        email: str = user["email"]
        # Обработка
    
    return len(users)

# Ошибки которые найдёт mypy:
result: int = process_users([])  # Error: Can't assign None to int
process_users("users")  # Error: Argument 1 has wrong type

3. Pylint - всеобъемлющий анализатор

Использовал раньше, сейчас на Ruff + MyPy:

# .pylintrc
[MASTER]
max-line-length = 100
limit-inference-results = 100
disable = 
    C0103,  # Invalid name
    C0111,  # Missing docstring
    W0212,  # Protected access
    R0903,  # Too few public methods

[DESIGN]
max-attributes = 7
max-arguments = 6
max-branches = 12
max-locals = 15
max-statements = 50

# CLI
# pylint myapp/

Минусы:

  • Медленный
  • Много false positives
  • Verbose конфиг

4. Flake8 - классический выбор

Эра flake8 прошла, но всё ещё используется:

# setup.cfg
[flake8]
max-line-length = 100
ignore = E203, W503
exclude = .git, __pycache__, migrations

# CLI
# flake8 .

5. Black - форматтер (теперь используем Ruff)

Любимый мной форматтер, но теперь использую ruff format:

# pyproject.toml
[tool.black]
line-length = 100
target-version = ['py311']
include = '\.pyi?$'

# CLI
# black .
# black --check .  # Проверить без изменений

6. Isort - сортировка импортов

Теперь встроен в Ruff, но отдельно используется так:

# pyproject.toml
[tool.isort]
profile = "black"
line_length = 100
known_first_party = ["myapp"]
known_django = ["django"]
default_section = "THIRDPARTY"
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "DJANGO", "FIRSTPARTY", "LOCALFOLDER"]

# CLI
# isort .

7. Bandit - проверка безопасности

Для поиска уязвимостей:

# .bandit
[bandit]
exclude_dirs = ["tests", ".venv"]
skips = [B101]  # assert_used - ок для тестов

# CLI
# bandit -r myapp/
# bandit -f csv myapp/ > report.csv

Пример проблем:

# Опасно
import pickle
data = pickle.loads(untrusted_data)  # Bandit will warn

# Опасно
import subprocess
subprocess.call("rm -rf /" + user_input)  # Shell injection

# Опасно
import hashlib
hashlib.md5(password)  # Use bcrypt instead!

# Опасно
sql = f"SELECT * FROM users WHERE id = {user_id}"  # SQL injection

8. Radon - метрики сложности

Для отслеживания усложнения кода:

# radon commands
# radon cc myapp/           # Cyclomatic complexity
# radon mi myapp/           # Maintainability index
# radon hal myapp/          # Halstead metrics
# radon mi myapp/ -a -nb    # A=average, nb=no rank

# Пример:
# radon cc myapp/ -a
# myapp/utils.py
#   process_data: B (8)  # Too complex!
#   format_output: A (2) # Good

9. Pylint и Pyflakes - какой выбрать

ИнструментСкоростьОхватТочностьКонфигурируемость
Ruff⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
MyPy⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Pylint⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Flake8⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
BlackN/AФорматирование⭐⭐⭐⭐⭐

10. Мой текущий stack (2024)

# requirements-dev.txt
ruff>=0.1.0
mypy>=1.0
bandit>=1.7
pytest>=7.0
pytest-cov>=4.0
# .github/workflows/lint.yml
name: Lint

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - run: pip install -r requirements-dev.txt
      
      - name: Ruff check
        run: ruff check . && ruff format --check .
      
      - name: MyPy type check
        run: mypy .
      
      - name: Bandit security check
        run: bandit -r myapp/
      
      - name: Tests with coverage
        run: pytest --cov=myapp --cov-report=term --cov-fail-under=85

11. Pre-commit hooks (обязательно!)

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

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.0.0
    hooks:
      - id: mypy
        args: [--strict]
        additional_dependencies: [pydantic]

  - repo: https://github.com/PyCQA/bandit
    rev: 1.7.4
    hooks:
      - id: bandit
        args: [-r, myapp/]

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: check-yaml
      - id: check-json
      - id: end-of-file-fixer
      - id: trailing-whitespace
# Установка
pre-commit install
pre-commit run --all-files

12. IDE интеграция (PyCharm example)

# PyCharm автоматически интегрируется с:
# - Ruff (Settings > Languages > Python > Linter)
# - MyPy (Settings > Languages > Python > Type Checker)
# - Black (Settings > Languages > Python > Formatter)

# VS Code
# Требует расширений:
# - Ruff
# - Pylance (встроенный mypy)
# - Python

Best Practices

  1. Используй Ruff вместо 5+ инструментов
  2. Всегда включай mypy на strict mode
  3. Настрой pre-commit hooks
  4. Запускай в CI/CD pipeline
  5. Не игнорируй ошибки, исправляй их
  6. Постепенно увеличивай strictness
  7. Обучай команду стандартам

Линтеры - это инвестиция в будущее проекта. Хороший код сегодня = меньше багов завтра.