Python のパッケージを Poetry で自作して Azure Artifacts に置いてみる
はじめに
仕事で Azure DevOps を使おうとしているが、いかんせん情報が少ない。(ただし、公式ドキュメントは充実している。)GitHub よりもケーススタディ的な記事が少なく自分が苦労したので、備忘録として記事を書きたくなった。今回は Azure Artifacts に Python の独自ライブラリを(手動で)置いてみることを試したので、それを書く。
また、前半はほとんど Python のパッケージ作成の話である。Python の自作パッケージ / ライブラリの配布等の記事も探しにくかったので、そちらのほうでも参考になれば幸いである。
そもそも Azure DevOps って何?
Azure DevOps の詳細については他の方々の記事等に任せる。基本的には無料で使える。
今回作ってみるもの
pip install
できるようなcatlib
という独自ライブラリを作ってみる。
talk
という関数を用意して猫と会話できる。動作は以下のようなイメージである。
>>> from catlib import talk
>>> talk("こんにちは!")
にゃん
>>> talk("Hello!")
にゃん
>>> talk("にゃん")
・・・は?
>>> talk("")
基本的には "にゃん" で返ってくるが、逆に "にゃん" と入力するとあきれられる仕様である。空文字列の場合は、空文字列で返す。
筆者の環境
Windows で行ったのでそれ特有の内容に一部なっているだろうが、概ね OS の違いはなく同じようにできるはずである。ただ他の OS ユーザーは適宜読み替えながら見てほしい。
対象 | 内容 |
---|---|
OS | Windows 11 Pro |
IDE | Visual Studio Code |
CLI | Power Shell, Git Bash |
環境構築
リポジトリの作成
Azure DevOps に入って適当なプロジェクトを作り、その中の Azure Repos で Git の新しいリポジトリ(本記事ではcatlib
)を作る。
このとき "Add a .gitignore:" で "Python" を選んでおくと Python まわりのソース管理上不必要なファイルをコミットしなくなるので便利。
ローカル環境構築
Poetry というモダンなパッケージ管理ツールを使いながら開発を進めたい。ただ、これの導入がちょっとややこしかったので書いておく。結論としては以下のようである。
-
Python と
pipx
[1]のインストール
Windows ユーザーであればscoop
をインストールする。Mac の場合brew
を使う。(以下のscoop
の部分をbrew
に読み替えること。)
scoop install python
を実行する。
scoop install pipx
を実行する。
pipx ensurepath
を実行する。 -
pipx
を用いてpoetry
をインストール
公式の手順にしたがってpipx install poetry
を実行する。
あとは以下のコマンドをたたいてpoetry
が使えるかチェックする。
poetry --version
これでpoetry
の準備ができた。
ちなみに Python にまつわる "管理ツール" はごちゃごちゃしていたので以下の記事が参考になった。
プロジェクトの準備
Azure Repos からcatlib
をクローンしてきて、そのフォルダに入る。
プロジェクトの作成
フォルダの中で以下のコマンドをたたく。
poetry init
するとインタラクティブにパッケージ名やら聞かれるので、今回はあまり気にせず概ねデフォルトのまま(Enter を押す)でよいが、以下の質問についてはno
と答える。
Would you like to define your main dependencies interactively? (yes/no)
デフォルトではyes
になっているが、パッケージの追加をここで行うものであり、あとからでもできるのでやらなくて良い。この質問のあと以下のように聞かれる。
Do you confirm generation? (yes/no)
これをデフォルトのyes
で回答すると以下のようなファイルが生成される。
[tool.poetry]
name = "catlib"
version = "0.1.0"
description = ""
authors = ["<名前>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
仮想環境の準備
また Python の仮想環境(.venv
)をプロジェクト内部に準備するように指定しておく。(デフォルトではユーザーのキャッシュフォルダ上に配置される。)そうしておく方が環境を汚さないし、特に VSCode がちゃんと認識してくれるのでこの設定にすることを推奨する。
以下のコマンドをたたく。
poetry config virtualenvs.in-project true --local
これをすると以下のようなファイルが生成されるはずである。
[virtualenvs]
in-project = true
この設定が反映されているかどうかを見るには以下のコマンドをたたくとわかる。
$ poetry config --list
...
virtualenvs.in-project = true
...
pytest
の導入
テストフレームワークであるpytest
を導入する。以下のコマンドをたたくとpytest
を導入できる。(テストツールであるpytest
は開発環境のみに必要なので--dev
オプションをつける。)
poetry add pytest --dev
これでインストールが走るはずである。外部ライブラリのインストールを始めて実行するのでpoetry.lock
ファイル(および.venv
フォルダ)が生成される。
もしここでインストールが走らなかったとしてもあとでインストール作業があるので次に進めて良い。
これでプロジェクトの準備が整った。
独自ライブラリの構築
テスト駆動で進めてみる。
基本的な構成の準備
まず以下のような二つのフォルダcatlib
とtests
を用意する。
{root}
├── pyproject.toml
├── ...
│
├── catlib # ライブラリの本体
│ ├── __init__.py
│ └── main.py
│
└── tests # テスト用
├── __init__.py
└── test_main.py
ライブラリの箱を用意する
catlib
フォルダの二つのファイルを以下のように編集する。
def talk(call: str) -> str:
pass
from catlib.main import *
一旦未実装で関数だけ用意しよう。あとはこの自作ライブラリをインストールするために以下のコマンドをたたく。
poetry install
このコマンドは自作ライブラリを認識させるだけのものではなく、pyproject.toml
の情報からインストールしてくれるコマンドでnpm install
みたいなものである。
失敗するテストを書く
今度はテストを書く。「今回作ってみるもの」で示した仕様をそのままテストに落とし込む。
from catlib import talk
def test_talk():
default_res = "にゃん"
copy_res = "・・・は?"
assert talk("こんにちは!") == default_res
assert talk("Hello!") == default_res
assert talk(default_res) == copy_res
assert talk("") == ""
この状態で一旦テストを実行してみる。以下のコマンドでできる。
poetry run pytest tests
テストが実行され、当然失敗する結果が返ってくることを確認できる。
テストを成功するように実装
次にテストをパスできるように実装を修正する。
def talk(call: str) -> str:
default_res: str = "にゃん"
if not call:
return ""
elif call == default_res:
return "・・・は?"
return default_res
この状態で再びテストを実行する。
poetry run pytest tests
するとテストをパスできることが確認できる。
Azure Artifacts へデプロイ
いよいよデプロイ作業を行う。まずはビルドから。
ビルド
以下のコマンドでビルドする。
poetry build
ビルドファイルはデフォルトでdist
フォルダに出力される。
フォルダの中には二つのファイルができていて、以下の二形式である。
-
~.whl
: ビルド済みのファイル -
~.tar.gz
: ソースコードの圧縮ファイル
これらはビルド時に-f [wheel/sdist]
オプションを使って指定することができる。wheel
が~.whl
を作成し、sdist
が~.tar.gz
を作る。今回はdist
フォルダをいったん削除してから、以下のコマンドを実行する。
poetry build -f wheel
Azure Artifacts の準備
Azure DevOps 側の準備をする。Azure Artifacts にフィードを用意する必要がある。
Azure DevOps のプロジェクトに入って、右のリストから Azure Artifacts をクリックする。 [Create Feed] からcatlib
の名前でフィードを作成する。ここは以下の公式ドキュメントを見ながらやるのが良い。
デプロイ
公式の指示[2]ではtwine
というツールを用いて行うが、今回は極力poetry
で完結させたい。
poetry publish
でデプロイ可能であるが、そのためにはデプロイ先を設定に登録しなければならない。また認証も必要になってくるので、まずはそのために必要なartifacts-keyring
を導入しておく。
poetry self add artifacts-keyring
そのあと、以下のようなコマンドでazure
としてデプロイ先と認証情報等を登録する。azure
ではない名前にしたい場合はrepository
やhttp-basic
の.
以下の名前を変えればよい。
poetry config repositories.azure <フィードのアップロード用URL> --local
poetry config http-basic.azure <ユーザー名> <PAT> --local
<フィードのアップロード用URL>
公式のドキュメントを見ると以下の二パターンであるが、[Connect to Feed] から URL を確認&コピーできるので、そこから行うのが確実である。
# プロジェクトスコープの場合
https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/pypi/upload
# 組織スコープの場合
https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/pypi/upload
なお、組織ではなく個人アカウント上でも Azure DevOps では一つの"組織"とみなすので、個人の場合<ORGANIZATION_NAME>
に個人アカウントの ID が入るだろう。
<ユーザー名>
どんな名前でも問題ない。
<PAT>
Personal Access Token で以下の記事を参考に取得する。[Scope] のところは "Packaging" の項目を "Read, write, & manage" にチェックするだけで今回は問題ない。
そうするとランダムのように見える文字列が発行されるので、それを<PAT>
に入れる。
これらを登録できたら以下のコマンドでデプロイできるはずである。
poetry publish -r azure
pip install
の確認
最後にpip install
可能かをテストする。
利用者仮想環境にインストール
利用者の環境はvirtualenv
を使って構築する。
pipx install virtualenv
virutalenv --version
適当にフォルダを用意して、そこに仮想環境(testenv
)を作る。
virtualenv testenv
source ./testenv/Scripts/activate
Power Shell の場合は、以下で有効化する。
./testenv/Scripts/activate.bat
(testenv) というのが先頭に表示されていれば成功である。
(testenv) 環境内で以下のコマンドを行う。
pip install catlib --index-url=<フィードのダウンロード用URL>
なお<フィードのダウンロード用URL>
は以下のようである。
# プロジェクトスコープの場合
https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/pypi/simple
# 組織スコープの場合
https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/pypi/simple
ただ、このままの URL では後述する対話での認証を行うことになるのでちょっと面倒くさい。
PAT を利用する場合、以下のように 上の URL の https://
の直後に<PAT>@
を挿入した URL にすればよい。
https://<PAT>@pkgs.dev.azure.com/~
対話での認証
User for <組織名>.pkgs.visualstudio.com:
と入力を求められるが、ここは任意の文字列でよい。次の
Password:
で PAT を入力する。
このあと、
Save credentials to keyring [y/N]:
という質問が出てくるが、認証情報を記憶させるかどうかなのでどちらでも好きな方で良い。y
を選ぶと認証情報をkeyring
で保持してくれて、以降入力をしなくてもよくなる。(ただし PAT の有効期限に注意。)
使ってみる
あとは適当なスクリプトファイル(user.py
)を書いて実行するとcatlib
が使えることがわかる。
from catlib import talk
print(talk("Hello!"))
print(talk(""))
print(talk("にゃん"))
(testenv) $ python user.py
にゃん
・・・は?
これで今回の目的を達成できた!🎉
Next Step
Azure Pipelines を使ってテスト、ビルド、デプロイの自動化をしたい。
ということで、そのパイプラインを作ってみたので以下の記事を書いた。
記事全体としての参考サイト
mypy
, pytest-cov
, taskipy
の導入
(おまけ1)Python の有用なパッケージの紹介は、優れた記事がたくさんあるので調べるとよい。
その中でもmypy
とpytest-cov
とtaskipy
とを導入してみる。
poetry add mypy pytest-cov taskipy --dev
mypy
は以下のように使って静的型チェックをしてくれる。型が動的であることが、Python の良いところであり悪いところでもあるが、mypy
はそれの頼もしい味方だ。
poetry run mypy <対象フォルダ>
pytest-cov
はpytest
のテストカバレッジを表示してくれる。pytest
に--cov
オプションで対象フォルダを指定する。
poetry run pytest --cov=<対象フォルダ>
taskipy
はタスクランナーで、様々なコマンドを一つに吸収してくれる感じのやつ。たとえばcatlib
でpyproject.toml
に以下のような記述を追加してみる。
[tool.taskipy.tasks]
test = "pytest tests --cov=catlib"
typecheck = "mypy catlib"
すると各ツールのコマンドの詳細を覚えなくても統一的に使えるようになる。
poetry run task test
poetry run task typecheck
twine
を利用したデプロイ
(おまけ2)公式の指示[2:1]と同じtwine
を使う方法も紹介する。ただし一部違うやり方である。また、Azure Pipelines でデプロイの自動化を行う際にはtwine
をむしろ使った方が良いので一応こちらも載せておく。
まずは、いくつかのツールをpipx
経由でインストールする。
pipx twine keyring artifacts-keyring
どうもartifacts-keyring
のインストールがうまくいかなかったので、エラーの指示にしたがって以下のコマンドでできた。
pipx install artifacts-keyring --include-deps
次にリポジトリ直下にtwine
用の設定ファイルを作成する。
{root}
├── .pypirc ← new!✨
├── pyproject.toml
├── ...
このファイルを以下のように編集する。
[distutils]
Index-servers =
catlib
[catlib]
Repository = <フィードのアップロード用URL>
username = <ユーザー名> # 任意の名前
password = <PAT>
<フィードのアップロード用URL>
は公式のドキュメントを見ると以下の二パターンであるが、[Connect to Feed] から URL を確認&コピーできる。(個人アカウントの場合 URL が異なるかもしれない。)
# プロジェクトスコープの場合
https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/pypi/upload
# 組織スコープの場合
https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/pypi/upload
<PAT>
としているところは Personal Access Token で以下の記事を参考に取得する。[Scope] のところは "Packaging" の項目を "Read, write, & manage" にチェックするだけで今回は問題ない。
そうするとランダムのように見える文字列が発行されるので、それを<PAT>
に入れる。
あとは以下のコマンドでアップロードできる。
twine upload -r catlib --config-file .pypirc dist/*
Azure Artifacts にcatlib
が置いてあれば成功である。
Discussion