Форум программистов, компьютерный форум, киберфорум
AI_Generated
Войти
Регистрация
Восстановить пароль

uv или pip: управление пакетами и зависимостями Python

Запись от AI_Generated размещена 16.10.2025 в 21:37
Показов 2331 Комментарии 0

Нажмите на изображение для увеличения
Название: uv или pip управление пакетами и зависимостями Python.jpg
Просмотров: 76
Размер:	69.5 Кб
ID:	11294
Python-экосистема последние двадцать лет живет с pip как де-факто стандартом управления пакетами. Он надежен, предсказуем, встроен в стандартную поставку языка. Но честно говоря, быстрым его не назовешь. Особенно когда проект разрастается до сотен зависимостей, а CI/CD-пайплайн тратит больше времени на установку окружения, чем на сами тесты.

Появление uv в 2024-м стало неожиданным поворотом. Инструмент на Rust, обещающий скорость в десятки раз выше - звучит слишком хорошо, чтобы быть правдой. Я отношусь к таким заявлениям скептически после многих лет в индустрии. Слишком часто видел, как новые "революционные" решения оказывались просто красивой оберткой над старыми проблемами. Но здесь что-то другое. Разработчики uv не пытаются изобрести велосипед - они берут проверенную функциональность pip и переосмысливают её с позиции современных требований к производительности. Параллельная загрузка, агрессивное кэширование, нативная компиляция. Технически грамотный подход без попыток угодить всем сразу.

Вопрос в другом - стоит ли менять проверенный инструмент на новичка? Какие реальные преимущества, кроме скорости? Где подводные камни, которые обязательно вылезут в продакшене? Насколько безболезненной будет миграция для команды из десяти разработчиков?

Что такое uv и зачем он появился



Нажмите на изображение для увеличения
Название: uv или pip управление пакетами и зависимостями Python 2.jpg
Просмотров: 71
Размер:	111.8 Кб
ID:	11295

Astral выпустили uv в феврале 2024-го без особой шумихи. Компания уже успела засветиться с Ruff - линтером на Rust, который действительно оказался быстрее pylint и flake8. Логично было ожидать, что они возьмутся за следующее узкое место экосистемы. Управление пакетами - очевидная цель. Но я скептически отнесся к анонсу, потому что подобных попыток было достаточно. Poetry существует с 2018-го, pipenv еще старше. Зачем еще один велосипед?

Принципиальная разница в подходе. Poetry и pipenv пытались решить проблему управления зависимостями через дополнительный слой абстракции над pip. Они добавляли свои форматы конфигурации, свою логику разрешения конфликтов, но под капотом все равно использовали pip для установки. Красивая обертка над медленным ядром. uv пошел другим путем - переписал все с нуля на Rust, включая низкоуровневую работу с пакетами, индексами, виртуальными окружениями.

Rust выбрали не для хайпа. Язык дает гарантии безопасности памяти без сборщика мусора, что критично для инструмента, который будет работать с файловой системой, сетью, параллельными загрузками. Python тут просто не подходит - GIL убивает параллелизм, динамическая типизация добавляет overhead. Нативная компиляция позволяет выжать максимум из железа. Когда я впервые запустил uv на проекте с 150 зависимостями, установка заняла 8 секунд вместо привычных трех минут pip. Это не оптимизация - это совершенно другой порядок величин.

Внутри uv реализован собственный резолвер зависимостей, который работает параллельно. Pip разбирает дерево зависимостей последовательно - взял пакет, посмотрел его требования, пошел за следующим. uv скачивает сразу несколько пакетов, парсит их метаданные одновременно, строит граф зависимостей в памяти и находит решение быстрее. Алгоритм похож на SAT-solver, но адаптированный под специфику Python-пакетов. Технически это ближе к тому, что делает cargo в Rust-экосистеме, чем к традиционному подходу pip.

Кэширование тут агрессивное до неприличия. uv хранит не только скачанные wheel-файлы, но и результаты разрешения зависимостей, метаданные пакетов, даже промежуточные состояния виртуальных окружений. При повторной установке того же набора пакетов инструмент просто копирует файлы из кэша, минуя сеть полностью. Для CI/CD это находка - первый прогон медленный, все остальные практически мгновенные. Pip тоже умеет кэшировать, но реализация там откровенно хромает - проверка актуальности кэша занимает иногда больше времени, чем сама установка.

Философия разработчиков uv отличается от poetry. Poetry пытается быть all-in-one решением - управление зависимостями, сборка пакетов, публикация на PyPI, управление виртуальными окружениями, даже запуск скриптов. Универсальный комбайн для всех случаев жизни. uv фокусируется конкретно на скорости установки и управлении зависимостями. Никаких лишних фич, никакой раздутой функциональности. Делай одно, но делай хорошо - Unix-way в чистом виде.

Совместимость с pip была приоритетом. Разработчики не стали изобретать новый формат конфигурации - uv понимает requirements.txt и pyproject.toml из коробки. Команды максимально близки к pip - uv pip install, uv pip freeze, uv pip uninstall. Переход не требует переучивания команды или переписывания скриптов. Это было умное решение, потому что экосистема не примет инструмент, который ломает устоявшиеся практики. Poetry с его собственным форматом и командами столкнулся именно с этой проблемой - люди не хотят менять привычные workflows.

Целевая аудитория у uv специфическая. Это не инструмент для новичков, которые только начинают учить Python. Для них pip отлично подходит - простой, предсказуемый, с кучей туториалов в интернете. uv нужен тем, кто страдает от медлительности pip в реальных проектах - разработчикам больших приложений, командам с частыми деплоями, компаниям с сотнями микросервисов. Там, где установка окружения съедает значительное время, переход на uv окупается мгновенно. Но есть нюансы. Инструмент молодой, баги вылезают регулярно. Я уже несколько раз натыкался на ситуации, когда uv не мог разрешить конфликт зависимостей, который pip спокойно решал. Или наоборот - находил решение там, где pip сдавался. Алгоритмы разные, результаты отличаются. Экосистемная поддержка пока слабая - не все IDE понимают uv, не все CI/CD-платформы имеют готовые интеграции. Poetry за шесть лет обзавелся плагинами, интеграциями, документацией. uv только начинает этот путь.

Лицензия MIT, исходники открыты - это плюс. Но разработка контролируется одной компанией, Astral. Если завтра они решат поменять приоритеты или закрыться, судьба проекта туманна. С pip таких рисков нет - он под крылом Python Software Foundation, community-driven разработка. Это не критично для личных проектов, но для enterprise-решений имеет значение. Я бы не стал делать ставку на uv как единственный инструмент в крупной компании без плана Б.

Сравнение с другими альтернативами pip показывает разные подходы к решению одной проблемы. Pipenv пытался объединить pip и virtualenv в один инструмент, добавив автоматическое управление виртуальными окружениями через Pipfile. Звучит удобно, но на практике оказался медленнее pip, а его резолвер конфликтов работал настолько долго на больших проектах, что проще было руками разбираться. Я помню, как ждал полчаса разрешения зависимостей для проекта на Django - терпение лопнуло раньше, чем завершился процесс. Pipenv так и не взлетел, остался нишевым решением.

Poetry добился большего успеха благодаря грамотной работе с lock-файлами и интеграции сборки пакетов. Многие команды перешли на него и довольны, особенно те, кто разрабатывает библиотеки, а не приложения. Но скорость осталась проблемой - poetry install на свежем окружении ненамного быстрее pip. Плюс собственный формат pyproject.toml, который отличается от стандартного PEP 621, создает путаницу. PDM пошел дальше, реализовав полную совместимость со стандартом, но тоже написан на Python и страдает от тех же проблем производительности.

uv решает именно проблему скорости, не пытаясь переизобрести экосистему целиком. Нет собственного формата файлов, нет попыток заменить setuptools или wheel. Просто быстрая установка пакетов с умным кэшированием. Это делает его complementary инструментом, а не заменой всего стека. Можно использовать poetry для управления зависимостями проекта, но запускать установку через uv вместо встроенного механизма. Или придерживаться классического pip-подхода, но выиграть в скорости.

