やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

PyPIパッケージ登録に必要なもの

 整理した。

PyPIパッケージ登録に必要なもの

 まとめ。

必要なもの

  1. アカウント
  2. ツール
  3. ファイル
  4. データ

1. アカウント

 各サイトに従って取得する。

2. ツール

3. ファイル

 GitHubリポジトリの中にPyPIパッケージをそのまま含める。

  • {git_repo}/
    • {package}/
      • __init__.py
      • {module}.py
    • tests/
      • __init__.py
      • tests_{module}.py
    • setup.py
    • requirements.txt
    • LICENSE.txt
    • README.md
    • CHANGES.md
    • MANIFEST.in
    • .gitignore

{package}/__init__.py

from {module} import *

__version__      = '0.0.1'
__author__       = 'ytyaru'
__author_email__ = 'pypi1@outlook.jp'
__copyright__    = 'Copyright (C) 2020 ytyaru'
__license__      = 'GNU Affero General Public License Version 3 (AGPL-3.0)'
__url__          = ''

__all__ = ['{module}']

setup.py

import setuptools

setuptools.setup(
    name=package_name,
    version=version,
    author=author,
    author_email=author_email,
    description=description,
    long_description=long_description,
    long_description_content_type="text/markdown",
    keywords="pypi,pip,practice,test,package,module",
    url="https://github.com/ytyaru/Python.PyPI.mypack",
#    download_url="",
    packages=setuptools.find_packages(),
    classifiers=[
        "Development Status :: 4 - Beta",
        "Programming Language :: Python",
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: GNU Affero General Public License v3",
        "Operating System :: OS Independent",
        "Intended Audience :: Developers",
        "Topic :: Utilities",
    ],
    python_requires='>=2.7',
)

 setup.cfgという中間ファイルで管理することもできるらしい。

 これをPythonBashなどで読み込んで使えるなら、こちらのほうが一元管理できて良いかもしれない。だが、特殊な記法であるためPythonで書いたほうが応用が効くかも? 将来、異なる形式になる可能性まで示唆されている。要検討。

requirements.txt

 依存パッケージ一覧を作る。

pip freeze > requirements.txt

 それを元に一括インストールする。

pip install -r requirements.txt

LICENSE.txt

 適当にググってテンプレを入手する。GitHubでもテンプレ作成できると思う。

README.md

 適当にググってテンプレを入手する。プロジェクトごとに項目を埋める。他のところでも共通の内容が多数含まれる。なんとかして一元管理したい。

[ja](./README.ja.md)

# {{package_name}}

{{description}}

# DEMO

(None)

# Features

* \_\_init\_\_.py
* setup.py
* Includes unit test code.

# Requirement

