Python パッケージを作って登録する

先々週くらいに仕事で pip パッケージを作った。いろいろと忘れていたので苦労した。 その時にioproveを作ったときのメモが役にたったので少し書き換えて晒しておく。 (いい加減、Dropbox, Evernote, Boostnoteとかに散らばったメモを統一したい)

ここでは pip パッケージを作って登録するまでの手順を記録する。 ioproveは書き捨てコードだけど、使いみちがあったので少し直してPyPIに登録した。

実際のPyPIへの登録の時にはtwineは使わなかったのだけど、メモを書く時に調べていたら見つけ良さそうだったためここでは使うように書いている。

ref.

パッケージのディレクトリ構造について

ディレクトリ構造は以下のようになっている。前にmodern-package-templateを使って生成したこともあって他にもファイルが生成されてたけど、だいたいこんな感じ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ tree
.
|-- NEWS.txt
|-- README.rst
|-- requirements.txt
|-- setup.py
|-- src
|   `-- ioprobe
|       |-- __init__.py
|       |-- helper.py
|       `-- test_ioprobe.py
`-- tox.ini
ファイル 意味
src ソースルート
tox.ini tox(テストランナー)の設定
setup.py パッケージのメタ情報
NEWS.txt, README.rst setup.py から読み込んでるドキュメント
requirements.txt pip で依存パッケージを読み込むために使う(setup.py あるし今はもう要らない)

setup.pyを書く

Pythonスクリプトでsetuptoolsをインポートして使う。 基本的に setup() 関数にメタ情報を渡せば終わり

 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
41
42
43
44
45
46
47
48
from setuptools import setup, find_packages

import os

here = os.path.abspath(os.path.dirname(__file__))
README = open(os.path.join(here, 'README.rst')).read()
NEWS = open(os.path.join(here, 'NEWS.txt')).read()

version = '0.1.1'

install_requires = []

setup(name='ioprobe',
        version=version,
        description="Observation tool for I/O per process",
        long_description=README + '\n\n' + NEWS,
        classifiers=[
            'Programming Language :: Python :: 2.7',
            'Programming Language :: Python :: 3',
            'Programming Language :: Python :: 3.3',
            'Programming Language :: Python :: 3.4',
            'Programming Language :: Python :: 3.5',
            'Programming Language :: Python :: 3.6',
            'Programming Language :: Python :: Implementation :: CPython',
            'Topic :: Software Development :: Libraries',
            'Environment :: Console',
            'Topic :: Utilities',
            ],
        keywords='proc system process io',
        author='Masumi Kanai',
        author_email='masumi.net@gmail.com',
        url='https://github.com/masu-mi/ioprobe',
        license='The BSD 3-Clause License',

        packages=find_packages('src'),
        package_dir={'': 'src'}, include_package_data=True,

        zip_safe=False,
        install_requires=install_requires,
        extras_require = {
            'test': [
                'pytest', 'tox',
                'mock;python_version<"3.3.0"'
                ],
            },
        entry_points={
            'console_scripts': ['ioprobe=ioprobe:main']
            })

classifiers

パッケージの分類タグをリストで渡す。(ref classifiers一覧)

install_requires

依存パッケージをリストで渡す。(<,>,==,<=,>=,!= などでバージョン指定が可能) 他に、provide,obsoletes

インストール条件を設定する

install_requires,extras_require ではインストールする条件を、 ; で区切った後に条件を記述できる。

'package_name;platform_system=="Windows"',
'package_name;python_version<"3.3.0"'

extras_require

条件ごとの依存パッケージをリストで渡す。 環境は setup.py のあるディレクトリで pip を使う際に下のようにサブコマンドの後ろで .[${env_name}] と指定する。

1
$ sudo pip install .[test]

entry_points

キーは console_scripts,gui_scripts が代表的で $script_name=${package_name}:${func_name} という形式の文字列のリストを値としたマップが格納される。

PyPIにアップロードする

PyPIへのアップロードの方法を書く。twinesetup.py それぞれでアップロードしてみる。 その際に認証情報などを ~/.pypirc に設定する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[distutils]
index-servers =
  pypi
  pypitest

[pypi]
username=USER
password=PASSWORD

[pypitest]
repository=https://test.pypi.org/legacy/
username=USER
password=PASSWORD

事前にビルドする。

1
$ python setup.py sdist bdist_wheel

setup.pyを使ってアップロードする

普通は setup.py でアップロードする。アップロードする。

 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
$ python setup.py upload -r pypitest
running sdist
running egg_info
writing requirements to src/ioprobe.egg-info/requires.txt
writing src/ioprobe.egg-info/PKG-INFO
writing top-level names to src/ioprobe.egg-info/top_level.txt
writing dependency_links to src/ioprobe.egg-info/dependency_links.txt
writing entry points to src/ioprobe.egg-info/entry_points.txt
reading manifest file 'src/ioprobe.egg-info/SOURCES.txt'
writing manifest file 'src/ioprobe.egg-info/SOURCES.txt'
running check
creating ioprobe-0.1.2
creating ioprobe-0.1.2/src
creating ioprobe-0.1.2/src/ioprobe
creating ioprobe-0.1.2/src/ioprobe.egg-info
copying files to ioprobe-0.1.2...
copying NEWS.txt -> ioprobe-0.1.2
copying README.rst -> ioprobe-0.1.2
copying setup.py -> ioprobe-0.1.2
copying src/ioprobe/__init__.py -> ioprobe-0.1.2/src/ioprobe
copying src/ioprobe/helper.py -> ioprobe-0.1.2/src/ioprobe
copying src/ioprobe/test_ioprobe.py -> ioprobe-0.1.2/src/ioprobe
copying src/ioprobe.egg-info/PKG-INFO -> ioprobe-0.1.2/src/ioprobe.egg-info
copying src/ioprobe.egg-info/SOURCES.txt -> ioprobe-0.1.2/src/ioprobe.egg-info
copying src/ioprobe.egg-info/dependency_links.txt -> ioprobe-0.1.2/src/ioprobe.egg-info
copying src/ioprobe.egg-info/entry_points.txt -> ioprobe-0.1.2/src/ioprobe.egg-info
copying src/ioprobe.egg-info/not-zip-safe -> ioprobe-0.1.2/src/ioprobe.egg-info
copying src/ioprobe.egg-info/requires.txt -> ioprobe-0.1.2/src/ioprobe.egg-info
copying src/ioprobe.egg-info/top_level.txt -> ioprobe-0.1.2/src/ioprobe.egg-info
Writing ioprobe-0.1.2/setup.cfg
Creating tar archive
removing 'ioprobe-0.1.2' (and everything under it)
running upload
Submitting dist/ioprobe-0.1.2.tar.gz to https://test.pypi.org/legacy/
Server response (200): OK

twineでアップロードする

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ twine upload --repository pypitest dist/*
Uploading distributions to https://test.pypi.org/legacy/
Uploading ioprobe-0.1.1-py2-none-any.whl
100%|############################################################| 11.5k/11.5k [00:03<00:00, 3.32kB/s]
Uploading ioprobe-0.1.2-py2-none-any.whl
100%|############################################################| 11.5k/11.5k [00:02<00:00, 5.42kB/s]
Uploading ioprobe-0.1.1.tar.gz
100%|############################################################| 10.4k/10.4k [00:02<00:00, 4.29kB/s]
Uploading ioprobe-0.1.2.tar.gz
100%|############################################################| 10.4k/10.4k [00:01<00:00, 9.83kB/s]
comments powered by Disqus