Slide 1

Slide 1 text

Pythonのパッケージ管理の中級者の壁を超える エムスリー株式会社 VPoE 河合俊典 (@vaaaaanquish)

Slide 2

Slide 2 text

自己紹介 Shunsuke Kawai (@vaaaaanquish) ● エムスリー株式会社 VPoE ● Google Cloud Champion Innovator (AI/ML) ● 機械学習ライブラリでOSS活動 ● PyCon APAC 2023 Patron ● 過去Pythonパッケージ管理について 大きな記事を書いている

Slide 3

Slide 3 text

今日する話/しない話 ● する話 󰢏 ○ Pythonのパッケージ管理がどのように作られているか ○ Pythonのパッケージ管理の何が大変なのか ● 前提 ○ Pythonのパッケージ管理ライブラリ(pip, pipenv, poetry,…,etc)を開発で一定使っている ○ ツールが沢山あり、それぞれ何をするものか/どのような機能があるかは大体知っている ● しない話 󰢃 ○ パッケージ管理何使えばいいの? ○ Pythonでの開発のベストプラクティス ○ 歴史的なお話

Slide 4

Slide 4 text

エンジニア採用ページ https://jobs.m3.com/engineer/ プロダクト紹介ページ https://jobs.m3.com/product/ エムスリーテックブログ https://www.m3tech.blog/ 「聴きたい話じゃないかもな…」 と思ったら! - エムスリーをもっと詳しく - 社員インタビュー https://www.wantedly.com/companies/m3_inc VPoE登壇資料 fukabori.fm https://fukabori.fm/episode/59 https://fukabori.fm/episode/60 Connpass公式グループ https://m3-engineer.connpass.com/ エンジニア公式Twitter https://twitter.com/m3_engi neering エンジニア公式YouTube https://www.youtube.com/channel /UC_DkAOcwgmtQnJLDctci4rQ

Slide 5

Slide 5 text

Pythonと パッケージ管理ツール 全体観

Slide 6

Slide 6 text

現代における”プログラミング言語のパッケージ管理”は Package managerという名前で機能の幅を広げている ※ビルド/パッケージングのように言葉選びが言語間で微妙に違ったりするのも注意

Slide 7

Slide 7 text

現代における”プログラミング言語のパッケージ管理”は Package managerという名前で機能の幅を広げている ※ビルド/パッケージングのように言葉選びが言語間で微妙に違ったりするのも注意 ● 最近は言語によって言語公式defaultであったり 拡張機能としてサポートされている場合がある ● ソフトウェア開発の定番が出来つつある現代では   「ユーザ的には1つのツールで済む方が便利だから」 → 適切な会話や技術選定をするためにpackage managerの   利用目的や前提を合わせるのも大切!

Slide 8

Slide 8 text

Pythonにおける”パッケージ管理”は 機能ごとに小さく作られたモジュールの集合体 ● Python(水色)、PyPA(黄色)、PyCQA(緑色)といった団体、それら以外のOSS開発者も ● 組み合わせた「パッケージ管理ライブラリ」が存在、小さく作る事で統廃合が可能に

Slide 9

Slide 9 text

※env内のpipは正確にはensurepipで微妙な違いがあるので注意 Pythonの”パッケージ管理ライブラリ”は作れる ● 見知ったパッケージ管理ライブラリ も小さなモジュールの組み合わせ ● 小さなモジュールの多くがPyPAがPEPに 沿って作成した堅いライブラリである   「俺の考えた最強のPackage Manager」                 は作れる!

Slide 10

Slide 10 text

