PyPIデビュー

普段からPythonでプログラミングをしているのでPyPIにライブラリを登録してみました。

以下は、その時のメモです。

アカウントの登録

PyPIにライブラリを登録/更新する際には認証が必要となります。そのため、まずアカウントの登録を行う必要があります。

CUIからアカウントを登録することもできるようですが、(よくわからなかったので)GUI操作によりアカウントの登録を登録することにしました。

まずアカウント登録の画面に移動します。
pypi-0

アカウント情報を入力してRegister画面を押下します。
pypi-1

「メール送ったよ」という旨のメッセージと共に、
pypi-2

入力したメールアドレス宛に登録用URLが記載されたメールが送られてきます。
pypi-3

登録用URLにアクセスすると利用規約が表示されます。「I agree」のチェックボックスを選択して「Confirm」ボタンを押下します。
pypi-4

登録が終わると完了画面が表示されます。
pypi-5

ライブラリを登録する

ライブラリの登録は大きく分けてライブラリ名の取得とファイルのアップロードに分かれています。
どちらの手順においてもsetup.pyが必要です。

setup.py

setup.pyはパッケージのメタ情報の定義に使用されます。中核をなすのがsetup関数で、引数に指定した情報がライブラリのメタ情報となります。

基本的な構成は次のようにすれば概ね大丈夫です。

#!/usr/bin/env python
# coding: utf-8
from setuptools import setup, find_packages
setup(
    name=ライブラリ名<str>,
    install_requires=依存ライブラリ名のリスト<list>,
    version=バージョン<str>,
    description=概要(200文字以内)<str>,
    long_description=詳しい説明(reST形式)<str>,
    classifiers=分類のリスト<list>,
    keywords=検索キーワード<str>,
    author=所有者名<str>,
    author_email=所有者のメールアドレス<str>,
    url=所有者のメールアドレス<str>,
    license=メールアドレス<str>,
    packages=パッケージ名のリスト<list>,
)

上記で特筆すべき項目は以下です。

version

パッケージのバージョンです。既に登録されているバージョンに対し、同じ形式のライブラリを登録することはできません。Typoのような軽微な修正であっても別バージョンとして登録する必要があります。
決まったフォーマットはありませんが、{major}.{minor}.{patch}とするのが慣例のようです。
また、バージョン番号の大きさではなく登録日時によってライブラリは最新(latest)であると判断されます。重複さえしなければ前より低いバージョン番号で登録することも可能ですが、混乱のもとなのでやめましょう。

install_requires

動作する上で必要なライブラリを列挙します。比較演算子によってバージョンを指定することもできます。
例えば、Django1.7以上を必要とする場合は「install_requires = [‘django >= 1.7′]」のように記述します。

参考リンク: setupスクリプトを書く

keywords

検索キーワードはpipやPyPIサイトの検索時に使用されます。リストではなく文字列で指定するということに気をつけてください。区切り文字は半角のカンマやスペースを使用するのが一般的なようです。

classifiers

ライブラリの分類を定義します。これは分類によりライブラリを探すための仕組みだと思われます。イメージ的にはGoogleキーワード検索に対するYahooカテゴリ検索(ディレクトリ検索)のようなものでしょうか。(違うかも)
ここからアクセスできます。使用可能な分類はこちらを参照してください。

packages

ライブラリに含めるパッケージをリストで指定します。指定方法はsetup.pyがおいてあるディレクトリを基点としたモジュールパスです。「パッケージ」なので__init__.pyを含まないディレクトリは無視されます。

また、指定したパッケージしかライブラリには追加されません。指定したパッケージを基点に再帰的に追加されるわけではないのです。
ただ、存在するパッケージをすべて列挙するというのは面倒なことですし、追加漏れも発生するかもしれません。階層が複数存在するパッケージなら尚更です。

ここで便利なのがfind_packages関数です。これはカレントディレクトリを基点としてパッケージを再帰的に検索し、結果のモジュールパスをリストで返却してくれます。単純にこの実行結果をpackages引数に与えるだけ必要なパッケージはすべてライブラリに追加されます。
ライブラリに追加したくないパッケージがある場合はexclude引数にBlob形式で指定することにより除外してくれます。

>>> from setuptools import find_packages
>>> find_packages()
['tesdat', 'tesdat.pattern', 'tesdat.container', 'tesdat.model', 'tesdat.common', 'tesdat.pattern.tests', 'tesdat.container.tests', 'tesdat.model.tests']
>>> find_packages(exclude=['*.tests'])
['tesdat', 'tesdat.pattern', 'tesdat.container', 'tesdat.model', 'tesdat.common']

もしpackages引数の変更が適用されない場合、「ライブラリ名.egg-info」ディレクトリを一旦削除してから再実行すると良いでしょう。

参考になるかわかりませんが、私が登録したライブラリのsetup.pyではこのようになっています。

ライブラリ名の登録

PyPIに登録されるライブラリは名前の重複が許されません。登録しようとしているライブラリの名前空間を取得する作業を先に行う必要があります。。

setup.pyにregister引数を指定して実行します。先ほどの手順でアカウント登録を済ませているため、最初の選択肢では「1」を選び、その後登録したアカウント名とパスワードを入力すれば完了です。

$ ./setup.py register
running register
running egg_info
writing tesdat.egg-info/PKG-INFO
writing top-level names to tesdat.egg-info/top_level.txt
writing dependency_links to tesdat.egg-info/dependency_links.txt
reading manifest file 'tesdat.egg-info/SOURCES.txt'
writing manifest file 'tesdat.egg-info/SOURCES.txt'
running check
We need to know who you are, so please choose either:
 1. use your existing login,
 2. register as a new user,
 3. have the server generate a new password for you (and email it to you), or
 4. quit
Your selection [default 1]: 
1
Username: crohaco
Password: 
Registering tesdat to https://pypi.python.org/pypi
Server response (200): OK
I can store your PyPI login so future submissions will be faster.
(the login will be stored in /home/crohaco/.pypirc)
Save your login (y/N)?y
.pypirc

何度も認証入力するのがめんどくさい人は.pypircに保存しましょう。上記手順の最後の選択肢で「y」と入力すればOKです。
しかし.pypircにはINI形式で認証情報が保存されています。セキュリティ的なリスクもあるのでマシンを複数人で共有している場合は適切なアクセス権を設定するか、心配な場合は使用後に削除するようにしましょう。

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

ファイルのアップロード

ファイルをアップロードするためにはsetup.pyの引数にパッケージ形式とuploadを指定して実行します。
通常の用途であればパッケージの形式には「sdist」のみを指定すれば十分です。

$ ./setup.py sdist upload
running sdist
running egg_info
writing tesdat.egg-info/PKG-INFO
writing top-level names to tesdat.egg-info/top_level.txt
writing dependency_links to tesdat.egg-info/dependency_links.txt
reading manifest file 'tesdat.egg-info/SOURCES.txt'
writing manifest file 'tesdat.egg-info/SOURCES.txt'
running check
creating tesdat-1.3.0
creating tesdat-1.3.0/tesdat
creating tesdat-1.3.0/tesdat.egg-info
creating tesdat-1.3.0/tesdat/common
creating tesdat-1.3.0/tesdat/container
creating tesdat-1.3.0/tesdat/container/tests
creating tesdat-1.3.0/tesdat/model
creating tesdat-1.3.0/tesdat/model/tests
creating tesdat-1.3.0/tesdat/pattern
creating tesdat-1.3.0/tesdat/pattern/tests
making hard links in tesdat-1.3.0...
hard linking README.rst -> tesdat-1.3.0
hard linking setup.py -> tesdat-1.3.0
hard linking tesdat/__init__.py -> tesdat-1.3.0/tesdat
hard linking tesdat/exceptions.py -> tesdat-1.3.0/tesdat
hard linking tesdat.egg-info/PKG-INFO -> tesdat-1.3.0/tesdat.egg-info
hard linking tesdat.egg-info/SOURCES.txt -> tesdat-1.3.0/tesdat.egg-info
hard linking tesdat.egg-info/dependency_links.txt -> tesdat-1.3.0/tesdat.egg-info
hard linking tesdat.egg-info/top_level.txt -> tesdat-1.3.0/tesdat.egg-info
hard linking tesdat/common/__init__.py -> tesdat-1.3.0/tesdat/common
hard linking tesdat/common/render.py -> tesdat-1.3.0/tesdat/common
hard linking tesdat/container/__init__.py -> tesdat-1.3.0/tesdat/container
hard linking tesdat/container/base.py -> tesdat-1.3.0/tesdat/container
hard linking tesdat/container/dict.py -> tesdat-1.3.0/tesdat/container
hard linking tesdat/container/list.py -> tesdat-1.3.0/tesdat/container
hard linking tesdat/container/ordereddict.py -> tesdat-1.3.0/tesdat/container
hard linking tesdat/container/tests/__init__.py -> tesdat-1.3.0/tesdat/container/tests
hard linking tesdat/container/tests/test_base.py -> tesdat-1.3.0/tesdat/container/tests
hard linking tesdat/container/tests/test_dict.py -> tesdat-1.3.0/tesdat/container/tests
hard linking tesdat/container/tests/test_list.py -> tesdat-1.3.0/tesdat/container/tests
hard linking tesdat/model/__init__.py -> tesdat-1.3.0/tesdat/model
hard linking tesdat/model/base.py -> tesdat-1.3.0/tesdat/model
hard linking tesdat/model/dict.py -> tesdat-1.3.0/tesdat/model
hard linking tesdat/model/list.py -> tesdat-1.3.0/tesdat/model
hard linking tesdat/model/tests/__init__.py -> tesdat-1.3.0/tesdat/model/tests
hard linking tesdat/model/tests/test_base.py -> tesdat-1.3.0/tesdat/model/tests
hard linking tesdat/model/tests/test_dict.py -> tesdat-1.3.0/tesdat/model/tests
hard linking tesdat/model/tests/test_list.py -> tesdat-1.3.0/tesdat/model/tests
hard linking tesdat/pattern/__init__.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/base.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/choice.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/cycle.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/hash.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/increment.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/limited_choice.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/sequence.py -> tesdat-1.3.0/tesdat/pattern
hard linking tesdat/pattern/tests/__init__.py -> tesdat-1.3.0/tesdat/pattern/tests
hard linking tesdat/pattern/tests/test_choice.py -> tesdat-1.3.0/tesdat/pattern/tests
hard linking tesdat/pattern/tests/test_cycle.py -> tesdat-1.3.0/tesdat/pattern/tests
hard linking tesdat/pattern/tests/test_hash.py -> tesdat-1.3.0/tesdat/pattern/tests
hard linking tesdat/pattern/tests/test_increment.py -> tesdat-1.3.0/tesdat/pattern/tests
hard linking tesdat/pattern/tests/test_limited_choice.py -> tesdat-1.3.0/tesdat/pattern/tests
hard linking tesdat/pattern/tests/test_sequence.py -> tesdat-1.3.0/tesdat/pattern/tests
Writing tesdat-1.3.0/setup.cfg
Creating tar archive
removing 'tesdat-1.3.0' (and everything under it)
running upload
Submitting dist/tesdat-1.3.0.tar.gz to https://pypi.python.org/pypi
Server response (200): OK

以上でライブラリの登録が終了しました。

ちゃんとpipでインストールできます。

$ pip install tesdat
Downloading/unpacking tesdat
  Downloading tesdat-1.3.0.tar.gz
  Running setup.py (path:/home/crohaco/test/build/tesdat/setup.py) egg_info for package tesdat
 
Installing collected packages: tesdat
  Running setup.py install for tesdat
 
Successfully installed tesdat
Cleaning up...

ライブラリの配布形式には他にもいくつかあるようです。

bdist_egg

bdist_eggはバイナリ形式の配布方法です。ここでいうバイナリとはpycやpyoのことではなく、C言語での拡張部分を指しています。バイナリで配布することのメリットはビルド環境(コンパイラやライブラリ)が整っていなくてもインストールすることが可能な点です。ただし、作成した環境と同じでないといけない(OSやPythonバージョン)という制限があります。ライブラリがすべてPythonで書かれている場合はこの形式で配布する意味は特にありません。

bdist

bdistもバイナリ形式の配布を意味していますが、配置は自分で行う必要があるらしいです。(詳しくない)
また原因はわかっていませんが、setup.pyの依存パッケージとしてインストールされる場合に、bdistが優先的に選択されてしまいbdistパッケージにはsetup.pyが含まれていないためインストールに失敗してしまうということがありました。特に理由がなければbdist形式のファイルは登録しなくてよいでしょう。