Python в Rye-у

5eef1b36b07874f5a856748180dcfaae.jpg

Rye — это пакетный менеджер для Python, написанный на Rust. Но Rye — это не только пакетный менеджер, но и удобный инструмент, который позволяет управлять проектами, зависимостями, виртуальными окружениями и версиями Python. Под капотом у Rye находится uv — более быстрый аналог pip, который, как и Rye, написан на Rust. Автором является небезызвестный Armin Ronacher.

Цель данной статьи провести краткое знакомство с новым инструментом управления зависимостями и проектами на Python.

Установка и настройка окружения

На macOS Rye можно установить через brew, но есть вероятность, что будет немного устаревшая версия. Другой способ установки:

curl -sSf https://rye.astral.sh/get | bash

Это универсальный способ, который будет работать и на macOS, и в Linux.
Я рекомендую устанавливать именно таким способом. И не только потому, что версия посвежее, но и потому, что в процессе такой установки будет возможность выбрать некоторые настройки. Например: использовать uv или pip для установки пакетов, заменять ли Python в системе или оставить как есть и т.д.

Стоит ещё отметить, что за собой Rye тащит сборку Python. А именно, используются вот эти вот готовые сборки:  https://github.com/indygreg/python-build-standalone. Для людей с паранойей это может быть определяющим моментом в вопросе использовать Rye или нет. Подробнее про этот аспект можно почитать по ссылке:  https://rye.astral.sh/guide/toolchains/cpython/

В завершении установки установщик спросит нужно ли добавить соурсинг прокладок. Правда, в моём случае установщик ошибся и записал соурсинг прокладок в .profile. Пришлось исправлять это самому

echo 'source "$HOME/.rye/env"' >> ~/.zshrc

Имейте в виду, что после установки и активации прокладок будет использовать тот Python, который закачает Rye, а не системный

% which python
/Users/max/.rye/shims/python

Для удобства добавим комплишены

rye self completion -s zsh > ~/.zfunc/_rye

Надо убедиться, что .zfunc существует и добавлен в FPATH

Знакомство

Для начала надо инициализировать проект

% rye init rye-test
success: Initialized project in /Users/max/work/rye-test
  Run `rye sync` to get started

Посмотрим что получилось

% cd rye-test
% ls -la
total 32
drwxr-xr-x   9 max  staff   288 Sep  3 10:40 .
drwxr-xr-x  69 max  staff  2208 Sep  3 10:37 ..
drwxr-xr-x   9 max  staff   288 Sep  3 10:37 .git
-rw-r--r--   1 max  staff    93 Sep  3 10:37 .gitignore
-rw-r--r--   1 max  staff     7 Sep  3 10:37 .python-version
drwxr-xr-x@  8 max  staff   256 Sep  3 10:40 .venv
-rw-r--r--   1 max  staff    40 Sep  3 10:37 README.md
-rw-r--r--   1 max  staff   469 Sep  3 10:37 pyproject.toml
drwxr-xr-x   3 max  staff    96 Sep  3 10:37 src

Можно заметить, что Rye создал всё необходимое для работы с проектом.

А вот что в pyproject.toml

[project]
name = "rye-test"
version = "0.1.0"
description = "Add your description here"
authors = [
    { name = "Maksim Piatyshev", email = "max@wectory.com" }
]
dependencies = []
readme = "README.md"
requires-python = ">= 3.8"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.rye]
managed = true
dev-dependencies = []

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src/rye_test"]

Сразу после создания можно запустить rye sync, который синхронизирует проект и создаст лок-файлы зависимостей.

Теперь всё готово для того, чтобы начать заводить себе новые зависимости

% rye add pyramid
Added pyramid>=2.0.2 as regular dependency
Reusing already existing virtualenv
Generating production lockfile: /Users/max/work/rye-test/requirements.lock
Generating dev lockfile: /Users/max/work/rye-test/requirements-dev.lock
Installing dependencies
Resolved 12 packages in 4ms
   Built rye-test @ file:///Users/max/work/rye-test