* [Raspbierry Pi](https://ja.wikipedia.org/wiki/Raspberry_Pi) 4 Model B Rev 1.2
* [Raspbian](https://ja.wikipedia.org/wiki/Raspbian) buster 10.0 2019-09-26 <small>[setup](http://ytyaru.hatenablog.com/entry/2019/12/25/222222)</small>
* Python 2.7.16
* Python 3.7.3

# Installation

\`\`\`sh
pip install {{package_name}}
\`\`\`

# Usage

a.py
\`\`\`python
import mypack
mypack.mymethod()
\`\`\`
\`\`\`sh
python a.py
\`\`\`
\`\`\`sh
mymethod return.
\`\`\`

# Note

* Register for the first time with PyPI. There may be mistakes or better ways

# Author

 {{author}}

* [![github](http://www.google.com/s2/favicons?domain=github.com)](https://github.com/ytyaru "github")
* [![hatena](http://www.google.com/s2/favicons?domain=www.hatena.ne.jp)](http://ytyaru.hatenablog.com/ytyaru "hatena")
* [![mastodon](http://www.google.com/s2/favicons?domain=mstdn.jp)](https://mstdn.jp/web/accounts/233143 "mastdon")

# License

This software is CC0 licensed.

[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png "CC0")](http://creativecommons.org/publicdomain/zero/1.0/deed.en)

 他の項目との共通点は以下のように多数ある。なんとかして一元管理したい。

  • {{package_name}}
  • {{description}}
  • Requirement
  • Installation
    • 自パッケージ
    • 依存パッケージ
  • {{author}}
  • License

 他、パッケージごとに別途記載する項目は以下。これはREADME.mdのみにある内容。

  • DEMO
  • Features
  • Usage
  • Note

CHANGES.md

# CHANGELOG

## V0.0.2

* Commit Message 3
* Commit Message 4

## v0.0.1

* Commit Message 1
* Commit Message 2

 gitのコミットメッセージ、タグ名と一致する。

  • なんとか一元管理したい。gitのhistoryから自動生成できないか?
  • リンクも欲しい
    • GitHubコミットごとのリンク
    • Issueリンク
    • Releaseリンク

MANIFEST.in

include LICENSE.txt
include README.md
include CHANGES.md
include *.md
exclude .gitignore
include tests.sh
exclude release.sh

 PyPIパッケージとして追加したいものはinclude、排除したいものはexclude。最低限必要なファイルは勝手にincludeしてくれるため明記不要。

.gitignore

*.pyc
*.egg-info/
dist/
build/

 GitHubにアップロードするとき対象外とするファイル一覧。ここではPythonのキャッシュやPyPI用ビルドしたときの各種ファイルを除外する。GitHubにアップすべきはソースコードのみ。コマンドさえ叩けば自動作成されるようなファイルはすべて除外する。

4. データ

  1. Python
    1. 公開APIシグニチャ(名前(パッケージ、モジュール、クラス、属性、関数、プロパティ)、引数、戻り値、スコープ(global, static, class, instance))
  2. メタ情報
    1. PyPIパッケージ名
    2. `python_requires='>=2.7',
    3. PyPIパッケージ説明
    4. 長い説明(README)
    5. URL
    6. 著作権者名
    7. 著作権者メールアドレス
    8. コピーライト
    9. ライセンス
    10. 検索キーワード
    11. classifiers
      • Development Status
      • Environment
      • Framework
      • Intended Audience
      • License
      • Natural Language
      • Operating System
      • Programming Language
      • Topic
      • Typing

パッケージ名

 最重要メタデータ。以下は共通する。

 できればGitHubリポジトリ名も共通だとわかりやすい。

 パッケージ名は以下に従わねばならない。

 この制約、かなり厳しい。特にPyPIでの重複チェック。世界中から集まるため、1秒後には重複するかもしれない。時間が経つほど飽和するため、いずれわかりにくくて長い名前しか使えなくなるだろう。その上、使える文字に記号がない。飽和するのが早まる。

 早めに名前だけでも確保すべき? だが実装のメドが立たないうちにそんなことをしたら世界的損失。さっさとコードを書くべき。

 そもそもメタデータ作成に時間を使いたくない。名前チェックは自動化したい。

グローバル名前空間上の公開API名(import,__all__

 最重要実データ。実際にPythonから本パッケージを使用するときに用いる名前。グローバル名前空間において本パッケージを参照するための名前である。

{package}/__init__.py

from {package} import {module}
__all__ = ['{module}']

名前汚染対策:1つのパッケージにつき1モジュールにまとめたい。  1つのパッケージにつき1モジュールにまとめたい。もし複数のモジュールを作るならサブパッケージにし、表出するのはトップモジュール1つだけにしたい。そのほうが名前汚染を最小限にできるから。

 Python名前汚染問題は深刻である印象。たとえば既存モジュール名と同一のモジュール名にすると誤動作してしまう。これでハマったことが何度もある。できるだけグローバル名前空間には新しい名前を追加しない方向が好ましい。たとえname.name.nameのように階層が深くなったとしても。

 そうすればパッケージのインポート処理も最初に書いたように簡略化できる。

自動化したい。 __all__だけなら以下で可能。

import os, glob

__all__ = [
    os.path.split(os.path.splitext(file)[0])[1]
    for file in glob.glob(os.path.join(os.path.dirname(__file__), '*.py'))
]

 カレントディレクトリ直下にある*.pyファイル名の配列を返す。拡張子は省かれる。

 ただ、from {package} import {module}と連携せねばならないため不十分。__init__.pyファイルをまるごと作成するようにしたほうが現実的か。

version

 パッケージ名に次ぐ重要メタデータ

 PyPIでパッケージ更新するときは必ずインクリメントせねばならない。同一バージョン値は1回しか使えない。当然の制約だが、思わぬ失敗の原因になる。インクリメントを忘れたり、メタデータ変更しただけでインクリメントせねばならず思い通りの採番ができなかったり。

 バージョン値の決定方法にはセマンティック バージョニングを用いる。ただしプレリリース識別子は使わないほうが無難。以下のように数値とドットのみで表現するほうがよい。

0.0.1  # 正式リリース前
0.1.0  # 正式リリース前
1.0.0  # 正式リリース(最初)

 gitコミット時に自動でインクリメントしてくれると嬉しい。ついでにコミットメッセージをCHANGES.mdに自動で反映してほしい。

 いくつかコミットしたあと、一定のメドが立ったらgit tab -a -mでバージョンタグを付与する。その後twineでPyPIにアップロードすれば、リリースとなる。そんな流れを自動化したい。

プレリリース識別子

プレリリース識別子

 正式版リリース前に、以下のようなバージョンとして指定することもできる。

 ただしSemVerPEP440で表現方法に違いがある。よって、混乱を避けるためプレリリース識別子は使わないほうが無難。

GitHub Release

GitHub Release

 git tagをつけるとリリース機能が使える。その時点におけるソースコード一式を圧縮ファイルでダウンロードできる。このときタグはv1.0.0のようにする。SemVerの前にvを付与する。

 以下のようにすることで、カレントディレクトリであるリポジトリのタグを最新順で表示できる。

git tag | sort -Vr

python_requires='>=2.7',

 重要メタデータ。この値や環境によっては本パッケージをインストールできなくなる。

 PythonAPIによって使えるAPIが異なる。また、バージョン差異によって名前や結果が異なる。様々なバージョン差異があるため、最低限必要なバージョンを指定することになる。それが本データ。

 できるだけ低いほうが多くの環境で使える。ただし実装は大変になる。こんなことはPython自身に管理してほしいのが本音。人間技ではムリ。だがやらねばならぬ。開発者にとって甚大な負担となる項目のひとつ。

パッケージ説明

 これは至るところで共通するはず。

 その他、CUIアプリケーションならヘルプの内容に記載するかもしれない。なんとかして一元管理したい。

 長い説明(setup.py,setuptools.setup(),long_description)は、README.mdをそのままぶち込めばいい。

URL

 パッケージ説明の総本山を示すアドレス。以下の箇所で共通するはず。

  • __init__.py__url__
  • setupurl
  • GitHubリポジトリhomepage
  • ReadMe.mdのDescriptionやDEMOにも?

 URLはホームページのこと。専用ページをつくる気がないならGitHubリポジトリURLにでもしておくと良さそう。以下のような候補がありうる。

 setup.pyには他にもdownload_urlがある。これをGitHubのダウンロードURLにすればGitHubAPIでダウンロード数を取得できると考えた。しかしGitHubのリリース機能から最新リンクを取得したかったができなかったため断念。Webスクレイピングすれば不可能ではないが、サイト構成が変更されると破綻するため自動化できるとは言い難い。やむなくダウンロード数の集計は諦める。

ライセンス

 重要。本パッケージを使うかどうかはライセンスで判断されることもあるはず。

 LICENSE.txt内にて著作者名、メールアドレス、コピーライトが必要になることがある。コピーライトは、著作者名が必要。任意でメールアドレスを<>で囲って表記することもある。つまり上記のような包含関係になる。

 ライセンスの選択については、頑張ってライセンスを学習するしかない。法律にまで手を伸ばせば本筋からかけ離れてしまうため、適当に決める。たとえば以下から探す。MITがよく使われている。

 __license__の文字列には明確なルールがないと思われる。マシンリーダブルでなく人間が読めればそれでいい感じ。でもそれだと毎回なにを入力すればいいか迷って面倒。やはり自動化したい。

その他PyPI検索ヒント

 PyPIでパッケージ検索するときに役立つ。

  • 検索キーワード
  • classifiers
    • Development Status
    • Environment
    • Framework
    • Intended Audience
    • License
    • Natural Language
    • Operating System
    • Programming Language
    • Topic
    • Typing

 重要度は低い。適当にデフォルト値にしつつ、必要に応じて変更できるようにするのがよいか。

 あるいはバージョン値の状態によってDevelopment Statusを自動的に繰り上げるようにすべきか。他にもPythonのバージョンなど他の項目と重複する部分がある。一元管理することで連動させるのが理想。

対象環境

$ uname -a
Linux raspberrypi 4.19.75-v7l+ #1270 SMP Tue Sep 24 18:51:41 BST 2019 armv7l GNU/Linux