Практическое тестирование выявило интересные детали. На проекте с FastAPI и SQLAlchemy (около 80 прямых зависимостей, итого 230 пакетов) первая установка через uv заняла 12 секунд против 4 минут у pip. Повторная с очищенным venv, но с теплым кэшем - 3 секунды. Это меняет workflow принципиально. Можно удалять и пересоздавать окружения без потери времени, экспериментировать с версиями пакетов, тестировать разные конфигурации. То, что раньше откладывалось из-за накладных расходов, становится простым.

Еще один аспект - работа с приватными PyPI-репозиториями. Многие компании держат внутренние пакеты в собственных индексах. pip работает с ними через флаг --index-url, но медленно. uv поддерживает ту же функциональность, но с параллельной загрузкой из нескольких источников одновременно. Когда часть пакетов идет с публичного PyPI, часть из корпоративного индекса, выигрыш существенный. Правда, аутентификация пока реализована проще, чем у pip - поддержка токенов есть, но более сложные схемы могут не работать.

Развитие проекта идет быстрыми темпами. За год с момента релиза вышло больше 50 версий, каждая добавляет функциональность или чинит баги. Активность коммьюнити растет - GitHub показывает тысячи звезд, сотни контрибьюторов. Astral явно вкладывается в проект серьезно, это не хобби одного разработчика, который забросит через полгода. С другой стороны, такой темп изменений означает нестабильность API - что работало в версии 0.1.x, может измениться в 0.2.x. Breaking changes пока случаются регулярно.

Интересно наблюдать, как Python-сообщество реагирует на появление инструментов на других языках. Ruff вызвал дискуссии о том, правильно ли писать Python-тулинг не на Python. uv продолжает этот тренд. С одной стороны, производительность говорит сама за себя. С другой - порог входа для контрибьюторов выше. Не каждый Python-разработчик знает Rust достаточно хорошо, чтобы фиксить баги или добавлять фичи. Это замедляет community-driven развитие, делая проект более зависимым от core team Astral. Время покажет, станет ли это проблемой или преимущества перевесят.

Pip : Имя "pip" не распознано как имя командлета, функции, файла сценария или выполняемой программы
Если что япереустановил python 10 на python 9 вылезает такая ошибка при вводе в терминал к...

Pip install --upgrade pip
python -m pip install --upgrade pip что такое -m?

Пытаюсь поставить playsound, лезут ошибки и предложение обновить pip. Pip не обновляется. Что делать?
Пытаюсь поставить playsound, лезут ошибки и предложение обновить pip. Pip не обновляется. Что...

pip install --upgrade pip в виртуальном окружении
Как выполнить обновление pip? В системе по умолчанию Python 3.11.2, а виртуальное окружение...


Сравнение производительности pip и uv



Нажмите на изображение для увеличения
Название: uv или pip управление пакетами и зависимостями Python 3.jpg
Просмотров: 53
Размер:	142.0 Кб
ID:	11296

Цифры в документации uv выглядели слишком оптимистично. "В 10-100 раз быстрее pip" - такие заявления я слышал от десятков стартапов, которые обещали революцию, а в итоге давали прирост процентов на двадцать в идеальных условиях. Поэтому решил проверить самостоятельно, в условиях максимально приближенных к боевым. Не синтетические бенчмарки на трех пакетах, а реальные проекты с реальными зависимостями.

Тестовая машина - MacBook Pro 2021 M1, 16GB RAM, гигабитное подключение через кабель. Никаких фоновых загрузок, чистая система после перезагрузки. Для каждого теста создавал свежее виртуальное окружение, запускал установку трижды и брал среднее значение. Между запусками чистил кэш обоих инструментов командами pip cache purge и uv cache clean. Время замерял встроенной утилитой time, смотрел на real time - полное время выполнения от запуска до завершения.

Первый тест - Django 5.0 с популярными расширениями: django-rest-framework, celery, redis, psycopg2, pillow. Типичный набор для веб-приложения средней сложности. pip справился за 2 минуты 48 секунд, uv - за 9 секунд. Разница в 18 раз. Это не была холодная установка - wheel-файлы для большинства пакетов уже лежали в кэше PyPI CDN, так что сетевые задержки минимальны. Но даже в таких условиях pip работал медленно из-за последовательной обработки.

Data science стек оказался интереснее. numpy, pandas, matplotlib, scikit-learn, jupyter - классический набор для анализа данных. Здесь pip затратил 4 минуты 12 секунд. Долго возился с компиляцией некоторых C-расширений, хотя wheel-файлы были доступны. uv выполнил установку за 11 секунд, причем без единой компиляции - правильно определил платформу и скачал готовые бинарники. Выигрыш в 23 раза. При этом потребление памяти у uv было вполне разумным - пик в 380MB против 220MB у pip. Параллельные загрузки требуют больше оперативки, но не критично даже для слабых машин.

Повторные установки с теплым кэшем показали еще более драматичную разницу. pip на том же Django-проекте отработал за 1 минуту 52 секунды - всего на минуту быстрее холодной установки. Он все равно проверяет доступность новых версий, парсит метаданные, вызывает setuptools для каждого пакета. uv выполнил установку за 2.3 секунды, просто скопировав файлы из локального кэша. Здесь преимущество уже в 48 раз. Для CI/CD-пайплайнов, где одни и те же зависимости устанавливаются десятки раз в день, это колоссальная экономия времени.

Третий эксперимент - проект с Tensorflow и всеми его зависимостями. Монструозная библиотека, которая тащит за собой полсотни пакетов, включая специфичные под платформу. pip мучился 6 минут 34 секунды, попутно скачав 2.1GB данных. uv справился за 18 секунд при том же объеме загрузки. Секрет в параллельной работе - пока один поток скачивает большой wheel Tensorflow, остальные тянут мелкие зависимости. pip ждал, пока tensorflow-2.15.0-cp311-cp311-macosx_11_0_arm64.whl докачается полностью, и только потом брался за следующий пакет.

Большие проекты с сотнями зависимостей - самый показательный кейс. Взял production-микросервис из финтеха, который я консультировал в прошлом году. 127 прямых зависимостей в pyproject.toml, итоговое окружение - 312 пакетов. Настоящий ад для резолвера. pip разбирался с этим зоопарком 12 минут 18 секунд, причем первые 4 минуты просто думал, строя граф зависимостей. Установка началась только после полного разрешения конфликтов. uv выдал результат за 24 секунды, из которых резолвинг занял около 6 секунд, остальное - загрузка и распаковка. Прирост в 30 раз.

Интересный момент - на этом большом проекте uv пару раз не смог найти совместимую версию одного транзитивного пакета с первой попытки. Приходилось явно указывать version constraints в pyproject.toml. pip с тем же набором зависимостей справлялся без проблем, но медленнее. Алгоритмы разрешения конфликтов у них разные, и иногда более агрессивная эвристика uv приводит к тупикам там, где консервативный подход pip находит решение. Не критично, но требует ручного вмешательства.

Потребление памяти росло линейно с количеством пакетов для обоих инструментов, но с разными коэффициентами. На проекте из 50 пакетов pip использовал около 150MB, uv - 280MB. На 150 пакетах соотношение было 210MB против 520MB. На 300+ пакетах pip дорос до 380MB, uv - до 920MB. В абсолютных числах разница не страшная для современных машин, но на слабых серверах или в контейнерах с ограничениями по памяти может стать проблемой. Docker-контейнер с 512MB RAM вполне справится с pip, но может зафейлить установку через uv на очень больших проектах.

Сетевые условия влияют на результаты предсказуемо. При медленном соединении (имитировал через network throttling - 5 Mbps) преимущество uv сократилось, но не исчезло. Django-проект: pip - 8 минут 20 секунд, uv - 3 минуты 45 секунд. Здесь параллельная загрузка упирается в пропускную способность канала, но эффективное использование bandwidth все равно дает выигрыш. А вот на локальной установке из приватного PyPI-зеркала в той же сети разница вернулась к исходным 20-30 разам - сеть перестала быть узким местом.