ここまでで言いたいこと ● “Package Manager”と言いつつ人類は全てをそこに求めている ○ 特に他の言語と比較したりされやすい ● Pythonは小さいモジュールが機能毎にある ○ マイクロかモノリシックか、に似たメリット/デメリットがある ○ easyでもone wayでもなく”simple” ■ PEP/PyPAが作るモジュールを使える ■ Pythonの拡張容易性の高さを活かせる土台がある ■ 一部の切り捨てもやりやすくarchivedやmergedなツールも多い ○ このおかげで”現代の”ソフトウェア開発のベストプラクティスを 詰め込んだようなCargo(Rust)を意識したpoetryやryeが登場している ○ 逆に2022年のPackaging Surveyでも多かった要望は 「絶対的なただ1つのPackage Manager」という事実もある 現代では大企業がバックに付くプログラミング言語も多い中 (Python Software Foundationがあるとは言え) ボランティアが多いPyPAを筆頭にコミュニティと合議的なPEP規格と OSS文化で30年以上続く土台を作っている。本当にすごい事。

Slide 11

Slide 11 text

依存解決から見た Pythonの パッケージ管理ツール 11

Slide 12

Slide 12 text

依存解決をしてみよう Package A >=1.0.0 Package C <=2.0.0 Package AとPackage Cが requirements.txt (pyproject.tomlでも何でも) に書いてあるとしましょう

Slide 13

Slide 13 text

依存解決の無い世界線 Package A Package B <=2.0.0 Package C Package B >=1.0.0 1. Package Aをinstall a. Package Bは2.0.0が入る 2. Package Cをinstall a. Package Bは1.0.0が入る → 最終的に全て満たせていてよさそう!

Slide 14

Slide 14 text

依存解決の無い世界線でのアップグレード Package B >=3.0.0 1. Package Aをinstall a. Package Bは2.0.0が入る 2. Package Cをinstall a. Package Bは3.0.0が入る 順番にinstallすると後からpackage Bを installした時にPackage Aは依存バージョン が満たせなくて困っちゃう💧 Package Cのバー ジョンを上げたら Bがv3.0.0以上 のみサポートに Collecting PackageB> =3.0.0 Using cached B.whl (7.6 kB) ERROR: PackageA x.x.x has requirement PackageB<=2.0.0, but you'll have PackageB 3.0.0 which is incompatible. Installing collected packages: … Successfully installed PackageD-x.x.x PackageB-3.0.0 これが実は昔のpip installの挙動 Package A Package B <=2.0.0 Package C Errorが出てるけどSuccessfully(pip checkで人間が解消したりしてた)

Slide 15

Slide 15 text

依存解決が導入された世界線 まず満たせない時は失敗する必要がある pip 20.2より『2020-resolver』が実装 現時点ではどんなツールでもオプション等使わない限り このような依存関係上のインストールは実行できない ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/user_guide/#fixing-confl icting-dependencies Package B >=3.0.0 Package A Package B <=2.0.0 Package C

Slide 16

Slide 16 text

少し複雑な依存解決をしてみよう Package A >=1.0.0 Package C <=2.0.0 ふむふむ、Package Bが被っているけど なんか1.0.0~2.0.0を入れれば大丈夫そうだ! 他のパッケージ依存がないか確認しよう Package B <=2.0.0 Package B >=1.0.0

Slide 17

Slide 17 text

依存解決をしてみよう Package A ==1.0.0 Package C ==2.0.0 Package B >=1.5.0 Package D >=1.0.0 Package E >=4.0.0 1. Package Aを1.0.0で一旦固定 a. AはB, Dに依存しているらしい b. Dも一旦1.0.0で固定 i. D 1.0.0はB 2.0.0以下が必要 c. Bは1.5.0以上 2.0.0以下で成り立ちそう d. Bを1.5.0で固定 i. 依存しているEは3.0.0以下が必要 2. Package Cを2.0.0で一旦固定 a. CはEに依存しているらしい b. C 2.0.0はEは4.0.0以上を必要としているが ①で得たEの情報とコンフリクト! 3. “戻って”Package Cを1.0.0で一旦固定 a. 同じくEに依存しているが3.0.0で成り立ちそう  (もう少し探索できるけど一旦ここでまとめると) ● A==1.0.0、B==1.5.0、C==1.0.0 D==1.0.0、E==3.0.0  → requirements.txtやpoetry.lockが完成! Package B <=2.0.0 Package D ==1.0.0 Package B ==1.5.0 Package E <=3.0.0 Package C ==1.0.0 Conflict! Package E >=3.0.0 Package A >=1.0.0 Package C <=2.0.0

