Ryeに見る自己完結型Pythonとライセンスの話

Ryeに見る自己完結型Pythonとライセンスの話

はじめに

Pythonの開発において、pyenvによるバージョンの切り替えと、Poetryによるプロジェクト管理の組み合わせを使用されている方は多いかと思います。


そんな中、ワンストップなプロジェクト・パッケージ管理ツールとして近年登場し、話題となっているのがRyeです。
rye-up.com
RyeはFlaskを生み出したArmin Ronacher氏の作成したツールで、現在はRuffやuvの製作元であるAstral配下のプロジェクトとなっています。Ruffやuv同様にRustで実装されたツールであり、高速で動作します。


既にこれらのツールを試されてる方もいらっしゃるでしょう。筆者も一部の検証や個人的な開発でRyeを試しており、他のツールとの違いや使い勝手を確認しているところです。


今回はそんな中で見られた、pyenvでインストールされたPythonとRyeでインストールされたPythonの違いと、要因となるライセンスの話を紹介したいと思います。

検証環境

WSL2上に用意した検証用の環境を使用します。

PS > wsl -v
WSL バージョン: 2.1.5.0
カーネル バージョン: 5.15.146.1-2
WSLg バージョン: 1.0.60
MSRDC バージョン: 1.2.5105
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windows バージョン: 10.0.22621.3007

Ubuntu 22.04のWSL用イメージを使用し、pyenv + Poetryをインストールする環境と、Ryeをインストールする環境をそれぞれ別のディストリビューションとして用意します。

PS > wsl -l -v
  NAME                         STATE           VERSION
* Ubuntu-22.04                 Running         2
  Ubuntu-22.04-pyenv           Running         2
  Ubuntu-22.04-rye             Running         2
  ...

Ubuntu-22.04-pyenvがpyenv + Poetryをインストールする環境、Ubuntu-22.04-ryeがRyeをインストールする環境です。
それぞれ初期状態からユーザのセットアップを済ませただけの状態です。


なお、Ubuntu 22.04のイメージには初期状態でPython 3.10.12がインストールされています。

$ python3 -V
Python 3.10.12

pyenv + Poetryのインストールとプロジェクト作成

Pythonを使用するだけならばpyenvだけでいいですが、プロジェクト作成まで含めてRyeと条件を合わせるため、Poetryも合わせてインストールします。

pyenvのインストール

Automatic installerを使用した手順でインストールします。

$ curl https://pyenv.run | bash

出力に従ってパスを追加しておきます。ここでは.profileに以下を追記します。

export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

pyenvのバージョンは2.4.0です。

$ pyenv --version
pyenv 2.4.0

この段階のpyenvはどのバージョンのpythonも持っていません。

$ pyenv versions
* system (set by /home/takahashi/.pyenv/version)

後述するRyeのツールチェインと合わせてPython 3.12.3をインストールします。
ほぼ初期状態のUbuntu 22.04にはコンパイラや依存ライブラリがインストールされていないため、Ubuntuで必要なパッケージをインストールし、Pythonのインストールを実行します。

$ sudo apt update
$ sudo apt install build-essential zlib1g-dev libssl-dev libbz2-dev libdb-dev libreadline-dev libffi-dev libgdbm-dev liblzma-dev libncursesw5-dev libsqlite3-dev uuid-dev tk-dev
$ pyenv install 3.12.3
Downloading Python-3.12.3.tar.xz...
-> https://www.python.org/ftp/python/3.12.3/Python-3.12.3.tar.xz
Installing Python-3.12.3...
Installed Python-3.12.3 to /home/takahashi/.pyenv/versions/3.12.3
$ pyenv versions
* system (set by /home/takahashi/.pyenv/version)
  3.12.3
Poetryのインストール

公式インストーラを使用してインストールします。

$ curl -sSL https://install.python-poetry.org | python3 -

.profileへパスを追加。

export PATH="/home/takahashi/.local/bin:$PATH"

Poetryのバージョンは1.8.3です。

$ poetry --version
Poetry (version 1.8.3)

プロジェクト内に仮想環境を作成するように設定。

$ poetry config virtualenvs.in-project true
$ poetry config virtualenvs.in-project
true
プロジェクト作成

pyenv-poetry-projectというプロジェクトを作成します。

$ poetry new pyenv-poetry-project
Created package pyenv_poetry_project in pyenv-poetry-project