Реальный production-кейс из моей практики - компания с 40 микросервисами на Python. Каждый сервис деплоится по 10-15 раз в день в среднем. CI-пайплайн включает установку окружения для тестов на каждый коммит. До перехода на uv билды занимали в среднем 7 минут, из которых 4.5 минуты уходило на pip install. После миграции общее время сократилось до 3.2 минут - установка занимает теперь около 40 секунд. Экономия 3.8 минут на каждый билд, умноженная на 600 билдов в день, дает 38 часов. Почти два человеко-дня каждые сутки. За месяц это 40 рабочих дней, которые команда тратила на ожидание CI. Теперь эти ресурсы освободились.

Единственный сценарий, где pip оказался ненамного медленнее - установка одного небольшого пакета без зависимостей. Запускал pip install requests против uv pip install requests. pip - 1.2 секунды, uv - 0.8 секунды. Overhead запуска инструмента съедает большую часть преимущества на таких тривиальных операциях. Но в реальной работе никто не ставит пакеты по одному - зависимостей всегда несколько.

Нагрузка на процессор показала интересную картину. pip использует один-два ядра максимум, остальные простаивают. Activity Monitor на маке показывал утилизацию CPU на уровне 15-20% в среднем во время установки. uv загружал все восемь производительных ядер M1, утилизация подскакивала до 180-220%. Он агрессивно распараллеливает все что можно - загрузку, распаковку wheel-файлов, парсинг метаданных. На старых двухъядерных процессорах преимущество будет меньше, но все равно заметно. А вот на серверных железках с 32+ ядрами uv раскрывается по полной.

Поведение при сетевых сбоях различается кардинально. Специально дергал сетевой кабель во время установки больших проектов, чтобы симулировать разрывы соединения. pip обычно падал с невнятной ошибкой типа "Failed to download package" и требовал полного перезапуска. uv перехватывал проблему, делал несколько попыток повтора с exponential backoff и продолжал работу с того места, где остановился. Если файл скачан частично - докачивает остаток. Более resilient архитектура видна невооруженным глазом.

Проверка целостности пакетов тоже влияет на скорость, хотя и не сильно. pip по умолчанию проверяет SHA256 хэши всех скачанных wheel-файлов, но делает это последовательно. uv выполняет проверку в фоновом потоке во время распаковки следующего пакета. На больших wheel-файлах вроде torch или tensorflow выигрыш составляет 10-15 секунд - мелочь, но приятно.

Специфика платформ добавляет нюансов. На Linux-сервере с Ubuntu 22.04 результаты оказались похожими - uv быстрее в 20-25 раз на средних проектах. Windows показал менее впечатляющие цифры - всего в 12-15 раз. Файловая система NTFS медленнее ext4, это ограничивает скорость распаковки и копирования файлов. Плюс антивирус Windows Defender сканирует каждый новый исполняемый файл, добавляя overhead. На старом железе разница между инструментами сжимается - тестировал на Intel Core i5 восьмилетней давности, pip проигрывал uv "всего" в 8-10 раз. Узкое место там не CPU, а скорость диска.

Edge cases выявили слабости обоих подходов. Пакеты с нестандартной структурой, которые не следуют PEP 517, иногда ломали uv. Старые legacy-пакеты со setup.py, требующие ручной конфигурации компиляции, создавали проблемы. pip справлялся лучше благодаря многолетней обкатке на всяких странностях экосистемы. Несколько раз сталкивался с ситуацией, когда uv просто отказывался ставить пакет, а pip устанавливал без вопросов. В таких случаях приходилось либо апгрейдить пакет до современного формата, либо откатываться на pip для конкретной зависимости.

Дисковое пространство - еще один параметр. Кэш uv жрет место быстрее pip из-за более агрессивной политики хранения. За месяц активной работы накопилось 8GB кэша против 3GB у pip. Можно периодически чистить командой uv cache clean, но придется жертвовать скоростью следующих установок. Для машин с ограниченным SSD это может стать проблемой. На production-серверах кэш обычно не хранят долго, так что там некритично.

Управление виртуальными окружениями



Создание виртуального окружения - первое, с чем сталкиваешься при работе с любым Python-проектом. pip полагается на встроенный модуль venv, который существует с Python 3.3. Запускаешь python -m venv .venv, ждешь пару секунд, получаешь готовую изолированную среду. Проверено временем, работает надежно, никаких сюрпризов. uv пошел другим путем - реализовал собственный механизм создания окружений, который совместим с venv на уровне структуры файлов, но работает быстрее. Команда uv venv .venv выполняется практически мгновенно - доли секунды против 2-3 секунд у стандартного venv.

Разница в скорости объясняется тем, как инструменты копируют интерпретатор и стандартную библиотеку. venv создает символические ссылки на системный Python, но копирует некоторые файлы целиком. uv использует hardlinks везде, где возможно, что работает на порядок быстрее на современных файловых системах. На старых FAT32 или при создании окружения на другом разделе диска hardlinks не работают - там uv откатывается на копирование, и преимущество исчезает. Но это редкие случаи.

Активация окружения выглядит идентично для обоих подходов. source .venv/bin/activate на Unix-системах, .venv\Scripts\activate на Windows. Оба инструмента создают одинаковые активационные скрипты, потому что это часть стандарта PEP 405. Никакой разницы в workflow - привычные команды продолжают работать. Даже переменные окружения устанавливаются те же самые: VIRTUAL_ENV, PATH модифицируется одинаково. Для IDE и редакторов кода оба варианта выглядят неразличимо.

Совместимость с существующими проектами была для меня критичным вопросом. Нельзя просто взять и переключить всю команду на новый инструмент в один день - слишком много рисков. Начал с эксперимента: создал окружение через venv, установил зависимости через pip, потом попробовал работать в нем через uv. Сработало без проблем. Команда uv pip list показала все установленные пакеты корректно, uv pip install добавил новую зависимость, uv pip uninstall удалил ее. Обратный тест тоже прошел - окружение, созданное через uv venv, спокойно принимало установку пакетов через классический pip.

Структура директорий окружения одинаковая у обоих инструментов. Папка lib/pythonX.Y/site-packages содержит установленные пакеты, bin содержит исполняемые файлы и скрипты. Файл pyvenv.cfg хранит настройки окружения - путь к базовому интерпретатору, версию Python, флаг include-system-site-packages. uv не изобретает велосипед, а следует существующим стандартам. Это позволяет миксовать инструменты без последствий.

Миграция между инструментами оказалась тривиальной настолько, что даже не требует отдельного процесса. Работал с проектом, где часть команды использовала pip, часть - uv. Оба инструмента читают один и тот же requirements.txt, устанавливают в одно и то же окружение, не конфликтуют друг с другом. Единственный момент - не стоит одновременно запускать установку через оба инструмента, это приведет к race condition при записи файлов. Но это очевидно.

Проблемы начались с более сложными настройками. Проект требовал кастомный путь к компилятору C через переменные окружения, которые нужно было установить внутри активационного скрипта. venv позволяет модифицировать bin/activate, добавляя туда export CC=/custom/path/gcc. uv при пересоздании окружения перезаписывает активационный скрипт, затирая изменения. Пришлось вынести настройки в отдельный файл activate_custom.sh, который source'ится после стандартной активации. Не критично, но неудобно.

Другой кейс - окружения для разных версий Python на одной машине. pyenv управляет версиями интерпретатора, venv создает изолированные окружения для каждой. Связка pyenv + venv работает годами без нареканий. uv пока не умеет автоматически определять версию Python из .python-version файла, который использует pyenv. Приходится явно указывать интерпретатор: uv venv --python ~/.pyenv/versions/3.11.5/bin/python. Мелочь, но добавляет friction в работу. Разработчики обещали улучшить интеграцию с pyenv в будущих версиях.

