ПроКодинг - Откроем для вас мир IT!

Вы когда-нибудь сталкивались с тем, что один проект работает идеально, а другой - нет, хотя оба используют одинаковый код? Часто причина в том, что зависимости разнятся. Глобальные пакеты, установленные в системе, могут ломать работу вашего проекта. Локальные пакеты в Python решают эту проблему - они позволяют вам изолировать зависимости внутри каждого проекта, не засоряя систему и не конфликтующие с другими проектами.

Что такое локальные пакеты и зачем они нужны?

Локальные пакеты - это библиотеки, которые вы разрабатываете и используете внутри одного проекта, а не устанавливаете глобально. Они могут быть вашими собственными модулями, написанными для повторного использования в нескольких приложениях, или кастомными утилитами, которые не имеют смысла вне вашего стека.

Представьте, что вы работаете в команде, которая пишет API для внутреннего сервиса. У каждого разработчика своя версия библиотеки для работы с базой данных. Если вы установите её глобально, то один человек может сломать работу другого. Локальные пакеты позволяют каждому проекту иметь свою копию - без конфликтов.

До Python 3.8 для этого использовали виртуальные окружения (virtualenv). Но они требовали активации, деактивации, и каждый раз при открытии терминала вы должны были помнить: «А в каком окружении я сейчас?». Теперь есть более простой способ - __pypackages__.

__pypackages__ - новый стандарт для локальных пакетов

Согласно PEP 582, Python 3.8+ поддерживает директорию __pypackages__ в корне проекта. Когда вы запускаете Python, он сначала смотрит в эту папку, прежде чем искать глобальные пакеты. Это значит: вы больше не обязаны активировать виртуальное окружение. Просто кладёте зависимости в __pypackages__/3.8/lib/ - и всё работает.

Как это выглядит на практике? Вы устанавливаете пакет командой:

pip install --target __pypackages__/3.8/lib some-package

Python сам найдёт его. Никаких source venv/bin/activate. Никаких лишних файлов. Только код и зависимости рядом.

Но что если вы пишете свою библиотеку? Как её установить локально, чтобы тестировать изменения без постоянной переустановки?

Установка в режиме разработки: pip install -e .

Если вы разрабатываете собственную библиотеку, стандартная установка через pip install . копирует файлы в site-packages. Но если вы меняете код - нужно снова запускать установку. Это медленно и неудобно.

Вот где пригодится флаг -e (editable):

pip install -e .

Эта команда создаёт символьную ссылку на вашу директорию с кодом. Любые изменения в файлах yourlib/ сразу становятся доступны в проекте. Никаких переустановок. Никаких перезапусков. Просто сохраняете файл - и код обновляется.

Для этого вам нужно минимум два файла в корне проекта:

  • setup.py - описывает пакет
  • yourlib/ - директория с вашим кодом

Пример setup.py:

from setuptools import setup, find_packages

setup(
    name="my-internal-lib",
    version="0.1.0",
    packages=find_packages(),
    install_requires=[
        "requests>=2.25.0",
        "pydantic>=1.8.0"
    ],
    python_requires=">=3.8"
)

После запуска pip install -e . вы можете импортировать from my_internal_lib import utils в любом другом проекте - даже если он лежит в другой папке.

Структура проекта Python с setup.py и директорией кода, связанной символической ссылкой.

Как публиковать внутренние пакеты без PyPI?

Часто компании не хотят выкладывать свои библиотеки в открытый PyPI. Они нуждаются в приватном репозитории. Для этого есть pypiserver - простой сервер, который запускается на локальной машине или внутреннем сервере.

Установите его:

pip install pypiserver

Создайте директорию для пакетов:

mkdir ~/private-pypi

Запустите сервер:

pypiserver -p 8080 ~/private-pypi

Теперь вы можете загружать туда свои пакеты:

twine upload -r local dist/*

Но сначала нужно собрать пакет. Для этого запустите:

python setup.py sdist bdist_wheel

После этого пакет появится в ~/private-pypi. Ваши коллеги могут установить его так:

pip install --index-url http://localhost:8080 my-internal-lib

Вы можете настроить аутентификацию, HTTPS, логирование - всё это поддерживается pypiserver. И даже запустить его как системный сервис через systemd, чтобы он работал всегда.

Почему это лучше, чем виртуальные окружения?

Виртуальные окружения (venv) - это старый, но надёжный способ. Но они требуют много рутины:

  • Создавать отдельную папку для каждого проекта
  • Активировать их каждый раз
  • Запоминать, где они лежат
  • Управлять разными версиями Python в разных окружениях

С __pypackages__ всё проще: вы работаете в одной директории, и Python сам знает, какие пакеты нужны. Нет активаций. Нет путаницы. Только код и зависимости рядом.

Кроме того, __pypackages__ работает с Git. Вы можете коммитить эту папку - и другие разработчики получат все зависимости при клонировании. Никаких requirements.txt, которые не всегда точно отражают состояние.

Как начать использовать локальные пакеты прямо сейчас

Вот простой план, как перейти на локальные пакеты:

  1. Создайте в корне проекта папку __pypackages__
  2. Установите зависимости командой pip install --target __pypackages__/3.8/lib package-name
  3. Для собственных библиотек создайте setup.py и установите их через pip install -e .
  4. Если работаете в команде - настройте pypiserver на внутреннем сервере
  5. Добавьте __pypackages__ в .gitignore (если не хотите коммитить зависимости) или оставьте - если хотите иметь полную изолированную среду

Не забудьте обновить документацию для вашей команды. Пусть каждый знает: «Здесь нет venv. Здесь - __pypackages__».

Внутренний PyPI-сервер на экране с загружаемыми пакетами, рядом команда разработчиков.

Что делать, если вы используете старую версию Python?

Если у вас Python 3.7 или ниже - __pypackages__ не поддерживается. Но вы всё равно можете использовать локальные пакеты. Просто установите их в pip install --user . - это установит пакет в домашнюю директорию пользователя, а не в систему.

Или добавьте путь к вашему коду в переменную окружения:

export PYTHONPATH="/path/to/your/lib:$PYTHONPATH"

Это не идеально, но работает. Главное - не устанавливайте библиотеки глобально. Это источник всех проблем.

Когда локальные пакеты не нужны

Если вы пишете скрипт для одного раза - например, для обработки одного CSV-файла - вам не нужны локальные пакеты. Установите зависимости глобально или через pip install -r requirements.txt и забудьте.

Но если вы:

  • Работаете в команде
  • Пишете повторно используемый код
  • Управляете несколькими проектами
  • Хотите избежать "а у меня работает, а у тебя - нет"

Тогда локальные пакеты - не опция. Это стандарт.

Заключение

Локальные пакеты в Python - это не про сложность. Это про простоту. Они убирают лишние слои: виртуальные окружения, глобальные зависимости, путаницу с версиями. Они дают вам контроль. Они делают ваш код предсказуемым.

Начните с __pypackages__ и pip install -e .. Потом добавьте pypiserver, если работаете в команде. Перестаньте жить в мире "у меня всё работает". Начните жить в мире "всё работает у всех".

Что делать, если pip install -e . не работает?

Проверьте, что в корне проекта есть файл setup.py и он правильно описывает пакет. Убедитесь, что вы запускаете команду из той же директории, где лежит setup.py. Также проверьте, что у вас установлены setuptools и wheel: pip install --upgrade setuptools wheel. Если ошибка связана с правами - попробуйте запустить команду без sudo.

Нужно ли коммитить папку __pypackages__ в Git?

Не обязательно. Лучше добавить __pypackages__ в .gitignore и использовать requirements.txt или pyproject.toml для управления зависимостями. Но если вы хотите, чтобы проект работал сразу после клонирования без установки - можно коммитить. Особенно удобно для CI/CD или десктопных приложений.

Можно ли использовать локальные пакеты с Poetry или Pipenv?

Да, но с ограничениями. Poetry и Pipenv создают свои изолированные окружения, которые не совместимы с __pypackages__. Если вы хотите использовать __pypackages__ - лучше отказаться от них и перейти на чистый pip. Это упростит стек и уберёт лишнюю абстракцию.

Как проверить, откуда импортируется пакет?

Запустите в Python: import package_name; print(package_name.__file__). Это покажет путь к файлу, из которого импортировался пакет. Если он ведёт в __pypackages__ - значит, всё работает правильно. Если в /usr/lib - значит, вы установили его глобально, и это может вызвать конфликты.

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

Запустите сервер с флагом -H 0.0.0.0: pypiserver -p 8080 -H 0.0.0.0 ~/private-pypi. Это сделает его доступным с других устройств в сети. Добавьте аутентификацию через --passwords, чтобы только авторизованные пользователи могли загружать пакеты. Не забудьте настроить firewall и HTTPS для безопасности.