Python3.12.3を使用して仮想環境を構築します。

$ cd pyenv-poetry-project/
$ pyenv local 3.12.3
$ poetry env use 3.12.3
Creating virtualenv pyenv-poetry-project in /home/takahashi/pyenv-poetry-project/.venv
Using virtualenv: /home/takahashi/pyenv-poetry-project/.venv
$ poetry run python -V
Python 3.12.3

Ryeのインストールとプロジェクト作成

インストール

こちらもインストール手順に従ってインストールを実行します。


Ryeのインストールスクリプトは対話形式となります。質問に対して以下の通り回答し、インストールします。

$ curl -sSf https://rye-up.com/get | bash
This script will automatically download and install rye (latest) for you.
######################################################################## 100.0%
Welcome to Rye!

This installer will install rye to /home/takahashi/.rye
This path can be changed by exporting the RYE_HOME environment variable.

Details:
  Rye Version: 0.33.0
  Platform: linux (x86_64)

✔ Continue? · yes
✔ Select the preferred package installer · uv (fast, recommended)
✔ What should running `python` or `python3` do when you are not inside a Rye managed project? · Run a Python installed and managed by Rye
✔ Which version of Python should be used as default toolchain? · cpython@3.12
Installed binary to /home/takahashi/.rye/shims/rye
Bootstrapping rye internals
Downloading cpython@3.12.3
Checking checksum
Unpacking
Downloaded cpython@3.12.3
Updated self-python installation at /home/takahashi/.rye/self

The rye directory /home/takahashi/.rye/shims was not detected on PATH.
It is highly recommended that you add it.
✔ Should the installer add Rye to PATH via .profile? · yes
Added to PATH.
note: for this to take effect you will need to restart your shell or run this manually:

    source "$HOME/.rye/env"

For more information read https://rye-up.com/guide/installation/

All done!

再ログインすれば使用可能な状態になっています。
pyenv + Poetryと比較して非常にシンプルです。

$ rye --version
rye 0.33.0
commit: 0.33.0 (58523f69f 2024-04-24)
platform: linux (x86_64)
self-python: cpython@3.12.3
symlink support: true
uv enabled: true

ツールチェインで使用するPython 3.12.3も同時にインストールされています。
プロジェクトでPython 3.12.3を使用する場合は、これを用いて仮想環境の構築ができます。

プロジェクト作成

rye-projectというプロジェクトを作成します。

$ rye init rye-project
success: Initialized project in /home/takahashi/rye-project
  Run `rye sync` to get started

作成されるプロジェクトで使用するPythonのバージョンは3.12.3となります。

$ cd rye-project/
$ cat .python-version
3.12.3

syncを実行し、仮想環境を構築します。

$ rye sync
Initializing new virtualenv in /home/takahashi/rye-project/.venv
Python version: cpython@3.12.3
Generating production lockfile: /home/takahashi/rye-project/requirements.lock
Generating dev lockfile: /home/takahashi/rye-project/requirements-dev.lock
Installing dependencies
   Built file:///home/takahashi/rye-project