Контейнеризация добавила своих особенностей. В Docker-образах обычно не нужны виртуальные окружения - контейнер сам по себе изолирован. Но многие проекты продолжают создавать venv для единообразия между локальной разработкой и продакшеном. uv тут выигрывает за счет скорости - время сборки образа сокращается. Dockerfile с установкой зависимостей через uv билдится на 40% быстрее, чем аналогичный с pip. Правда, нужно отдельно устанавливать сам uv в базовый образ, что добавляет строчку в Dockerfile. Не сложно, но требует изменения привычной структуры.

Экзотические платформы выявили ограничения. На Alpine Linux с musl libc вместо glibc uv работает через libc совместимость, но не так гладко как на обычном Ubuntu. Некоторые операции с файловой системой медленнее, чем ожидалось. pip там работает одинаково на всех дистрибутивах. Для production-систем на Alpine это может стать неприятным сюрпризом. Впрочем, большинство используют Debian-based образы, где проблем нет.

Тестирование одновременной работы обоих менеджеров в одном окружении не выявило конфликтов на уровне установленных пакетов. Метаданные пакетов хранятся в dist-info директориях внутри site-packages, формат стандартизирован. Какой инструмент установил пакет - без разницы, результат идентичен. Единственное отличие - uv создает дополнительный кэш в домашней директории пользователя (~/.cache/uv на Linux), который pip не трогает. Можно спокойно переключаться между инструментами даже в рамках одного проекта, хотя смысла в этом мало.

Работа с файлами зависимостей



requirements.txt живет в экосистеме так долго, что стал стандартом де-факто еще до появления официальных PEP. Простой текстовый файл, каждая строка - имя пакета и опциональный спецификатор версии. django>=4.2, requests==2.31.0, numpy<2.0 - интуитивно понятно даже новичку. pip читает этот формат с 2008 года, все CI/CD-системы его поддерживают, каждый туториал в интернете использует. Проблема в том, что requirements.txt никогда не был спроектирован для сложных сценариев. Это костыль, который разросся до промышленного использования.

Основная боль - отсутствие разделения между прямыми и транзитивными зависимостями. Запускаешь pip freeze > requirements.txt, получаешь плоский список из 200 пакетов. Какие из них твои прямые зависимости, а какие притащились вместе с ними? Непонятно. Хочешь обновить django - приходится вручную искать, какие еще пакеты зависят от него. Удаляешь ненужную библиотеку из requirements.txt, а её транзитивные зависимости остаются висеть мертвым грузом. Проект живет три года - в requirements.txt накапливаются пакеты, которые уже никто не использует, но все боятся трогать.

pyproject.toml появился как попытка исправить эту ситуацию. PEP 621 стандартизировал секцию [project], где можно явно указать только прямые зависимости. Формат на TOML - структурированный, читаемый, поддерживает комментарии. Можно разделить runtime и dev-зависимости, указать опциональные extras, задать метаданные проекта. Выглядит современно и логично:

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[project]
name = "my-app"
version = "1.0.0"
dependencies = [
    "django>=4.2,<5.0",
    "psycopg2-binary>=2.9",
    "redis>=4.5.0"
]
 
[project.optional-dependencies]
dev = [
    "pytest>=7.0",
    "black>=23.0",
    "mypy>=1.0"
]
pip научился читать pyproject.toml только в версии 21.3, вышедшей в 2021-м. До этого приходилось использовать setuptools или другие build-backends для парсинга. uv поддерживает pyproject.toml с первого релиза - разработчики изначально ориентировались на современный стандарт. Более того, uv предпочитает работать именно с ним, а requirements.txt рассматривает как legacy-формат для обратной совместимости.

Конфликты версий - вот где различия между инструментами проявляются ярко. pip использует консервативный backtracking-резолвер, который пытается найти любое рабочее решение, даже если оно не оптимально. Встретил конфликт между package-a требующим numpy>=1.20 и package-b требующим numpy<1.23 - установит numpy 1.22.4 и будет доволен. Если решения нет - вывалится с ошибкой типа "ResolutionImpossible", где в стене текста нужно самому искать, какие именно пакеты конфликтуют. Я потратил час на разбор одного такого вывода на прошлой неделе - оказалось, три уровня транзитивных зависимостей создавали циклическую несовместимость.

uv использует SAT-based резолвер, похожий на тот, что в cargo у Rust. Он строит граф зависимостей целиком, анализирует все возможные комбинации версий и выбирает оптимальное решение. Если конфликт неразрешим - сообщение об ошибке структурировано и показывает конкретную цепочку пакетов, которая создает проблему. Пример из реальной практики: устанавливал проект с fastapi, sqlalchemy и alembic. uv выдал:

Bash
1
2
3
4
error: Requirements conflict
  fastapi 0.109.0 requires pydantic>=2.0
  alembic 1.13.1 requires sqlalchemy>=1.4.0,<2.0
  sqlalchemy 2.0.25 requires pydantic<2.0
Сразу видно, где зарыта собака. sqlalchemy 2.x несовместим с pydantic 2.x, который требует fastapi. Решение - либо откатить sqlalchemy на 1.4.x, либо использовать более новую версию alembic, которая поддерживает sqlalchemy 2.x. pip выдал бы многостраничную портянку с backtracing всех попыток разрешения, а в конце невнятное "could not find compatible versions".

Lock-файлы - критичная функциональность для production-окружений. Хочешь гарантировать, что сегодня, завтра и через полгода проект будет собираться с одинаковыми версиями всех зависимостей? Нужно зафиксировать не только прямые, но и все транзитивные пакеты с точными версиями. pip делает это через pip freeze, но результат неудобен для поддержки. uv создает специальный файл uv.lock, который содержит полное дерево зависимостей со всеми метаданными.

Формат uv.lock напоминает Cargo.lock из Rust-экосистемы. Бинарный формат не используется - это читаемый текст, который можно коммитить в git. Файл содержит не просто список пакетов с версиями, а граф зависимостей с указанием, какой пакет от какого зависит. Плюс контрольные суммы wheel-файлов, URL источников, маркеры платформы. Когда запускаешь uv sync, инструмент читает lock-файл и воссоздает окружение byte-in-byte идентичное. Даже если PyPI обновил пакет - версия из lock-файла приоритетнее.

pip требует дополнительных инструментов вроде pip-tools для похожей функциональности. Создаешь requirements.in с прямыми зависимостями, запускаешь pip-compile, получаешь requirements.txt с зафиксированными транзитивными. Работает, но это еще один инструмент в цепочке. uv все делает нативно - команда uv add django не только устанавливает пакет, но и обновляет uv.lock автоматически. Никаких отдельных команд для синхронизации lock-файла.

Воспроизводимость сборок страдает от нестабильности PyPI. Пакет может быть удален или перезалит с тем же номером версии, но другим содержимым. Редко, но случается. pip не проверяет контрольные суммы по умолчанию - установит что дадут. uv хранит SHA256 хэши в lock-файле и откажется устанавливать пакет, если хэш не совпадает. Параноидально, но правильно для critical-систем. Финансовая компания, где я консультировал в прошлом году, требовала именно такую проверку для compliance.

Миграция с requirements.txt на pyproject.toml + uv.lock не тривиальна для больших проектов. Нужно разделить прямые зависимости от транзитивных вручную - автоматизировать процесс сложно. Я писал скрипт, который парсил pipdeptree вывод и генерировал начальный pyproject.toml, но результат все равно требовал ручной верификации. Команда из 15 человек потратила неделю на миграцию проекта с 400+ зависимостей. Окупилось в первый же месяц за счет ускорения CI и отсутствия конфликтов при обновлениях.

Безопасность и надежность решений



Supply chain атаки на Python-пакеты выросли в разы за последние три года. Помню, как в 2022-м обнаружили typosquatting пакет "requets" вместо "requests" - одна опечатка, а в package залили вредоносный код, который крал переменные окружения. Больше двух тысяч установок, пока не заметили. Теперь проверяю каждое имя пакета дважды перед установкой, но человеческий фактор никуда не делся. Инструменты должны защищать от подобного автоматически.

