[Dd]enzow(ill)? with DB and Python

DBとか資格とかPythonとかの話をつらつらと

PyPIにコマンド実行可能なパッケージを登録した話

自分で作ったwsgi_lineprof_reporterをPyPIに登録した際の備忘録です。ぐぐると記事は多いのですが時期ごとに手順が異なったりするので2017/09時点で一旦まとめておきます。

また、今回はpip installするだけでOSコマンドとして実行できるようにしたかったのでそれも対応しておきました。

大まかな流れ

自作のパッケージをPyPIに登録してpip installできるようにするには以下の流れで行います。

  • パッケージ内にsetup.pyを作成
  • PyPIのアカウント登録をし、.pypircにアカウント情報を記入
  • twineをインストール
  • パッケージをアップロード用に固める
  • twineで固めたファイルをPyPIにアップロード

パッケージ内にsetup.pyを作成

パッケージングした際に含めるファイルやメタ情報等を記入するファイルです。requestsの作者であるkennethreitz氏が公開されているサンプルがあるのでそれを参考にします。

# coding: utf-8

from setuptools import setup, find_packages
from wlreporter import __VERSION__


with open('README.md') as f:
    readme = f.read()

with open('LICENSE') as f:
    license_txt = f.read()

setup(
    name='wlreporter',
    version=__VERSION__,
    description='create report for wsgi_lineprof.',
    entry_points={
        "console_scripts": [
            "wlreporter = wlreporter.wlreporter:main"
        ]
    },
    long_description=readme,
    author='denzow',
    author_email='denzow@gmail.com',
    url='https://github.com/denzow/wsgi_lineprof_reporter',
    license=license_txt,
    packages=find_packages(exclude=('sample',))
)

基本的にはsetuptools.setupに必要な引数を渡して呼び出すように書きます。READMEやLICENSEはファイルが存在するので、そちらの内容を渡せるようにするのが一般的です。

サンプルにない部分でいうとentry_pointsという引数を追加しています。これを指定することでpip installするだけで独自のコマンドをインストールするようにしてくれます。

    entry_points={
        "console_scripts": [
            "wlreporter = wlreporter.wlreporter:main"
        ]
    },

今回は、wlreporterというコマンドでwlreporterパッケージ内のwlreporterモジュールに含まれているmain関数を実行するように指定できます。

.
├── LICENSE
├── MANIFEST.in
├── README.md
:
├── wlreporter
│   ├── __init__.py
│   └── wlreporter.py  <-- この中のmain関数が呼ばれる

さて、setup.pyはこれでいいのですがMANIFEST.iniも作成しておく必要があります。PyPIにアップロードするためのパッケージに追加で含めるファイルを指定します。

include LICENSE
include README.md

pip installをするとインストール先でもsetup.pyが実行されますが、その際にREADME.mdやLICENSEをopen()します。MANIFEST.iniを正しく用意していないと以下のエラーで失敗していまいます。

FileNotFoundError: [Errno 2] No such file or directory:...

PyPIのアカウント登録をし、.pypircにアカウント情報を記入

PyPiからアカウントを登録します。

f:id:denzow:20170930170751p:plain
pypiのトップページから登録

登録が完了したら、$HOME/.pypircを以下の内容で作成します。

[distutils]
index-servers =
    pypi
[pypi]
username: PyPIのユーザ名
password: PyPIのパスワード

twineをインストール

PyPIへのアップロード等はsetup.pyのサブコマンドからもできるのですがtwineを使うほうが簡単らしいので導入しておきます。

$ pip install twine

ただ、このあたりは少し微妙な情報もあったのでtwineを使わなくなるかもしれません。

パッケージをアップロード用に固める

setup.pyのディレクトリでpython setup.py sdistを実行してパッケージングします。

(python3.6) denzownoMacBook-Pro:wsgi_lineprof_reporter denzow$ python setup.py sdist
running sdist
running egg_info
creating wlreporter.egg-info
writing wlreporter.egg-info/PKG-INFO
writing dependency_links to wlreporter.egg-info/dependency_links.txt
writing entry points to wlreporter.egg-info/entry_points.txt
writing top-level names to wlreporter.egg-info/top_level.txt
writing manifest file 'wlreporter.egg-info/SOURCES.txt'
reading manifest file 'wlreporter.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'wlreporter.egg-info/SOURCES.txt'
running check
creating wlreporter-0.2.0
creating wlreporter-0.2.0/wlreporter
creating wlreporter-0.2.0/wlreporter.egg-info
copying files to wlreporter-0.2.0...
copying LICENSE -> wlreporter-0.2.0
copying MANIFEST.in -> wlreporter-0.2.0
copying README.md -> wlreporter-0.2.0
copying setup.py -> wlreporter-0.2.0
copying wlreporter/__init__.py -> wlreporter-0.2.0/wlreporter
copying wlreporter/wlreporter.py -> wlreporter-0.2.0/wlreporter
copying wlreporter.egg-info/PKG-INFO -> wlreporter-0.2.0/wlreporter.egg-info
copying wlreporter.egg-info/SOURCES.txt -> wlreporter-0.2.0/wlreporter.egg-info
copying wlreporter.egg-info/dependency_links.txt -> wlreporter-0.2.0/wlreporter.egg-info
copying wlreporter.egg-info/entry_points.txt -> wlreporter-0.2.0/wlreporter.egg-info
copying wlreporter.egg-info/top_level.txt -> wlreporter-0.2.0/wlreporter.egg-info
Writing wlreporter-0.2.0/setup.cfg
creating dist
Creating tar archive
removing 'wlreporter-0.2.0' (and everything under it)

これでパッケージングされたファイルが作成されます。

(bottle_sample) denzownoMacBook-Pro:wsgi_lineprof_reporter denzow$ ls -ltr
total 56
-rw-r--r--   1 denzow  staff   1071  9 20 22:52 LICENSE
drwxr-xr-x  11 denzow  staff    374  9 24 12:52 sample
-rw-r--r--   1 denzow  staff     33  9 30 14:24 MANIFEST.in
drwxr-xr-x   5 denzow  staff    170  9 30 14:34 wlreporter
-rw-r--r--   1 denzow  staff    653  9 30 14:36 setup.py
-rw-r--r--   1 denzow  staff  15541  9 30 14:38 README.md
drwxr-xr-x   7 denzow  staff    238  9 30 16:55 wlreporter.egg-info  ★
drwxr-xr-x   3 denzow  staff    102  9 30 16:55 dist  ★

パッケージ名.egg-infodist/wlreporter-0.2.0.tar.gzが作成されています。これを配布するのですが、一旦ここで正しく動作するかを確認しておきます。

$ pip install dist/wlreporter-0.2.0.tar.gz

問題ないようであればPyPIに登録します。

twineで固めたファイルをPyPIにアップロード

以下を実行するとPyPIにファイルがアップロードされ、登録完了です。

$ twine upload dist/wlreporter-0.2.0.tar.gz

こんな感じで登録されています。

pypi.python.org

この時点でpip install wlreporterでインストールできるようになります。

ちなみに。。。

twine uploadの前にtwine registerをしている手順が多いのですが、現在は不要のようです。以下のエラーがでました。

(python3.6) denzownoMacBook-Pro:wsgi_lineprof_reporter denzow$ twine register dist/wlreporter-0.2.0.tar.gz 
Registering package to https://upload.pypi.org/legacy/
Registering wlreporter-0.2.0.tar.gz
HTTPError: 410 Client Error: Project pre-registration is no longer required or supported, so continue directly to uploading files. for url: https://upload.pypi.org/legacy/

なかなか手順がかわるのはつらいですね・・・