Slide 18

Slide 18 text

Backtrackingによる依存解決 先程紹介した方法は(ほぼ)Backtrackingと呼ばれるアルゴリズム ● 候補となるバージョンを決定 → 深さ優先探索 → バージョンがコンフリクトすればそれを解に → 再帰的に繰り返し ● pipやpipenvが利用しているresolvelibではBacktrackingによる依存解決が行われている ● 課題:依存関係をバージョンごとに走査し直す必要があり 確認したいパッケージに対して指数関数的に時間が増加する  → 一定走査を諦めたりキャッシュを使うなど工夫されたBacktracking拡張ないし別アルゴリズムが存在  → 例えばpip 23.1でBacktrackingに導入されたBackjumpでは速度が大きく改善された Package A Package B Package C Package C Package C Package B Package A

Slide 19

Slide 19 text

依存地獄(Dependency Hell) ● 素よりライブラリ依存は複雑なGraphである ● 依存解決が可能か 対応する全てのバージョンにおける依存関係 はどのようになっているか 最終的にどのバージョンを選ぶと良いか ● これらを計算するのが 『dependency resolver (solver)』 計算大変、例えば最近LLM周りで持て囃されているLangChain langchain = { version="*", extras=["all"] } → 288個のライブラリに依存   M2 Mac上のpipenvでは   425回PyPIにアクセスし、6522個の依存関係を確認   その依存解決に102秒かかる   1つのライブラリを入れたいだけなのに… ※これが普遍的に高速に解けるアルゴリズム思い付いた方DMでこっそり教えて下さい!

Slide 20

Slide 20 text

lockファイルの生成 ● 現代のPackage Managerの役割の1つ ○ 依存解決で得られた各ライブラリの全てのバージョン、hash値が記されている ○ 複数の環境で「同じパッケージ」をインストールするために必要 Package A >=1.0.0 Package B Package C ==1.5.0 requirements lock Package A ==1.0.0 Package B ==0.2.0 Package D ==3.2.1 requirements(dependency)ファイル だけではCやDのバージョンに差異が 生まれる可能性がある 手元の開発、他環境で”再現”するため に必須となっているファイル 一方「”同じ”とは何か」がPythonの 依存解決における一番大きな議論に なっている… lockファイルはPEPで標準化されていません でしたが、現代の開発においては非常に良く 使われる機能で標準化の議論も進んでいます

Slide 21

Slide 21 text

[ここで質問] みなさんにとって “同じパッケージ” とはなんですか? ● バージョンが同じ? ● 中身のコードが同じ? ● 依存先のパッケージのバージョンも同じ? ● パッケージ内に含まれる全てのバイナリが同じ? ● バイナリも外部の依存ツールも同じなら同じ? ● バイナリも外部の依存ツールもOSもCPUも同じなら同じ?

Slide 22

Slide 22 text

依存解決で出てくるPythonならではの課題『再現性』 ● packageの配布方法は大きく2種類 ○ bdist(wheel): build済みのファイルが配布される ○ sdist: install時にbuildされる ○ 配布方法はパッケージの開発者が選択 ● metadataを知る方法も異なる ○ bdist ■ PyPIではJSON APIに問い合わせると hash値や依存先の情報が返却される(※最新の適切なwheelの形式を取っていれば) ○ sdist ■ PC上でbuildしてみるまでhash値も依存先も”正確には”わからない       → 依存関係を知るために一部download, build必須問題       → バイナリを一意に識別するartifact hashが一部build必須問題      ※最適な.whlを選ぶ方法問題もあるが割愛

Slide 23

Slide 23 text

