pythonアプリケーションをrpmにパッケージング

# はじめに

Pythonで開発したアプリケーションを別の環境に持っていったり、第三者に配布する時、`setup.py`の書き方を何度も調べてる自分に向けたそれです。

Pythonのパッケージングツールって色々有りますけど、
とりあえず今回は`pip`をインストールしたらセットでくっついてくる`setuptools`を使おうと思います。

[公式](https://docs.python.jp/3/distributing/index.html)さんもおすすめしていることですし。

`setuptools`の使い方を説明した記事等はネットにゴロゴロ転がっているので、
今回は[以前の記事](https://qiita.com/mypaceshun/items/5067561d6739cc9e5199)と絡めて、
rpmパッケージにするところまでやりたいと思います。

`setuptools`に関してより細かい内容が気になる方は以下が非常に参考になりました

* [pythonのsetup.pyについてまとめる](https://qiita.com/Tadahiro_Yamamura/items/2cbcd272a96bb3761cc8)
* [Pythonのパッケージングのベストプラクティスについて考える2018](https://techblog.asahi-net.co.jp/entry/2018/06/15/162951)

パラメータの一覧などを参照したいときは[setuptools公式](https://setuptools.readthedocs.io/en/latest/setuptools.html#developer-s-guide)なども参考になります。
ただし英語なので私みたいな英語苦手エンジニアは苦労するかもしれません()

# 検証環境

私がこの記事を書く上で検証につかった環境がこちら

``` console
$ python3 --version
Python 3.4.9
$ pip list
Package     Version
----------- -------
Click       7.0    
flake8      3.6.0  
Jinja2      2.10   
MarkupSafe  1.1.0  
mccabe      0.6.1  
pip         18.1   
pycodestyle 2.4.0  
pyflakes    2.0.0  
PyYAML      3.13   
setuptools  40.6.3 
wheel       0.32.3
$
$ cat /etc/centos-release
CentOS Linux release 7.6.1810 (Core)
```

# setup.pyとsetup.cfg

`setuptools`はバージョン30.3.0から引数を設定ファイルから受け取る仕組みが追加されました。
それまではパッケージングに含める情報をすべて`setup.py`ファイル内`setup()`関数の初期値として列挙し、以下のようなコードになっていたと思います。

``` python:setup.py
from setuptools import setup

setup(
  name = 'packageName',
  version = '1.0.0',
  description = 'python application',
  install_requires = ["package"],
  extras_require = {
    'dev': ['dev-package'],
  },
  entry_points = {
    'console_scripts': [
      'command = package.pakcage:main'
    ]
  }
)

```

正直見にくい。かと言って`setuptools`自体がこういうものなのでこうするしかなかった...

しかし`setup.cfg`を使うとこうなります！！

``` python:setup.py
from setuptools import setup

setup()
```


おーすっきり！肝心の設定はどこ言ったかと言うと`seutp.cfg`という別ファイルでiniファイルのような形式[^1]で記述します。

``` INI
[metadata]
name = packageName
version = 1.0.0
license = file: LICENSE

[options]
zip_safe = False
install_requires =
  package

[options.extras_require]
dev = 
  dev-package

[oprions.entry_points]
console_scripts =
  command = package.package:main

```

こっちのほうがわかりやすくて好みです(個人差あり)。

`license`でやっているように`file:`のようなディレクティブをつけて別ファイルから読み込んだりすることも出来ます。たまに`setup.py`でpythonコードごりごりにこねくり回してるパッケージが有りますが、設定ファイルでコードはかけないので、その値を入れるキーのみ`setup.py`内で指定するような形になると思います。

指定できる値やディレクティブは公式で一覧になっているのでご参照ください。
[Configuring setup() using setup.cfg files](https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files)

さて`setup.py`と`setup.cfg`が書けたらもう簡単です。
下記のコマンドを実行すると必要なファイルがすべてまとめられたtarボールが作成されます。

``` console
$ python setup.py sdist
```

`dist`というディレクトリが作成され、作成されたtarボールが保存されています。
ちなみにこのtarボールのまま`pip`コマンドでインストールも可能です。

``` console
$ pip install package.tar.gz
```

[^1]: 厳密にはpython標準の`configparser`に依存します

# wheelパッケージを作成

wheelとは[PEP 427](https://www.python.org/dev/peps/pep-0427/)で標準化されているPythonのパッケージング形式です。
中身はzipファイルなので解凍するといろいろ出てきます。
とりあえずPythonのアプリケーションをパッケージングするなら`tar`より`wheel`形式のほうがよいということですね。

wheel形式でパッケージングする方法は`setup.py`が完成していればあっという間です。
以下のコマンドでwheel形式のパッケージが作成されます。

``` console
$ python setup.py sdist_wheel
```

`dist`というディレクトリの下に`pakcagename-1.0.0-py3-none-any.whl`と言うようなファイルが出来ます。
これももちろん`pip`コマンドでインストール可能です。

``` console
$ pip install packagename-1.0.0-py3-none-any.whl
```

# rpmパッケージに組み込む

今回のメインです！

Pythonで作ったアプリケーションをrpmファイルにパッケージングしたい時、
ありますよね〜

そんなときは

``` console
$ python setup.py bdist_rpm
```

以上！！

もちろんですけど`rpm-build`が入ってないと動きません。これで`setuptools`がよしなにspecファイルを作ってパッケージをビルドしてくれます。

# まとめ

楽ちん♪

ただ、この方法では自分の書いたコードのみしかインストールされません(あたりまえ)。
依存ライブラリなどがある場合はwheelパッケージを作り、自分でspecファイルを作りこねくり回さないといけなくなります。

絶賛苦戦中です。