意外と知らない？pipコマンド

よく使うコマンドから、込み入ったコマンドまでを使用ケース毎に紹介します。

# Case 1. 依存関係のケア

パッケージの依存関係で問題が生じて、スクリプトが上手く動作しない...。

(1) 依存関係の問題をチェックしましょう。

```bash
$ pip check
# パッケージの依存関係に問題がない場合、
# No broken requirements found.
# と出力されます。
```

(2) パッケージの要求されているバージョンが確認できたら、利用可能なバージョンを確認します。

```bash
$ pip install <package>==
# ERROR: Could not find a version that satisfies the requirement <package>== (...
# とエラーが出力され、利用可能なパッケージのバージョンが確認出来ます。
```

(3) バージョンを指定してインストール

```bash
$ pip install <package>==<version>

# 最新状態にしたい時は
$ pip install -U <package>
# とあるバージョン未満で最新の状態にしたい時は
$ pip install -U "<package> < <version>" # 不等号 < を使ってます。
```

## インストール済みパッケージの依存関係を確認

パッケージの詳細と共に依存関係を確認するには`show`を使います。

```bash
$ pip show <package>
```

`pipdeptree`パッケージを使うと、バージョン情報込みの依存関係を表示できます。

```bash
$ pip install pipdeptree

# <package>が要求するパッケージとそのバージョンを表示。
$ pipdeptree -p <packgage>
# <package>を要求するパッケージと、要求されるバージョンの表示。
$ pipdeptree -r -p <packgage>
```

## インストールしていないパッケージの詳細&依存関係の確認

次のように[PyPi](https://ja.wikipedia.org/wiki/Python_Package_Index)に聞くとインストールしていないパッケージの詳細も調べることが出来ます。

```python
import requests
from pprint import pprint # 出力整形のためにインポート

package = '<package>'
version = '<version>'
url = f'https://pypi.org/pypi/{package}/{version}/json'
# バージョンを指定しないなら
# url = f'https://pypi.org/pypi/{package}/json'

json = requests.get(url).json()
pprint(json['info'])
```

指定したパッケージが要求するパッケージは次で確認出来ます。

```python
pprint(json['info']['requires_dist'])
```



# Case 2. オフライン環境でパッケージをインストール

`ssh`で接続した先で`pip install <package>`したいけど、接続先がオフライン!

(1) 先ずはオンラインで、必要なパッケージを`./src`直下にダウンロードします(バージョンを指定したい時は`<package>==<version>`としましょう)。

```bash
$ pip download -d ./src <package>
# or
$ pip download -d ./src -r requirements.txt
```

(2) `scp`で`./src`を転送。

```bash
$ scp -r -C ./src <user>@<host>:<path>

# 容量が大きい場合は圧縮して送ろう。
$ tar czf src.tar.gz src
$ scp -C ./src.tar.gz <user>@<host>:<path> 
```

(3) `ssh`で接続後、`src`ディレクトリの中に入ります。以下のコマンドでインストール完了です！

```bash
$ pip install <package (file name)> # カレントディレクトリから1つずつインストール。
# or 
$ pip install ./* # まとめてインストール。

# srcを圧縮した場合は、事前に解凍しましょう。
$ tar xzf src.tar.gz
```

## `pip download`のあれこれ

ソースパッケージをダウンロード。

```bash
$ pip download -d ./src --no-binary :all: <package>
```

バージョンやプラットフォームを指定してバイナリパッケージをダウンロード。

```bash
$ pip download -d ./src --only-binary :all: --platform <platform> --python-version <python_version> --implementation <implementation> --abi <abi> <package>
```

以下、参考ページです。

* [pip download — pip 19.3.1 documentation](https://pip.pypa.io/en/stable/reference/pip_download/)
* [pip download --platform --python-version does not work for some packages · Issue #6121 · pypa/pip](https://github.com/pypa/pip/issues/6121)

# Case 3. `pip install`の保存先を変える

とあるパッケージを試しに使ってみたいけど、グローバル、ローカルのpipを汚したくない。仮想環境を使う程ではないしな...。

(1) ディレクトリを指定してパッケージをインストール。

```bash
$ pip install -t <dir> <package>
```

(2) スクリプトに以下の行を加えることで、`<dir>`直下のパッケージが使用可能になります。

```python
import sys
sys.path.append(dirpath)

# パスの指定方法その1
import os
dirpath = sys.path.append(os.path.join(os.path.dirname(__file__), '<dir>の相対パス'))
# パスの指定方法その2
from pathlib import Path
dirpath = sys.path.append(str((Path(__file__).parent/'<dir>の相対パス').resolve()))
# パスの指定方法その3
dirpath = '<dir>の絶対パス'
```

`dirpath`に相対パスを書いても実行出来ますが、書いたスクリプトを他のスクリプトから呼び出す場合、`__file__`を用いるか、絶対パスで`dirpath`を指定する必要があります。

(3) パッケージが要らなくなったらディレクトリを消去。

```bash
$ rm -r <dir>
```

## インストール済みパッケージの違うバージョンを試したい

パッケージをインストールしたディレクトリ`<dir>`内でスクリプトを実行すれば、`<dir>`内のパッケージが優先してインポートされます。

もし、`<dir>`の外でスクリプトを実行し、かつ既にグローバルorローカルにインストールしてあるパッケージの別バージョンが使いたい時は、次の一行を加えましょう。

```python
sys.path = sys.path[::-1]
```

パッケージは`sys.path`の先頭のパスから優先的に読まれます。新たにパスを追加した後にリストを逆転させることで、追加したパスを最優先で読ませることが出来ます。