Опирается на правила:
PY-3.1,PY-3.2,PY-3.3,PY-3.4,PY-RUFF-1,PY-RUFF-3,PY-RUFF-4из Python Style Guide → раздел 3. Импорты.
Важно знать
- Только абсолютные импорты —
from ..core.order import Orderзапрещён на любой глубине пакета.- Wildcard запрещён —
from app.core.order import *ломает mypy и делает неизвестным происхождение имён.- Неиспользуемые импорты удаляются автоматически при
ruff check --fix(правилоF401).- Порядок групп: stdlib → third-party → local; пустая строка между группами;
ruff(isort-совместимый) поддерживает его механически — руками не расставлять.- Стиль импорта:
import moduleдля модулей целиком;from module import nameдля конкретных имён; не смешивать ради «короче».- ruff покрывает весь раздел механически —
F401(unused),F403(wildcard),I001(порядок isort); в findings ревью дублировать не нужно.- Конфигурация
ruff— вpyproject.toml, раздел[tool.ruff]; отдельные.isort.cfg/.flake8не используются.# noqa: F401без обоснования запрещён — если импорт нужен только для side-effect, добавить# noqa: F401 # justify: ....
Структура импортов — первый сигнал о связности модуля. Relative import from ..order import Order скрывает реальный путь к символу и ломается при переносе пакета. Wildcard делает невидимым, откуда пришло каждое имя — mypy отказывается анализировать такой код в strict-режиме. Ruff устраняет всю механику: группировку, сортировку, поиск unused — за CI.
Только абсолютные импорты
PY-3.1: путь всегда от корня пакета.
from app.core.order import Order
from app.customer.repository import CustomerRepository
import logging
from ..core.order import Order
from .repository import CustomerRepository
Relative-импорт кажется удобным внутри одного пакета — from . import utils короче. Но он привязывает файл к своей позиции в иерархии: стоит переместить order/ в domains/order/ — все from .. сломаются. Абсолютный путь работает независимо от расположения файла, IDE навигирует по нему напрямую, а ruff может автоматически исправить порядок.
Без wildcard
PY-3.2: from module import * не допускается.
from app.core.order import Order, OrderStatus, OrderId
from app.core.order import *
Wildcard-импорт переносит в пространство имён модуля всё, что экспортирует источник. Последствия:
- mypy в
--strictне может отследить типOrder, пришедшего через*— проверка типов перестаёт работать. - При конфликте имён (
Orderиз двух пакетов) Python молча выбирает последний wildcard — ошибку не увидите до runtime. - Рефакторинг — переименовать
OrderStatusвOrderStateв источнике — не найдёт использований через*.
Ruff ловит F403 на CI и блокирует merge.
Порядок групп: ruff делает это за вас
PY-3.3: три группы, пустая строка между ними.
import logging
import uuid
from datetime import datetime
from decimal import Decimal
from fastapi import Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.order import Order, OrderStatus
from app.customer.repository import CustomerRepository
Группы:
- stdlib — стандартная библиотека Python (
logging,uuid,datetime,decimal,pathlib). - third-party — пакеты из
pip/pyproject.toml(fastapi,pydantic,sqlalchemy,structlog). - local — внутренние модули проекта (
app.*,src.*).
ruff (isort-совместимый) расставляет группы и сортирует внутри при ruff check --fix. Конфигурация в pyproject.toml:
[tool.ruff]
line-length = 88
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W"]
[tool.ruff.lint.isort]
known-first-party = ["app"]
Поле known-first-party говорит ruff, что app.* — локальный пакет, а не third-party. Без него все from app. попадут в группу third-party и порядок сломается.
Ручная расстановка порядка — потеря времени и источник конфликтов в merge при добавлении нового импорта. Делегировать ruff и не трогать руками.
Стиль импорта: модуль vs конкретное имя
PY-3.4: import module для модулей целиком; from module import name для конкретных имён.
import logging
from app.core.order import Order
from decimal import Decimal
from logging import getLogger, warning, error, debug, info
import app.core.order
Правило разделяет два случая:
import module — когда модуль используется как пространство имён и вызов выглядит как logging.info(...). Это особенно важно для стандартной библиотеки: logging.getLogger(__name__) сразу сообщает, откуда функция, без поиска по импортам.
from module import name — когда конкретные имена используются напрямую: Order, Decimal, AsyncSession. Длинный путь app.core.order.Order(...) каждый раз — избыточен.
Смешанный стиль ради краткости (from logging import * или import app.core.order с доступом через app.core.order.Order) запрещён — он затрудняет поиск по кодовой базе и делает неоднозначным происхождение имён.
Пример из сервиса OrderService:
import logging
from decimal import Decimal
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.order import Order, OrderStatus
from app.customer.repository import CustomerRepository
logger = logging.getLogger(__name__)
class CreateOrderHandler:
def __init__(self, session: AsyncSession, customers: CustomerRepository) -> None:
self._session = session
self._customers = customers
async def execute(self, customer_id: UUID, amount: Decimal) -> Order:
customer = await self._customers.find_by_id(customer_id)
if customer is None:
raise ValueError(f"customer {customer_id} not found")
order = Order.create(customer_id=customer_id, amount=amount)
self._session.add(order)
return order
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
from ..core.order import Order | PY-3.1 | from app.core.order import Order |
from app.core.order import * | PY-3.2 | явный список: from app.core.order import Order, OrderStatus |
Неиспользуемый импорт без # noqa | PY-3.3 | удалить; ruff check --fix делает автоматически |
# noqa: F401 без обоснования | PY-RUFF-3 | # noqa: F401 # justify: side-effect import |
| Ручная расстановка порядка импортов | PY-3.3 | ruff check --fix (I001) |
from logging import getLogger, info, debug, ... | PY-3.4 | import logging + logging.getLogger(__name__) |
Конфиг isort в .isort.cfg отдельно от ruff | PY-RUFF-1 | [tool.ruff.lint.isort] в pyproject.toml |
select = [] в ruff без "I" (isort off) | PY-RUFF-X1 | добавить "I" в select |
Куда дальше
- python/naming.md —
snake_case,PascalCase, константыUPPER_SNAKE_CASE, Pydanticid_+ alias. - python/type-hints.md —
X | NoneвместоOptional[X],Protocolдля портов,mypy --strict. - python/ruff-mypy.md — полная настройка
pyproject.toml:select,line-length,mypyв CI. - Стандарты → Code Style — хаб языковых биндингов: Java, Node, Python, Go.