Pythonでオリジナルのコマンドを実装してみる【後編】

### 前回の続きです。

【前編】
https://qiita.com/K_Takata/items/3c0e632db7b662f88f57

こちらの記事で自動生成されているコマンドラインの起点……

```jupyter.py
#!/anaconda3/bin/python

# -*- coding: utf-8 -*-
import re
import sys

from jupyter_core.command import main

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(main())
```
の生成方法についてすっ飛ばしていたのですが、結構重要なポイントなのでこちらで解説させていただきます。

## 目標
前回と同様です。
macまたはLinuxのコマンドラインで

```
$ MyCommand
```

と打ち込むと

```
ジャンプで連載中のチェンソーマンが好き
```

と出力されるようになる。

```
$ MyCommand --help
```

と打ち込むと、

```
ジャンプで連載中のチェンソーマンをダイレクトマーケティングするコマンド
```
と、MyCommandの説明が出力される。

↑こちらのコマンドの起点を自動生成してみましょう。
ついでに後半でちょっと複雑なコマンドを作ってみましょう。

## よくわかる？解説
ローカルにテキトーにcloneしてくれや！
(すみません、諸事情でリポジトリはアーカイブしました)

よくわからなかったらテキトーに参考にしながら作ってください。
解説しますね。

構造はこんな感じです。

setup.pyを見てみましょう。

```
├── App
│   ├── __init__.py
│   ├── command1.py
│   └── command2.py
├── README.md
└── setup.py
```

```setup.py
from setuptools import setup, find_packages
setup(
    name='MyCommand',
    version='0.1.0',
    entry_points={'console_scripts': ['MyCommand = App.command1:main']},
    install_requires=[
        'click'
    ],
    packages=find_packages()
    )
```
今回必要な最低限のみ記載しています。

nameとversionは実際にPyPIにアップして世界中のみんなに使ってもらう際に重要になるのですが、自分で使う分にはテキトーで大丈夫です。PyPIにアップする際にテキトーなバージョンでアップすると後で痛い目に会うので注意しましょう。

今回の内容で一番重要なのが
`entry_points`
の部分で、ここにコマンドの起点を定義します。
後々わかるので大丈夫です。

`install_requires`はなんとなく分かると思いますが、依存モジュールで、ここに定義しておけば後はinstall時になんとなく一緒に持ってきてくれます。

今回は~~ワイ推奨パッケージ~~便利なコマンドラインパーサのclickを使っているので、依存モジュールとして定義しています。
`packages=find_packages()` は、`__init__.py` があるフォルダをパッケージとして認識するために必要です。
……ちょっとややこしいですね。

ディレクトリ構造に戻りますが

```
├── App
│   ├── __init__.py
│   ├── command1.py
│   └── command2.py
├── README.md
└── setup.py
```
この`__init.py__` は、見ていただければ分かるのですが空ファイルです。
何のためにこの世に存在しているのかというと、"App" フォルダがパッケージだよという証明……みたいなものです。

さっきの
`entry_points={'console_scripts': ['MyCommand = App.command1:main']}`
の部分での指定は「Appパッケージのcommand1.pyのmainメソッドがコマンドの起点ですよー」くらいの意味です。
「重要なフォルダには `__init.py__` を用意するんやで」 くらいの認識でもOKです。

ということでcommand1.pyを見てみましょう。

```command1.py
import click

@click.command(help='ジャンプで連載中のチェンソーマンをダイレクトマーケティングするコマンド')
def main():
    click.echo('ジャンプで連載中のチェンソーマンが好き')
    pass
```

前編の実装よりめっちゃシンプルになりましたね。
それもそのはず、
`$ MyCommand --help`
の出力内容がclickライブラリの標準機能にまとめられてスッキリシンプルになりました。

出力もちょっと豪華になります。

```
$ mycommand --help
Usage: mycommand [OPTIONS]

  ジャンプで連載中のチェンソーマンをダイレクトマーケティングするコマンド

Options:
  --help  Show this message and exit.
```
ちなみに前回の実装方法にせよ今回の実装方法にせよ、コマンドは大文字でも小文字でもどっちでもOKです。今回は小文字で統一します。

さて、いよいよ。
どうやってこいつをコマンドとして自動生成するのか。

ローカルでsetup.pyのあるディレクトリにcdしたあと、以下のように打ち込むだけです。

```
$ pip install .
```

.はカレントディレクトリの意味なので、要するにpip installで指定のディレクトリにあるsetup.pyを叩いています。

pip install 〇〇 は便利なもので、PyPIにあるパッケージを指定するとPyPIから持ってきてくれますし、ローカルディレクトリのsetup.pyのあるディレクトリを指定すると、よしなにパッケージを展開してくれます。

コマンド実行後、mycommandが使えるようになってるはずです。
簡単ですね。

# ちょっとだけ応用
`mycommand one`  `mycommand two` みたいに、コマンドごとに処理を変えたいってこともありますよね？
夕飯にカレーが食べたくなるくらいの頻度であるはずです。

`setup.py` の
`entry_points={'console_scripts': ['MyCommand = App.command1:main']}`
のcommand1の部分をcommand2に書き換えてみましょう。

それから改めて、

```
$ pip install .
```
してみてください。

command2の内容はこんな感じです。

```command2.py
import click

@click.group()
def main():
    pass

@main.command()
def one():
    click.echo('処理1')

@main.command()
def two():
    click.echo('処理2')
```
~~チェンソーマン絡ませるとややこしいのでめっちゃ単純にしました。~~
これだけです。これだけで、

```
$ mycommand one
処理1
```
```
$ mycommand two
処理2
```
実装完了です。
夢が広がりますね。

## まとめ
**pipは優秀。**

以上です。お読みいただきありがとうございました。

## 参考
Pythonでグローバルコマンドを含んだパッケージを作る
https://qiita.com/fetaro/items/bb0eb8292127b5d1e9a8

Python: コマンドラインパーサの Click が便利すぎた
https://blog.amedama.jp/entry/2015/10/14/232045