nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pythonのパッケージを開発するとき、依存するライブラリはピンポイントでバージョン固定しないようにしましょう

結論

setup.cfg1(やsetup.py2)のinstall_requiresextras_require
pyproject.toml3dependenciesoptional-dependenciesについての話です4

開発しているパッケージ(awesome_lib)が依存するライブラリ(例:other_lib)について5

  • DON'T🙅‍♂️ other_lib==1.2.3のように特定のバージョンをピンポイント📌で指定する
  • DO🙆‍♂️
    • バージョン指定しない other_lib
    • 指定が必要な場合は範囲で other_lib<2

目次

オススメ資料「パッケージングしよう」

みんなのPython勉強会(2022年1月)のaodagさん6発表「パッケージングしよう」7

アーカイブもあります(このトピックは19:01〜)

書き起こしてみます

ここ(install_requires)にバージョン指定できるんですけど、直接ここにバージョン固定して書かないでください。
使う人は、依存関係ガチガチに固定されても、他のライブラリとぶつかってインストールできなくなるだけなので、
最低このバージョン以上にしてなど、範囲指定で書きましょう

other_lib==1.2.3のようにバージョンを固定すると、開発しているライブラリ(awesome_lib)のユーザがインストールできないことがあるわけですね。

本記事執筆の経緯

ホッテントリで見かけたこちらの記事がきっかけです。

chat-hatenablog、面白そう!👏
試してみたい〜
そんな私にとって、GitHubからインストールできるのは嬉しいです!

読んでいて、ピンポイントでバージョン固定されていることに気づいたので、以下のようにコメントしました。

Pythonで作ったCLIツールをGitHubから直接pipでinstallできるようにする方法 - $shibayu36->blog;

「こうするともっと便利という情報があれば知りたい。」で浮かんだのは、install_requiresにはバージョンを直接指定せず、範囲指定するです。ref: <a href="https://www.slideshare.net/aodag/python77/26" target="_blank" rel="noopener nofollow">https://www.slideshare.net/aodag/python77/26</a>

2023/05/04 02:26

ですが、私の言葉足らずな感じがぬぐえず、今回記事としてアウトプットしました(ブックマークコメントの100文字は短すぎたのです)。

インストールするユーザからの見え方

install_requiresには例えば"openai==0.27.5"と指定されています8
chat-hatenablogを使いたい」ユーザが0.27.5以外のバージョンのopenaiをインストールしていた場合、環境構築でちょっと苦労することになってしまいます。

動作検証環境はこちら

  • Python 3.10.9
  • pip 23.1.2
  • setuptools 67.7.2

OpenAIの開発が活発(執筆時点で最新は0.27.6)なので、私みたいなユーザは最新を使っていきます。

$ pip install git+https://github.com/shibayu36/chat-hatenablog.git openai==0.27.6

(略)

ERROR: Cannot install chat-hatenablog==0.3.0 and openai==0.27.6 because these package versions have conflicting dependencies.

The conflict is caused by:
    The user requested openai==0.27.6
    chat-hatenablog 0.3.0 depends on openai==0.27.5

To fix this you could try to:
1. loosen the range of package versions you've specified
2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts

これは「他のライブラリとぶつかってインストールできなくなる」を露骨に示すためのあまり現実的ではない例です。

現実的にありそうなのは以下でしょうか。

  1. openaiをインストール済み
  2. そこにchat-hatenablogを追加でインストール
$ pip install openai

(略)

Successfully installed aiohttp-3.8.4 aiosignal-1.3.1 async-timeout-4.0.2 attrs-23.1.0 certifi-2022.12.7 charset-normalizer-3.1.0 frozenlist-1.3.3 idna-3.4 multidict-6.0.4 openai-0.27.6 requests-2.30.0 tqdm-4.65.0 urllib3-2.0.2 yarl-1.9.2

$ pip install git+https://github.com/shibayu36/chat-hatenablog.git

(略)

  Attempting uninstall: openai
    Found existing installation: openai 0.27.6
    Uninstalling openai-0.27.6:
      Successfully uninstalled openai-0.27.6
Successfully installed PyYAML-6.0 SQLAlchemy-2.0.12 chat-hatenablog-0.3.0 dataclasses-json-0.5.7 html2text-2020.1.16 langchain-0.0.154 marshmallow-3.19.0 marshmallow-enum-1.5.1 mypy-extensions-1.0.0 numexpr-2.8.4 numpy-1.24.3 openai-0.27.5 openapi-schema-pydantic-1.2.4 packaging-23.1 pydantic-1.10.7 python-dotenv-1.0.0 regex-2023.5.5 tenacity-8.2.2 tiktoken-0.3.3 typing-extensions-4.5.0 typing-inspect-0.8.0

openaiのバージョンがchat-hatenablogで指定された0.27.5まで下がります。
同時にインストールしない場合は後勝ちになるようですね。

openaiをアップデートすることはできます(ERRORと表示されますが、exit codeは0です)

$ pip install openai==0.27.6

(略)

Installing collected packages: openai
  Attempting uninstall: openai
    Found existing installation: openai 0.27.5
    Uninstalling openai-0.27.5:
      Successfully uninstalled openai-0.27.5
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
chat-hatenablog 0.3.0 requires openai==0.27.5, but you have openai 0.27.6 which is incompatible.
Successfully installed openai-0.27.6

この動きを全員が気にするわけではないと思いますが、

  • setup.pyのinstall_requiresは依存ライブラリのバージョンを固定する用途では使わない慣習という理解
  • 開発者が少し手を動かすだけで上記の動きは取り除ける

という2つの理由からinstall_requiresには"openai"(バージョン指定しない)という書き方が私からの提案となります。

私の考え:パッケージのinstall_requiresは環境の再現に使うrequirements.txtとは用途が違うのではないか

  • install_requiresextra_requires
    • パッケージのユーザや開発者に一緒にインストールしてほしい依存ライブラリを(必要であれば範囲で)指定する
  • requirements.txtなど
    • Python環境再現に使う
    • パッケージの開発者間で揃えたい場合はpip install -r requirements.txtで環境構築する運用になると考えます

これは私がPythonで開発する機会が多いがために馴染んでいる考え方にすぎず、他の言語のパッケージ管理からするとちょっと異質に見えるかもしれませんね。


  1. https://setuptools.pypa.io/en/latest/userguide/declarative_config.html
  2. setup.pyどうしても必要ではないなら避けよう(setup.cfgかpyproject.tomlを使おう)という内容を知りまして、カッコ書きにしています。ref:https://setuptools.pypa.io/en/latest/userguide/quickstart.html#setup-py
  3. https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
  4. aodagさんのトーク(19:01の少し前のところ)との関連でいうと、pyproject.tomlではめちゃめちゃわかりやすくなってますね
  5. パッケージとライブラリは同じ意味という認識です。この記事では開発するのはパッケージ、パッケージが依存するのはライブラリという語用にしてみました
  6. aodagさんはPyCon JPでもPythonのパッケージングについて何度も発表されており、非常に信頼できる情報筋と認識しています
  7. 過去のパッケージング関連記事でも参照しました。Pythonでリソースファイルと一緒にパッケージをインストールするための設定値(📣MANIFEST.inが必要です) - nikkie-ftnextの日記
  8. https://github.com/shibayu36/chat-hatenablog/blob/04b4486e830380a294867e9dcde010989417aa48/setup.py#L23