Pythonスクリプトをパッケージ化して配布する方法

# はじめに
トレロをコマンドラインから操作するスクリプトをPythonでつくったものの、同じコマンドをローカルのマックだけでなく作業専用のEC２でも実行したくなり、[こちらの記事](https://qiita.com/propella/items/803923b2ff02482242cd)を参考にさせていただき、スクリプトのパッケージ化と配布方法を調べてまとめてみました。

# 環境
- python 3.8
- pip 20.1.1

# ディレクトリ構成（STEP1)
配布を考えずにスクリプトを実装した初期の状態です。

```
trello-cli
|__ trello_cli
    ├── __init__.py #メインスクリプト
    |__ __main__.py #コマンドから呼ばれるファイル
```
最初のディレクトリ構成はこんな感じで、コマンドの実行は、

```
$ cd trello-cli
$ python -m trello-cli 
```
となり、trello-cliのディレクトリに移動する必要があります。また、このコマンドで、__main__.pyが実行されます。

\_\_main\_\_.pyの中身はこんな感じで、\_\_init\_\_.pyの中の、main()関数を呼び出しているだけです。実際の処理は、\_\_init\_\_.pyの、main()関数内に実装をしています。

```__main__.py
from . import main

main()
```

# 依存するパッケージを管理する（STEP2)
このスクリプトを使用するために、pipインストールする必要があるものを保存します。

```
$ pip freeze > requirements.txt
```

私の場合ファイルの中身はこんな感じになりました。なんか色々と書かれていますが、必要だったのは、py-trelloと、python-dotenvです。

```requirements.txt
certifi==2020.6.20
chardet==3.0.4
idna==2.10
oauthlib==3.1.0
py-trello==0.17.1
python-dateutil==2.8.1
python-dotenv==0.15.0
pytz==2020.4
requests==2.24.0
requests-oauthlib==1.3.0
six==1.15.0
urllib3==1.25.11
```

この時点でディレクトリ構成は以下のようになります。

```
trello-cli
├── requirements.txt
|__ trello_cli
    |__ __init__.py
    |__ __main__.py
```

# setup.pyを書きます（STEP3)

```setup.py
import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    name="Trello-CLI",
    version="0.0.1",
    install_requires=[
        "requests",
    ],
    entry_points={
        'console_scripts': ['trello=trello_cli:main'],
    },
    author="Yokohama",
    author_email="xxx@example.com",
    description="Trello card managment on CLI",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="",
    packages=setuptools.find_packages(),
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.8',
)
```
- `install_requires`にパッケージが依存するパッケージを書くと pip が自動インストールしてくれます。
- `entry_points`の`console_scripts`で指定した内容が、コンソール上でコマンドとして実行できるようになります。この場合は、`trello`コマンドで、`trello-cli/__main__.py`が実行されます。

この時点でディレクトリ構成は以下のようになります。

```
trello-cli
├── requirements.txt
├── setup.py
|__ trello_cli
    |__ __init__.py
    |__ __main__.py
```

# 配布物を作る（STEP4)
いよいよこのスクリプトを`pip install`できる状態にします。

```
$ python setup.py bdist_wheel
```

そうすると、以下のディレクトリ構成図のように、distというディレクトリが作成され、その中に`pip install`用のファイルが作成されます。

```
trello-cli
├── dist
│   └── Trello_CLI-0.0.1-py3-none-any.whl
├── requirements.txt
├── setup.py
|__ trello_cli
    |__ __init__.py
    |__ __main__.py
```

# 実際にEC2上で使ってみる
今回の目的はローカルと同じコマンドをEC2上で使うことなので、作成した配布物を使って早速EC2上にインストールをしてみます。まず、仮想環境を使うにしても、pythonとpipはローカルと同じバージョンにしておく必要があります。

```
$ cd trello-cli
$ pip install ./trello-cli/dist/Trello_CLI-0.0.1-py3-none-any.whl
$ trello
```

ローカルと同じようにコマンドを実行することに成功しました。

# 課題
Trelloの`API Key`などソースコード中に書きたくないものは、python-dotenvを使い、`.env`ファイルに記述をしたのですが、インストール先でも当然`.env`ファイルを用意してあげないとエラーになります。

どの様な状況でpythonを実行しているかで、展開されるディレクトリの場所は変わりますが、私の場合は`anaconda`で実行しているため、`~/anaconda3/lib/python3.8/site-packages/trello_cli/.env`という、とんでもなく分かりづらい場所に、`.env`ファイルを作成して上げる必要が有りました。

もっとスムーズにできる方法を知っている方いらっしゃいましたらコメント頂けますと幸いです。

# 参考
https://qiita.com/propella/items/803923b2ff02482242cd