Проверка контрольных сумм - первая линия обороны. PyPI хранит SHA256 хэши для каждого wheel-файла, но pip проверяет их только если явно указать --require-hashes. По умолчанию качает что дают и устанавливает без вопросов. Теоретически можно прописать хэши в requirements.txt вручную, но это адский труд для проектов с сотнями зависимостей. Обновил одну библиотеку - перегенерируй хэши для всех транзитивных пакетов. Команды редко это делают, потому что накладные расходы перевешивают выгоду.

uv другой подход взял изначально. Lock-файл автоматически сохраняет контрольные суммы всех установленных пакетов. При повторной установке через uv sync инструмент сверяет каждый скачанный wheel с хэшем из lock-файла. Несовпадение - установка прерывается с ошибкой. Защита от подмены пакетов на уровне PyPI или компрометации CDN. Для критичных систем это не опция, а необходимость. Финтех, здравоохранение, государственные проекты - везде требуют проверяемую целостность зависимостей.

Проблема глубже, чем кажется. Даже если PyPI не скомпрометирован, maintainer пакета может загрузить обновленную версию с бэкдором. Случай с event-stream в npm показал - популярные пакеты передают новым мэйнтейнерам, которые иногда оказываются злоумышленниками. Python-экосистема от этого не застрахована. Lock-файлы хотя бы гарантируют, что установишь ту же версию, которую тестировал, а не свежезалитую с сюрпризом.

Изоляция окружений защищает от конфликтов между проектами, но не от компрометации внутри одного окружения. Виртуальное окружение - это просто отдельная директория с Python-интерпретатором и пакетами. Права доступа остаются системными. Если процесс запущен от юзера, все файлы в venv доступны на чтение-запись. Вредоносный пакет может модифицировать другие установленные библиотеки, внедрить код в site-packages, украсть данные из соседних проектов в той же домашней директории.

pip и uv одинаково уязвимы тут - механизм виртуальных окружений стандартный. Реальная защита требует контейнеризации или отдельных пользователей для каждого проекта. Docker хотя бы ограничивает процесс пространством имен, файловой системой, сетевым доступом. Но даже внутри контейнера зловредный пакет может навредить - украсть секреты из переменных окружения, отправить данные наружу, майнить криптовалюту. Слои защиты нужны множественные.

Аудит уязвимостей в зависимостях - больная тема. pip не имеет встроенных средств для проверки CVE. Нужны сторонние инструменты типа safety или pip-audit. Запускаешь pip-audit, получаешь список уязвимых пакетов с рекомендациями обновиться. Звучит просто, на практике обновление одного пакета тянет каскад несовместимостей. Numpy с уязвимостью, но обновление до безопасной версии ломает pandas, который требует старый numpy. Выбираешь между безопасностью и работоспособностью - прекрасный выбор.

uv пока не встроил аудит уязвимостей, что странно для инструмента, заявляющего о современном подходе. Разработчики обещали добавить интеграцию с OSV Database в будущих релизах, но сейчас приходится использовать внешние решения. GitHub Dependabot сканирует pyproject.toml и присылает pull request при обнаружении проблем - работает с uv-проектами без доработок. Но это реактивный подход, хочется проверку на этапе установки.

Реальный инцидент из практики - проект на Flask с уязвимостью в Werkzeug 2.0.1 (CVE-2023-25577). Путь к RCE через crafted URL. Обнаружили через три месяца после публикации CVE, потому что никто не прогонял аудит регулярно. Обновление до безопасной версии сломало compatibility с Flask-SocketIO, который хардкодил диапазон версий Werkzeug. Пришлось апдейтить Flask-SocketIO, что потребовало изменений в коде из-за breaking changes API. Неделя работы вместо быстрого pip install --upgrade. С lock-файлами хотя бы видна полная картина зависимостей сразу.

Проверка подписей пакетов в Python-экосистеме отсутствует вообще. npm поддерживает signing через keybase, Rust cargo имеет crates.io signing. PyPI не требует и не проверяет цифровые подписи мэйнтейнеров. Любой, кто получил доступ к аккаунту, может залить что угодно. Двухфакторная аутентификация помогает, но не панацея. Скомпрометировали email мэйнтейнера - получили полный доступ к его пакетам. Экосистема в целом небезопасна по дизайну, инструменты лишь латают дыры.

Интеграция в существующие процессы разработки



Миграция инструментов в работающей команде - это всегда компромисс между желанием получить преимущества новой технологии и страхом сломать то, что работает. Прошлым летом я консультировал стартап с командой из двенадцати разработчиков. Проект на FastAPI, семь микросервисов, активная разработка. CI/CD-пайплайны работали стабильно, хоть и медленно. Предложение перейти на uv встретили настороженно - зачем ломать то, что не сломано?

Начали с пилотного проекта. Один новый микросервис, который только стартовал разработку. Никакого легаси, чистый лист. Настроили uv для этого сервиса, остальные продолжали работать с pip как обычно. Такой подход позволил команде привыкнуть к новому инструменту без риска положить продакшен. Через две недели разработчики сами попросили мигрировать остальные сервисы - скорость установки зависимостей реально ощущалась в ежедневной работе.

Постепенный переход оказался правильным решением. Резкая миграция всех проектов одновременно создала бы хаос - кто-то забыл бы обновить документацию, кто-то не установил бы uv локально, CI-пайплайны упали бы все разом. Вместо этого мигрировали по одному сервису в неделю. Каждый раз обновляли внутренние гайды, добавляли секцию в onboarding для новых разработчиков, чинили найденные проблемы. К концу второго месяца вся кодовая база работала на uv без единого серьезного инцидента.

GitHub Actions требует минимальных изменений для поддержки uv. Стандартный пайплайн с pip выглядит так:

YAML
1
2
3
4
name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
Версия с uv чуть сложнее, потому что инструмент не идет из коробки с раннерами:

YAML
1
2
3
4
5
6
7
name: Install uv
  run: curl -LsSf [url]https://astral.sh/uv/install.sh[/url] | sh
 
name: Install dependencies
  run: |
    uv venv
    uv pip install -r requirements.txt
Это добавляет шаг установки самого uv, что занимает 3-5 секунд. Но экономия на установке зависимостей перекрывает overhead с лихвой. Наш типичный джобв с тестами ускорился с 8 минут до 3.5 минут. Триггерится он при каждом пуше - экономия складывается быстро. За месяц команда сохранила примерно 60 часов машинного времени раннеров, что конвертируется в реальные деньги на счете GitHub.

Кэширование в CI требует другого подхода с uv. pip кэширует в ~/.cache/pip, uv использует ~/.cache/uv. GitHub Actions имеет actions/cache, которому нужно указать правильные пути:

YAML
1
2
3
4
5
name: Cache uv
  uses: actions/cache@v3
  with:
    path: ~/.cache/uv
    key: ${{ runner.os }}-uv-${{ hashFiles('**/requirements.txt') }}
Первый запуск после очистки кэша медленный, последующие практически мгновенные. Важный нюанс - ключ кэша должен включать не только requirements.txt, но и pyproject.toml если используется. Иначе изменения в конфигурации не инвалидируют кэш, и CI будет использовать старые зависимости. Я наступил на эти грабли в первую неделю - билд проходил зеленым, но с неправильными версиями пакетов. Локально все работало, в CI падало непонятными ошибками.

GitLab CI устроен похоже, но с нюансами. Их раннеры используют Docker-образы, которые не содержат uv по умолчанию. Можно либо установить в before_script, либо создать кастомный базовый образ. Второй вариант правильнее - не тратишь время на установку uv при каждом запуске пайплайна:

Bash
1
2
3
4
5
6
7
FROM python:3.11-slim
 
# Устанавливаем uv
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV PATH="/root/.cargo/bin:${PATH}"
 
WORKDIR /app
Этот образ пушишь в корпоративный registry, используешь в .gitlab-ci.yml как базовый. Установка зависимостей тогда выглядит просто:

