2018/04/25

[Python] setup.py に dependency_links を書いても "Could not find a version that satisfies the requirement" が出る場合の対処方法4種

2018/10/16 追記
Pip 18.1 以降は dependency_links を使わない方法が正式になりました。

dependency_links を使わないでレポジトリを明示的に指定する方法

Python で pip install 可能なパッケージを作成した際、 PyPI に登録してしまえば pip が適切に処理してくれるので簡単です。

しかし、内部用のライブラリの場合は明示的にリポジトリの場所を指定しなければなりません。 そのための機能として、setuptools.setup() には dependency_links という引数があります。

ところが、pip install しようとした場合、dependency_links は無視されてしまい、うまくいきません。 dependency_links は、基本的に python setup.py install の場合を想定しているようです。

ただ、pipsetup.py の上位ラッパー1のはず。 対処方法が無いはずはないと思って調べてみました。

普通に pip install するとエラーが発生する

まずはエラーが発生する状況を説明します。

例として、インハウスレポジトリ myserver.com に登録してある mytestlib というパッケージを想定します。

この時、mytestlib を使用するアプリケーション(mytestapp)の setup.py は以下のようになります。

mytestapp/setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# -*- coding: utf-8 -*-
 
import setuptools
 
with open('README.md') as f:
    readme = f.read()
 
with open('LICENSE') as f:
    license = f.read()
 
 
setuptools.setup(
    name='mytestapp',
    version='0.1',
    description='Test Application for checking dependency_links',
    long_description=readme,
    url='',
    licence=license,
    packages=setuptools.find_packages(exclude=('tests', 'docs')),
    dependency_links=[
        'git+https://myserver.com/kokufu/mytestlib.git#egg=mytestlib-0.1',
    ],
    install_requires=[
        'mytestlib==0.1'
    ],
    test_suite='tests',
    entry_points={
        'console_scripts': [
            'mytestapp=mytestapp.main:main'
        ]
    }
)

これを以下のように pip を使ってインストールしようとすると、"Could not find a version that satisfies the requirement mytestlib==0.1" とエラーが発生してしまいます。

  Cloning ssh://git@myserver.com/kokufu/mytestapp.git to /tmp/pip-req-build-yxzmlrqy
Collecting mytestlib==0.1 (from mytestapp==0.1)
  Could not find a version that satisfies the requirement mytestlib==0.1 (from mytestapp==0.1) (from versions: )
No matching distribution found for mytestlib==0.1 (from mytestapp==0.1)

pip install--process-dependency-links をつけると以下のようにインストール可能です。

$ pip install --process-dependency-links git+ssh://git@myserver.com/kokufu/mytestapp.git
  Cloning ssh://git@myserver.com/kokufu/mytestapp.git to /tmp/pip-req-build-1k5_70ia
DEPRECATION: Dependency Links processing has been deprecated and will be removed in a future release.
Collecting mytestlib==0.1 (from mytestapp==0.1)
  Cloning https://myserver.com/kokufu/mytestlib.git to /tmp/pip-install-vnodyi4t/mytestlib
DEPRECATION: Dependency Links processing has been deprecated and will be removed in a future release.
...略
Successfully installed mytestapp-0.1 mytestlib-0.1

ただ、よく見てみると DEPRECATION: Dependency Links processing has been deprecated and will be removed in a future release. との警告が。 --process-dependency-links は将来的には無くなる可能性があるわけです。

この DEPRECATION、有効な代替手段が無いようで、DEPRECATION 自体を取りやめるべきだという議論も起きています。

参考

Un-deprecate --process-dependency-links until an alternative is implemented · Issue #4187 · pypa/pip · GitHub

現時点では確実なことは言えませんが、DEPRECATION が取りやめになるか、他に有効な代替手段が提供されるのではないかと思います。

2018/8/24 追記
「有効な代替手段が無いようで」と書いたのですが、[Issue #4187](https://github.com/pypa/pip/issues/4187) のその後の議論で [PEP 508](https://www.python.org/dev/peps/pep-0508/) の `@` 以降にURLを書く方法が有効な代替手段であると結論付けられました。 こちらは、[PR #4175](https://github.com/pypa/pip/pull/4175) で実装され、次のリリースで有効になるようです。 次のリリースは 10月とのことです。それ以降は PEP 508 の `@` を使うのがよいでしょう。

多分、--process-dependency-links の正統な代替手段なのだと思われます2

以下のように、pip の実行時に信頼できるパッケージを列挙します。 この時、setup.py 内の dependency_links は無くてもかまいません。

$ pip install --find-links="git+https://myserver.com/kokufu/mytestlib.git#egg=mytestlib-0.1" git+ssh://git@myserver.com/kokufu/mytestapp.git
...略
Successfully installed mytestapp-0.1 mytestlib-0.1

参考

Python: PyPI にないパッケージを依存パッケージにするには - CUBE SUGAR CONTAINER

この方法、確かに動作するのですが、依存パッケージの数が増えてくると手間がかかりすぎてしまいます3

また、mytestlib パッケージの中でさらに dependency_links が書かれている場合、それも --find-links で指定しなければなりません。 依存関係が複雑になってきた場合、これは現実的とは言えません。

解決法3 setup.py install を使う

以下のように一度 clone してから、python setup.py install すると dependency_links が適切に処理されます。

$ cd mytestapp
$ python setup.py install
...略
Finished processing dependencies for mytestapp==0.1

しかし、この方法、せっかく登場した pip の恩恵が受けられません。特に mytestlib パッケージの中でさらに dependency_links が書かれている場合はそれが適切に処理されません。 これは、--find-links と同様の問題です。

参考

virtualenv - Difference between 'python setup.py install' and 'pip install' - Stack Overflow

解決法4 --requirement を使う

この方法は解決法とは言えないのですが、一応書いておきます。

まず、以下のような requirements.txt を用意します。

requirements.txt
1
2

pip --requirement に先の requirements.txt を指定すればインストール可能です。

1
2
3
$ pip install --requirement requirements.txt
...略
Successfully installed mytestapp mytestlib

この --requirement、そもそも特定の環境をフリーズして別の場所で再現させることを目的とした機能なので、このように使うのが適切とは思えません。

さらに、この方法も mytestlib パッケージがさらに内部ライブラリに依存している場合、それらを自分で requirements.txt に列挙しなければなりません4

結局、どの方法を使うべきか

上記4つの方法の中で、多段の依存関係を適切に処理できるのは「解決法1 --process-dependency-links を使う」だけです。

そのため、現時点では --process-dependency-linksを使っておくのが良いだろうと思います。 DEPRECATION の警告が出るのが気になるところですが、議論の様子からすると、他に有効な代替手段が出てこない限り削除されることは無いのではないでしょうか。

しばらくは、--process-dependency-links を使いながら、議論の行方を注意深く見守るのが良いのだろうと思います5

  1. この表現が適切かどうかは怪しいですが 
  2. でも使い勝手がすごく悪い 
  3. 対処方としては README にインストールコマンドを書いておくくらいか? 
  4. そもそも、この方法は別の手段で正しくインストールされた環境があって、それをフリーズするのに使うので、自分で依存関係を探索して列挙するのは本末転倒 
  5. ただし、dependency_links にあまり信頼できないパッケージを指定するのは止めましょう 
?

0 件のコメント: