kanta's spike

GitHubにPythonのプログラムを公開する時に、利用しているPythonのバージョンとパッケージを指定したい。

解決策

一般的に、この用途にはパッケージマネージャーというものを利用するようだ。各種開発言語には、それぞれのパッケージマネージャーが存在する。1

Pythonの場合、以下の3つが有名みたいだ。2

パッケージマネージャーごとに特徴があるようだが、私は以下の理由で、簡単に利用できそうなPoetryを採用する。

  • 仮想環境の利用が簡単(poetry runpoetry shellで利用やアクティベートできる)
  • Python標準のpyproject.tomlで依存関係が定義されるためシンプル

Poetryにより生成される以下のファイルをコミットしておくと、Pythonのバージョンとパッケージを指定できる。

  • pyproject.toml
  • poetry.lock 3

Poetryを使う

Jupyter Notebookの導入を例に、Poetryの使い方をまとめる。

Poetryのインストール

まずは、Poetryをインストールします。私の環境はmacOSなのでbrewでインストールする。

% brew install poetry
..略..
% poetry --version
Poetry (version 1.3.2)

プロジェクトの初期化

次に、プロジェクト用の作業ディレクトリを作成し、移動する。

mkdir ~/spike/69_use-jupyter-notebook
cd ~/spike/69_use-jupyter-notebook

そして、poetry initでプロジェクトを初期化する。 いくつか質問されるので、適当に回答する。(回答はproject.tomlに記録されるので、あとで簡単に修正できる。)

% poetry init
This command will guide you through creating your pyproject.toml config.

Package name [69_use-jupyter-notebook]:
# ..略..
Compatible Python versions [^3.11]:
# ..略..
Do you confirm generation? (yes/no) [yes]

プロジェクトに合うPythonのバージョンを導入

Pythonのバージョンを^3.11 4 と指定したので、pyenv3.11のPythonを利用するようにする。

% pyenv install 3.11.2
python-build: use openssl@1.1 from homebrew
python-build: use readline from homebrew
Downloading Python-3.11.2.tar.xz...
-> https://www.python.org/ftp/python/3.11.2/Python-3.11.2.tar.xz
Installing Python-3.11.2...
# ..略..
% pyenv local 3.11
% cat .python-version
3.11
% pyenv rehash
% python3 --version
Python 3.11.2

仮想環境の設定

デフォルトでは、Poetry{cache-dir}/virtualenvsを仮想環境とし、そこにパッケージをインストールする。

今回は、プロジェクト用に専用の仮想環境を作成したいと思う。 そのためには、以下を実行し設定を変更する必要がある。 5

% poetry config --local virtualenvs.in-project true
% cat poetry.toml
[virtualenvs]
in-project = true

--localオプションを指定して実行したので、poetry.tomlが作成され、そこに設定内容が記録される。

Jupyter Notebookの導入

Jupyter Notebookを導入するには、notebookパッケージが必要である。 Poetryで、notebookを依存するパッケージに追加する。6

% poetry add notebook
Using version ^6.5.2 for notebook

Updating dependencies
Resolving dependencies... (2.1s)

Writing lock file

Package operations: 77 installs, 0 updates, 0 removals

  • Installing six (1.16.0)
  • Installing attrs (22.2.0)
  • Installing platformdirs (3.0.0)
..略..

今回は、numpyscipymatplotlibを利用したいのでインストールしたいと思う。

まず、numpyをインストールする。

% poetry add numpy
Using version ^1.24.2 for numpy

Updating dependencies
Resolving dependencies... (0.5s)

Writing lock file

Package operations: 1 install, 0 updates, 0 removals

  • Installing numpy (1.24.2)
%

次に、scipyをインストールする。しかし、エラーが発生した。

% poetry add scipy
Using version ^1.10.1 for scipy

Updating dependencies
Resolving dependencies... (0.0s)

The current project's Python requirement (>=3.11,<4.0) is not compatible with some of the required packages Python requirement:
  - scipy requires Python <3.12,>=3.8, so it will not be satisfied for Python >=3.12,<4.0

Because no versions of scipy match >1.10.1,<2.0.0
 and scipy (1.10.1) requires Python <3.12,>=3.8, scipy is forbidden.
So, because 69-use-jupyter-notebook depends on scipy (^1.10.1), version solving failed.

  • Check your dependencies Python requirement: The Python requirement can be specified via the `python` or `markers` properties

    For scipy, a possible solution would be to set the `python` property to ">=3.11,<3.12"

    https://python-poetry.org/docs/dependency-specification/#python-restricted-dependencies,
    https://python-poetry.org/docs/dependency-specification/#using-environment-markers
%

これは、プロジェクトが許容するpythonのバージョンと、scipyが許容するpythonのバージョンがマッチしていないため、エラーが発生している。

プロジェクトが許容するpythonのバージョンはproject.tomlに記載されている。 今回はpythonのバージョンが、python = "^3.11"となっており、これはpythonのバージョンが>=3.11.0 < 4.0を意味する。

しかし、今インストールしようとしているscipypython<3.12,>=3.8バージョンまでに対応している。7 そのため、ミスマッチが発生している。