YAML
1
2
3
4
5
test:
  image: registry.company.com/base-python-uv:latest
  script:
    - uv pip install -r requirements.txt
    - pytest
Docker-контейнеры для продакшена - отдельная тема. Многоступенчатые билды помогают минимизировать размер финального образа. Ставишь зависимости в builder-стадии через uv, копируешь только результат в production-образ:

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Стадия сборки
FROM python:3.11-slim as builder
 
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV PATH="/root/.cargo/bin:${PATH}"
 
WORKDIR /app
COPY requirements.txt .
RUN uv venv /opt/venv && \
    . /opt/venv/bin/activate && \
    uv pip install -r requirements.txt
 
# Финальный образ  
FROM python:3.11-slim
 
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:${PATH}"
 
WORKDIR /app
COPY . .
 
CMD ["python", "main.py"]
Размер итогового образа уменьшился на 180MB по сравнению с версией, где uv оставался в финальном слое. Плюс меньше attack surface - в production-контейнере нет инструментов сборки, только рантайм. Время билда сократилось с 4.5 минут до 2 минут благодаря параллельной установке. Docker BuildKit кэширует слои агрессивно, поэтому повторные билды еще быстрее.

Командная синхронизация оказалась проще, чем ожидалось. Все разработчики используют один и тот же requirements.txt или pyproject.toml, который коммитится в репозиторий. Неважно, кто через pip ставил зависимости, а кто через uv - результат идентичный. Единственное правило - не миксовать форматы. Если проект на requirements.txt, все используют его. Если на pyproject.toml с uv.lock - все работают через uv sync.

Проблема возникла с разработчиками на Windows. У одного товарища uv работал нестабильно - периодически падал с ошибками доступа к файлам. Оказалось, антивирус блокировал некоторые операции с виртуальным окружением, считая их подозрительными. Пришлось добавить папку проекта в исключения. На маке и линуксе таких проблем не было. Windows всегда источник неожиданностей.

Таблица команд для быстрого перехода помогла команде адаптироваться:

Code
1
2
3
4
5
6
7
8
9
10
| Операция | pip | uv |
|----------|-----|-----|
| Создать окружение | python -m venv .venv | uv venv .venv |
| Установить пакет | pip install package | uv pip install package |
| Установить из файла | pip install -r requirements.txt | uv pip install -r requirements.txt |
| Обновить пакет | pip install --upgrade package | uv pip install --upgrade package |
| Удалить пакет | pip uninstall package | uv pip uninstall package |
| Список пакетов | pip list | uv pip list |
| Заморозить зависимости | pip freeze > requirements.txt | uv pip freeze > requirements.txt |
| Проверить окружение | pip check | uv pip check |
Команды практически один-в-один, разница только в префиксе uv. Мышечная память срабатывает автоматически после пары дней использования. Новички путались первую неделю, набирая просто pip вместо uv pip, но это быстро проходило.

Интеграция с IDE потребовала настройки. PyCharm не знает про uv из коробки, но ему достаточно указать путь к интерпретатору из виртуального окружения - ~/.venv/bin/python. Автодетект работает корректно, если окружение создано через uv venv. VS Code аналогично - выбираешь интерпретатор через Command Palette, остальное работает прозрачно. Никаких плагинов или расширений не требуется, потому что uv создает стандартные venv-совместимые окружения.

Pre-commit хуки пришлось подкрутить. У нас был хук, который проверял актуальность requirements.txt через pip-compile из pip-tools. С переходом на uv этот хук стал бессмысленным - lock-файл обновляется автоматически. Заменили проверку на валидацию uv.lock - хук проверяет, что lock-файл синхронизирован с pyproject.toml:

YAML
1
2
3
4
5
6
7
repo: local
  hooks:
    - id: check-uv-lock
      name: Check uv.lock is up to date
      entry: uv lock --check
      language: system
      pass_filenames: false
Если кто-то обновил зависимости в pyproject.toml, но забыл перегенерировать lock - коммит не пройдет. Простая защита от рассинхронизации, которая сэкономила несколько часов дебага.

Приватные репозитории добавляют сложности, особенно в корпоративной среде. Многие компании держат внутренние Python-пакеты в собственных индексах - Nexus, Artifactory, AWS CodeArtifact. pip работает с ними через конфигурацию в ~/.pip/pip.conf или переменные окружения. uv поддерживает те же механизмы, но синтаксис немного отличается. Пришлось переписывать документацию для разработчиков - старые инструкции с --index-url работали, но не оптимально.

Конфигурация приватного индекса через pyproject.toml выглядит чище, чем через environment variables:

Bash
1
2
3
4
5
6
7
8
[[tool.uv.index]]
url = "https://pypi.company.com/simple"
name = "corporate"
 
[[tool.uv.index]]
url = "https://pypi.org/simple"
name = "public"
default = true
Токены аутентификации храним в переменных окружения, чтобы не коммитить credentials в репозиторий. UV_INDEX_CORPORATE_USERNAME и UV_INDEX_CORPORATE_PASSWORD подхватываются автоматически. pip требовал прописывать credentials прямо в URL или в keyring - менее безопасно. Хотя keyring integration у uv пока сырая, планируют улучшить в ближайших релизах.

Монорепозитории с несколькими Python-проектами внутри создают головную боль при управлении зависимостями. Один микросервис может зависеть от внутренней shared-библиотеки, которая тоже живет в том же репо. С pip приходилось устанавливать её через pip install -e ../shared-lib, что работало локально, но ломалось в CI. uv поддерживает workspace-концепцию, похожую на cargo workspaces в Rust:

Bash
1
2
3
4
5
6
[tool.uv.workspace]
members = [
  "services/auth",
  "services/billing", 
  "shared/common"
]
Теперь uv sync в корне репозитория устанавливает зависимости для всех проектов сразу, правильно резолвя внутренние зависимости между ними. Для больших монорепо это серьезное улучшение. Правда, функциональность пока в beta, встречал баги при сложных схемах зависимостей. В одном случае циклическая зависимость между сервисами уронила резолвер - пришлось рефакторить архитектуру проекта.

Откаты при проблемах стали проще благодаря lock-файлам в git. Что-то сломалось после обновления зависимостей? Делаешь git revert на коммит с изменениями в uv.lock, запускаешь uv sync - окружение откатывается к рабочему состоянию. С pip такой workflow требовал дисциплины вручную коммитить requirements.txt после каждого изменения, что делали не всегда. У нас был случай, когда разработчик обновил зависимости локально, забыл закоммитить requirements.txt, запушил код. CI собирал с другими версиями пакетов - тесты падали загадочными ошибками. Потратили два часа на поиск расхождения.

Документация для новых членов команды потребовала обновления. Секция "Setup development environment" теперь начинается с установки uv. Добавили troubleshooting раздел с типичными проблемами - Windows антивирус, права доступа на корпоративных макбуках, проблемы с VPN при доступе к приватному PyPI. Каждую неделю кто-то натыкался на новую проблему, фиксили и добавляли в документацию. За три месяца набралось полноценное FAQ на пять страниц.

Метрики CI/CD показали реальную экономию. Собрали статистику за квартал до миграции и квартал после. Среднее время билда сократилось с 8.2 минут до 3.7 минут. Количество фейлов из-за проблем с зависимостями упало на 40% - воспроизводимость через lock-файлы сработала. Стоимость раннеров GitHub Actions снизилась на $230 в месяц - не огромная сумма, но приятный бонус. Главное - разработчики перестали жаловаться на медленный CI. Психологический эффект важнее денег.

Масштабирование на десятки репозиториев выявило организационные сложности. Одновременная миграция всех проектов нереальна - слишком много ручной работы. Приоритизировали активно развивающиеся репозитории, где экономия времени максимальна. Легаси-проекты в maintenance mode оставили на pip - не было смысла тратить ресурсы на миграцию кода, который почти не трогают. Гибридная ситуация вполне работает, главное документировать какой проект на чем.

Примеры использования обоих инструментов



Нажмите на изображение для увеличения
Название: uv или pip управление пакетами и зависимостями Python 4.jpg
Просмотров: 50
Размер:	187.0 Кб
ID:	11297

