PyPIパッケージ定義ファイル作成方法 - __init__.py setup.py MANIFEST.in の書き方

# はじめに

[PyPIパッケージ公開手順](https://qiita.com/shinichi-takii/items/e90dcf7550ef13b047b5) でPyPIに公開するまでの方法を説明しました。
この記事では、パッケージ作成に必要なファイルの作成方法について説明します。


# 概要

パッケージを作成するために必要なファイルは、3つあります。
`MANIFEST.in` ファイルは必要な場合だけ、作成してください。

- `__init__.py` : パッケージ基本情報を記述します。
- `setup.py` : パッケージ作成情報を記述します。
- `MANIFEST.in` : 配布物に追加、または除外するファイルを記述します。


## ファイル構成の例

以下のファイル構成を例に説明します。

```
.
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── README.rst
├── <package>
│   ├── __init__.py
│   └── <module>.py
├── requirements.txt
├── setup.cfg
├── setup.py
├── test
│   ├── __init__.py
│   └── test_foo.py
└── test-requirements.txt
```


# `__init__.py` : パッケージ基本情報を記述

パッケージの著作権情報や、公開モジュールなどを記述します。

```python:__init__.py
from .<ファイル> import *

__copyright__    = 'Copyright (C) 2018 Your Name'
__version__      = '1.0.0'
__license__      = 'BSD-3-Clause'
__author__       = 'Your Name'
__author_email__ = 'Your@Email'
__url__          = 'http://github.com/account/repository'

__all__ = ['公開モジュール', ...]
```

|定義変数|説明|
|:--|:--|
|`__copyright__`   |著作権|
|`__version__`     |バージョン番号 ※使える文字は、英数字・ドット(".")です。|
|`__license__`     |ライセンス情報|
|`__author__`      |著作者名|
|`__author_email__`|著作者メールアドレス|
|`__url__`         |WebサイトURL|
|`__all__`         |`import *` したときに公開するモジュールのリスト|


# `setup.py` : パッケージ作成情報を記述

パッケージ作成に必要なモジュール配布物を定義するファイルです。

### 基本的な記述内容

- `from setuptools import setup` : パッケージ作成に必要なセットアップモジュールをインポートします。
- `setup( ... )` : setup スクリプトを記述します。


### 具体的な記述内容

このサンプルで必要な編集箇所は、 `package_name`, `description`, `keywords`, `classifiers` のみです。
`__init__.py` や `requirements.txt` などの定義ファイルを再利用して、できるだけ同一内容を重複して記述せずに済むように工夫しました。

#### setup.py サンプル

```python:setup.py
# -*- coding: utf-8 -*-

from setuptools import setup
from codecs import open
from os import path
import re

package_name = "パッケージ名"

root_dir = path.abspath(path.dirname(__file__))

def _requirements():
    return [name.rstrip() for name in open(path.join(root_dir, 'requirements.txt')).readlines()]


def _test_requirements():
    return [name.rstrip() for name in open(path.join(root_dir, 'test-requirements.txt')).readlines()]

with open(path.join(root_dir, package_name, '__init__.py')) as f:
    init_text = f.read()
    version = re.search(r'__version__\s*=\s*[\'\"](.+?)[\'\"]', init_text).group(1)
    license = re.search(r'__license__\s*=\s*[\'\"](.+?)[\'\"]', init_text).group(1)
    author = re.search(r'__author__\s*=\s*[\'\"](.+?)[\'\"]', init_text).group(1)
    author_email = re.search(r'__author_email__\s*=\s*[\'\"](.+?)[\'\"]', init_text).group(1)
    url = re.search(r'__url__\s*=\s*[\'\"](.+?)[\'\"]', init_text).group(1)

assert version
assert license
assert author
assert author_email
assert url

with open('README.rst', encoding='utf-8') as f:
    long_description = f.read()


setup(
    name=package_name,
    packages=[package_name],

    version=version,

    license=license,

    install_requires=_requirements(),
    tests_require=_test_requirements(),

    author=author,
    author_email=author_email,

    url=url,

    description='一言で書けるパッケージ概要',
    long_description=long_description,
    keywords='キーワード1, キーワード2, キーワード3',

    classifiers=[
        'Development Status :: 5 - Production/Stable',
        'License :: OSI Approved :: BSD License',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Topic :: Database',
        'Topic :: Software Development :: Libraries :: Python Modules',
    ],
)
```


#### setup スクリプト定義項目

setup スクリプトで定義する具体的な項目を説明します。
なお、これがすべてではありません。
他の項目については、[Python公式ドキュメント](https://docs.python.jp/3/distutils/setupscript.html)などを参照してください。

- `name` : パッケージ名
- `packages` : モジュールリスト
- `version` : バージョン番号
- `license` : パッケージに適用するライセンス
  'MIT', 'BSD-3-Clause' などを記述します。

- `install_requires` : 依存関係のあるパッケージのリスト
- `tests_require` : ユニットテスト時に依存関係のあるパッケージのリスト
- `author` : 著作者名
- `author_email` : 著作者のメールアドレス
- `url` : パッケージのWebサイト
  GitHubのリポジトリURLなどを指定します。

- `description` : 概要説明
- `long_description` : 詳細説明
  一般的には `README.rst` ファイルの内容をそのまま書き込むことが多いようです。

- `keywords` : PyPI検索ヒントキーワード
  PyPIで検索対象にしたいキーワードをスペース区切りで記述します。

- `classifiers` : 分類語のリスト
  PyPIの [List trove classifiers](https://pypi.python.org/pypi?%3Aaction=list_classifiers) ページから適切な分類語を選んでください。


# `MANIFEST.in` : 配布物の追加・除外ファイル定義を記述

`MANIFEST.in` ファイルがなくても、 `python setup.py sdist` コマンド実行時に最低限必要なソースコード配布物は含まれます。
しかし、LICENSEファイルなど、他にも包含したいことの方が多いのではないでしょうか。
そんなときは、 `MANIFEST.in` ファイルを作成してください。

- `include <ファイル名>` : 追加したいファイルを指定します。
- `exclude <ファイル名>` : 除外したいファイルを指定します。
- `recursive-include <ディレクトリ> <ファイル名>` : 特定ディレクトリ配下で、追加したいファイルを指定します。
- `recursive-exclude <ディレクトリ> <ファイル名>` : 特定ディレクトリ配下で、除外したいファイルを指定します。
- いずれも、ファイル名にはワイルドカードを使うことができます。

```txt:MANIFEST.inの例
include LICENSE
include *.md
exclude README.md
include requirements.txt
include test-requirements.txt
include setup.cfg
```

# 関連記事
[PyPIパッケージ公開手順](https://qiita.com/shinichi-takii/items/e90dcf7550ef13b047b5)