PyPIデビュー
2014-11-03

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

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

アカウントの登録

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

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

pypi-0

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

pypi-1

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

pypi-2

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

pypi-3

登録用URLにアクセスすると利用規約が表示されます。

I agree のチェックボックスを選択して「Confirm」ボタンを押下します。

pypi-4

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

pypi-5

ライブラリを登録する

Warning

  • この記事は 2014 年のもので、少し古いですが、 (2018/3 に少しいじったので) Python のバージョンが古くなければ多分動きます

    • アップロードの際には 最新の Python を使うことを推奨します。(レガシーな API を参照してしまう可能性があるため)

  • 前まで ライブラリの登録は大きく分けてライブラリ名の取得とファイルのアップロードに分かれていたのですが、 最新のAPIでは ファイルのアップロードを行うと 自動的にライブラリの登録まで完了するようです。

setup.py

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

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

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

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

version

パッケージのバージョンです。既に登録されているバージョンに対し、同じ形式のライブラリを登録することはできません。 Typoのような軽微な修正であっても別バージョンとして登録する必要があります。

決まったフォーマットはありませんが、 {major}.{minor}.{patch} とするのが慣例のようです。

また、バージョン番号の大きさではなく登録日時によってライブラリは最新(latest)であると判断されます。 重複さえしなければ前より低いバージョン番号で登録することも可能ですが、混乱のもとなのでやめましょう。

install_requires

動作する上で必要なライブラリを列挙します。比較演算子によってバージョンを指定することもできます。

例えば、Django1.7以上を必要とする場合は install_requires = ['django >=1.7'] のように記述します。

参考

setupスクリプトを書く

author_email

所有者のメールアドレスを記述します。なにげに必須項目です。

多分最近(2018)仕様が変わったのかと

keywords

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

classifiers

ライブラリの分類を定義します。これは分類によりライブラリを探すための仕組みだと思われます。イメージ的にはGoogleキーワード検索に対するYahooカテゴリ検索(ディレクトリ検索)のようなものでしょうか。(違うかも)

ここ からアクセスできます。 使用可能な分類は list_classifiers を参照してください。

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」ディレクトリを一旦削除してから再実行すると良いでしょう。

認証

PyPI にアップロードする際に認証情報が必要になります。

./setup.py register とすることで 認証関連処理 ができます。

  • ログイン

  • 新規ユーザ登録

  • 新規パスワード発行

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: righ
Password: (入力する)

実際のところ、このコマンドはそこまで使う必要がないかもしれません。

新ユーザ登録, 新規パスワード発行PyPIサイト 上から行えますし、 ログイン後の認証情報は ~/.pypirc に直接記入すれば機能するためです。

.pypirc

何度も認証入力するのがめんどくさい人は .pypirc に保存しましょう。

.pypirc にはINI形式で認証情報が平文で保存されています。

セキュリティ的なリスクもあるのでマシンを複数人で共有している場合は適切なアクセス権を設定するか、心配な場合は使用後に削除するようにしましょう。

パスワード部分だけ削除しておけば アップロードのたびにパスワードだけ聞かれるようになります。

[distutils]
index-servers =
    pypi

[pypi]
username:[ユーザ名]
password:[パスワード]

.pypirc リファレンス

ファイルのアップロード

ファイルをアップロードするためには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形式のファイルは登録しなくてよいでしょう。