Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. src layout: Правильная структура проектов
src_layout_structure

src layout: Правильная структура проектов

Почему src layout лучше flat, pip install -e ., pyproject.toml, правильные импорты без sys.path

Ваш проект растёт. Тесты начинают импортировать код странными путями: sys.path.append('../src'). В production код работает, локально — нет. Что не так?

Цель: Научиться правильно структурировать проекты с src layout.

Вы точно готовы?

Убедитесь, что понимаете:

# Базовые импорты Python from mypackage import module import mypackage.module # Относительные импорты from . import module from .. import parent_module

Если импорты Python непонятны — освежите основы Python.

Проблема: flat layout

Плохая структура: flat layout

my-project/
├── mypackage/
│   ├── __init__.py
│   ├── models.py
│   └── services.py
├── tests/
│   └── test_services.py
├── setup.py
└── README.md

Что не так?

Проблема 1: Импорты не работают

# tests/test_services.py from mypackage.services import UserService # ❌ ModuleNotFoundError!

Почему: Python не знает где искать mypackage!

Плохие решения:

# ❌ ПЛОХО #1: sys.path хак import sys sys.path.append('../') from mypackage.services import UserService # ❌ ПЛОХО #2: Относительные пути import os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from mypackage.services import UserService # ❌ ПЛОХО #3: PYTHONPATH # export PYTHONPATH=/path/to/project from mypackage.services import UserService

Проблемы:

  • ❌ Хрупкие пути
  • ❌ Не работает в CI
  • ❌ Разные пути на разных машинах

Проблема 2: Тестируете неправильный код

# Запускаем тесты pytest tests/ # ✅ Тесты проходят!

НО:

# Устанавливаем пакет pip install . # Импортируем установленную версию python -c "from mypackage import services" # ❌ Получаем старый код! (кеш .pyc или неактуальная установка)

Проблема: Тесты запускают код из рабочей директории, а production использует установленный пакет!

Проблема 3: Случайные импорты

# tests/test_services.py # ❌ Может импортировать tests/models.py вместо mypackage/models.py! from models import User

Проблема: Python ищет модули начиная с текущей директории!

Решение: src layout

Правильная структура: src layout

my-project/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── models.py
│       └── services.py
├── tests/
│   └── test_services.py
├── pyproject.toml
└── README.md

Ключевое отличие: Код в src/mypackage/, не в корне!

Почему это лучше?

1. Импорты всегда из установленного пакета

# Установка в editable режиме pip install -e . # Теперь импорты работают ВЕЗДЕ pytest tests/ python -c "from mypackage import services" # ✅ Работает!

2. Невозможно случайно импортировать неустановленный код

# tests/test_services.py from mypackage.services import UserService # ✅ Всегда из src/mypackage/

3. Одинаковое поведение локально и в production

# Локально pip install -e . pytest # Production pip install . python app.py # ✅ Одинаковый код в обоих случаях!

Создание src layout проекта (5 минут)

Шаг 1: Создайте структуру

mkdir my-project cd my-project # Создайте src директорию mkdir -p src/mypackage touch src/mypackage/__init__.py # Создайте tests директорию mkdir tests touch tests/__init__.py # Создайте pyproject.toml touch pyproject.toml

Структура:

my-project/
├── src/
│   └── mypackage/
│       └── __init__.py
├── tests/
│   └── __init__.py
└── pyproject.toml

Шаг 2: pyproject.toml

# pyproject.toml [build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "mypackage" version = "0.1.0" description = "My awesome package" authors = [ {name = "Your Name", email = "you@example.com"} ] requires-python = ">=3.8" dependencies = [ "requests>=2.28.0", "pydantic>=2.0.0", ] [project.optional-dependencies] dev = [ "pytest>=7.0.0", "pytest-cov>=4.0.0", "pytest-xdist>=3.0.0", ] [tool.pytest.ini_options] testpaths = ["tests"] addopts = "-v --tb=short" [tool.setuptools.packages.find] where = ["src"]

Ключевые части:

  • [project] — метаданные пакета
  • dependencies — зависимости для production
  • dev — зависимости для разработки (pytest, etc.)
  • [tool.setuptools.packages.find] — где искать пакеты (src/)

Шаг 3: pip install -e .

# Установка в editable (разработческом) режиме pip install -e . # Или с dev зависимостями pip install -e ".[dev]"

Что делает -e (editable):

  • Создаёт symlink на src/mypackage/
  • Изменения в коде сразу доступны (не нужно переустанавливать)
  • Работает как установленный пакет

Проверка:

# Импортируем из любого места python -c "from mypackage import __version__" # ✅ Работает! # Запускаем тесты pytest # ✅ Импорты работают!

Миграция с flat на src layout (3 минуты)

До (flat layout)

my-project/
├── mypackage/
│   ├── models.py
│   └── services.py
└── tests/
    └── test_services.py

Миграция

# Создайте src директорию mkdir src # Переместите пакет в src mv mypackage src/ # Создайте pyproject.toml (см. выше) # Установите в editable режиме pip install -e ".[dev]" # Запустите тесты pytest

После (src layout)

my-project/
├── src/
│   └── mypackage/
│       ├── models.py
│       └── services.py
├── tests/
│   └── test_services.py
└── pyproject.toml

✅ Импорты работают без хаков!

Практический пример

Создайте код

# src/mypackage/__init__.py __version__ = "0.1.0" # src/mypackage/calculator.py def add(a, b): """Складывает два числа""" return a + b def multiply(a, b): """Умножает два числа""" return a * b

Напишите тесты

# tests/test_calculator.py from mypackage.calculator import add, multiply def test_add(): assert add(2, 3) == 5 def test_multiply(): assert multiply(4, 5) == 20

Запустите

# Установка (если ещё не сделали) pip install -e ".[dev]" # Тесты pytest # ✅ Работает!

Импортируйте из любого места

# Python REPL python >>> from mypackage import __version__ >>> print(__version__) 0.1.0 >>> from mypackage.calculator import add >>> add(10, 20) 30

✅ Всё работает как настоящий пакет!

Best practices

1. Всегда используйте src layout

✅ src/mypackage/
❌ mypackage/

2. pyproject.toml вместо setup.py

# ✅ СОВРЕМЕННО # pyproject.toml # ❌ УСТАРЕЛО # setup.py

3. Editable install для разработки

# ✅ Разработка pip install -e ".[dev]" # ✅ Production pip install .

4. Разделяйте зависимости

[project] dependencies = [ "requests", # Production ] [project.optional-dependencies] dev = [ "pytest", # Только для dev ]

5. Никогда не используйте sys.path

# ❌ ПЛОХО import sys sys.path.append('../') # ✅ ХОРОШО # pip install -e . from mypackage import module

Что вы изучили

  • Проблемы flat layout — импорты, тестирование неправильного кода
  • src layout — правильная структура проектов
  • pyproject.toml — современная конфигурация
  • pip install -e . — editable install для разработки
  • Миграция — как перейти с flat на src
  • Best practices — src/, pyproject.toml, no sys.path

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Продвинутые фикстуры: scope и autouse