依存解決で出てくるPythonならではの課題『再現性』 ● 「じゃあ全て適切なbdistにすれば良い?」 ○ 過去のsdistはどうするのか ○ 全てのプラットフォームでbuildするのは誰がやるのか ○ スパコンみたいなエッジケースはどうするのか ○ PyPI以外のPackage Registryが絡む場合はどうするのか ○ 監査上の理由から「バイナリのインストールはNG」という場合はどうするのか … ● 再現性が依存解決に与える問題 ○ 中身が同じである事を証明できないと人によってはlockファイルの価値がなくなる → 環境によって動作が違うということになる つまり完全に再現可能かを知るためには ”””依存解決中に一部packageをdownload/buildしてみる必要がある””” しかも必要性が想定される全てのパッケージ、バージョンで!!重い!! Pythonの利用ケースの多様性 30年以上の歴史の中で 順に生まれたsdist, egg, bdist, wheelの PEP規格という特性もある (例えばwheel PEP427採択は2012年)

Slide 24

Slide 24 text

進む再現性への対策 ● 近年ではbdist(wheel)での公開が推奨/一般的になりつつある ○ 例えばTensorflowのインストール ■ かなり簡単になった、buildしたい場合はgit clone ○ 先程のLangChainの依存パッケージだと10/288がsdist ● sdistでもパッケージ作成者が適切に設定したmetadataに書かれた情報を信頼する方法 ○ PyPIのAPIがartifact hash、依存情報を返す ○ 完全な同一とは言えるのか議論があったが採択され実装が進む (PEP 658, 2023/5/10 Accept) ● どこまでやるかだが、緩く「どうせ一緒でしょ」と捉える方法もありえる  → poetryなど3rd party package manager 個人的にはGitHub Actions等で CI/CDのテンプレート化が かなり進んだのも大きい PyPIダウンロード上位360のう ちsagemaker, future, pyspark を除いてすべてwheel配布 (2023/10/10) https://pythonwheels.com Package Manager視点では PyPI以外のPackage Registryや git等で考慮しないといけない点 まだあるが… これからは全てのwhlをPyPIに適 切に挙げてくれ… metadataを書いてくれ…

Slide 25

Slide 25 text

つまり依存解決からパッケージ管理を見た時に言えること ● パッケージ依存が解決出来ない場合はそれを知らせる必要がある ● 効率的かつ高速な依存解決solver/resolverが搭載されている方が良い ● lockファイル等で”パッケージの再現性”を担保する事が求められている ● 再現性、パッケージが同じとは何か、について歴史と文化が伴う ● 再現性に関する歴史と文化によって依存解決も影響を受けている 2023年より立ち上がった python-packaging-strategy -discussionという公式 スレッドを”必ず”見てほしい Intelスポンサードの作成された pypackaging-nativeという ドキュメントに課題がまとまって いるので”必ず”見てほしい

Slide 26

Slide 26 text

パッケージ管理を 依存解決の側面から 攻め落とす

Slide 27

Slide 27 text

PubGrubによる依存解決 ● Dart言語のパッケージマネージャーpubに導入された依存解決アルゴリズム (Dart>=2.0.0) ○ Swift(spm, swift>=4.2.2)、Ruby(bundler>=2.4)、poetryのmixology ○ 2012年に提案されたアプローチ ■ CDCLアルゴリズムをベースとしている ● conflictした時の理由を覚えておき探索範囲を削る手法 ● Go(dep)にはシンプルなCDCLが実装されている ■ 覚えているconflictの理由を使って、別の競合の探索範囲も削る ● Good ○ 探索範囲が狭くなるのでdownload/buildしながら依存解決 しないといけない状況と相性が良い ○ 「依存解決できない」理由も明確に示せる ● Issue ○ 探索範囲を大きく削っている分、出せない回答もある ※本スライド作成にあたり調査中「npmやcargoもpubgrubだよ」という記述を数回見たがコード見る限りはまだbacktracking、変更を進める議論はある(2023/10/08) ・WikipediaのConflict-driven clause learningのページと pubの公式READMEに視覚的に分かりやすい図がある ・OSC2021 OnlineFukuokaの村上涼さんのPubGrub解説が YouTubeに上がっている どちらもオススメ

