こんにちは。
本記事は株式会社インティメート・マージャーのAdvent Calendar 2023の7日目の記事になります。
突然ですがみなさん、Poetry使ってますか?とても便利ですよね!
アドベントカレンダーの2日目の記事でも出てきた話題ですが、今回はそのPoetryを使っているとよく違いが分からなくなるDependency group
とExtras
という概念について、環境構築の仕方から備忘録的な意味も込めて軽く記事にまとめたので、Poetryを使ってアプリケーション開発をする際の参考にしていただければと思います!
忙しい人のため
- Dependency group: 開発時に使うもの、poetryでしかインストール出来ない
- Extras: エンドユーザーがpipインストール時にも指定出来るようにするためのもの
前提
- Ubuntu: 22.04.2 LTS
- Docker: 24.0.7, build afdd53b
- VSCode: 1.84.2
- DevContainer: v0.321.0
- Poetry: 1.7.1
Poetryとは
公式ドキュメントによると、PoetryとはPythonの「依存管理」と「パッケージング」のためのツールとあります。
Poetry is a tool for dependency management and packaging in Python.
今回の記事では主に「依存管理」の用途で使うので、パッケージングについてあまり詳しくは触れません。(パッケージングに関しては、少し古い記事になりますが以下の記事が詳しくまとまっていると思います。)
環境構築
今回はアドベントカレンダーの2日目の記事に近い形でVSCode
+DevContainer
+Poetry
を使って環境を構築していきたいと思います。
実は最低限のpyproject.toml
ファイルさえあればDockerfile
が無くても開発が出来ます(もちろんDevContainerを使うのでdockerコマンド自体は必要です)。
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.11"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
{
"python.analysis.extraPaths": [
"/usr/local/py-utils/venvs/poetry/lib/python3.11/site-packages",
],
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
{
"name": "Python 3",
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
"postCreateCommand": "pipx install poetry && poetry install --no-root",
"waitFor": "postCreateCommand",
"containerUser": "vscode",
"containerEnv": {
"PYTHONUNBUFFERED": "1",
"PYTHONDONTWRITEBYTECODE": "1",
"POETRY_VERSION": "1.7.1",
"POETRY_HOME": "/etc/poetry",
"POETRY_NO_INTERACTION": "1",
"POETRY_VIRTUALENVS_CREATE": "false"
},
"customizations": {
"vscode": {
"extensions": ["ms-python.python"]
}
}
}
※最新のDevContainerであればmount等の設定を書かなくてもデフォルトでプロジェクトフォルダが/workspaces/$project
にマウントされます。
動かしてみる
上記の設定でDevContainerを立ち上げてアタッチすると以下のような構成でlockファイルが新たに作成された状態で開発環境が構築されます。
.
├── .devcontainer
│ └── devcontainer.json
├── .vscode
│ └── setting.json
├── poetry.lock
└── pyproject.toml
あとはapp/main.py
などを作成して、poetry run
コマンドで実行してあげることでpythonのコードを実行することができます。
$ mkdir app
$ echo 'print("Hello, World!")' > app/main.py
$ poetry run python app/main.py
Hello, World!
パッケージインストール
まだこのままだと標準ライブラリしか入っていないため、外部ライブラリを使うことが出来ません。
Poetryではライブラリを追加するために、以下の方法があります。
-
pyproject.toml
ファイルを直接編集する方法 -
poetry add
コマンドで追加する方法
1つ目のpyproject.toml
ファイルを編集する方法では、poetry lock
コマンド等を実行しないとpoetry.lock
ファイルに反映されないためpoetry add
コマンドをお勧めします。
ここでは実際にpoetry add
コマンドでpendulum
を追加してみます。
$ poetry add pendulum
Using version ^2.1.2 for pendulum
Updating dependencies
Resolving dependencies... (0.6s)
Package operations: 4 installs, 0 updates, 0 removals
• Installing six (1.16.0)
• Installing python-dateutil (2.8.2)
• Installing pytzdata (2020.1)
• Installing pendulum (2.1.2)
Writing lock file
$ cat << EOS > app/main.py
> import pendulum
>
> print(pendulum.Date.today())
> EOS
$ poetry run python app/main.py
2023-12-07
今日の日付が出力されていることから正しく追加されていることが分かります。
このときpyproject.toml
ファイルには以下のように依存関係が追記されます。
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.11"
+ pendulum = "^2.1.2"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Dependency groupsとExtras
ここで先ほどのpoetry add
コマンドのオプションを見てみると、--group
,--extras
,--optional
というオプションが見つかります。
--group (-G): The group to add the dependency to.
--extras (-E): Extras to activate for the dependency. (multiple values allowed)
--optional: Add as an optional dependency.
optional
は一旦置いておき、group
とextras
の違いは一体何なのか見てみましょう。これは公式ドキュメントに書いてあり、
Dependency groups, other than the implicit main group, must only contain dependencies you need in your development process. Installing them is only possible by using Poetry.
To declare a set of dependencies, which add additional functionality to the project during runtime, use extras instead. Extras can be installed by the end user using pip.
とのことで、つまり以下ような違いがあります。
- group
- poetryからのみインストール出来る
- 開発時に使うもの
- extras
- poetry、pipインストール時にも指定出来る
- 主にエンドユーザーが使うもの
言い換えると、groupには開発時にテスト環境のみにインストールしたいpytest
やflake8
などを指定し、extrasにはプラグインのようなインストール時に取捨選択させるたいものを指定するのが正しい使い方になります。
poetry addコマンド実行結果
group
実際にgroupに追加するコマンドを実行してみると以下のようになります。
$ poetry add --group dev pytest
Using version ^7.4.3 for pytest
Updating dependencies
Resolving dependencies... (0.7s)
Package operations: 3 installs, 0 updates, 0 removals
• Installing iniconfig (2.0.0)
• Installing pluggy (1.3.0)
• Installing pytest (7.4.3)
Writing lock file
最新のPoetryではpoetry add
コマンドの--dev (-D)
オプションは非推奨になっています。
pyoroject.tomlファイルには[tool.poetry.group.<group名>.dependencies]
というテーブルが追加されます。
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.11"
pendulum = "^2.1.2"
+ [tool.poetry.group.dev.dependencies]
+ pytest = "^7.4.3"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
extras
extrasについてはgroupへの追加とは少し異なり、cryptoというextrasにcryptographyを追加しようとしてgroupのときと同様に以下のコマンドを実行すると、
$ poetry add --extras crypto cryptography
Using version ^41.0.7 for cryptography
Updating dependencies
Resolving dependencies... (0.6s)
No dependencies to install or update
Writing lock file
以下のように、pyproject.toml
ファイルのメインの依存関係[tool.poetry.dependencies]
に特殊な形でcryptography[crypto]
をインストールする設定として追加されます。
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.11"
pendulum = "^2.1.2"
+ cryptography = {version = "^41.0.7", extras = ["crypto"]}
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
実際にpoetry add cryptography[crypto]
でも同じように追記されます。
ではどうすればextrasに追加出来るかというと、ドキュメントにある通りoptional
というものを使います。
Poetry supports extras to allow expression of:
- optional dependencies, which enhance a package, but are not required; and
- clusters of optional dependencies.
一旦、poetry remove
コマンドで余計に入れたものを取り除いておきます。
$ poetry remove cryptography
Updating dependencies
Resolving dependencies... (0.2s)
Package operations: 0 installs, 0 updates, 3 removals
• Removing cffi (1.16.0)
• Removing cryptography (41.0.7)
• Removing pycparser (2.21)
Writing lock file
正しくextrasに設定するには、まずpoetry add
コマンドでメインの依存関係にoptional
な設定を追加します。
$ poetry add --optional cryptography
Using version ^41.0.7 for cryptography
Updating dependencies
Resolving dependencies... (0.3s)
No dependencies to install or update
Writing lock file
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.11"
pendulum = "^2.1.2"
+ cryptography = {version = "^41.0.7", optional = true}
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
そしてpyproject.tomlを直接編集して[tool.poetry.extras]
の設定を追加します。
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.11"
pendulum = "^2.1.2"
cryptography = {version = "^41.0.7", optional = true}
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
+
+ [tool.poetry.extras]
+ crypto = ["cryptography"]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
追加したらpoetry lock
コマンドでpoetry.lock
ファイルも更新しておきます。
$ poetry lock
Updating dependencies
Resolving dependencies... (0.4s)
Writing lock file
実際にインストールしてみる
最後にpip install
コマンドでextrasが正しく設定出来ていることを確認しておきます。
まずpoetry build
コマンドでここまでのコードをパッケージングします。
$ poetry build
Building app (0.1.0)
- Building sdist
fatal: not a git repository (or any parent up to mount point /workspaces)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
- Built app-0.1.0.tar.gz
- Building wheel
fatal: not a git repository (or any parent up to mount point /workspaces)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
- Built app-0.1.0-py3-none-any.whl
成果物はdist/
ディレクトリに作成されるので、pip install
コマンドでインストールします。
$ pip install dist/app-0.1.0.tar.gz
Defaulting to user installation because normal site-packages is not writeable
Processing ./dist/app-0.1.0.tar.gz
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing metadata (pyproject.toml) ... done
Collecting pendulum<3.0.0,>=2.1.2 (from app==0.1.0)
Downloading pendulum-2.1.2.tar.gz (81 kB)
...
$ pip list
Package Version
--------------- -------
app 0.1.0
pendulum 2.1.2
pip 23.2.1
python-dateutil 2.8.2
pytzdata 2020.1
setuptools 68.2.2
six 1.16.0
wheel 0.41.3
pip install dist/app-0.1.0.tar.gz[crypto]
も実行してみると違いが分かります。(pip uninstall
コマンドではarchiveファイルパスが指定出来ないので、DevContainerにアタッチし直すなどしてお試しください。)
$ pip install dist/app-0.1.0.tar.gz[crypto]
Defaulting to user installation because normal site-packages is not writeable
Processing ./dist/app-0.1.0.tar.gz
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing metadata (pyproject.toml) ... done
Collecting cryptography<42.0.0,>=41.0.7 (from app==0.1.0)
Obtaining dependency information for cryptography<42.0.0,>=41.0.7 from https://files.pythonhosted.org/packages/62/bd/69628ab50368b1beb900eb1de5c46f8137169b75b2458affe95f2f470501/cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl.metadata
Downloading cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl.metadata (5.2 kB)
...
$ pip list
Package Version
--------------- -------
app 0.1.0
cffi 1.16.0
cryptography 41.0.7
pendulum 2.1.2
pip 23.2.1
pycparser 2.21
python-dateutil 2.8.2
pytzdata 2020.1
setuptools 68.2.2
six 1.16.0
wheel 0.41.3
これでextrasが正しく設定されていることが分かりました。
まとめ
本記事ではPoetryのDependency group
とExtras
周りの違いや扱い方などを実際に触れながら見てきました。
Dependency group
に関してはテスト環境などを分離するために使えて、Extras
は機能を分けるために使えるというものでした。
このExtras
を上手く使うことでただのパッケージングだけでなく、Monorepoでの開発を行ったり、一部機能を共有したサービスの開発などを一つのpyproject.toml
ファイルで管理したりなどすることも出来たりします。(Dependency group
にもoptional groupsというものがありこちらを使うのが正しそうですが、サービスが増えていくとどうしても記述が汚くなっていってしまうので悩ましいです。)
あとがき
ここまで読んでいただいてありがとうございました!
明日はstrapiというCMSについてのお話だそうです!
ぜひお楽しみに!