管理人は国家試験勉強のため、4月より一年間このブログの更新頻度を週一回に落とします。ご容赦のほどを。
roadtoalinuxmintuser

theming

roundup
Linux Mint 18.3 : Python ではじめるコマンドラインツールの作成とパッケージ化 02:32
アイキャッチ
今回、Python でシンプルなコマンドラインツールを作成していくにあたり、pokéapi という、番号でポケモンに関する様々な情報を取得できる REST API を使用します。コマンドラインツールの名前は「pokepy」とし、番号を指定すると、ポケモンの名前、タイプ、特性を返してくれるツールを作成します。タイプや特性の id はその番号のポケモンのタイプと特性を表しているわけではなく、タイプや特性特有の id であることに留意してください。構文は以下のような感じになります:
pokepy (pokemon|type|ability) (--id|-i)=<数字>
サンプルは GitHub にアップロードしています:
ShellingfordX/pokepy: A pokeapi wrapper command line tool

使用するモジュール


コマンドラインツールのオプションをパースするモジュール docopt と HTTP リクエストを送信するためのモジュール requests を使います。コマンドのオプションをパースするモジュールには argparse もありますが、大きな違いは argparse が使用するコマンドのオプションを定義するとヘルプが自動生成されるのに対して、docopt はヘルプを書くことによってコマンドのオプションを自動生成してくれる点です。

もし、argparse で pokepy の引数・オプションを解析しようとすると以下のようになります:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import argparse

parser = argparse.ArgumentParser(description='A simple command line tool that shows pokemon info', version='0.1.0') #パーサーオプジェクトを作成します
parser.add_argument('uri', help='Specify pokemon, type or ability') #位置引数
parser.add_argument('-i','--id', type=int, nargs=1, default=1, help='Specify a pokemon number') #オプション引数

args = parser.parse_args() #引数・オプションを解析します
print "番号:",args.id,"¥n","pokemon? type? ability?:",args.uri #表示しています

実行例:
./pokepy.py pokemon --id 25
実行結果:
番号: [25] 
pokemon? type? ability?: pokemon

少し説明を加えると、位置引数はハイフンがつかない、必須の引数のことです。これを要求するためにこのコードを書いています:
parser.add_argument('uri', help='Specify pokemon, type or ability') #位置引数

同時に、help='Specify pokemon, type or ability' と書くことで、./pokepy.py -h を実行した時のヘルプに表示するようにしています:
positional arguments:
uri Specify pokemon, type or ability

同様にオプション引数も指定しています。ロング形式の --id とショート形式の -i どちらでも使えるようにし、type=int で受け取る値を数値として読み込み、nargs=1 で渡せる値は 1 つ、default=1 で id の指定がなかった場合に代入される値を指定、最後にヘルプに表示するテキストを指定しています。これはこれで便利ですが、今回はヘルプを書いて、引数を自動生成してくれる docopt を使います:
sudo apt install -y python-docopt
また、python-pip もインストールしておきましょう。今回はコマンドラインツールを作成するだけでなく、作成するスクリプトは pip でインストールするからです:
sudo apt install -y python-pip更に、パッケージ化に setuptools モジュールも使います:
sudo apt install -y python-setuptools

ファイル構成


このプロジェクトに必要な最小構成は次のとおりです:

pokepy/
├── pokepy
│ ├── __init__.py
└── setup.py

ですが、README.rst と MANIFEST.in も追加します:
pokepy/
├── pokepy
│ ├── __init__.py
├── setup.py
├── README.rst
└── MANIFEST.in

これからそれぞれのファイルについて述べていきます。

pokepy/__init__.py


pokepy の機能を実装しているスクリプトです。__init__.py はパッケージ化するにあたり必要なファイルで、import して読み込みたいモジュールがある場合、ディレクトリに含める必要が有ります。初期化のコードを書くか空っぽのままで、機能は別のファイルとして分離するようですが最小構成ということでこのなかに書いてしまいます:
'''
Usage:
pokepy (pokemon | type | ability) --id=ID
Options:
-i --id=ID # specify the id of the pokemon, type or ability
-h --help # Show this help
'''


import requests


POKEAPI = 'https://pokeapi.co/api/v2/{path}/{id}'


def get_api_path(arguments):
'''
Get pokemon or type or ability command from command line
arguments.
'''
paths = ['pokemon', 'type', 'ability']
for path in paths:
if arguments[path]:
break
return path


def get_id(arguments):
'''
Get id from command line arguments.
'''
return arguments['--id']


def call_pokeapi(path, id_number, key='name'):
'''
Call the RESTful PokeAPI and parse the response. If pokemon, ability or
type ids are not found than the error message detail is returned.
'''
url = POKEAPI.format(path=path, id=id_number)
response = requests.get(url)
response_json = response.json()
try:
res = response_json[key]
except:
res = response_json['detail']

return res


def __main__():
'''
Entrypoint of command line interface.
'''
from docopt import docopt
arguments = docopt(__doc__, version='0.1.0')
path = get_api_path(arguments)
id_number = get_id(arguments)
print(call_pokeapi(path, id_number))

