Правильная структура проекта упрощает разработку, тестирование и распространение кода. Python предлагает несколько подходов к организации, и выбор между ними зависит от масштаба проекта и требований.
Два основных подхода
Flat layout
Пакет находится в корне проекта. Простой, подходит для небольших проектов:
myproject/
mypackage/
__init__.py
core.py
utils.py
tests/
test_core.py
test_utils.py
pyproject.toml
README.md
Src layout
Пакет лежит внутри директории src/. Рекомендуется для библиотек и серьезных проектов:
myproject/
src/
mypackage/
__init__.py
core.py
utils.py
tests/
test_core.py
test_utils.py
pyproject.toml
README.md
Почему src layout лучше
Src layout имеет важное преимущество: при запуске тестов Python не может случайно импортировать пакет из текущей директории вместо установленной версии. Это предотвращает целый класс труднодиагностируемых ошибок.
# With flat layout:
# python -m pytest
# Python might import from ./mypackage/ instead of installed version
# With src layout:
# python -m pytest
# Python can only import the INSTALLED version
# ./src/mypackage/ is not on sys.path
Полная структура проекта
Для серьезного проекта структура выглядит так:
myproject/
src/
mypackage/
__init__.py
__main__.py # Entry point: python -m mypackage
core/
__init__.py
models.py
services.py
api/
__init__.py
routes.py
middleware.py
utils/
__init__.py
validators.py
helpers.py
config.py
exceptions.py
tests/
__init__.py
conftest.py # Shared pytest fixtures
unit/
test_models.py
test_services.py
integration/
test_api.py
docs/
index.md
pyproject.toml
uv.lock
.gitignore
.env.example
Makefile
Dockerfile
README.md
Конфигурация с setuptools
setuptools -- стандартный инструмент сборки Python-пакетов:
# pyproject.toml with setuptools
[build-system]
requires = ["setuptools>=70.0", "setuptools-scm>=8"]
build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "mypackage"
version = "1.0.0"
description = "A well-structured Python package"
readme = "README.md"
requires-python = ">=3.12"
license = "MIT"
authors = [
{ name = "Developer", email = "[email protected]" }
]
classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"httpx>=0.27",
"pydantic>=2.6",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"mypy>=1.8",
"ruff>=0.3",
"pre-commit>=3.6",
]
[project.urls]
Homepage = "https://github.com/user/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/user/mypackage"
[project.scripts]
myapp = "mypackage.cli:main"
[tool.setuptools.packages.find]
where = ["src"]
Конфигурация с Poetry
Poetry -- популярный инструмент управления зависимостями:
# pyproject.toml with Poetry
[tool.poetry]
name = "mypackage"
version = "1.0.0"
description = "A well-structured Python package"
authors = ["Developer <[email protected]>"]
readme = "README.md"
packages = [{ include = "mypackage", from = "src" }]
[tool.poetry.dependencies]
python = ">=3.12"
httpx = ">=0.27"
pydantic = ">=2.6"
[tool.poetry.group.dev.dependencies]
pytest = ">=8.0"
mypy = ">=1.8"
ruff = ">=0.3"
[tool.poetry.scripts]
myapp = "mypackage.cli:main"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Конфигурация с uv
uv использует стандартный pyproject.toml и добавляет свой lock-файл:
# pyproject.toml with uv (standard format)
[project]
name = "mypackage"
version = "1.0.0"
description = "A well-structured Python package"
requires-python = ">=3.12"
dependencies = [
"httpx>=0.27",
"pydantic>=2.6",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"mypy>=1.8",
"ruff>=0.3",
]
[project.scripts]
myapp = "mypackage.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
# Working with uv
uv sync # Install dependencies
uv sync --dev # Install with dev deps
uv run myapp # Run the CLI tool
uv run pytest # Run tests
uv build # Build the package
uv publish # Publish to PyPI
Entry points (точки входа)
Entry points позволяют создавать CLI-команды из функций Python:
[project.scripts]
myapp = "mypackage.cli:main"
myapp-admin = "mypackage.admin:cli"
# src/mypackage/cli.py
import sys
def main() -> int:
"""Main entry point for the CLI."""
print("Hello from myapp!")
return 0
if __name__ == "__main__":
sys.exit(main())
После установки пакета команда myapp будет доступна в терминале:
pip install .
myapp # Prints "Hello from myapp!"
# Or without installation via uv
uv run myapp
Файл __main__.py
Позволяет запускать пакет через python -m:
# src/mypackage/__main__.py
"""Allow running package with: python -m mypackage"""
from mypackage.cli import main
import sys
sys.exit(main())
python -m mypackage # Same as running myapp
Конфигурация инструментов
Все инструменты настраиваются в одном pyproject.toml:
# Ruff — linter and formatter
[tool.ruff]
target-version = "py312"
line-length = 88
src = ["src"]
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "A", "SIM"]
[tool.ruff.lint.isort]
known-first-party = ["mypackage"]
# Mypy — type checker
[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
warn_unused_configs = true
packages = ["mypackage"]
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
# Pytest
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra -q --strict-markers"
markers = [
"slow: marks tests as slow",
"integration: marks integration tests",
]
# Coverage
[tool.coverage.run]
source = ["mypackage"]
branch = true
[tool.coverage.report]
fail_under = 90
show_missing = true
Makefile для автоматизации
# Makefile
.PHONY: install test lint format check build
install:
uv sync --dev
test:
uv run pytest --cov=mypackage --cov-report=term-missing
lint:
uv run ruff check src/ tests/
uv run mypy src/
format:
uv run ruff format src/ tests/
uv run ruff check --fix src/ tests/
check: lint test
build:
uv build
clean:
rm -rf dist/ build/ *.egg-info
find . -type d -name __pycache__ -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
Шаблон .gitignore
# Python
__pycache__/
*.py[cod]
*.egg-info/
dist/
build/
# Virtual environments
.venv/
venv/
# IDE
.idea/
.vscode/
*.swp
# Testing
.pytest_cache/
htmlcov/
.coverage
# Environment
.env
# OS
.DS_Store
Thumbs.db
Рекомендации по именованию
| Элемент | Конвенция | Пример |
|---|---|---|
| Пакет | lowercase, short | mypackage |
| Модуль | lowercase, underscores | data_models.py |
| Класс | CamelCase | UserService |
| Функция | snake_case | get_user_by_id() |
| Константа | UPPER_CASE | MAX_RETRIES |
| Приватный | _prefix | _internal_helper() |