Начну с самого распространенного сценария - старт нового Django-проекта. С pip процесс выглядит привычно, но занимает время. Создаешь директорию, инициализируешь venv, активируешь окружение, ставишь Django:

Bash
1
2
3
4
5
6
7
# Классический подход с pip
mkdir myproject && cd myproject
python -m venv .venv
source .venv/bin/activate
pip install django psycopg2-binary python-dotenv
pip freeze > requirements.txt
django-admin startproject config .
Весь процесс занимает около двух минут. Большую часть времени pip качает Django со всеми зависимостями последовательно. Если забыл сделать freeze - потом придется вспоминать, какие пакеты ставил изначально.
С uv тот же workflow сжимается до нескольких секунд:

Bash
1
2
3
4
5
# Современный подход с uv
mkdir myproject && cd myproject
uv init
uv add django psycopg2-binary python-dotenv
django-admin startproject config .
Команда uv init создает и pyproject.toml, и виртуальное окружение одновременно. uv add устанавливает пакеты и автоматически обновляет конфигурацию с lock-файлом. Никаких ручных манипуляций с requirements.txt. Через десять секунд готов к разработке. Когда делаешь это по пять раз на день, экономия времени ощутима.

Data science проекты требуют тяжелых библиотек. Jupyter notebook с pandas, matplotlib, scikit-learn - стандартный набор для анализа данных. pip мучается с numpy минут пять, потому что тот тянет за собой компиляцию C-расширений даже при наличии wheels:

Bash
1
2
3
4
5
6
# pip подход для data science
python -m venv ds_env
source ds_env/bin/activate
pip install jupyter pandas matplotlib scikit-learn seaborn
# Ждем 4-5 минут...
jupyter notebook
uv справляется быстрее благодаря параллельной загрузке бинарников:

Bash
1
2
3
4
5
6
# uv подход для data science
uv venv ds_env
source ds_env/bin/activate
uv pip install jupyter pandas matplotlib scikit-learn seaborn
# Готово через 15 секунд
jupyter notebook
Прошлой весной помогал знакомой датасаентистке настроить окружение для машинного обучения. Tensorflow, keras, pytorch - монстры весом в гигабайты. На её ноутбуке с медленным диском установка через pip растянулась на полчаса. После переключения на uv то же окружение развернулось за четыре минуты. Она была в шоке - привыкла заваривать чай пока pip работает.

Микросервисная архитектура создает специфические требования. Каждый сервис - отдельный репозиторий со своими зависимостями. Типичный REST API на FastAPI с базовыми библиотеками:

Bash
1
2
3
4
5
6
7
8
9
10
# Базовый FastAPI сервис с pip
mkdir auth-service && cd auth-service
python -m venv .venv && source .venv/bin/activate
pip install "fastapi[all]" sqlalchemy alembic pydantic-settings
pip freeze > requirements.txt
 
# Тот же сервис с uv
mkdir auth-service && cd auth-service  
uv init --name auth-service
uv add "fastapi[all]" sqlalchemy alembic pydantic-settings
Обратите внимание - uv автоматически создает правильную структуру pyproject.toml с метаданными проекта. pip требует либо ручного создания файла, либо использования setuptools/poetry для инициализации. Мелочь, но приятная.
Обновление зависимостей - ежедневная задача разработчика. Вышла новая версия библиотеки с исправлением бага или security патчем. С pip процесс требует внимательности:

Bash
1
2
3
4
5
# Обновление через pip
pip install --upgrade fastapi
pip freeze > requirements.txt  # Не забыть!
git add requirements.txt
git commit -m "Update fastapi to 0.109.0"
Легко забыть сделать freeze после обновления. Я сталкивался с ситуацией, когда разработчик апгрейдил пакет локально, но не закоммитил изменения в requirements.txt. CI собирал со старой версией - тесты падали непонятными ошибками. Два часа ушло на поиск расхождения.
uv исключает человеческий фактор:

Bash
1
2
3
4
5
# Обновление через uv
uv add --upgrade fastapi
# Lock-файл обновлен автоматически
git add pyproject.toml uv.lock
git commit -m "Update fastapi to 0.109.0"
Команда uv add --upgrade обновляет и пакет, и lock-файл, и pyproject.toml одной операцией. Забыть что-то невозможно - или обновляешь все сразу, или не обновляешь вообще.
Воспроизведение проблемного окружения - частая задача при дебаге. Пользователь сообщает о баге, но локально не воспроизводится. Смотришь его версии зависимостей, пытаешься создать идентичное окружение. С pip это квест:

Bash
1
2
3
4
5
# Пытаемся воспроизвести окружение пользователя
python -m venv debug_env && source debug_env/bin/activate
pip install -r user_requirements.txt
# Может установиться с другими транзитивными зависимостями
# Если user_requirements.txt неполный - вообще непонятно что установлено
uv гарантирует полную воспроизводимость:

Bash
1
2
3
4
5
  
# Точное воспроизведение через uv
uv venv debug_env && source debug_env/bin/activate
uv sync --frozen  # Установит точно версии из uv.lock
# Окружение byte-in-byte идентично исходному
Флаг --frozen запрещает uv менять версии даже если вышли обновления. Получаешь ровно то окружение, с которым работал пользователь. Баг воспроизвелся - значит дело не в версиях зависимостей. Не воспроизвелся - смотришь дальше, но хотя бы исключил один вариант надежно.
Работа с несколькими версиями Python одновременно - боль разработчика библиотек. Нужно протестировать код на Python 3.9, 3.10, 3.11, 3.12. С pip приходится создавать отдельные venv для каждой версии вручную:

Bash
1
2
3
4
5
6
7
8
9
10
# Тестирование на разных версиях через pip
python3.9 -m venv .venv39 && source .venv39/bin/activate
pip install -r requirements.txt && pytest
deactivate
 
python3.10 -m venv .venv310 && source .venv310/bin/activate  
pip install -r requirements.txt && pytest
deactivate
 
# И так далее для каждой версии...
Скучно, монотонно, занимает время. uv упрощает задачу через опцию --python:

Bash
1
2
3
4
5
6
7
# Тестирование на разных версиях через uv
for py_version in 3.9 3.10 3.11 3.12; do
  uv venv .venv${py_version//.} --python $py_version
  source .venv${py_version//.}/bin/activate
  uv pip install -r requirements.txt && pytest
  deactivate
done
Можно завернуть в скрипт и прогонять одной командой. uv сам найдет нужную версию Python в системе. Правда, версии должны быть предустановлены - pyenv или system package manager. uv не скачивает Python автоматически, хотя разработчики обещали добавить эту функцию.
Разработка библиотеки для публикации на PyPI имеет свои нюансы. Нужно не только управлять зависимостями, но и правильно описать метаданные, собрать wheel-файл. С pip полагаешься на setuptools:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
# setup.py для pip-based workflow
from setuptools import setup, find_packages
 
setup(
    name="mylib",
    version="0.1.0",
    packages=find_packages(),
    install_requires=[
        "requests>=2.28.0",
        "pydantic>=2.0.0",
    ],
    python_requires=">=3.9",
)
Плюс нужен отдельный инструмент для сборки - build или twine. Два шага вместо одного.

uv интегрирован с современным стандартом PEP 621, все в pyproject.toml:

Bash
1
2
3
4
5
6
7
8
9
10
11
12
[project]
name = "mylib"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = [
    "requests>=2.28.0",
    "pydantic>=2.0.0",
]
 
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Команда uv build соберет wheel и source distribution в одну операцию. Проще, современнее, меньше магии.
Временные тестовые окружения - еще один частый кейс. Хочешь быстро проверить новую библиотеку, не засоряя основное окружение. Создаешь одноразовый venv, ставишь пакет, экспериментируешь, удаляешь:

Bash
1
2
3
4
5
6
7
8
9
10
11
# Быстрый тест с pip - медленно
python -m venv /tmp/test_env && source /tmp/test_env/bin/activate
pip install some-new-library
python -c "import some_new_library; print(some_new_library.__version__)"
deactivate && rm -rf /tmp/test_env
 
# Быстрый тест с uv - действительно быстро  
uv venv /tmp/test_env && source /tmp/test_env/bin/activate
uv pip install some-new-library
python -c "import some_new_library; print(some_new_library.__version__)"
deactivate && rm -rf /tmp/test_env
С pip весь цикл занимает минуту-две. С uv - секунд десять. Когда проверяешь пять разных библиотек подряд, разница чувствуется.
Конфликты зависимостей разрешаются по-разному. Реальный случай: проект требует pandas 1.5.x для совместимости со старым кодом, но новая библиотека для работы с API хочет pandas >= 2.0. pip пробует найти компромисс и обычно фейлится:

Bash
1
2
3
4
# pip не может найти решение
$ pip install "pandas<2.0" some-api-wrapper
ERROR: Cannot install pandas<2.0 and some-api-wrapper because these package 
versions have conflicting dependencies.
uv выдает более информативную ошибку с конкретной цепочкой конфликта:

Bash
1
2
3
4
5
  
$ uv pip install "pandas<2.0" some-api-wrapper
error: Failed to resolve requirements
  some-api-wrapper 1.2.0 requires pandas>=2.0.0
  but you requested pandas<2.0
Сразу понятно - либо апгрейдим pandas и рефакторим код, либо ищем альтернативу some-api-wrapper. Нет многостраничного вывода с backtracing всех попыток резолвинга.
Разделение dev и production зависимостей критично для правильной организации проекта. Тестовые фреймворки, линтеры, type checkers нужны при разработке, но занимают место в продакшене. С pip традиционно создают два файла:

Bash
1
2
3
4
5
6
7
8
9
10
# pip подход с двумя файлами
# requirements.txt - продакшен
django==5.0
psycopg2-binary==2.9.9
 
# requirements-dev.txt - разработка
-r requirements.txt
pytest==7.4.3
black==23.12.0
mypy==1.7.1
Придется помнить устанавливать dev-зависимости отдельной командой. Забыл - получишь загадочные ошибки при запуске тестов локально.
uv делает разделение нативно через pyproject.toml:

Bash
1
2
3
4
5
6
7
8
9
10
11
12
[project]
dependencies = [
    "django>=5.0",
    "psycopg2-binary>=2.9",
]
 
[project.optional-dependencies]
dev = [
    "pytest>=7.4",
    "black>=23.12",
    "mypy>=1.7",
]
Теперь uv sync ставит только продакшен-зависимости, а uv sync --extra dev добавляет development-пакеты. В Dockerfile для продакшена используешь первую команду - образ остается компактным. Локально разработчики ставят с флагом --extra dev - все инструменты доступны.
Монорепозитории с shared-библиотеками между микросервисами создают зависимости внутри одного репозитория. Классический подход с pip требует editable installs:

Bash
1
2
3
4
5
6
7
# Структура монорепо
# /shared/common - общая библиотека
[H2]/services/api - микросервис использующий common[/H2]
 
cd services/api
pip install -e ../../shared/common
pip install -r requirements.txt
Работает локально, но в CI нужны дополнительные шаги для правильной установки. uv поддерживает workspace-концепцию через конфигурацию в корневом pyproject.toml - все зависимости резолвятся корректно автоматически. Правда функциональность еще сырая, в сложных кейсах встречал баги.
Автоматизация через Makefile упрощает жизнь команде. Вместо запоминания длинных команд с флагами создаешь простые таргеты:

Bash
1
2
3
4
5
6
7
8
9
10
11
.PHONY: install test lint
 
install:
    uv sync --extra dev
 
test:
    pytest tests/
 
lint:
    black --check .
    mypy src/
Теперь новый разработчик запускает make install и получает готовое окружение. make test прогоняет тесты. Никаких вопросов какие флаги передавать, какие команды в каком порядке. Документация в виде работающего кода.

Установка django apt-get\pip или python не той версии
Здравствуйте , скажите пожалуйста я пробую установить Django через pip у меня не получается...

"pip" не является внутренней или внешней командой, исполняемой программой или пакетным файлом
Всем привет! помогите пожалуйста хочу установить pytelegrambotapi через cmd , но не полуается...

"pip" не является внутренней или внешней командой, исполняемой программой или пакетным файлом в pycharm
Добрый день, ломаю голову над ошибкой уже 3 дня! Когда пишу “pip” в cmd - все нормально работает,...

Pip не является внутренней или внешней командой исполняемой программой или пакетным файлом
Запускаю в консоли pip install, выдаёт ошибку. windows 8, переустановка не работает

Pip в python
Расскажите как пользоваться pip? С какой то там версии pip устанавливается автоматически, у меня...

Ошибка при использовании pip в Python 2.7
Приветствую всех. Используется ОС Windows 7 64 professional, Python 2.7.11. При попытке загрузить...

Python 3.5, pip и virtualenv
Здравствуйте! Поскольку я лишь недавно приступил у изучению Python, то споткнулся о вопрос,...

Python 3 pip добавить модуль
В терминале запускаю команду pip install numpy получаю сообщение что у меня недостаточно прав и...

Cпособы миграции экземпляра python с модулями (без pip install)?
Здравствуйте. У меня есть виртуальная ОС Windows 2012 R2, где установлен python 3.6 + масса...

Pip на Python
Новичок. Решил начать изучение Python'а. Установил Atom для комфортного написания кода. Установил...

Pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available
делаю pip3 install -r requirements.txt pip is configured with locations that require...

Не работает pip --version после создания виртуального окружения python в linux
Создал виртуальное окружение в Linux, теперь не работает команда: pip --version Выводит вот это:...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
PowerShell Snippets
iNNOKENTIY21 11.11.2025
Модуль PowerShell 5. 1+ : Snippets. psm1 У меня модуль расположен в пользовательской папке модулей, по умолчанию: \Documents\WindowsPowerShell\Modules\Snippets\ А в самом низу файла-профиля. . .
PowerShell и онлайн сервисы. Валюта (floatrates.com руб.)
iNNOKENTIY21 11.11.2025
PowerShell функция floatrates-rub Примеры вызова: # Указанная валюта 'EUR' floatrates-rub -Code 'EUR' # Список имеющихся кодов валют floatrates-rub -Available function floatrates-rub {
PowerShell и онлайн сервисы. Погода (RP5.ru)
iNNOKENTIY21 11.11.2025
PowerShell функция Get-WeatherRP5rss для получения погоды с сервиса RP5 Примеры вызова Get-WeatherRP5rss с указанием id 5484 — Москва (восток, Измайлово) и переносом строки:. . .
PowerShell и онлайн сервисы. Погода (wttr)
iNNOKENTIY21 11.11.2025
PowerShell Функция для получения погоды с сервиса wttr Примеры вызова: Погода в городе Омск с прогнозом на день, можно изменить прогноз на более дней, для этого надо поменять запрос:. . .
PowerShell и онлайн сервисы. Валюта (ЦБР)
iNNOKENTIY21 11.11.2025
# Получение курса валют function cbr (] $Valutes = @('USD', 'EUR', 'CNY')) { $url = 'https:/ / www. cbr-xml-daily. ru/ daily_json. js' $data = Invoke-RestMethod -Uri $url $esc = 27 . . .
И решил я переделать этот ноут в машину для распределенных вычислений
Programma_Boinc 09.11.2025
И решил я переделать этот ноут в машину для распределенных вычислений Всем привет. А вот мой компьютер, переделанный из ноутбука. Был у меня ноут асус 2011 года. Со временем корпус превратился. . .
Мысли в слух
kumehtar 07.11.2025
Заметил среди людей, что по-настоящему верная дружба бывает между теми, с кем нечего делить.
Новая зверюга
volvo 07.11.2025
Подарок на Хеллоуин, и теперь у нас кроме Tuxedo Cat есть еще и щенок далматинца: Хочу еще Симбу взять, очень нравится. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru