疑問
社内ツール提供の目的でgitなどのVCS管理下のpackageを直接pipでインストール出来るようにしたいです。ここで、プライベートなpackageに依存する場合、どうやってpip install
で社内に配布可能なのか、よくわかりません。
動機
- 普通に
setup.py
を書いて試したけど、この挙動が自身の想定と異なっていて、setuptools.setup
関数の引数の意味(特にinstall_requires
とdependency_links
)をもしかすると勘違いしているのかなぁ、と思ったのがきっかけ。
https://stackoverflow.com/questions/12518499/pip-ignores-dependency-links-in-setup-py も参照。
解決策
以下のような手順で進めていく:
-
ライブラリAを作成する
-
ライブラリAに
setup.py
を追加し、このライブラリをリモートレポジトリAにpushする -
ライブラリAに依存するライブラリBを作成する。
-
ライブラリBに
setup.py
とrequirements.txt
を追加する -
pip install
する。
これで、プライベートなpackageに依存するgithub のリポジトリをpipでインストールする手筈は整った🙌
以下、詳細な説明。
- gitで管理されている
abc-road
というライブラリをpip install
で社内に配布したいのが目的。ここで、このライブラリはabc-bicycle
というプライベートなpackageに依存する前提で話をすすめる。
- ライブラリ直下に以下のsetup.pyを書く:
from setuptools import setup, find_packages
project_name = 'abc-bicycle'
setup(name=project_name,
version='0.0.1',
description='description',
long_description='long description',
packages=find_packages(),
include_package_data=True, # 気にせずTrue
zip_safe=False,
install_requires=[
#library in PyPi only
"pandas==0.20.1",
],
# 書くべきでない(詳細を参照)
# dependency_links = [],
entry_points="""
""")
これを含んだライブラリをリモートにpushする。この時、git@github.com:org_name/private_repo_name_a.git
をリモートレポジトリと設定する。ここで、上記のproject_name
変数とprivate_repository_name_a
は違う名前であっても良い。
3&4. このrepositoryに依存するライブラリを新規作成し、pip
でinstallできるようにしよう。
ここで、git@github.com:org_name/private_repo_name_b.git
をリモートレポジトリとする。
setup.pyをおんなじように書く:
from setuptools import setup, find_packages
project_name = 'abc-road'
setup(name=project_name,
version='0.0.1',
description='description',
long_description='long description',
packages=find_packages(),
include_package_data=True, # 気にせずTrue
zip_safe=False,
entry_points="""
""")
また、ライブラリ直下にrequirements.txtを以下のように書く(PyPi以外のライブラリをここに突っ込む):
-e git+ssh://git@github.com/org_name/private_repo_name_a.git@master#egg=abc_bicycle
いくつか留意点を。
-
#egg
以下の部分は依存するライブラリのsetup関数のname引数であることに留意すること。この部分は必須。 - ブランチ指定もできて、
@branch_name
みたいな記法となる。ただし、マスターブランチの場合は指定しなくても良い。 - バージョン指定するときは、
#egg=abc_bicycle-0.1.1
みたいな感じで書く。
ここで、<project_name>-<version>
のように、hyphen(-
)でレポジトリ名とバージョン名が識別されるため、レポジトリ名にhyphenを使用している場合は、underbar(_
)に置換しておこう。
5. そして、以下のオプションをつけて、pip install
する:
# インストールする
pip install -r requirements.txt
pip install -e git+ssh://git@github.com/org_name/private_repo_name_b@master#abc_road
# 再インストールの場合、ローカルにライブラリのキャッシュが残っている可能性があるので、いくつかオプションをつける(--no-cache-dirと-I)と確実
pip --no-cache-dir install -I -r requirements.txt
pip --no-cache-dir install -I -e git+ssh://git@github.com/org_name/private_repo_name_b@master#egg=abc_road
ここまでで、本題は終わり。以下、補足事項。
補足
仮想環境の準備
# pyenvでpython3.6.1をインストール
brew install pyenv # pythonのバージョン切り替えを管理
pyenv install 3.6.1
# インストールされてるpythonのバージョン(複数)を確認
pyenv versions
pyenv global 3.6.1 # defaultのpython versionを3.6.1.に変更
pyenv rehash # 諸々更新する必要がある
# virtualenvをインストールする (pipは元から入っている)
pip install --user virtualenv # sudoの問題のため、--userをつけてインストールした
# virtualenvはPython の isolated な仮想環境を便利に作成するコマンド(pオプション無しの場合、pyenv globalで指定したPythonのversionが入る
virtualenv test01 # 標準で setuptools, pip, wheelがインストールされる
# 仮想環境を起動する
source ./test01/bin/activate
# この後、それぞれの仮想環境に対して、pipして、ライブラリをinstall, uninstallする
pipの使用方法
以下の環境で試している:
$ pip list --format=columns
Package Version
---------- -------
pip 9.0.1
setuptools 28.8.0
virtualenv 15.1.0
最初はローカル環境で試す:
# ローカルにインストール
pip install -e . # --editableでもよい
## ローカルに(強制)再インストール
pip --no-cache-dir install -I -e .
## pip内のpackageを全て削除( grep -v "^-e" でvcsのものも削除できる)
pip freeze | grep -v "^-e" | xargs pip uninstall -y
## vcs管理下のもののpackageを削除する
pip freeze | sed "s/.*#egg=//g" | xargs pip uninstall -y
# requirements.txtに記載のpackagesを全てinstall
pip install -r requirements.txt
うまくいったら、リモートレポジトリに成功したやつをプッシュした上で以下のようにコマンドを叩く:
# project_nameはsetup関数のname引数に記載のやつ
pip install -e git+ssh://git@github.com/org_name/private_repo_name.git@master#egg=project_name
用語
http://www.yunabe.jp/docs/python_package_management.html のキーワードの節を見ましょう。
一般的と思われるsetuptools
モジュールを用いてライブラリのインストール環境を整える。
setuptools
の引数について
https://setuptools.readthedocs.io/en/latest/ の"New and Changed setup() Keywords" を主に参照する。ここでは、上記で用いられている引数について、簡単に解説、補足を行う:
MetaData
https://setuptools.readthedocs.io/en/latest/setuptools.html#metadata も見よう。name
とversion
以外は適当で良い。
-
name .. projectの名前(
str
型)。これは、repository_nameと異なっていても構わない。
PyPiに登録したときは、インストール時にpip install project_name
とすれば良い。gitなどvcsに管理されたものをインストールする時には、pip install -e vcs+proto://host/path@revision#egg=[project]-[version]
みたいな感じでegg
の引数部分にproject_nameが指定される。 -
version .. version名(str型) 文字通り、version指定。
-
description, long_despription .. ライブラリの概要とか書けばいいと思うよ。
Option
https://setuptools.readthedocs.io/en/latest/setuptools.html#options も見よう。
- packages .. package名(
str
型)のlist
を突っ込む。とくに何もなければ、、find_packages()
を引数に渡す。こうすれば、このライブラリのパッケージ名を取得してくれる。
ちなみに、ライブラリ直下にsrcディレクトリがあり、import src
みたいにせずに、srcより下のディレクトリをパッケージと認識させたい場合は、引数を以下のようにすると良い!
packages=find_packages("src"),
package_dir={'':'src'},
-
include_package_data .. ライブラリ内にあるdata形式をstrのlistで指定できる。
True
ならば、全てのdataをパッケージとして包むことになる。 -
zip_safe .. ライブラリが安定版なら、
True
, 開発中ならFalse
にする。(とりあえずはFalse
指定で良い) -
install_requires .. 依存するライブラリ(
str
型)のリストを渡す。解決策では、プライベートレポジトリのライブラリはここに含めていないが、理由は次節で解説する。 -
dependency_links .. PyPi以外のレポジトリを参照する際、どこから入手したらいいかを書く。例えば、
["git+ssh://git@github.com/usr_name/private_repo_name.git@master#egg=abc_bicycle-0.0.1"]
みたいに記述する。本記事では、dependency_links
は使わないようにしているが、次節で解説する。
dependency_links
を用いるべきでない理由
簡単にいうと、以下の2点に集約される:
-
pip1.5の時点で、
pip
はdependency linksを をデフォルトで無視するようになっている1。dependency linksの引数を有効にするには、--process-dependency-links
が必要である。
しかし、一つでも依存ライブラリのsetup関数にdependency links引数を指定していたら、pip install
時に--process-dependency-links
オプションをつける必要があり(つまり、--process-dependency-links
が遺伝する)、使う側に不便を強いる。 -
dependency linksをつけた依存ライブラリは
pip freeze > requirements.txt
する時、vcsから入手したということにならない(install requires
の情報のみ書き込まれる。)というわけで、freezeした後のpip isntall -r requirements.txt
は失敗してしまい、もはや、pip freeze
は使い物にならない。
なので、install_requires
には、PyPi
にあるライブラリのみを記述し、その他は、requirements.txt
に追加しておくのがよい。
それでもdependency_links
を使ってみる
abc-bicycle
(private_repo_name_a
の方)はdependency_links
と無縁なので変化なし。
リモートレポジトリがgit@github.com:org_name/private_repo_name_b.git
のライブラリの場合、setup.pyを以下のように変更する:
from setuptools import setup, find_packages
setup(name='abc-road',
version='0.0.1',
description='description',
long_description='long description',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires= [
"pandas",
'abc-bicycle==0.0.1' # バージョン名を明記
],
# vcs(gitなど)の場合は、必ず、eggを追記する必要がある
dependency_links = [
"git+ssh://git@github.com/org_name/abc-bicycle.git@master#egg=abc_bicycle-0.0.1"],
entry_points="""
""")
dependency_links
のeggの書き方は「解決策」の章の3&4.の詳細(下の方)と全く同じ。ただし、バージョン名は必須なところが相違点。
ポイントは、install_requires
とdependency_links
の両方にPyPi以外のライブラリの情報を付記することだ。pip install
で dependency_links
を有効にする(つまり、--process-dependency-links
オプションを追加する)と、install_requires
の各ライブラリについて、dependency_links
のリンク先と合致するかを確認した上で、PyPi内で符合するライブラリを探す。
もし、所要のPyPi以外の依存ライブラリをdependency_links
のみに書くと、install_requires
に記載されていないため、無視される。
上記でprivateなpackageをdependency_links
に渡したので、requirements.txt
は不要。
そして、pip install
のコマンドは以下のようになる:
# --process-dependency-linksをつける必要がある
pip install -e git+ssh://git@github.com/org_name/private_repo_name_b.git@master#egg=abc_road --process-dependency-links
これでOK!!
参考url
- https://setuptools.readthedocs.io/en/latest/ setuptoolsのdocument
- https://pip.pypa.io/en/latest/reference/pip_install/#git pipのdocument