PyPI登録手順〜pip installするまで〜

この記事を書こうと思ったのは，[PyData.Okinawa #34 - LT大会](https://pydataokinawa.connpass.com/event/82009/)で「PyPIにパッケージをアップロードした話」をしたことがきっかけです。このLTをするにあたって，PyPIにパッケージをアップロードする知見を得たので，PyPIにアップロードしてみたいけどどうすればいいのかわからない人の助けになればいいと思います。
初投稿なので，参考になったら:thumbsup:かコメントいただけると嬉しいです！！

実際に発表したLT大会の資料です。
[PyPIにパッケージをアップロードした話](https://speakerdeck.com/takezyou/pypiniatupurodositahua-pydata-dot-okinawa-number-34-ltda-hui)

# PyPIとは
PyPIは，Python Package Indexの略称で，Pythonのパッケージを管理するサイトです。
PyPIにパッケージをアップロードすることで，pipを用いてインストールできるようになります。
PyPIの読み方は，パイピーアイです。
最近Python Package Indexのサイトが新しくなったので，最新のやり方になります。
# アップロードまでの流れ
ここでは，アカウント作成からアップロードするまでの流れを記述します。

- アカウント作成
- setup.pyの作成
- 開発
- テスト
- 配布物の作成
- パッケージの登録/アップロード

## アカウント作成
PyPIでは，本番環境とテスト環境があるのでそれぞれアカウントを作成する必要があります。なので，まず初めにアカウントを作成しましょう。

- 本番環境：https://pypi.org/account/register/
- テスト環境：https://test.pypi.org/register/

## setup.pyの作成
基本的にはPyPI公式のGitHubにある例がかなりオススメです。
https://github.com/pypa/sampleproject/blob/master/setup.py

実際に作成したコマンドのsetup.pyはこちらになります。

```python:setup.py
#!/usr/bin/env python
# coding: utf-8

from setuptools import setup, find_packages
from codecs import open
from os import path

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

with open(path.join(here, 'README.md'), encoding='utf-8') as f:
    long_description = f.read()

setup(
    name='pydata',
    version='1.0.0',
    description='PyData Location List',
    long_description=long_description,
    url='https://github.com/takezyou/pydata',
    author='takezyou',
    author_email='kaitokun07@icloud.com',
    license='MIT',
    # 実際に動かす時に依存関係にあるライブラリをinstallしてくれる
    install_requires=['beautifulsoup4', 'lxml'],
    keywords='pydata',
    packages=find_packages(exclude=('tests')),
    # 今回コマンドを作ったのでconsole_scriptsを記述している
    entry_points={
        "console_scripts": [
            "pydata=pydata.__init__:main",
        ],
    },
    classifiers=[
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
    ],
)
```
## 開発
今回作成したコマンドのディレクトリ構造はこんな感じです。
pydataがメインのプログラムになります。
testsにlist.pyのテストを書いています。
こちらが作成した https://github.com/takezyou/pydata のリポジトリになります。
ここは，頑張って開発しましょう！！

```
.
├── LICENSE.txt
├── README.md
├── pydata
│   ├── __init__.py
│   ├── command.py
│   └── list.py
├── setup.py
└── tests
    ├── __init__.py
    └── test_list.py
```
## テスト
テストでは，基本的に機能ごとにテストします。
testの確認方法はtestsに書いておけば下記のような感じで実行できます。

```
$ python setup.py test
```

## 配布物の作成
作成したものをパッケージにする作業です。
`sdist`で配布物を作るやり方もあるのですが今回は，[wheel](https://github.com/pypa/wheel)と言われるものでバイナリパッケージを作ります（公式のドキュメントは`wheel`をオススメしています）。コマンド1つで簡単に作成することができます。
※不要なパッケージファイルは事前に削除しておきましょう

```
$ pip install wheel
$ python setup.py bdist_wheel
```

## パッケージの登録とアップロード
まず，初めにホームディレクトリに`.pypirc`を作成します。

```shell:.pypirc
[distutils]
index-servers =
    pypi
    testpypi

# 本番環境
# passwordとusernameはアカウント作成の時のものを利用する
[pypi]
username: takezyou
password: user_password

# テスト環境
# passwordとusernameはアカウント作成の時のものを利用する
[testpypi]
repository: https://test.pypi.org/legacy/
username: takezyou
password: user_password
```

アップロードする時に`twine`を用いてパッケージをアップロードします。
※同じバージョンのものは1度しかアップロードできないので気を付けましょう。

```
$ pip install twine
```

では，準備が整ったのでテスト環境にアップロードしてみます。

```
# テスト環境に先ほど作ったバイナリのパッケージをアップロードする
$ twine upload --repository testpypi dist/*

# テスト環境にアップロードしたパッケージをインストールする
$ pip install --index-url https://test.pypi.org/simple/ <パッケージ名>
```

テスト環境でインストールしたものが実際に問題がなければ本番環境にアップロードします。

```
# 本番環境にパッケージをアップロード
$ twine upload --repository pypi dist/*
```
アプッロードできるとこんな感じになります。

<img width="1280" alt="スクリーンショット 2018-05-05 14.59.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/152495/51cc914b-a1cb-9add-9175-c7347c12fca2.png">

# pip installしよう！！！
では，最後に作成したパッケージをpip installします。
利用できたらokです！！

```
# 実際にpip installする
$ pip install <パッケージ名>
```

# 参考文献
- [Packaging and distributing projects](https://packaging.python.org/tutorials/distributing-packages/)
- [Pythonで作成したライブラリを、PyPIに公開/アップロードする](https://qiita.com/icoxfog417/items/edba14600323df6bf5e0)
- [PyPIデビュー2015](https://tell-k.github.io/pyconjp2015/#1)