【Python】自作モジュールを配布物用にパッケージ化して、pipでインストールできるようにする

投稿日 2023年1月18日 >> 更新日 2023年2月28日

概要

自作したPythonのモジュールを、自分もしくは他の人が使用できるように配布物としてパッケージ化し、Pythonのバージョン管理ツールであるpipでインストールできるようにします。

尚この記事の参考元はPython公式チュートリアルの「Python のプロジェクトをパッケージングする」ですが、追加設定で自作モジュールの依存関係にあるサードパーティーの設定や、自作モジュールをビルドするに当たってpyproject.tomlという設定ファイルのみでパッケージ化を完結するという記事内容になります。

そしてパッケージ化された配布物を、ローカルにpipでインスールします。
自作のパッケージをGitHubやPyPI(Python Package Index)にアップロードする方法は別途記事で実装します。

開発環境&使用ライブラリ

実行環境
Windows Subsystem for Linux 1
Python 3.7.0
pip 23.0.1
使用ライブラリ ライセンス
build==0.10.0 MIT

自作モジュールの作成

今回パッケージ化するモジュールでは「requests」と「beautifulsoup4」というスクレイピングに必要なサードパーティを使用するので、仮想環境下にインストールしておきます。

必要に応じてpipを最新の状態にしておきます。

(venv)$ pip install --upgrade pip

(venv)$ pip install requests bs4

自作モジュールは「example.py」と言うファイル名にしておき、以下のようなコードとなります。

# example.py

from bs4 import BeautifulSoup as bs4
import requests


def get_data(url, tag):

    gets = requests.get(url)
    soup = bs4(gets.text, 'html.parser')
    results = []
    for tag in soup.find_all(tag):
        results.append(tag.text)

    return results


if __name__ == '__main__':
    url = 'https://zerofromlight.com/blogs'
    tag = 'h5'

    for text in get_data(url, tag):
        print(text)

上記のファイルを実行すると、「https://zerofromlight.com/blogs」ページにあるHTMLタグの「h5」要素を取得してきます。

(venv)$ ls
example.py venv

(venv)$ python3 example.py
【Python】音声合成ライブラリのPyttsx3を使ってサクッと文章から音声生成
【Python】音声認識ライブラリのSpeechRecognitionでサクッと文字起こし
【pyenv】pythonビルド後のModuleNotFoundError: No module named '_sqlite3'
【WSL1】pyenvの導入と使用例
【Python】Dropbox APIでアクセストークンを自動更新する
【Django】django-storagesを使用してMediaストレージをDropboxにホストする
【Python】Dropbox AIPを使ってファイルを操作する
【Dropbox】開発用アプリの作成とアクセストークンの取得
【Heroku・Django】リモートサーバーにあるデータやファイルをローカル側に移行・コピー(転送)
【git】gitで設定されている内容の確認・変更・削除

Webサイト内で使用されいるHTML要素を調べる方法は、GoogleChromeの場合だと、以下のメニューバーを開き「その他のツール」から「デベロッパーツール」を開きます。

すると以下のようにHTML要素を可視化するとことができます。

次はインタプリタから自作モジュールを使用してみます。

Pythonインタプリタが実行されたディレクトリに自作モジュールが配置されている場合は、直接ファイルを指定してインポートします。

Python 3.7.0 (default, Nov 11 2022, 12:50:32)
[GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>>
>>> url = 'https://zerofromlight.com/blogs'
>>> tag = 'h5'
>>>
>>> for tag in example.get_data(url, tag):
...     print(tag)
...
【Python】音声合成ライブラリのPyttsx3を使ってサクッと文章から音声生成
【Python】音声認識ライブラリのSpeechRecognitionでサクッと文字起こし
【pyenv】pythonビルド後のModuleNotFoundError: No module named '_sqlite3'
【WSL1】pyenvの導入と使用例
【Python】Dropbox APIでアクセストークンを自動更新する
【Django】django-storagesを使用してMediaストレージをDropboxにホストする
【Python】Dropbox AIPを使ってファイルを操作する
【Dropbox】開発用アプリの作成とアクセストークンの取得
【Heroku・Django】リモートサーバーにあるデータやファイルをローカル側に移行・コピー(転送)
【git】gitで設定されている内容の確認・変更・削除
>>>

これで自作モジュールの作成と実装は以上なので、作成したモジュールを配布物としてパッケージ化していきます。

自作したモジュールを配布物としてパッケージ化する

今回パッケージ化するモジュールは、「example.py」というファイルをインポートし、「get_data()」関数を呼び出すことでWebサイトのスクレイピングを行えるという内容です。

パッケージ化するに当たって、必要となるファイルがあるので準備してきます。

パッケージ化するに当たってのディレクトリ構成

パッケージ化するに当たっての最終的なディレクトリ構成は以下のよう形になります。

my_project
    |-- LICENSE
    |-- pyproject.toml
    |-- README.md
    |-- src/
        |-- site_requests/
            |-- __init__.py
            |-- example.py
    |-- tests/
    |-- venv/

上記のディレクトリ構成でビルド(パッケージ化)を行いますが、ファイルは必要に応じて増えます。

例えばドキュメントファイルを格納する為の「doc」ディレクトリや配布物に対してのファイルの追加や削除の指示を設定できる「MANIFEST.in 」等も構成する場合もあります。

それぞれのmy_projectディレクトリのファイル群は以下の通りです。

ファイル名 内容
LICENSE ライセンス条項を明示するファイル。
pyproject.toml ビルドしてモジュールをパッケージ化するための設定ファイル。
README.md GitHubでもお馴染みの使用方法や注意事項を書き込むファイル。
src パッケージ化する自作モジュールのソースコードを配置します。
src(source)ディレクトリはモジュールが配置されている場所が分かりやすいように作成されているディレクトリなので、パッケージ名のsite_requestでも問題はありません。
site_requests 今回自作モジュールがビルドされる予定のパッケージとなるディレクトリ。
パッケージ内には自作モジュールの他に「__init__.py」という空のファイルが必要です。
tests 自作モジュールをテストする為のファイルを配置します。
venv 仮想環境用のディレクトリ

管理用ディレクトリの作成とパッケージ化に必要なソースコードを配置

パッケージ化するに必要なファイル群を管理するディレクトリを「my_project」として作成します。「my_project」ディレクトリ内にソースコードを格納する為の「src」ディレクトリを作成します。パッケージ名に当たる「site_requests」ディレクトリを作成します。

ちなみに、ローカルで使用する分には好きなパッケージ名を与えられますが、PyPIにアップロードする際はパッケージ名が被らないようにする必要があります。

$ mkdir my_project

$ mkdir my_project/src

$ mkdir my_project/src/site_requests

「src/site_requests」ディレクトリ内に、「__init__.py」と自作モジュールの「example.py」を配置します。

$ touch my_project/src/site_requests/__init__.py

$ mv example.py my_project/src/site_requests/.

「my_project」内に作業場を移して、仮想環境を構築します。自作モジュールが依存しているサードパーティをインストールして、実際に稼働するかチェックします。

$ cd my_project

$ python3 -m venv venv

$ . venv/bin/activate

(venv)$ pip install --upgrade pip

(venv)$ pip install requests bs4

(venv)$ python3
Python 3.7.0 (default, Nov 11 2022, 12:50:32)
[GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from src.site_requests import example
>>>
>>>
>>> url = 'https://zerofromlight.com/blogs'
>>> tag = 'h5'
>>>
>>> for tag in example.get_data(url, tag):
...     print(tag)
...
【Python】音声合成ライブラリのPyttsx3を使ってサクッと文章から音声生成
【Python】音声認識ライブラリのSpeechRecognitionでサクッと文字起こし
【pyenv】pythonビルド後のModuleNotFoundError: No module named '_sqlite3'
【WSL1】pyenvの導入と使用例
【Python】Dropbox APIでアクセストークンを自動更新する
【Django】django-storagesを使用してMediaストレージをDropboxにホストする
【Python】Dropbox AIPを使ってファイルを操作する
【Dropbox】開発用アプリの作成とアクセストークンの取得
【Heroku・Django】リモートサーバーにあるデータやファイルをローカル側に移行・コピー(転送)
【git】gitで設定されている内容の確認・変更・削除
>>>

ここまでの「my_project」ディレクトリの構成は以下の通りです。

my_project
    |-- src/ # 追加
        |-- site_requests/ # 追加
            |-- __init__.py # 追加
            |-- example.py # 移動
    |-- venv/

README.mdファイルの作成

README.mdファイルには使用方法や注意事項等を書き込みます。

GitHubやPyPIにアップロードされる際にはそのパッケージのトップページにREADME.mdの内容が表示されます。

マークダウン記法で書き込む事ができます。

(venv)$ touch README.md

(venv)$ vi README.md

以下はREADME.mdファイルに書き込んだ内容です。

# Webスクレイピングツール

スクレイピングを行いたいサイトのURLとタグ名を指定し、欲しい情報をゲットしてきます。

# 使用方法

ビルドされたアーカイブ(dist/)を使用してローカル内でインストール

```
$ pip install .
```

Pythonインタプリタでの実装

```shell
>>> from site_request import example
>>>
>>> url = 'https://zerofromlight.com/blogs'
>>> tag = 'h5'
>>>
>>> for tag in example.get_data(url, tag):
...     print(tag)
...
【Python】音声合成ライブラリのPyttsx3を使ってサクッと文章から音声生成
【Python】音声認識ライブラリのSpeechRecognitionでサクッと文字起こし
...
...
>>>
```

ここまでの「my_project」ディレクトリの構成は以下の通りです。

my_project
    |-- README.md # 追加
    |-- src/
        |-- site_requests/
            |-- __init__.py
            |-- example.py
    |-- venv/

LICENSEファイルの作成

パッケージ化されたものを他の人に使用させるためにアップロードする際は、そのパッケージにライセンス条項を明示することが重要であるとPython公式チュートリアルでは記されています。
そのパッケージを利用するユーザーにとっても、どんな条件で使用して良いかが記されていれば、後にトラブルを回避する事ができます。

依存関係にあるサードパーティがある場合はその各パッケージに準拠したライセンスを作成したりします。

pipで管理されているサードパーティの情報は、「pip show ...」で表示することができます。

(venv)$ pip show bs4
Name: bs4
Version: 0.0.1
...

「grep」コマンドと併用してパッケージ名とライセンス項目だけ抽出します。

※「pip show ...」により表示されたテキストを「grep」コマンドによって抽出したい行を取得。

(venv)$ pip show bs4 requests | grep -i -e name -e license
Name: bs4
License: MIT
Name: requests
License: Apache 2.0

「bs4」では「MIT」ライセンスが使われており、「requests」では「Apache Software」ライセンスが使われています。

各ライセンスの目的や特徴を簡単に読んだところ、「MIT」ライセンスが妥当だったのと、Python公式チュートリアル(LICENSE)でも使用されているので、MITライセンスの内容をコピーして使います。

私はこちらのMITライセンスをコピーして、「my_project」内に「LICENSE」ファイルを作成して貼り付けました。

※以下はMITラインセンス条項(Copyright(c) [...] [...]の部分はご自身のパッケージに関する情報を記入します。)

MIT License

Copyright (c) 2023 Python web scraping tool

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

ここまでの「my_project」ディレクトリの構成は以下の通りです。

my_project
    |-- LICENSE # 追加
    |-- README.md
    |-- src/
        |-- site_requests/
            |-- __init__.py
            |-- example.py
    |-- venv/

pyproject.tomlファイルの作成

自作モジュールをビルドしてパッケージ化するための設定を行います。

見慣れない形式の「.toml」ファイルですが、ソフトウェアを作成する際に使用される設定ファイルには「json」や「yaml」等があり、その類の1つが「toml」です。

こちらの記事で詳しくご説明されています。

Python公式チュートリアル(pyproject.toml)の設定とは少し異なるので、見比べながら設定内容を把握してみてください。

今回の自作モジュールには依存関係のあるサードパーティを含める必要があったので、それらに関する設定も行っています。

pyproject.tomlの設定内容は以下となりました。

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "site_requests"
version = "0.0.1"
authors = [
    { name="taro yamada", email="yamada@example.com" },
]
description = "A small web scraping package"
readme = "README.md"
license = {file = "LICENSE"}
requires-python = ">=3.7"
dependencies =  [
    "bs4 == 0.0.1",
    "requests >= 2.28.2",
]
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

[project.urls]
"Homepage" = "https://github.com/taro-yamada/testproject"
"Bug Tracker" = "https://github.com/taro-yamada/testproject/issues"

「build-system」テーブルのキー

キー 内容
requires モジュールをビルドするために必要なパッケージングツール名
build-backend ビルドを実行する際に必要なPythonオブジェクトの名前

「project」テーブルのキー

キー 内容
name パッケージ名。PyPIにアップロードする際に名前の衝突が内容に予め使用できるパッケージ名を調べておく必要がある。
version パッケージのバージョン。PyPIに再アップロードする際は、バージョンを更新する必要がある。
authors 著作者の名前と連絡先。「{}(波括弧)」を追加すれば複数指定可能
description 簡単なパッケージの説明
readme パッケージの使用方法や注意事項等が書き込まれているファイルへのパス。ルートに配置されている「README.md」を指定。
license ルートに配置されている「LICENSE」ファイルを指定しています。ファイルではなくテキストの内容を指定することもできます。{text = MIT License}
requires-python パッケージが稼働できるPythonのバージョンを指定。
dependencies パッケージが依存しているサードパーティを設定。test.pypiにアップロードして、インストールする際は「ERROR: No matching distribution found for ...」となる可能性があるので注意。
classifiers 誰向けであるか、どのシステムで実行できるかなどを設定します。PyPIにアップロードされているパッケージで現在定義されているclassifiers内の項目から絞り込みのような検索が行えます。

「project.url」テーブルのリンク名

PyPIにアップロードされた際に「プロジェクトのリンク」として表示される任意リンク名です。

リンク名 内容
Homepage GitHubに登録しているリポジトリのURLの例です。
Bug Tracker バグやエラーが起こった際のURLの例です。

補足

「pyproject.toml」ファイルを作成することで、pipを使ってインストールする事が可能となります。

「pip install .」とコマンドを実行すると、作業ディレクトリにある「pyproject.toml」もしくは「setup.py」ファイルを参照してインストールが行われます。

よって、現在開発中の「src」ディレクトリに配置されている「example.py」をすぐにサードパーティとして使用することができます。

そしてもっと便利に開発を行えるように、「-e(editable)」オプションを付けてインストールをします。

これは編集可能な状態のままサードパーティをインストールして使用できる状態です。

(venv)$ pip install -e .

ここまでの「my_project」ディレクトリの構成は以下の通りです。

my_project
    |-- LICENSE
    |-- pyproject.toml # 追加
    |-- README.md
    |-- src/
        |-- site_requests/
            |-- __init__.py
            |-- example.py
    |-- venv/

testsディレクトリの作成

自作モジュールのテスト用ファイルは、「tests」ディレクトリに格納します。

今回は「tests」ディレクトリだけ作成して、中身は空のままにします。

(venv)$ mkdir tests

ここまでの「my_project」ディレクトリの構成は以下の通りです。

my_project
    |-- LICENSE
    |-- pyproject.toml
    |-- README.md
    |-- src/
        |-- site_requests/
            |-- __init__.py
            |-- example.py
    |-- tests/ # 追加
    |-- venv/

ビルドを実行しアーカイブファイルを作成する

ビルドに必要なファイル群が揃ったら、ビルドツールをインストールしてビルドを開始します。

ビルドツールのインストール

(venv)$ pip install build

ビルドをする際は、設定ファイルである「pyproject.toml」を参考に配布物を作成するので、設定ファイルと同じディレクトリでビルドを実行するコマンドを打ちます。

(venv)$ python3 -m build
* Creating venv isolated environment...
...
Successfully built site-requests-0.0.1.tar.gz and site-requests-0.0.1-py3-none-any.whl

上記のようにビルドが終わると、ビルドを実行したディレクトリに「dist」ディレクトリが作成されています。

「dist」は「distribution」の略で、配布や公開等の意味となります。

「dist」ディレクトリの中には、インストールに必要なデータが格納されたアーカイブファイルが2つ配置されています。

ファイル名 内容
tar.gz アーカイブファイルを圧縮したファイル
whl Pythonで利用されるZIPフォーマットのアーカイブファイル

各ファイルの必要性についてPython公式チュートリアル(配布物アーカイブを生成する)で説明されています。

ここまでの「my_project」ディレクトリの構成は以下の通りです。

my_project
    |-- dist/ # 生成
        |-- site_requests-0.0.1-py3-none-any.whl # 生成
        |-- site_requests-0.0.1.tar.gz # 生成
    |-- LICENSE
    |-- pyproject.toml
    |-- README.md
    |-- src/
        |-- site_requests/
            |-- __init__.py
            |-- example.py
    |-- tests/
    |-- venv/

自作したパッケージをインストール

自作したパッケージをインストールする方法は2つあります。

1つ目は「pyproject.tomlファイルの作成」でも説明した「pyproject.toml」を参照したインストール。

2つ目が「dist」のアーカイブファイルを指定したインストール。

各操作を行う前に、pipの中身を空にしておきます。

(venv)$ pip freeze > uninstall.txt

(venv)$ pip uninstall -y -r uninstall.txt

「pyproject.toml」を参照したインストール

「pyproject.toml」を参照した自作モジュールのインストール方法は、以下のようなディレクトリ構造で行います。

my_project
    |-- LICENSE
    |-- pyproject.toml
    |-- README.md
    |-- src/
        |-- site_requests/
            |-- __init__.py
            |-- example.py
    |-- tests/
    |-- venv/

作業ディレクトリに「LICENSE」「README.md」「pyproject.toml」「src」のファイル群が揃っている状態で、「pip install .」を実行します。

(venv)$ pip install .

するとpipは作業ディレクトリ内にある設定ファイル(tomlファイルやsetup.py)を参照してインストールを行います。

インストールされた内容を確認すると以下のようになります。

(venv)$ pip freeze
beautifulsoup4==4.11.1
bs4==0.0.1
certifi==2022.12.7
charset-normalizer==3.0.1
idna==3.4
requests==2.28.2
soupsieve==2.3.2.post1
urllib3==1.26.14
site_requests @ file:///mnt/c/Users/Documents//my_project

これで自作モジュールをサードパーティとして使うことができます。

自作モジュールを開発中でプログラムを試す際に何度もインストールを繰り返さないようにするには、エディタブルモードである「-e」オプションを付けてインストールします。

一旦インストールされているパッケージ群をアンインストールして実行します。

(venv)$ pip freeze > uninstall.txt

(venv)$ pip uninstall -y -r uninstall.txt

エディタブルモードでインストール

(venv)$ pip install -e .

インストールされた内容を確認すると以下のようになります。

(venv)$ pip freeze
beautifulsoup4==4.11.1
bs4==0.0.1
certifi==2022.12.7
charset-normalizer==3.0.1
idna==3.4
requests==2.28.2
soupsieve==2.3.2.post1
urllib3==1.26.14
# Editable install with no version control (site_requests==0.0.1)
-e /mnt/c/Users/Documents/my_project

自作モジュールがインストールされている状態で、自作モジュールを編集してみます。

「example.py」に「print()」文を追記します。

from bs4 import BeautifulSoup as bs4
import requests

print('開発中のモジュールです') # 追記

def get_data(url, tag):

    gets = requests.get(url)
    soup = bs4(gets.text, 'html.parser')
    results = []
    for tag in soup.find_all(tag):
        results.append(tag.text)

    return results


if __name__ == '__main__':
    url = 'https://zerofromlight.com/blogs'
    tag = 'h5'

    for text in get_data(url, tag):
        print(text)

コードを追記したら、インタプリタでpipにインストールされている自作モジュールをインポートしてみます。

>>> from site_requests import example
開発中のモジュールです
>>>

「-e」オプションは編集を加えながらモジュールを試せるので、非常に便利です。

アーカイブファイルを指定したインストール

アーカイブファイルとは、ビルド後に生成された「dist」ディレクトリ内にあるファイル群です。

my_project
    |-- dist/
        |-- site_requests-0.0.1-py3-none-any.whl
        |-- site_requests-0.0.1.tar.gz

このファイルこそ自作モジュールのパッケージなので、各種ファイルまでの相対パスを指定してpipでインストールできます。

# 「tar.gz」ファイルを指定の場合
$ pip install dist/site_requests-0.0.1.tar.gz

# 「whl」ファイルを指定の場合
$ pip install dist/site_requests-0.0.1-py3-none-any.whl

オフライン環境で自作モジュールのパッケージをインストールすることができました。

PyPI(Python Package Index)に「dist」ディレクトリ内のファイルをアップロードすることによって、オンライン環境を通じて「pip install site_requests」コマンドでインストールできるようになります。

パッケージ化した配布物をGitHubやPyPIにアップロードする

GitHubにアップロードした自作モジュールを、pipを使ってオンラインでインストールします。

以下の記事をご参照ください。

PyPIにアップロードした自作パッケージを、pipを使ってインストールします。

以下の記事をご参照ください。

それでは以上となります。

最後までご覧いただきありがとうございました。

一覧へ戻る