PyPIにライブラリを公開するときにやったこと(Python)

# はじめに

Pythonistaのみなさんこんにちわ。
Pythonでライブラリを書いていると、やむを得ず公開せざるを得ない場面に出くわすことがあると思います(?)
久しぶりに自作ライブラリをPyPIで公開したのですが、数年ぶりで勝手がわからず、1から試行錯誤したので、メモ代わりにこの記事を執筆しています。

# 先行記事

@shinichi-takii さんが詳しい記事をいくつも書かれているので、基本これらのページをご覧になれば十分です。

- [PyPIパッケージ公開手順](https://qiita.com/shinichi-takii/items/e90dcf7550ef13b047b5)
- [PyPIパッケージ定義ファイル作成方法 - __init__.py setup.py MANIFEST.in の書き方](https://qiita.com/shinichi-takii/items/6d1063e0aa3f79e599f0)

本稿は実際にライブラリを公開したときの実況や補足説明的な内容になります。

# Python処理系の選択

Windows環境なのでPython処理系は複数の選択肢があり、私はmsys2のpacmanでx64版のPython3を入れました。

他にAnacondaやら公式のWindows版やら色々あるのですが、Cython等のビルドが必要なライブラリをpipで入れるときの安定性はmsys2のPythonがピカイチだと思います。

WindowsとLinux環境下でライブラリの挙動を揃える必要があり、互換性重視で選んだという側面もあります。

なお、Python3は **python3**、Pipは **pip3** のコマンド名になります。

# 準備

1. PyPIのアカウントを作ります。(説明省略)
1. アカウント認証ファイル(.pypirc)作成、は別に無くてもいいです
1. 公開に必要なツール(wheel/twine)をインストール

```shell
$ pip3 install wheel twine
```

# 今回公開したモジュールについて

今回公開したライブラリは [SBPL](https://pypi.org/project/sbpl/) という名称で、その名の通りSBPLを使ってSATO社のラベルプリンタでリモート印刷するためのモジュールです。PyPIおよびGitHubの公開ページのURLを書いておきますので必要に応じてご覧ください。

- https://pypi.org/project/sbpl/
- https://github.com/kanryu/sbpl

# 公開モジュールのdocstringを整える

このライブラリは小規模なので、 **sbpl/\_\_init\_\_.py** 中で全て定義を済ませています。

Python言語はdocstringという形でライブラリのソースコード中にAPIドキュメントを直接記述できるようになっており、それに従って記述することでAPIドキュメントは自動生成できます。

しかしdocstringの書式やドキュメント生成ツールなどは複数の選択肢がありカッチリ定まっていないところがあるので、今回は以下のようにしました。

- docstringの書式はreStructuredTextとする
- APIドキュメントの生成はsphinxで行う

docstringの書式は他にGoogle流やらNumPy流やらあるようですが、いずれも本ライブラリ用としては大げさで記述量が多くなってしまうので、簡潔なreStructuredTextで十分と判断しました。

sphinxはRDBのER図を作成しようとして使ったことがある(ただしこれは失敗した)という程度の理由です。

具体的なHTMLマニュアルの生成方法については既に記事がありますのでこちらをご覧になるといいでしょう。

- [Sphinx入門。Sphinxでdocstringの生成](https://qiita.com/Arvelt/items/45428c5f9661cec3f513)

なお、配布パッケージにAPIドキュメントを収録することは可能だと思いますが、みんなどうせGitHubを見に行くだろうからということで収録はやってません。

## docstringの中身

詳しくは実際の[ソースコード](https://github.com/kanryu/sbpl/blob/master/sbpl/__init__.py)をご覧になったほうが早いと思いますが、軽く紹介しておくと、こんな書式になりました。

### ヘッダ

```Python
"""
SBPL module for remote printing

This provides the function of remote printing directly 
to the printer existing on your LAN by using SBPL 
(SATO Barcode Printer Language) provided by SATO Corp.

This enables arbitrary label cutting which can not be controlled
by a normal Windows printer.

This module is a prototype and may not satisfy your work, 
but since it is Pure Python, you can add and change features yourself.
"""

__copyright__    = 'Copyright (C) 2018 KATO Kanryu'
__version__      = '0.1.1'
__license__      = 'MIT'
__author__       = 'KATO Kanryu'
__author_email__ = 'k.kanryu@gmail.com'
__url__          = 'https://github.com/kanryu/sbpl'
```

### クラス定義

```Python
class TtfGlyph:
    """
    Handle glyph data for one character of TrueType font.

    Fonts are rendered by FreeType-py and kept by instances of this class.
    Each glyph is rendered as a 1 bpp monochrome bitmap and issued as a GB command.
    It can be cached in units of character strings and SBPL can be actually 
    generated after calculating the range of output character strings.

    :param c: The character to render. len(c)==1
    :type c: str
    :param face: freetype.face
    :type face: freetype.face
    :param expansion: Font expansion. maybe expansion = XXpt*72
    :type expansion: int
    """
    def __init__(self, c, face, expansion):
        ...
```

### メソッド定義

```Python
def generate(self, gen, position):
    """
    Print the stored glyph data

    :param gen: Generator
    :type gen: LabelGenerator
    :param position: Coordinates to be printed
    :type position: tuple or list of 2-int
    """
    ...
```

# GitHubにリポジトリを作成する

***『sbpl』*** というリポジトリ名で、***MIT***ライセンスとしてGitHub上にリポジトリを作成しました。READMEやらLICENSEやら、雛形を取ってくるのが面倒なので、GitHubにやってもらったほうが楽だと思います。リポジトリが出来たらローカル上にcloneして、実際の登録作業を開始です。

# setup.pyを記述する

この内容で実際に運用できるようになったので、まるごと貼り付けておきます。

```Python
from setuptools import setup

requires = ["freetype-py>=2.0.0"]

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

setup(
    name='sbpl',
    version='0.1.1.rev4',
    description='SBPL module for remote printing',
    long_description=readme,
    long_description_content_type='text/markdown',
    url='https://github.com/kanryu/sbpl',
    author='KATO Kanryu',
    author_email='k.kanryu@gmail.com',
    license='MIT',
    keywords='sbpl printing socket freetype',
    packages=[
        "sbpl",
    ],
    install_requires=requires,
    classifiers=[
        'Topic :: Printing',
    ],
)
```

setupメソッドの **long_description** 引数に ***README.md*** の内容をまるごと突っ込んでいます。するとPyPI上の紹介ページの本文になります。long_descriptionにはreStructuredTextが暗黙的に想定されており、MarkDown形式で登録するときには **long_description_content_type** 引数に細工が必要です。

ライセンスはファイルとしては登録せず、ライセンスの種類(MIT)だけを記述しています。オリジナルのライセンスでない限りはこれで十分だと思います。

バージョン名が ***0.1.1.rev4*** とややかっこ悪いことになっていますが、プログラム本体の修正なしにパッケージの上げ直しを何度かやったのでこういう結果になりました。同じバージョンで上げ直しができないんですよね…。

# README の記述

GitHubのリポジトリを作った時点でMarkDown形式で雛形が作られているので、それを書き換えて作成します。docstringを一通り作成済みならば、記述すべき内容は大体揃っていると思います。ほぼコピペで作りました。

# ソースコード配布物の作成

```shell
$ python setup.py sdist
```

# ライブラリをPyPIにアップロード

```shell
$ twine upload --repository pypi dist/*
```

テスト登録をすっ飛ばしていきなり本番に上げてます。行儀悪いです。元記事通り、テスト登録やって確認してからにしましょう。

元記事の ***.pypirc*** をすっ飛ばしているので、アップロード時にアカウントを聞いてきます。面倒だと思う人は素直に設定ファイルを作りましょう。私は多少の緊張感はあったほうがいいと思うので、敢えて無しでやっています。

以上。