Prepared 12 packages in 1.34s
Uninstalled 1 package in 0.62ms
Installed 12 packages in 11ms
 + hupper==1.12.1
 + pastedeploy==3.1.0
 + plaster==1.1.2
 + plaster-pastedeploy==1.0.1
 + pyramid==2.0.2
 ~ rye-test==0.1.0 (from file:///Users/max/work/rye-test)
 + setuptools==74.1.0
 + translationstring==1.4
 + venusian==3.1.0
 + webob==1.8.8
 + zope-deprecation==5.0
 + zope-interface==7.0.3
Done!

Установка прошла, действительно, быстро и просто. После этого с проектом и его зависимостями можно работать как обычно — например, написать какой-то рабочий и полезный код без багов или что такое. Ну вы и сами всё знаете.

Интересности в Rye

Пока не было ничего необычного. Но у Rye есть кое-что…

Прокладки (shims)

После установки Rye предоставляет прокладки для python и python3. Эти прокладки работают так, что если мы не находимся в проекте под присмотром Rye, то будет запускаться системный Python, а если в проекте — Python из виртуального окружения проекта даже, если мы не делали его активацию. Это может быть полезно для тех, кто забывает активировать виртуальное окружение перед работай над проектом.
Дополнительно можно настроить Rye так, чтобы за пределами проекта прокладка подставляла не системный Python, а тот, что Rye приносит с собой во время установки

rye config --set-bool behavior.global-python=true

Причём, это никак не повлияет на что-то в системе, что зависит от системного Python.

https://rye.astral.sh/guide/shims/

Рабочие окружения (workspaces)

Рабочие окружения в Rye позволяют объединить несколько проектов и использовать одно общее виртуальное окружение для них. Это полезно для проектов в монорепозитории, где несколько пакетов или сервисов используют одни и те же зависимости — снижает накладные расходы на создание и управление зависимостями в нескольких проектах, обеспечивает согласованность версий и минимизирует конфликты зависимостей.

Чтобы включить эту возможность нужно в самом верхнем проекте прописать блок tool.rye.workspace

[tool.rye]
virtual = true

[tool.rye.workspace]
members = ["myname-*"]

Так все проекты, которые подпадают под шаблон myname-* станут частью данного workspace.

Необязательная опция virtual = true говорит, что топовый проект виртуальный в терминологии Rye — не устанавливаемый Python-пакет. Такой проект только содержит базовые настройки и зависимости и не будет установливаться как Python-пакет при деплое (не попадёт в requirements.lock). Вместо этого он служит только для хранения настроек рабочего окружения и управления зависимостями. Хотя, топовый проект worspace и не обязан быть виртуальным.

Рассмотрим на примере как сделать workspace.

Инициализация топового проекта с ключом виртуальности

rye init --virtual rye-test-workspace

Добавим признак рабочего окружения в pyproject.toml

...

[tool.rye.workspace]
members = ["rye-test-*"]

Опция members позволяет ограничить каталоги по шаблону, которые будут считаться частью нашего рабочего окружения.

Синканём

% rye sync
Initializing new virtualenv in /Users/max/work/rye-test-workspace/.venv
Python version: cpython@3.12.5
Generating production lockfile: /Users/max/work/rye-test-workspace/requirements.lock
Generating dev lockfile: /Users/max/work/rye-test-workspace/requirements-dev.lock
Installing dependencies
warning: Requirements file requirements-dev.lock does not contain any dependencies
No requirements found (hint: use `--allow-empty-requirements` to clear the environment)
Done!

Рабочее окружение готово к добавлению проектов

% rye init rye-test-1
% rye init rye-test-2

Посмотрим что внутри

% ls -la rye-test-*
rye-test-1:
total 32
drwxr-xr-x   7 max  staff  224 Sep  4 18:23 .
drwxr-xr-x  12 max  staff  384 Sep  4 18:23 ..
-rw-r--r--   1 max  staff   93 Sep  4 18:23 .gitignore
-rw-r--r--   1 max  staff    7 Sep  4 18:23 .python-version
-rw-r--r--   1 max  staff   42 Sep  4 18:23 README.md
-rw-r--r--   1 max  staff  474 Sep  4 18:23 pyproject.toml
drwxr-xr-x   3 max  staff   96 Sep  4 18:23 src

rye-test-2:
total 32
drwxr-xr-x   7 max  staff  224 Sep  4 18:23 .
drwxr-xr-x  12 max  staff  384 Sep  4 18:23 ..
-rw-r--r--   1 max  staff   93 Sep  4 18:23 .gitignore
-rw-r--r--   1 max  staff    7 Sep  4 18:23 .python-version
-rw-r--r--   1 max  staff   42 Sep  4 18:23 README.md
-rw-r--r--   1 max  staff  474 Sep  4 18:23 pyproject.toml
drwxr-xr-x   3 max  staff   96 Sep  4 18:23 src

Теперь давайте сделаем синк в одном из проектов

% cd rye-test-1
% rye sync
Reusing already existing virtualenv
Generating production lockfile: /Users/max/work/rye-test-workspace/requirements.lock
Generating dev lockfile: /Users/max/work/rye-test-workspace/requirements-dev.lock
Installing dependencies
Resolved 2 packages in 1ms
   Built rye-test-1 @ file:///Users/max/work/rye-test-workspace/rye-test-1
   Built rye-test-2 @ file:///Users/max/work/rye-test-workspace/rye-test-2
Prepared 2 packages in 1.91s
Installed 2 packages in 1ms
 + rye-test-1==0.1.0 (from file:///Users/max/work/rye-test-workspace/rye-test-1)
 + rye-test-2==0.1.0 (from file:///Users/max/work/rye-test-workspace/rye-test-2)
Done!

Видно, что Rye обнаружил уже существующее виртуальное окружение в проекте-родителе и не стал создавать его заного. Кроме этого Rye обнаружил второй проект и синканул и его тоже.

Попробуем добавить зависимость в текущий проект

% rye add lxml==5.2.0
Added lxml==5.2 as regular dependency
Reusing already existing virtualenv
Generating production lockfile: /Users/max/work/rye-test-workspace/requirements.lock
Generating dev lockfile: /Users/max/work/rye-test-workspace/requirements-dev.lock
Installing dependencies
Resolved 3 packages in 2ms
   Built rye-test-1 @ file:///Users/max/work/rye-test-workspace/rye-test-1
Prepared 1 package in 169ms
Uninstalled 1 package in 0.57ms
Installed 2 packages in 2ms
 + lxml==5.2.0
 ~ rye-test-1==0.1.0 (from file:///Users/max/work/rye-test-workspace/rye-test-1)
Done!

Пакет lxml был добавлен не только в зависимости проекта rye-test-1, но в общее виртуальное окружение.
Теперь, если вдруг нам понадобился lxml в проекте rye-test-2 и мы решили его туда добавить, то будет использован уже добавленый в rye-test-1 пакет.
А вот, если в rye-test-2 мы решили установить пакет версии посвежее, то Rye нам сообщит о конфликте версий в зависимостях между проектами и не даст этого сделать (хотя список зависимостей rye-test-2 будет обновлён)

% cd ../rye-test-2
% rye add lxml==5.3.0
Added lxml==5.3.0 as regular dependency
Reusing already existing virtualenv
Generating production lockfile: /Users/max/work/rye-test-workspace/requirements.lock
  × No solution found when resolving dependencies:
  ╰─▶ Because you require lxml==5.2 and lxml==5.3.0, we can conclude that your requirements are unsatisfiable.
error: could not write production lockfile for workspace

Caused by:
    Failed to run uv compile /var/folders/dw/vk1bw2wd18zdpgs2m8lfzw4r0000gn/T/.tmpOcYdri/requirements.txt. uv exited with status: exit status: 1

К сожалению, Rye не говорит в каком проекте искать устаревшую зависимость. Но есть один способ это узнать

% cat ../requirements.lock
# generated by rye
# use `rye lock` or `rye sync` to update this lockfile
#
# last locked with the following flags:
#   pre: false
#   features: []
#   all-features: false
#   with-sources: false
#   generate-hashes: false
#   universal: false

-e file:rye-test-1
-e file:rye-test-2
lxml==5.2.0
    # via rye-test-1

Здесь сразу становится понятно, что устаревшая зависимость находится в rye-test-1.

Заключение по рабочим окружениям в Rye

Рабочие окружения в Rye — это мощный инструмент для разработки в монорепозиториях и мультипроектных средах. Они позволяют эффективно управлять зависимостями, используя общее виртуальное окружение для всех проектов внутри workspace. Это устраняет дублирование пакетов, сокращает время на настройку и синхронизацию окружений, а также уменьшает объем занимаемого пространства.

Кроме того, Rye помогает отслеживать и предотвращать конфликты версий: если какая-либо библиотека уже установлена в одной из частей workspace, и вы пытаетесь добавить её с несовместимой версией в другом проекте, Rye заблаговременно сообщит о проблеме. Это обеспечивает согласованность и предсказуемость зависимостей в масштабах всего рабочего окружения.

Таким образом, использование workspace особенно удобно в следующих сценариях:

  • Разработка в больших командах, где каждый проект имеет много общих зависимостей.

  • Работа с микросервисами или библиотеками внутри одной кодовой базы (монорепозитория), где важно поддерживать согласованные версии пакетов.

  • Проекты, где важно минимизировать время установки и обновления зависимостей и избежать возможных ошибок, связанных с несовместимостью версий.

Если у вас есть такие задачи, рабочие окружения в Rye могут значительно упростить процесс разработки и сделать управление зависимостями более прозрачным и удобным.

https://rye.astral.sh/guide/workspaces/

Заключение

Мы рассмотрели лишь базовые возможности Rye и одну из его ключевых особенностей — рабочие окружения. За рамками статьи остались такие важные аспекты, как упаковка проектов в Docker-образы, управление устанавливаемыми инструментами, автоматическая сборка и публикация пакетов, а также интеграция с CI/CD. Тем не менее, даже эти примеры показывают, насколько Rye отличается от других инструментов управления зависимостями в Python. Его легковесность, производительность и возможность управления рабочими окружениями делают Rye перспективным инструментом для тех, кто ищет более эффективный и гибкий способ организации Python-проектов. Использование Rye может стать отличным решением как для небольших команд, так и для крупных проектов, где важны согласованность зависимостей, скорость работы и удобство управления версиями Python.

Rye всё ещё находится в активной разработке, и его возможности продолжают расширяться. В будущем, он вполне может стать стандартным выбором для Python-разработчиков, особенно для тех, кто ценит производительность и простоту в настройке рабочих окружений. Если вы ещё не пробовали Rye, возможно, самое время протестировать его на небольшом проекте и оценить, насколько он подходит под ваши нужды.

Полезные ссылки:
https://rye.astral.sh/guide/
https://docs.astral.sh/uv/

© Habrahabr.ru