ターミナルからPythonパッケージの依存関係を探すコマンドを作ってみた

## はじめに
&emsp;Pythonには多くのパッケージがありますね。そのパッケージを管理するためにpipを使っている人も多いのではないでしょうか。pipは簡単にパッケージを導入できるシステムですが、パッケージ同士の依存関係がわからない！なんてことに直面したことはありませんか？~~(私はあります。)~~

&emsp;インストール済みのパッケージなら、ターミナルから調べることができますが、インストールしてあるかどうかに関わらずうまく調べる方法はないものか？と思い、シェルコマンドを作ってみました。

## 環境
* MacBook Pro 2017
* macOS Big Sur 11.6
* Python 3.10.0
* Z Shell 5.8

## インストール済みのパッケージの依存関係を調べる

&emsp;復習にはなりますが、インストール済みのパッケージの依存関係は、ターミナルから次のように調べることができます。

```zsh:terminal
$ pip3 show <パッケージ名>
```

&emsp;例として、numpyについて調べてみると、以下のように出力されました。

```:terminal
$ pip3 show numpy

Name: numpy
Version: 1.21.3
Summary: NumPy is the fundamental package for array computing with Python.
Home-page: https://www.numpy.org
Author: Travis E. Oliphant et al.
Author-email: 
License: BSD
Location: /usr/local/lib/python3.10/site-packages
Requires: 
Required-by: altair, matplotlib, opencv-python, pandas
```

&emsp;```pip3 show```コマンドでは、パッケージの名前やバージョンの他、保存されている場所や依存関係がわかります。numpyの場合、インストール済みのパッケージの中に依存しているもの(Requires)はなく、依存されているもの(Required-by)として、```altair, matplotlib, opencv-python, pandas```があるようです。ではインストールしていないパッケージを指定してみるとどうなるのでしょうか。

```
$ pip3 show Pyarrow

WARNING: Package(s) not found: Pyarrow
```

&emsp;このように"**not found**"になってしまうようです。

## 未インストールパッケージの依存関係を調べる
&emsp;以下のQiita記事を参考にさせていただきました。

https://qiita.com/tmurakami1234/items/b407dc4cc558564882bf#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%B8%88%E3%81%BF%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E4%BE%9D%E5%AD%98%E9%96%A2%E4%BF%82%E3%82%92%E7%A2%BA%E8%AA%8D

### 手順1： 動作をスクリプトとして書き出す

&emsp;とりあえず、処理を書き出します。おそらくどの言語を使っても良いとは思いますが、pipがPython関連なので~~(シェルの文法がよくわからないので)~~、私はPythonで記述しました。

そして完成したコードがこちら。

```python:pip_requirements_search.py
import requests
import sys
from pprint import pprint

def pipreq(s_1,i_1):
    package = s_1
    version = i_1
    if version != "none":
        url = f'https://pypi.org/pypi/{package}/{version}/json'
    else:
        url = f'https://pypi.org/pypi/{package}/json'
    json = requests.get(url).json()
    pprint(json['info']['requires_dist'])

if __name__ == "__main__":
    print("調べたいモジュールの名前とバージョンを空白区切りで入力してください。\
    \nバージョンを指定しないときはnoneと入力してください。\n")
    s_1, i_1 = input().split()
    pipreq(s_1, i_1)
```

&emsp;標準入力としてパッケージ名とバージョン名を受け取り、pypiのサイト上のjsonから探し出すようになっています。そして、依存関係にあるパッケージ名を出力するようになっています。

&emsp;実行例はこちら。

```:terminal
$ python3 pip_requirements_search.py
調べたいモジュールの名前とバージョンを空白区切りで入力してください。    
バージョンを指定しないときはnoneと入力してください。

pandas none
['python-dateutil (>=2.7.3)',
 'pytz (>=2017.3)',
 'numpy (>=1.17.3) ; platform_machine != "aarch64" and platform_machine != '
 '"arm64" and python_version < "3.10"',
 'numpy (>=1.19.2) ; platform_machine == "aarch64" and python_version < "3.10"',
 'numpy (>=1.20.0) ; platform_machine == "arm64" and python_version < "3.10"',
 'numpy (>=1.21.0) ; python_version >= "3.10"',
 "hypothesis (>=3.58) ; extra == 'test'",
 "pytest (>=6.0) ; extra == 'test'",
 "pytest-xdist ; extra == 'test'"]

```

&emsp;このように、依存関係のみが出力されるスクリプトを書くことができました。

### 手順2: シェルのエイリアスとして設定する
&emsp;手順1だけでも動作に問題はないのですが、いちいちファイルを指定して実行するのが面倒…ということで、シェルのエイリアスにしました。順を追って説明します。

&emsp;まず、シェルの設定ファイルを開きます。私の場合は```zsh```を使っているので、```.zshrc```を開きます。エディタは```vim```ですが、<font color="grey">~~宗教上の理由がある人は~~</font>```emacs```などでも問題ないです。

```:terminal
$ vi ~/.zshrc
```

&emsp;次に、以下のようにスクリプトファイルのパスを記述し、エイリアスにします。コマンドの名前は自分が覚えられるものにしましょう。わかりやすくホームディレクトリ直下に配置していますが、好きな場所で良いでしょう。

```:.zshrc
alias prs="python3 /Users/hogehoge/pip_requirements_search.py"
```

&emsp;作業内容を保存したあと、反映して設定完了です。(**&larr;この工程を忘れると "command not found" になります。**)

```:terminal
$ source ~/.zshrc
```

### 手順3: 権限を付与する
&emsp;実行権限がないと、"**permission denied**" というエラーが出てしまいます。そのため、権限を付与しましょう。ターミナルで以下のように入力します。```sudo```なので、パスワードが求められます。ログインパスワードを入力しましょう。

```:terminal
$ sudo chmod 755 ~/pip_requirements_search.py
```


## おわりに
よく使う処理は、このようにシェルのエイリアスにしておくと結構便利ですね。