やりたいこと

  • Pythonモジュールをパッケージ化
  • モジュールのテストのために、pytestのディレクトリ構成(src、test)を使用
  • モジュールを使った、コマンドラインツールをインストール

作成するファイル一覧

1
2
3
4
5
6
7
8
9
10
11
12
13
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── setup.cfg
├── setup.py
└── src
├── __init__.py
└── packagedata
├── __init__.py
├── config
│ └── test.ini
└── getpackagedata.py

setup.py

setup.pysetup()を呼ぶだけの内容。

1
2
3
from setuptools import setup

setup()

setup.cfg

setup.cfgはパッケージに関する情報を記述する。
[metadata]は自身の内容を記述する。通常gitリポジトリで作成するLICENSEREADME.mdは流用する形にしている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[metadata]
name = package_with_data
version = attr: src.packagedata.__version__
url = https://xxxxxxxxxxxxxxxxxxxxx
author = XXXXXXXXXX
author_email = xxxxxxx@xxxxxxxxxxxx
license_file = LICENSE
description = package with data
long_description = file: README.md
long_description_content_type = text/markdown

[options]
zip_safe = False
package_dir=
=src
packages = find:
include_package_data=True
install_requires =

[options.packages.find]
where=src

[options.entry_points]
console_scripts =
getdata = packagedata.getpackagedata:main

パッケージにpythonファイル以外のデータを含める

どのファイルを含めるかはMANIFEST.inを使用する

1
include_package_data=True

MANIFEST.in

パッケージに含むもの含まないものを定義する。

1
2
include src/*/config/*.ini
global-exclude *.py[cod] __pycache__ *.so

例えば外部ファイルVERSIONでバージョン番号を決めて、version = file: VERSIONで読み込みたい場合、MANIFEST.inVERSIONを加える必要がある。

サンプルコード(src/packagedata/getpackagedata.py)

1
2
3
4
5
6
7
8
import pkg_resources

def main():
print(pkg_resources.resource_filename('packagedata', 'config'))
print(pkg_resources.resource_string('packagedata', 'config/test.ini').decode())

if __name__ == '__main__':
main()

サンプルコード(src/packagedata/config/test.ini)

1
test=OK

パッケージ作成と実行例

パッケージの作成

python setup.py sdistでパッケージ化する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
$ python setup.py sdist
running sdist
running egg_info
creating src/package_with_data.egg-info
writing src/package_with_data.egg-info/PKG-INFO
writing dependency_links to src/package_with_data.egg-info/dependency_links.txt
writing entry points to src/package_with_data.egg-info/entry_points.txt
writing top-level names to src/package_with_data.egg-info/top_level.txt
writing manifest file 'src/package_with_data.egg-info/SOURCES.txt'
reading manifest file 'src/package_with_data.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no previously-included files matching '*.py[cod]' found anywhere in distribution
warning: no previously-included files matching '__pycache__' found anywhere in distribution
warning: no previously-included files matching '*.so' found anywhere in distribution
writing manifest file 'src/package_with_data.egg-info/SOURCES.txt'
running check
creating package_with_data-0.0.1
creating package_with_data-0.0.1/src
creating package_with_data-0.0.1/src/package_with_data.egg-info
creating package_with_data-0.0.1/src/packagedata
creating package_with_data-0.0.1/src/packagedata/config
copying files to package_with_data-0.0.1...
copying LICENSE -> package_with_data-0.0.1
copying MANIFEST.in -> package_with_data-0.0.1
copying README.md -> package_with_data-0.0.1
copying setup.cfg -> package_with_data-0.0.1
copying setup.py -> package_with_data-0.0.1
copying src/package_with_data.egg-info/PKG-INFO -> package_with_data-0.0.1/src/package_with_data.egg-info
copying src/package_with_data.egg-info/SOURCES.txt -> package_with_data-0.0.1/src/package_with_data.egg-info
copying src/package_with_data.egg-info/dependency_links.txt -> package_with_data-0.0.1/src/package_with_data.egg-info
copying src/package_with_data.egg-info/entry_points.txt -> package_with_data-0.0.1/src/package_with_data.egg-info
copying src/package_with_data.egg-info/not-zip-safe -> package_with_data-0.0.1/src/package_with_data.egg-info
copying src/package_with_data.egg-info/top_level.txt -> package_with_data-0.0.1/src/package_with_data.egg-info
copying src/packagedata/__init__.py -> package_with_data-0.0.1/src/packagedata
copying src/packagedata/getpackagedata.py -> package_with_data-0.0.1/src/packagedata
copying src/packagedata/config/test.ini -> package_with_data-0.0.1/src/packagedata/config
Writing package_with_data-0.0.1/setup.cfg
creating dist
Creating tar archive
removing 'package_with_data-0.0.1' (and everything under it)

パッケージのインストール

pipコマンドでインストール。

1
2
3
4
5
6
7
8
9
10
11
$ cd dist
console_scripts-0.0.1.tar.gz
$ pip install package_with_data-0.0.1.tar.gz
Processing ./package_with_data-0.0.1.tar.gz
Building wheels for collected packages: package-with-data
Building wheel for package-with-data (setup.py) ... done
Created wheel for package-with-data: filename=package_with_data-0.0.1-py3-none-any.whl size=2338 sha256=6f907b30a1c7b26650c8bec598858ceb917410ffc97c4dde3348c3e24702612a
Stored in directory: /root/.cache/pip/wheels/05/c8/a8/fef26a83f0af30cd5c9aece9b54869e169ca26b4d3d5bf7a00
Successfully built package-with-data
Installing collected packages: package-with-data
Successfully installed package-with-data-0.0.1

パッケージに含めたデータの参照

1
2
3
$ getdata
/usr/local/lib/python3.8/site-packages/packagedata/config
test=OK