これを修正するには、プロジェクトが許容するpythonのバージョンを厳密にする必要がある。 まだ、リリースもされていないpythonのバージョンは無視して、以下のようにpython = "~3.11"とし、python>=3.11.0 < 3.12を対象とした。 8

# ..略..
[tool.poetry.dependencies]
python = "~3.11"
# ..略..

再度、scipyをインストールする。今度はちゃんとインストールできる。

% poetry add scipy
Using version ^1.10.1 for scipy

Updating dependencies
Resolving dependencies... (0.5s)

Writing lock file

Package operations: 1 install, 0 updates, 0 removals

  • Installing scipy (1.10.1)

最後に、matplotlibをインストールする。

% poetry add matplotlib
Using version ^3.7.0 for matplotlib

Writing lock file

Package operations: 7 installs, 0 updates, 0 removals

  • Installing contourpy (1.0.7)
  • Installing cycler (0.11.0)
  • Installing fonttools (4.38.0)
  • Installing kiwisolver (1.4.4)
  • Installing pillow (9.4.0)
  • Installing pyparsing (3.0.9)
  • Installing matplotlib (3.7.0)
%

プロジェクトディレクトリ内に、仮想環境用の.venvディレクトリが作成され、そこにパッケージがインストールされる。

% ls .venv
bin             etc             lib             pyvenv.cfg      share

そして、pyproject.tomlにはnotebookパッケージとそのバージョンが記録される。

# ..略..
[tool.poetry.dependencies]
python = "~3.11"
notebook = "^6.5.2"
numpy = "^1.24.2"
scipy = "^1.10.1"
# ..略..

さらに、poetory.lockに依存するパッケージも含めた詳細なパッケージの情報が記録される。

head poetry.lock
# This file is automatically @generated by Poetry and should not be changed by hand.

[[package]]
name = "anyio"
version = "3.6.2"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "main"
optional = false
python-versions = ">=3.6.2"
files = [

もし、GitHubにプロジェクトを公開する場合、poetry.lockを登録しておけば、 プロジェクトをクローンしたユーザーは、以下のコマンドにより、poetry.lockに記載されたバージョンの依存関係も含む全パッケージをインストールできる。9

poetry install

Jupyter Notebookの実行

poetry runの後に、コマンドを実行すると、プロジェクトの仮想環境で実行される。

% poetry run jupyter notebook
[I 11:21:43.847 NotebookApp] Writing notebook server cookie secret to /Users/kanta/Library/Jupyter/runtime/notebook_cookie_secret
[I 11:21:44.345 NotebookApp] Serving notebooks from local directory: /Users/kanta/spike/69_use-jupyter-notebook
[I 11:21:44.345 NotebookApp] Jupyter Notebook 6.5.2 is running at:
[I 11:21:44.345 NotebookApp] http://localhost:8888/?token=3f11e224193b1fe12c2241acf4f8a47eec902761062a8fe8
[I 11:21:44.345 NotebookApp]  or http://127.0.0.1:8888/?token=3f11e224193b1fe12c2241acf4f8a47eec902761062a8fe8
[I 11:21:44.345 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
# ..略..

また、poetry shellで仮想環境をアクティベートしてから、jupyter notebookを実行しても同様になる。10

% poetry shell
Spawning shell within /Users/kanta/spike/69_use-jupyter-notebook/.venv
% emulate bash -c '. /Users/kanta/spike/69_use-jupyter-notebook/.venv/bin/activate'
(69-use-jupyter-notebook-py3.11) % jupyter notebook
[I 11:23:35.318 NotebookApp] Serving notebooks from local directory: /Users/kanta/spike/69_use-jupyter-notebook
[I 11:23:35.318 NotebookApp] Jupyter Notebook 6.5.2 is running at:
[I 11:23:35.319 NotebookApp] http://localhost:8888/?token=a8feeef6303a95334b208be924ee88542bd9c3bdd9676870
[I 11:23:35.319 NotebookApp]  or http://127.0.0.1:8888/?token=a8feeef6303a95334b208be924ee88542bd9c3bdd9676870
[I 11:23:35.319 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).

参考


  1. パッケージマネージャーという用語には、いくつか分類があるようだ。ここでは、Python言語のパッケージ管理ツールを話題にしている。 ↩︎

  2. 昔からpipとrequirements.txtを利用するシンプルな方法が昔から利用されていたそうだが、この方法は複雑なパッケージの依存関係に対応しにくいそうだ。 ↩︎

  3. 依存関係のあるパッケージのバージョンを指定する場合はpoetry.lockもコミットする ↩︎

  4. バージョン3.11以上、4.0未満の意味 ↩︎

  5. --localオプションなしで設定すると、すべてのプロジェクト共通の設定になる。 ↩︎

  6. addで指定したパッケージ名からパッケージと依存するパッケージの適切なバージョン情報を取得して、インストールする。 ↩︎

  7. まだリリースもされていない<4.0のバージョンなんて対応できる訳ないですよね ↩︎

  8. バージョンの指定方法については、Dependency specificationを参照 ↩︎

  9. poetry.lockが存在しない場合は、pyproject.tomlに記載されたパッケージと、依存するパッケージの最新バージョンがインストールされる ↩︎

  10. 起動したシェルを抜けるには、deactivateを実行するかexit(C-dでも良い)を実行する ↩︎

作成日: 2023/02/17