3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-05-06

前回の続きです。

【前編】
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してくれや!
https://github.com/kizashitakata/MyCommand

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

構造はこんな感じです。

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

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?