docopt


このファイルにはコマンドラインツールのエントリーポイント(プログラムの開始となる位置)を含んでいます。後述する setup.py で __main__ 関数がエントリーポイントとなるよう指定します。端末から実行するとき __main__ 関数が実行されるわけです。最初から数行は docopt を使うために、ドキュメントストリングを記述しています。利用者がこの使用法に従わなかった場合、このドキュメントストリングがヘルプとして表示されます。__main__ 関数内の
from docopt import docopt
で docopt モジュールを読み込み、
arguments = docopt(__doc__, version='0.1.0')
としてあげることで、端末から入力された引数を参照することができます。この時、「version=数字」も指定してあげることで --version を指定した時に、指定したバージョン(ここでは 0.1.0)が返されます。docopt は内部で sys.argv を使って引数を読み取り、パースしています。パースされた引数は辞書オブジェクトとして返します。なので以下のように値を取り出すことが出来ます:
arguments = docopt(__doc__, version='0.1.0')
arguments['--id']


requests


3つのユーザー定義関数を定義しています。引数から pokemon か type か ability を得る関数(get_api_path)、引数から id を取得する関数(get_id)、GET リクエスト送り JSON データを取得し必要な情報を返す関数(call_pokeapi)の3つです。
response = requests.get(url)
で GET リクエストを行い、Requests オブジェクトに対して json() メソッドを使うことで JSON 形式のレスポンスの内容を辞書として扱うことが出来ます:
response = requests.get("https://pokeapi.co/api/v2/pokemon/150/")
response_json = response.json()
print response_json['name']

出力例:
mewtwo

もし、クエリ文字を追加したい場合は以下のようにし params キーワードを使って辞書として引数を渡すことが出来ます:
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://example.org/get", params=payload)
print r.url

出力例:
http://example.org/get?key2=value2&key1=value1

setup.py


パッケージ化(配布可能に)するのに必要なスクリプトです:
from setuptools import setup, find_packages
from codecs import open
from os import path

here = path.abspath(path.dirname(__file__))

# Get the long description from the README file
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
long_description = f.read()

setup(
name='pokepy',
version='0.1.0',
description='A Pokeapi wrapper command line tool',
long_description=long_description,
url='https://github.com/ShellingfordX/pokepy',
author='Shellingford',
author_email='example@gmail.com',
license='Apache 2.0',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Topic :: Software Development :: Build Tools',
'License :: OSI Approved :: Apache License',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.5',
],

keywords='Pokeapi REST client wrapper command line interface',
packages=find_packages(),
install_requires=['docopt', 'requests'],
extras_require={ },
package_data={},
data_files=[],

entry_points={
'console_scripts': [
'pokepy=pokepy:__main__',
],
},
)

ちゃんと動作させる上で大事なのは:
install_requires=['docopt', 'requests'],
entry_points={
'console_scripts': [
'pokepy=pokepy:__main__',
]
です。
pip でインストールする必要がある依存関係は install_requires に指定します。エントリーポイント(プログラムの開始点)としてを指定するには、辞書の console_scripts の値として指定します。上の意味は「端末から pokepy を実行したならば、pokepy モジュールの __main__ 関数を呼び出せ」です。classifiers 引数に指定する値は ここ から選びます。

README.md -> README.rst


README.md は GitHub で管理していれば自動生成してくれます。setup.py では long_description の中身に README ファイルを流用していますが、読み込んでいるファイル形式は README.rst です。PyPL が md (マークダウン) 形式には対応していないため、一般的には rst (reStructuredText) 形式を使うのが普通なようです。そこで、md 形式から rst 形式に変換する必要が有ります。変換するには pandoc を使うのが便利です:
sudo apt install -y pandoc
ワーキングディレクトリを README.md がある場所に移し、以下を実行すれば README.md から README.rst を作成することが出来ます:
pandoc -f markdown -t rst -o README.rst README.md
また、Python ファイルでないもので、インストールに含めたいものは MANIFEST.in に指定する必要が有ります:
include README.rst


インストール & 試用


pokepy ディレクトリ内に入り、pip でインストールします。-e は --editable のショート形式で、開発中はソースを編集したい時もあるので、再インストールする必要なく編集できるようにするためのオプションです:
cd pokepy && sudo pip install -e .
Successfully installed pokepy
と返されたらインストール成功です。
試しに図鑑番号 25 のポケモンの名前を表示してみます:
pokepy pokemon -i 25
出力例:
pikachu

以上で Python で端末で動く API ラッパーを作成する方法を習得しました。このモジュールをテンプレートにして、より便利でより複雑なツールを作ってはいかがでしょうか。

参考元: Datahovel
| 1/1 |

Show some apps list:


miku

Follow on your feed reader:


About this blog:

Linux Mint-centric chronicle.
Since 2009.
Info 更新日は土曜日です。

roadtoapython

roadtorubyist

git