Built 1 editable in 251ms
Installed 1 package in 1ms
 + rye-project==0.1.0 (from file:///home/takahashi/rye-project)
Done!
$ rye run python -V
Python 3.12.3

pyenv環境とRye環境のPythonの挙動の違い

pyenv + Poetryを用いて構築されたプロジェクトと、Ryeを用いて構築されたプロジェクトのPythonの動作の違いを見てみましょう。


プロジェクトの仮想環境上でPythonを対話モードで起動します。


pyenv + Poetry

$ poetry run python
Python 3.12.3 (main, May  9 2024, 17:52:24) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>


Rye

$ rye run python
Python 3.12.3 (main, Apr 15 2024, 18:25:56) [Clang 17.0.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>


まず見られるのがPythonをビルドしたコンパイラの違いです。
pyenv + Poetryの方はpyenvでPython 3.12.3をインストールした時にGCCによってビルドされた旨が表示されています。
一方、Ryeの方はRyeのインストールとは関係なく、ClangによってビルドされたPythonであることが表示されています。


関数を定義して呼び出してみます。


pyenv + Poetry

$ poetry run python
Python 3.12.3 (main, May  9 2024, 17:52:24) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def function(x: int, y: int) -> int:
...     return x + y
...
>>> function(1, 2)
3
>>>

特に変わったところはありません。


Rye
こちらも同様に関数を定義していきます。

$ rye run python
Python 3.12.3 (main, Apr 15 2024, 18:25:56) [Clang 17.0.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def function(x: int, y: int) -> int:
...

インデントしようとTabキーを押下しましたが、カーソルが移動しません。
そのままreturnと入力しようとrを入力すると、このようになりました。

$ rye run python
Python 3.12.3 (main, Apr 15 2024, 18:25:56) [Clang 17.0.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def function(x: int, y: int) -> int:
...
bck?r

引き続きreturn x + yを入力し、リターンキーを押下しましたが、インデントが効いておらず、エラーとなってしまいました。

$ rye run python
Python 3.12.3 (main, Apr 15 2024, 18:25:56) [Clang 17.0.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def function(x: int, y: int) -> int:
...
...
  File "<stdin>", line 3

    ^
IndentationError: expected an indented block after function definition on line 1
>>>

readlineモジュール

この挙動の違いについてはRyeのFAQに記載があります。

The Python builds that Rye uses are compiled against libedit rather than readline for licensing reasons.

Ryeの使用するPythonではライセンス上の理由から、readlineではなくlibeditからコンパイルされたものであると記載されています。


Pythonの標準ライブラリにはreadlineというモジュールが存在します。
docs.python.org

主に対話モードでの補完や履歴の管理を行うモジュールで、input()の挙動にも影響します。
ドキュメントの注釈にもある通り、このモジュールの実装にはGNU readlineが使われる場合と、libeditが使われる場合があります。
実際にreadline.__doc__を確認すると、pyenvのものはGNU readline、Ryeのものはlibeditであることがわかります。


pyenv + Poetry

$ poetry run python
Python 3.12.3 (main, May  9 2024, 17:52:24) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import readline
>>> readline.__doc__
'Importing this module enables command line editing using GNU readline.'


Rye

$ rye run python
Python 3.12.3 (main, Apr 15 2024, 18:25:56) [Clang 17.0.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import readline
>>> readline.__doc__
'Importing this module enables command line editing using libedit readline.'


libeditはGNU readlineと互換性を持ちますが、キーバインドなどが異なるため、挙動に違いが出てきます。

なぜRyeのPythonはlibeditを使用するのか

これについて話す前に、特定のバージョンのPythonをインストールする際のpyenvとRyeの動作の違いを把握しておく必要があります。

pyenvよるPythonのインストール

特定のバージョンのPythonを新しくインストールする際、pyenvはそのバージョンのPythonのソースコードをダウンロードし、ユーザの環境上でビルドします。
ビルドに際してコンパイラや依存ライブラリが必要となり、これらを事前にインストールしておく必要があります。


今回の検証環境ではPythonのインストール時に必要なパッケージをインストールしましたが、環境によってはこの作業は不要な場合もあります。使い込まれた環境ならば、必要なコンパイラやライブラリは既にインストールされているかもしれません。また、同じ手順を実行したとしても、1週間後、1か月後では異なるライブラリがインストールされるかもしれません。


一部の標準ライブラリに含まれるモジュールのコンパイルがスキップされる場合もあります。
今回の検証環境の場合、インストール時に必要とされるパッケージのうち、ビルドに必須なのはbuild-essential、zlib1g-dev、libssl-devのみで、それ以外のパッケージはインストールされていなくてもビルドに成功します。


readlineモジュールもこれに該当し、libreadline-devをインストールしていない場合はコンパイルされません。

$ pyenv install 3.12.3
Downloading Python-3.12.3.tar.xz...
-> https://www.python.org/ftp/python/3.12.3/Python-3.12.3.tar.xz
Installing Python-3.12.3...
...
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'readline'
WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
...
Installed Python-3.12.3 to /home/takahashi/.pyenv/versions/3.12.3

これにより、標準ライブラリにreadlineモジュールを含まないPythonがビルドされます。
特定のモジュールがコンパイルされなかった旨の警告は出力されますが、ユーザがそれを無視したり、気づかなかった場合には、標準ライブラリの一部が欠落した状態のPythonがこの環境では使用されることになります。

Python 3.12.3 (main, May  9 2024, 17:34:42) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import readline
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'readline'


このようにpyenvによってインストールされたPythonはビルド環境への依存が強く、同じディストリビューション、同じバージョンであっても細かい差異を生じます。
実行環境によっては、これらの差異が挙動の違いとして現れたり、問題を引き起こす可能性もあります。

RyeによるPythonのインストール

Ryeは上記のような差異が生じることを良しとしていません。
Portable CPython - Rye

Unlike many other Python versions you can install on your computer are non-portable which means that if you move them to a new location on your machine, or you copy it onto another computer (even with the same operating system) they will no longer run. This is undesirable for what Rye wants to do. For one we want the same experience for any of the Python developers, no matter which operating system they used. Secondly we want to enable self-contained Python builds later, which requires that the Python installation is portable.

環境の差異によって生じる問題を回避し、様々なユーザ間で共通のユーザ体験を提供できるようにするため、Ryeはビルド済みのPythonと標準ライブラリ一式をダウンロードする形式をとっています。
Ryeのインストール時の出力からも、ビルドではなくダウンロードしたものを展開していることがわかります。

...
Downloading cpython@3.12.3
Checking checksum
Unpacking
Downloaded cpython@3.12.3
...

Ryeを使用する場合はコンパイラやライブラリのインストールは不要であり、またそれらに依存しません。Ryeをインストールするだけで使い始められるため、ユーザから見てもシンプルに扱うことができます。


CPythonの場合、ダウンロードされるPythonはPyOxidizer projectのpython-build-standaloneリポジトリのものとなります。
gregoryszorc.com
github.com


このプロジェクトは自己完結型で高い移植性を持つPythonディストリビューションを作成するもので、Ryeの考え方に合致しています。


これにより、Ryeにおいては同じディストリビューション、同じバージョンならば同じPythonがインストールされることが保証されます。

ライセンスの問題

※ライセンスに対する見解は筆者個人のものであり、誤りが含まれる可能性があります。あくまで参考資料としてご認識いただければ幸いです。


readline、libeditと聞いて、既にライセンスの問題を想起された方もいらっしゃるかもしれません。
上記の動作の違いがどのようにlibeditを使用することに繋がるのでしょうか?


libeditを使用していることについて、RyeとPyOxidizer projectの双方で言及されています。

Portable CPython - Rye

libedit instead of readline: unfortunately readline is GPL2 licensed and this is a hazard for redistributions. As such, the portable Python builds link against the more freely licensed libedit instead.

(GPL2と表記されていますが、おそらく誤りです。)
Running Distributions — python-build-standalone documentation

Most licenses are fairly permissive. Notable exceptions to this are GDBM and readline, which are both licensed under GPL Version 3.


We build CPython against libedit - as opposed to readline - to avoid this GPL dependency. This requires patches on CPython < 3.10. Distribution releases before 2023 may link against readline and are therefore subject to the GPL.

Behavior Quirks — python-build-standalone documentation

Some functionality may behave subtly differently as a result of our choice to link libedit by default. (We choose libedit by default to avoid GPL licensing requirements of readline.)


Ryeの使用するpython-build-standaloneは、ビルド済みPython並びに標準ライブラリ一式を派生物として提供、すなわち再頒布するため、含まれるPython本体や標準ライブラリのライセンスを考慮する必要があります。


Python本体と多くの標準ライブラリのライセンスはPSFライセンスと呼ばれるBSDスタイルの独自ライセンスです。
docs.python.org
このライセンスはGPLと互換性を持つため、派生物に含めた場合はその派生物にGPLを適用できますが、PSFライセンス自体はコピーレフトではありません。


一方、GNUと名に冠しているように、GNU readlineライブラリのライセンスはGPLv3です。
tiswww.case.edu

Readline is free software, distributed under the terms of the GNU General Public License, version 3. This means that if you want to use Readline in a program that you release or distribute to anyone, the program must be free software and have a GPL-compatible license. If you would like advice on making your license GPL-compatible, contact licensing@gnu.org.


Python本体や標準ライブラリと、GNU readlineライブラリを用いてコンパイルされたreadlineモジュールを派生物に同梱した場合、派生物に適用できるライセンスはより強いコピーレフト性を持つGPLv3を適用する必要があると思われます。*1


仮にpython-build-standaloneがGPLv3を適用した場合、python-build-standaloneをから作成した派生物についてもGPLv3を適用する必要が出てくるため、ユーザの選択肢を狭めることになります。


これを回避するため、python-build-standaloneではGNU readlineライブラリと互換性があり、BSDライセンスであるlibeditを使用してコンパイルしたreadlineモジュールを採用し、派生物はBSDライセンスで提供されています。
このようにGPL回避のためにreadlineの代替としてlibeditを使用しているケースは、python-build-standalone以外でも見られます。*2

ユーザへの影響

Pythonでバッチスクリプトを作成して個人や社内で使用したり、サーバやアプリケーションを開発して機能のみをWebサービスとして公開するといった用途では、ライセンス絡みの問題が起こることはあまりないと思われます。
作成したプログラムをモジュールとして公開する場合でも、Python本体や依存モジュール自体を派生物に同梱するという場面はほとんどないでしょう。


一方、Python本体や標準ライブラリを含めた派生物を作成し再頒布する場合には、python-build-standaloneと同様にライセンスに関して確認する必要があります。
例としてはPyInstaller等でバンドルやビルドしたアプリケーションを配布する場合や、実装したスクリプトと動作環境一式を内包したDockerイメージを配布する場合がこれに該当するでしょうか。*3


python-build-standaloneのLicensingでも、派生物に含める場合はライセンスを理解する必要があることが強調されています。

It is important to understand the licensing requirements when integrating the output of this project into derived works.


もっともこれはRyeを使用している場合に限りません。pyenvでも同じことです。
むしろGNU readlineライブラリからコンパイルされたreadlineモジュールを派生物に含む可能性については、pyenv環境のほうが注意を払う必要があります。


作成される派生物に何が含まれるのか、どのようなライセンスが適用可能なのかは、使用するツールや設定、環境によって異なります。
派生物を配布する場合、開発者はこれらをきちんと確認し、違反がないように派生物にライセンスを適用する、あるいは使用するライブラリやパッケージを別のもにするといった対応をとる必要があります。
また、エンジニア個人でライセンスに違反していないかを判断するのは難しいですし、危険を伴います。実際に頒布する場合は、専門家や法務に確認を取るべきです。

まとめ

今回はpyenvとRyeでインストールされるPythonの挙動の違いと、その原因となるライセンスにまつわる話を紹介させていただきました。


本記事ではreadlineモジュールの違いについて触れましたが、Ryeやpython-build-standaloneにはreadline以外にも、各自でビルドしたPythonとの違いや制約事項があります。
Ryeを使用して開発を行う場合は、これらに一度目を通しておくのがいいかと思います。
また、Pythonにおいてはあまり意識されていないかもしれませんが、ライセンス違反とならないように、使用するツールやライブラリのライセンスはしっかりと確認しなければならないと改めて認識しました。


Python本体や依存するモジュールのバージョンを管理するのは重要ですが、同じバージョンのPythonでもビルドされた環境によって差異があることは認識しておく必要があります。
この点において、使用するPythonを共通化できるRyeのアプローチは、動作の軽さ、シンプルさを含めてとても魅力的に感じます。

イントロダクションにあるように、まだ実験的な段階のツールであるため使用できる場面は限られます。また、Ryeは今後はuvへと引き継がれていくことが公開されており、流動的な状態であると思われます。
uv: Python packaging in Rust

Ryeおよびuvはまだ初期段階ですが、将来性のあるプロジェクトであると感じるため、動向を注視しつつ、引き続き試していきたいと思います。



デジタルテクノロジー統括部 デジタルソリューション部 Webアプリエンジニアグループ リードエンジニア 髙橋 大地の写真


髙橋 大地 Daichi Takahashi


デジタルテクノロジー統括部 デジタルソリューション部 リードエンジニア

組み込み系からWebサービス、クラウドとさまざまな開発に携わった後、2020年1月にパーソルキャリアに入社。現在はAIを用いたサービスのバックエンド開発を担当。

※2024年5月現在の情報です。

*1: もしGPLv3と互換性のないライセンスを持つ標準ライブラリが存在した場合は組み合わせることができないため、GPLv3を適用することはできなくなります。

*2: 例えばPHPはPython同様にGNU readlineライブラリとlibeditを選択可能です。PHP: 要件 - Manual MySQLは5.6からlibeditを使用します。https://downloads.mysql.com/docs/mysql-5.6-relnotes-en.pdf

*3: Linux Foundationが発行したホワイトペーパーにあるように、Dockerコンテナのライセンスの管理はより複雑です。 Dockerコンテナ:オープンソース ライセンスの考慮事項は何か - The Linux Foundation