6
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Pythonパッケージの作成と公開(setup.py, PyPI)

pip installでインストールできるPythonパッケージのホスティングハブ。読みはパイ ピーアイ。

この記事ではsetuptoolsとsetup.pyを使う。pyproject.tomlやpoetryについては他の記事をあたってほしい。

バージョンの設定

pypi.org、test.pypi.orgともに同じ名前(バージョン)のアップロードによる上書きはできない(インデックスは独立)。更新/修正したい場合はバージョンを上げること。

0.1.0-alpha < 0.1.0-alpha1 < 0.1.0-beta < 0.1.0-beta1 < 0.1.0 < 0.1.1

ディレクトリ構成(例)

  • LIBRARY_NAME
    • setup.py
    • PACKAGE_NAME
      • __init__.py
      • YOUR_SCRIPT.py
    • LICENSE
    • README.md
    • .gitignore
    • MANIFEST.in(optional)

ファイルの例

__init__.py
__VERSION__ = '20210903.1'

# from .YOUR_SCRIPT import *
from .YOUR_SCRIPT import some_function
.gitignore
__pycache__/

/build
/dist
/*.egg-info

install後の利用例

import PACKAGE_NAME as PKG

PKG.some_function() # defined in YOUR_SCRIPT.py

setup.pyの作成

設定ファイルとしてsetup.cfgを読み込む方法もあるが、ここでは1ファイルの作成で済む、setup関数に引数として設定を渡す方法を書いておく。

setup.py

from setuptools import setup, find_packages
import sys
from typing import (
    List,
)

from PACKAGE_NAME import __VERSION__ as VERSION

install_requires: List[str] = [
    # dependencies like requirements.txt
    # 'numpy>=1.20.2', # https://pypi.org/project/numpy/
]

python_version = sys.version_info
if python_version < (3, 7):
    # backport for dataclasses, https://pypi.org/project/dataclasses/
    install_requires += [ 'dataclasses>=0.8' ] 

setup(
    name='LIBRARY_NAME',
    version=VERSION, # '0.1.0-alpha', # == 0.1.0-alpha0 == 0.1.0a0
    # license='MIT',

    # packages=[ 'PACKAGE_NAME', ],
    packages=find_packages(),
    include_package_data=True,

    # entry_points = {
    #     'console_scripts': [
    #         # create `main` function in PACKAGE_NAME/scripts/my_command_module.py
    #         'my_command_name = PACKAGE_NAME.scripts.my_command_module:main',
    #     ],
    # },

    install_requires=install_requires,

    author='YOUR_NAME',
    author_email='YOUR_EMAIL',

    url='YOUR_WEBSITE_OR_REPOSITORY_URL',
    description='SHORT_DESCRIPTION',

    long_description=open('README.md', 'r').read(),
    long_description_content_type='text/markdown',

    classifiers=[
        'Development Status :: 3 - Alpha',
        # 'License :: OSI Approved :: MIT License',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: 3.8',
        'Programming Language :: Python :: 3.9',
    ],
)

MANIFEST.in

MANIFEST.in
include README.md
include LICENSE

include *.txt
recursive-include path/to/dir *.txt *.png

ローカルでビルドして公開する場合(または処理の流れ)

事前セットアップ

pip3 install wheel twine
  • wheel: wheelパッケージング
  • twine: アップロードツール

パッケージング

python3 setup.py sdist
python3 setup.py sdist bdist_wheel

※ sdistはソースコード配布、bdist_wheelはバイナリ(wheel)配布。単純なPythonスクリプトのみの場合sdistのみでも問題なかった。ビルドが必要なパッケージで事前ビルド済みのバイナリを添付できる、ということと思われる(sdistのみの場合、wheelの生成はpip installしたときにされるみたい)。

実験用アップロード(インストールテスト)

pypi.orgと同じように動作する https://test.pypi.org/ を実験的なアップロードに使用できる。pypi.orgとアカウントは独立していて、こちらの記事でおすすめされているようにアカウント名を変えると間違えなくてよさそう。

前述の通り一度アップロードすると同じPyPIリポジトリには(同じバージョンで)上書きできないので、バージョン設定に注意。

# 特定のsdistのみの場合
twine upload --repository testpypi dist/YOUR_PACKAGE.tar.gz

# dist以下全ファイル
twine upload --repository testpypi dist/*

本番用アップロード

# 特定のsdistのみの場合
twine upload --repository pypi dist/YOUR_PACKAGE.tar.gz

# dist以下全ファイル
twine upload --repository pypi dist/*

GitHub Actionsを使う場合

pypa/gh-action-pypi-publish: GitHub Action, for publishing distribution files to PyPI

上のGitHub Actionを使うと便利。

例:aoirint-miyadaiku-theme-blog/deploy_pypi.yml at master · aoirint/aoirint-miyadaiku-theme-blog

GitHub Release作成時にPyPIにアップロード

name: Publish a package to PyPI

on:
  release:
    types:
      - created

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Setup Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.x

      - name: Install Dependencies
        run: |
            pip3 install -r requirements.txt
            pip3 install wheel

      - name: Build Package
        run: python3 setup.py sdist bdist_wheel

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}

PyPIを経由しないパッケージのインストール

gitリポジトリからのインストール

PyPIにpushしなくてもGitHubやGitLabのリポジトリ、ローカルのgitリポジトリからインストールすることができる。ディレクトリ構成はPyPIにpushする場合と同じ。

pip3 install git+https://github.com/USERNAME/REPOSITORY
pip3 install git+ssh://github.com/USERNAME/REPOSITORY
pip3 install git+file:///home/USER/REPOSITORY

ブランチやコミットハッシュを指定することもできるらしい(詳しくは上の公式ドキュメント参照)。

ファイルシステムからのインストール

gitリポジトリ同様、ファイルシステムからsetup.pyのあるディレクトリを指定してインストールできる。

# カレントディレクトリからインストール
pip3 install .

参考

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
6
Help us understand the problem. What are the problem?