Вы когда-нибудь сталкивались с тем, что один проект работает идеально, а другой - нет, хотя оба используют одинаковый код? Часто причина в том, что зависимости разнятся. Глобальные пакеты, установленные в системе, могут ломать работу вашего проекта. Локальные пакеты в 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 в любом другом проекте - даже если он лежит в другой папке.
Как публиковать внутренние пакеты без 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, которые не всегда точно отражают состояние.
Как начать использовать локальные пакеты прямо сейчас
Вот простой план, как перейти на локальные пакеты:
- Создайте в корне проекта папку
__pypackages__ - Установите зависимости командой
pip install --target __pypackages__/3.8/lib package-name - Для собственных библиотек создайте
setup.pyи установите их черезpip install -e . - Если работаете в команде - настройте pypiserver на внутреннем сервере
- Добавьте
__pypackages__в.gitignore(если не хотите коммитить зависимости) или оставьте - если хотите иметь полную изолированную среду
Не забудьте обновить документацию для вашей команды. Пусть каждый знает: «Здесь нет venv. Здесь - __pypackages__».
Что делать, если вы используете старую версию 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 для безопасности.