Slide 28

Slide 28 text

libmambaによる依存解決 ● condaで使われる依存解決ライブラリ ○ conda packageはsdist/bdistではない方法で配布 ■ Anacondaリポジトリ(つまりPyPIミラー)内でhashを計算する ■ artifact hash問題がほぼAnaconda任せにできる ○ libsolvがベース ■ 2007年からopenSUSEのコミュニティで開発されているbacktrackingリゾルバ ■ 重み付き辞書によるソートにより高速化 ● Good ○ 再現性問題をAnacondaリポジトリが引き受け考える事を減らすことで 歴史ある安定して早いsolverのPythonバインディングを利用できている ● Issue ○ Hardlinkでの高速化も計っているのでpip等他ツールで環境が壊れやすい ○ Anaconda一部営利企業で有償化、Package Registry運営は想像するだけで大変そう… mamba documentのpackage_resolutionのページ conda公式BlogのUnderstanding and Improving Conda’s performanceという記事にそれぞれ アルゴリズムの実行例があるのでオススメ 依存関係ビューア等も作ってい てすごい

Slide 29

Slide 29 text

さらに深みに行く依存解決から見たパッケージ管理 ● solverのアルゴリズム ○ まだまだ沢山ある ○ 「充足可能性問題」「Satisfiability Problem(SAT)」 ○ 探索範囲削減による問題、セキュリティ課題、 SATの並列化、統計的推論としての拡張…etc ● ビルドキャッシュや外部のツール、ライブラリとの併走戦略 ○ envやdockerとのやり取りはどうするのか ○ bazel、pants等を用いた場合ビルドサーバの概念が入る ● その他にもある余地 ○ package registoryは本当に必要か? ○ そもそも複数のバージョンが入って問題な時と問題でない時がないか? ■ 例えばJavaScript(node)、goの依存に対する考え方は面白い ○ 最近フロントエンド界隈で早いとされているBunではlockファイルをバイナリで扱う事 までしてパフォーマンスを上げていたりする ○ multi-languageなパッケージ管理はどうか? ■ conda packageの概念をうまく使ったpixi等がある

Slide 30

Slide 30 text

おわりに Pythonの パッケージ管理 の中級者の壁を超える

Slide 31

Slide 31 text

パッケージ管理、今日の話を踏まえると後はこれくらい 作れそうな気がしてきましたね!完全に中級者の壁は超えました! ※もっと言えばセキュリティやライセンス、 クロスプラットフォーム…etcな話もありますが一旦ね

Slide 32

Slide 32 text

今日のまとめ ● Pythonのパッケージ管理ライブラリは小さな実装、小さなPackageの集合体 ○ easyでもone wayでもなく”simple” ○ パッケージ管理に求められる要件年々増える中で工夫している ● 特にPython特有の課題として依存解決が様々な所で障壁になっている ○ sdist/bdist、artifact hash問題、「同じパッケージ」かどうか ○ 様々な解消の方法があり継続して議論も進んでいる ● 壁を超えた皆さんなら「俺の考えた最強のパッケージ管理ライブラリ」を作れる! こういった内容からPackage Managerを理解し 自作するなどしてみて実力をつけ PyPAの制作物や主要なPackage Managerへ コミットできる上級者になって頂く事を 期待しています

Slide 33

Slide 33 text

PackagingCon 2023 in Berlin (10/26~28) PyPAやcondaの開発者、他言語やOSの Package Managerを作る巨人が集まる神イベ ント!YouTubeにArchivesもあるよ! donate.pypi.org PyCon 2023 Day2 (10/28) Packaging Toolの開発パターン別 プラクティス紹介 by Peacock Pythonのパッケージ管理 開発者にdonation! Packaging関連で参考にした URLや文献をまとめたgist 技術書典15(2023.11.12〜) エムスリーテックブック5 “自作PythonPackageManager入門” で書く予定です是非(なお進捗)

Slide 34

Slide 34 text

Thank you 34