Python in Rye

Rye — is a Python package manager written in Rust. But Rye is not only a package manager, but also a convenient tool that allows you to manage projects, dependencies, virtual environments, and Python versions. Under the hood, Rye has uv — a faster analogue of pip, which, like Rye, is written in Rust. The author is the well-known Armin Ronacher.

The purpose of this article is to provide a brief introduction to the new dependency and project management tool for Python.

Installation and configuration of the environment

On macOS, Rye can be installed via brew, but there is a chance that it will be a slightly outdated version. Another way to install:

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

This is a universal method that will work on both macOS and Linux.
I recommend installing it this way. Not only because the version is newer, but also because during this installation you will be able to choose some settings. For example: use uv or pip to install packages, replace Python in the system or leave it as is, etc.

It is also worth noting that Rye brings a Python build with it. Specifically, these ready-made builds are used: https://github.com/indygreg/python-build-standalone. For people with paranoia, this can be a deciding factor in whether to use Rye or not. You can read more about this aspect at the link: https://rye.astral.sh/guide/toolchains/cpython/

At the end of the installation, the installer will ask whether it is necessary to add the sourcing of the gaskets. However, in my case, the installer made a mistake and wrote the sourcing of the gaskets in .profile. I had to fix it myself.

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

Please note that after installing and activating the shims, the Python that Rye downloads will be used, not the system one.

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

For convenience, we will add complies

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

We need to make sure that .zfunc exists and is added to FPATH

Acquaintance

First, you need to initialize the project

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

Let's see what happens

% 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

You can see that Rye has created everything necessary to work with the project.

And here's what's in 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"]

You can run it immediately after creation. rye syncwhich will synchronize the project and create dependency lock files.

Now everything is ready to start creating new addictions.

% 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!

The installation was really quick and easy. After that, you can work with the project and its dependencies as usual – for example, write some working and useful code without bugs or whatever. Well, you know everything yourself.

Interesting things in Rye

Nothing out of the ordinary so far. But Rye has something…

Shims

After installation, Rye provides shims for python and python3. These shims work so that if we are not in a project under Rye's supervision, the system Python will be launched, and if in a project – Python from the project's virtual environment, even if we did not activate it. This can be useful for those who forget to activate the virtual environment before working on a project.
Additionally, you can configure Rye so that outside the project the shim substitutes not the system Python, but the one that Rye brings with it during installation.

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

Moreover, this will not affect anything in the system that depends on the system Python.

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

Workspaces

Workspaces in Rye allow you to combine multiple projects and use a single shared virtual environment for them. This is useful for projects in a monorepo where multiple packages or services use the same dependencies – it reduces the overhead of creating and managing dependencies across multiple projects, ensures version consistency, and minimizes dependency conflicts.

To enable this feature, you need to register a block in the topmost project tool.rye.workspace

[tool.rye]
virtual = true

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

So all projects that fall under the template myname-* will become part of this workspace.

Optional option virtual = true says that the top project is virtual in Rye terminology – a non-installable Python package. Such a project only contains basic settings and dependencies and will not be installed as a Python package during deployment (it will not get into requirements.lock). Instead, it serves only to store settings for the working environment and manage dependencies. Although, the top worspace project does not have to be virtual.

Let's look at an example of how to make a workspace.

Initialization of the top project with the virtual key

rye init --virtual rye-test-workspace

Let's add a working environment flag to pyproject.toml

...

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

Option members allows us to restrict directories by pattern that will be considered part of our working environment.

Sinkanem

% 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!

The work environment is ready to add projects

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

Let's see what's inside

% 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

Now let's make a sync in one of the projects

% 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!

It is clear that Rye detected an existing virtual environment in the parent project and did not create it anew. In addition, Rye detected a second project and synched it as well.

Let's try to add a dependency to the current project

% 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!

Plastic bag lxml was added not only to the project dependencies rye-test-1but in a common virtual environment.
Now, if suddenly we needed it lxml in the project rye-test-2 and we decided to add it there, then the one already added will be used rye-test-1 plastic bag.
But if in rye-test-2 we decided to install a package of a newer version, then Rye will inform us about a version conflict in the dependencies between projects and will not allow us to do this (although the list of dependencies rye-test-2 will be updated)

% 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

Unfortunately, Rye doesn't tell you which project to look for the outdated dependency in. But there is one way to find out.

% 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

Here it immediately becomes clear that the outdated dependency is in rye-test-1.

Conclusion on Work Environments in Rye

Workspaces in Rye are a powerful tool for developing in monorepositories and multi-project environments. They allow you to efficiently manage dependencies by using a common virtual environment for all projects within workspace. This eliminates duplication of packages, reduces the time spent on setting up and synchronizing environments, and reduces the amount of space occupied.

In addition, Rye helps track and prevent version conflicts: if a library is already installed in one of the parts workspaceand you try to add it with an incompatible version in another project, Rye will proactively report the problem. This ensures that dependencies are consistent and predictable across the entire work environment.

Thus, the use workspace especially useful in the following scenarios:

  • Development in large teams, where each project has many common dependencies.

  • Working with microservices or libraries within a single codebase (monorepo), where it is important to maintain consistent versions of packages.

  • Projects where it is important to minimize the time of installation and updating dependencies and avoid possible errors related to version incompatibility.

If you have such tasks, working environments in Rye can greatly simplify the development process and make dependency management more transparent and convenient.

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

Conclusion

We have covered only the basic capabilities of Rye and one of its key features — work environments. Such important aspects as packaging projects into Docker images, managing installed tools, automatically building and publishing packages, and CI/CD integration were left out of the article. However, even these examples show how different Rye is from other Python dependency management tools. Its lightweight nature, performance, and ability to manage work environments make Rye a promising tool for those looking for a more efficient and flexible way to organize Python projects. Using Rye can be a great solution for both small teams and large projects where dependency consistency, speed, and ease of Python version control are important.

Rye is still under active development, and its capabilities continue to expand. In the future, it may well become the standard choice for Python developers, especially those who value productivity and ease of setting up work environments. If you haven’t tried Rye yet, it might be time to test it out on a small project and see how it suits your needs.

Useful links:
https://rye.astral.sh/guide/
https://docs.astral.sh/uv/

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *