Skip to main content

安全なPythonパッケージを作成するための完全ガイド

著者
Headshot of Gourav Singh Bais

Gourav Singh Bais

feature assert in python

2024年5月8日

0 分で読めます

Pythonパッケージの作成には、適切なディレクトリ構造の検討、パッケージファイルの作成、デプロイ前のパッケージメタデータの設定など、いくつかの作業が必要です。テスト用のサブディレクトリを作成し、分かりやすいドキュメントを用意するなど、ほかにもいくつかの手順があります。パッケージの準備ができたら、さまざまな配布アーカイブに公開できます。これで、ほかの人がインストールして使えるPythonパッケージが完成します。

このガイドでは、最新のPythonパッケージを作成するためのすべての手順を詳しく解説します。このチュートリアルに必要なコードはこちらのGitHubリポジトリで確認できます。このチュートリアルを実践すると、既存のパッケージを使うのと同じくらい簡単にPythonパッケージを作成できることが分かるでしょう。まずは、Pythonでパッケージを使う方法から見ていきましょう。

Pythonパッケージの作成方法

Pythonパッケージを作成する前に、さまざまなインストール方法やインポート方法、パッケージの基本的な構造を確認しておきましょう。

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

Pythonパッケージを使うには、PyPIで公開されているパッケージや、組織内の専有コードを含むプライベートインデックスを利用できます。

Pythonパッケージをインストールする方法は、次の2つです。

PyPIを利用する

PyPIは、公開されているすべてのPythonパッケージを収録した公式リポジトリです。

$ pip install package_name

プライベートインデックスを利用する

PyPIで公開されているパッケージと、プライベートインデックスでのみ利用できるパッケージの両方が必要になる場合があります。プライベートインデックスは通常、ソースコードを公開しない専有プロジェクトを扱う大規模な組織で使われます。組織内でPythonパッケージを保存し、配布することができます。

プライベートインデックスからパッケージをインストールするには、pip installコマンドに-- index-urlフラグを付け、続けてプライベートインデックスのURLとパッケージ名を指定します。パッケージのインストールには、次のようなコマンドを実行します。

$ pip install -- index-url <private-index-url> package_name

セキュリティ上の理由から、プライベートパッケージインデックスではTransport Layer Security(TLS)を使用することをおすすめします。プライベートインデックスに適切なTLS証明書がない場合は、pipが有効なTLS証明書のないインデックスを信頼できないと判断するため、そこからパッケージをインストールするにはpip installコマンドで-- trusted-hostフラグを指定する必要があります。

Pythonパッケージのインポート

Pythonでは、import文を使ってパッケージをインポートできます。Pythonインタープリターは、キャッシュ、組み込みライブラリ、インストール済みのパスからパッケージを検索します。パッケージが見つからない場合は、ImportErrorが発生します。

次のようにfromキーワードを使って、パッケージから特定のモジュールをインポートすることもできます。

from datetime import date

次のコマンドを使えば、パッケージから複数またはすべてのモジュールをインポートすることもできます。

from datetime import date, datetime
from datetime import *

asキーワードを使って、パッケージに別名を付けることもできます。たとえば、`pandas`パッケージをpdという別名で使うには、次のようにします。

import pandas as pd

Pythonパッケージの構成要素

Pythonパッケージは、次のファイルやサブディレクトリを含むディレクトリ構造に従う必要があります。

  • パッケージ名のサブディレクトリと初期化ファイル__init__.pyを含むsrcディレクトリ。srcディレクトリには、パッケージのソースコードを含むmodule.pyファイルも1つ以上配置します

  • パッケージのさまざまな機能をテストするスクリプトを含むtestsディレクトリ。このディレクトリをパッケージに追加するかどうかは任意です。

  • パッケージの設定情報と依存関係を記述するpyproject.tomlファイル。

  • パッケージの利用条件を定めるLICENSEファイル。選択できるライセンスは数多くあり、独自のライセンスを作成することもできます。

  • パッケージの利用者に役立つ情報とともに、分かりやすいドキュメントや使用手順を記載するREADME.mdファイル。

ここまでに説明したファイルとディレクトリは、パッケージ作成に必要な基本要件です。さらに複雑なパッケージでは、ファイルやサブモジュール、テストが追加されることもあります。

コードのパッケージ化

Pythonパッケージを一般公開するには、まずパッケージを作成する必要があります。このチュートリアルでは、キロメートル(km)をマイル(mi)に変換するシンプルなPythonプログラムを作成します。

Pythonパッケージの命名

Pythonパッケージに名前を付けるときは、PyPIを確認して、選んだ名前がすでに使われていないか調べる必要があります。通常、開発者はPyPIとパッケージの名前を同じにしますが、別の名前を選んでもかまいません。

パッケージをインストールするときはPyPI上の名前を使い、インポートするときはパッケージ名を使います。

Pythonパッケージの設定

前述の要件に従って、パッケージのディレクトリ構造を整えます。km2miles.pyファイルにこちらのコードを配置します。こちらのコードを使って__init__.pyファイルを作成できます。

km2miles.pyファイルには、NumPyライブラリを使ってキロメートルをマイルに変換する関数が含まれています。__init__.pyファイルはメインモジュールをインポートし、パッケージのバージョンを指定します。

パッケージを公開する前に、pyproject.tomlファイルを設定する必要があります。ここではビルドシステムも指定します。設定方法は次に説明します。

ビルドシステムの設定

Pythonパッケージで公開するファイルを生成するには、ビルドシステムが必要です。pipなどのビルドフロントエンドや、setuptools、Flit、Hatchling、PDMなどのビルドバックエンドを利用できます。

ここではsetuptoolsを使います。既存のPythonパッケージの依存関係を自動的に解決するビルドツールだからです。実際の用途に合わせてPythonパッケージを作成する際に役立ちます。

ビルドシステムはpyproject.tomlファイルを使用します。このファイルには、プロジェクトの配布パッケージを作成するためのバックエンドツールへの指示が記述されています。ビルドシステムの仕様は次のように定義します。

# pyproject.toml
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"

pyproject.tomlファイルを使わず、代わりにsetup.pyファイルでPythonパッケージを設定するチュートリアルを見かけることがあります。以前は、setuptoolsが複雑なパッケージのインストール処理にsetup.pyファイルを使っていたためです。しかし、現在のプロジェクトでは、Pythonプロジェクトの設定にはsetup.pyやsetup.cfgではなく、一般にpyproject.tomlファイルを使うことが推奨されています。

Pythonパッケージの設定

ビルドシステムを設定したら、パッケージ固有の情報を設定します。これらの設定情報は、.tomlファイルのビルドシステム情報の下に記述します。

[project]
name = "milesConverter"
version = "1.0.0"
description = "Convert KMs to Miles"
readme = "README.md"
authors = [{ name = "name", email = "name@gmail.com" }]
license = { file = "LICENSE" }
classifiers = [
 "License :: OSI Approved :: MIT License",
 "Programming Language :: Python",
 "Programming Language :: Python :: 3",
 "Operating System :: MacOS :: MacOS X",
 "Operating System :: Microsoft :: Windows",
 "Intended Audience :: Education"
]
keywords = ["converter", "miles", "kms"]

通常、プロジェクトの設定には次の情報を含めます。すべてのパッケージに含めることをおすすめしますが、必須ではない項目もあります。

  • 名前:(必須)PyPIで使用するパッケージ名

  • バージョン:(必須)パッケージの現在のバージョン。パッケージのPyPIメインページにも表示されます。

  • 説明:(必須)PyPIページに表示するパッケージの追加情報。

  • 作者:(必須)パッケージの開発者に関する情報。

  • Readme:(任意)パッケージの説明と使い方を含むMarkdown形式の情報。パッケージのPyPIメインページに表示されます。

  • ライセンス:(任意ですが、強く推奨)ほかの人がパッケージを利用する際の条件。

  • 分類子:(任意)対象ユーザー、プラットフォームの互換性、パッケージの成熟度を示すカテゴリです。PyPIで検索されやすくなります。使用可能な分類子の一覧はこちらで確認できます。

  • キーワード:(任意)関連する語句で検索したユーザーがPyPIでパッケージを見つけやすくするための単語一覧です。たとえば、マイルをキロメートルに変換するパッケージを探す場合、「converter」「miles」「kms」などのキーワードで検索することが考えられます。

依存関係の指定

プロジェクトの`pyproject.toml`)ファイルで依存関係を指定するには、プロジェクトの説明の後にdependenciesフィールドを追加します。

dependencies = ["numpy >= 1.20.3"]

これにより、プロジェクトがNumPyライブラリに依存していることを指定できます。プロジェクトをインストールすると、pipがこれらの依存関係を解決します。依存関係を指定する際は、次の点に注意してください。

  • サードパーティパッケージがすでに対応している依存関係は指定しないでください。

  • 特定のバージョンで追加された新機能に必要な依存関係の下限を指定するには、>=を使います。

  • メジャーバージョンの更新時に互換性の問題を引き起こす可能性がある依存関係の上限を指定するには、<を使います。

オプションの依存関係は、project.optional-dependenciesフィールドを使って追加できます。

[project.optional-dependencies]
dev = ["pip-tools", "pytest"]

これらの依存関係は自動ではインストールされませんが、pip install package_name[optional_dependency_name]という構文でプロジェクトのインストール時に指定できます。

パッケージのバージョン管理

前述のとおり、Pythonパッケージにはバージョン番号を付け、パッケージのドキュメントに記載することでバージョン管理できます。バージョン番号には、一般的に使われるセマンティックバージョニング形式MAJOR.MINOR.PATCHを使用します。MAJORはプロジェクトの大幅な変更、MINORは新機能、PATCHはバグ修正やパフォーマンスの向上を表します。

設定ファイルでバージョンを追加する方法は次のとおりです。

version = "1.0.0"

これにより、プロジェクトのバージョンが1.0.0に設定されます。新しいバージョンをリリースする際に、バージョン番号を更新できます。

パッケージのテスト

Pythonパッケージをテストする方法はいくつかあります。小規模なプロジェクトでは手動テストで十分な場合もありますが、手作業を介さずにコードが一定の基準を満たしていることを確認できるよう、ほかの種類の自動テストも導入するのが望ましいです。自動テストを利用すれば、パッケージの信頼性に対する確信を高められます。

  • 単体テスト: `robot`、`pytest`、`testify`、`nose`などのテストフレームワークを使い、コードの特定部分を切り分けてテストします。

  • 統合テスト: テスト環境を構築してコードを実行し、パッケージの各部分がどのように連携するかをテストします。統合テストには`tox`などのパッケージを利用できます。

  • 手動テスト: コードを実行し、期待どおりの結果になるか確認します。任意のPythonクライアントを使って手動テストを実行できます。

これらのテストはすべて、このチュートリアルで前述したtestsフォルダに格納できます。複雑なプロジェクトでは、テストの分析・可視化レポートやカバレッジレポートも必要になることがあります。その場合は、coverage.pyや`pytest-cov`などのパッケージを利用できます。

パッケージへのリソースファイルの追加

データファイル、バイナリ、マニュアル、設定ファイルなど、ソースコード以外のファイルをPythonパッケージに含めるには、MANIFEST.inというリソースファイルを作成します。このファイルのルールを使って、パッケージのビルド時に含めるファイルや除外するファイルを指定できます。

たとえば、次のコードを使うと、src/mileConverterディレクトリにあるすべての.tomlファイルが含まれます。

# MANIFEST.in
include src/mileConverter/*.toml

この目的のために、別の.tomlファイルを指定することもできます。

パッケージへのライセンスの付与

Pythonパッケージにライセンスを含め、ほかの人が利用できるようにするには、パッケージディレクトリにLICENSEというファイルを作成し、choosealicense.comなどにある選択肢から既存のライセンスを選びます。

pyproject.tomlファイルで、ライセンスをPyPIページに表示するには、license変数にLICENSEファイル名を指定する必要があります。これは、パッケージのpyproject.tomlファイルの設定を開始したときに行ったことです。

最も人気のあるオープンソースライセンスには、MIT License、Apache License、GNU General Public License(GPL)などがありますが、ほかにも多くの選択肢があります。

パッケージをローカルにインストールする

ここまでで、必要なファイルをすべて作成し、パッケージに必要な設定もすべて完了しました。PyPIに公開する前に、pipを使ってローカルにインストールし、エラーがないか確認しましょう。以下のコマンドのように、パッケージをローカルにインストールすることを示す-eまたは- editableタグを指定するだけです。

$ pip install -e .

パッケージをローカルにインストールできたので、PyPIに公開する準備が整いました。

パッケージをPyPIに公開する

パッケージを公開するには、まずビルドする必要があります。

パッケージをビルドする

buildパッケージを使って、Pythonパッケージのwheelファイルを生成します。ビルドフロントエンドをインストールするには、次のコマンドを実行します。

$ pip install build

次に、pyproject.tomlファイルがあるディレクトリに移動し、以下のコマンドを実行します。

$ python -m build

これにより、.tomlファイルと同じ場所にdist/フォルダが作成され、次の2つのファイルが格納されます。

dist/
├── milesConverter-1.0.0-py3-none-any.whl
└── milesConverter-1.0.0.tar.gz

`.whl`ファイルはビルド済み配布物で、tar.gzファイルはソース配布物です。これらのファイルは最終的にPyPIにアップロードできますが、まずはテストが必要です。

パッケージをテストする

パッケージをテストするには、PyPIへのアップロードにも使えるライブラリ`twine`をインストールします。次のコマンドでインストールできます。

$ pip install twine

twineをインストールしたら、次のコマンドを使って、パッケージの説明がPyPIで正しく表示されるか確認できます。

$ twine check dist/*

前のコマンドを実行すると、次のような出力が表示されます。

(base) gouravbais@Gouravs-Air Python_Package % twine check dist/*
Checking dist/milesConverter-1.0.0-py3-none-any.whl: PASSED
Checking dist/milesConverter-1.1.0-py3-none-any.whl: PASSED
Checking dist/milesConverter-1.0.0.tar.gz: PASSED
Checking dist/milesConverter-1.1.0.tar.gz: PASSED

すべてのテストに合格すれば、パッケージの準備は完了です。

パッケージをTestPyPIサービスにアップロードする

パッケージを一般公開する前に、TestPyPIサービスを使ってテストと検証を行いましょう。パッケージをTestPyPIにアップロードすればテストと検証ができますが、その前にPyPIのウェブサイトでアカウントを作成する必要があります。続いてtwineを使い、次のコマンドを実行してパッケージをアップロードします。PyPIのユーザー名とパスワードが必要です。

$ twine upload -r testpypi dist/*

コマンドが正常に実行されると、結果を確認するためのURLが表示されます。TestPyPIのウェブサイトでパッケージを検索することもできます。TestPyPIサービスからパッケージをダウンロードするには、次のコマンドを使います。

$ pip install -i https://test.pypi.org/simple/ milesConverter==1.0.0

ダウンロード後、パッケージが正しく動作するかテストできます。

パッケージを正式なPyPIサービスにアップロードする

正式なPyPIサービスへのアップロード方法は、TestPyPIサービスへのアップロード方法と同様です。前の手順でパッケージを検証したら、正式なPyPIサービスにアップロードできます。ここでもtwineツールを使って、パッケージファイルをPyPIにアップロードします。

ターミナルで次のコマンドを実行し、dist/*をパッケージファイルへのパスに置き換えます。

twine upload dist/*

パッケージをアップロードしたら、ウェブサイトにアクセスして検索できます。

パッケージをインストールする

次に、パッケージをテストします。次のコマンドでパッケージをダウンロードできます。

$ pip install milesConverter

パッケージをインストールしたら、任意のPythonエディターまたはPythonランタイムを開き、パッケージが動作するかテストします。

from milesConverter import km2miles
a = km2miles.km2miles(16)
print(a)

次のような出力が表示されます。

9.941936

これでパッケージがPyPIにアップロードされ、pipを使って任意のPython環境から利用できるようになりました。

パッケージをプライベートPythonインデックスに公開する

プライベートPythonインデックスを使ってパッケージを配布する組織がある理由を、先ほど説明しました。また、Pythonパッケージをプライベートインデックスに公開するのは簡単です。次の手順に従うだけです。

  • 配布用の.whlファイルまたは.tar.gzファイルを含むディレクトリ構造を、パッケージ用に設定します。

  • 自動インデックス作成を有効にして、パッケージのルートディレクトリを配信するようウェブサーバーを設定します。

  • twineを使い、プライベートウェブサーバーのURLを指定して、パッケージをプライベートインデックスにアップロードします。$ twine upload - repository-url your_web_server milesConverter/*

  • プライベートインデックスからパッケージをインストールするには、ウェブサーバーのURLを指定してpipコマンドを使います($ pip install - extra-index-url your_web_server milesConverter)。

これらの手順を完了すると、プライベートウェブサーバー上でパッケージを利用できるようになり、あなたやチームで使用できます。

Pythonパッケージ作成時のセキュリティのベストプラクティス

パッケージの公開方法がわかったところで、安全かつ正しく動作する状態で公開するために、いくつかのベストプラクティスを検討しましょう。

依存関係に注意する

プロジェクトでサードパーティの依存関係を使う前に、そのドキュメントを確認し、セキュリティチェックや脆弱性チェックの仕組みがあるか確認しましょう。多くのライブラリでは、既知のセキュリティ問題やその対処方法について、ドキュメントに専用のセクションが設けられています。こうした情報があれば、サードパーティ製パッケージを選ぶ際の安心感につながります。

ドキュメントがない場合は、Snykなどのツールを使って、利用を検討しているサードパーティライブラリの脆弱性を特定できます。

文字列と生のSQLの扱いに注意する

Pythonで文字列を扱う際、特にコードで生のSQLを使う場合は、細心の注意が必要です。この部分の処理を誤ると、文字列インジェクションやSQLインジェクション攻撃の危険にさらされる可能性があります。攻撃者が文字列、より一般的にはSQLクエリに悪意のあるコードを注入すると、データベースで予期しない任意のSQLコマンドが実行されるおそれがあります。

こうした攻撃を防ぐには、特にユーザー入力を扱う場合、文字列のフォーマット方法を慎重に検討してください。文字列のフォーマットには、f-string機能を活用して標準ライブラリを使うのが最善です。

生のSQLを使う場合は、ユーザー入力をSQL文字列に直接連結するのではなく、必ずプレースホルダーを使ったクエリを使用してください。ユーザー入力が適切にエスケープ、引用符処理、検証されるため、SQLインジェクション攻撃の防止に役立ちます。

デシリアライズは慎重かつ安全に行う

Pythonのpickleモジュールは安全性が高いとは言えず、pickle bombなどの攻撃に対して脆弱な可能性があります。パッケージの安全性を確保するには、pickleを基盤とする安全なラッパーである`dill`など、別のデシリアライズ用ライブラリの利用を検討してください。

pickleの脆弱性の1つは、デシリアライズ中に任意のコードが実行される可能性があることです。この場合、攻撃者はデシリアライズ時に実行されるコードを含む悪意のあるpickleファイルを注入できます。dillでは、unpickleを許可するモジュールや関数を指定できるため、デシリアライズ中の任意コード実行を防止できます。そのため、dillはpickleより安全です。

シークレットの削除も忘れずに

機密データを扱う際は、シークレットを安全に保存することが非常に重要です。安全に保存されていないシークレットは攻撃者にアクセスされ、不正にシステムやデータへアクセスするために悪用される可能性があります。keyring、passlib、pycryptodome、pycryptoなどのシークレット管理ライブラリを使えば、シークレットを暗号化して保存し、こうした事態を防げます。

さらに、シークレットは定期的にローテーションする必要があります。これにより漏えいのリスクを抑えられます。上記のツールの一部にはこの機能があり、HashiCorp Vaultなどのツールでも利用できます。

ツールを使ってコードの脆弱性を定期的にスキャンする

ここまで見てきたように、Pythonプロジェクトに影響を及ぼすさまざまな脅威、特に依存関係の管理、文字列のフォーマット、生のSQLクエリ、デシリアライズに関する脅威を把握することが重要です。こうした問題をすべて自力で解決することも可能かもしれませんが、おすすめはできません。プロジェクトの脆弱性をスキャンし、問題を解決できるツールを使うほうが効果的です。

Snykのようなツールは、コードの脆弱性を定期的にスキャンして、作業を効率化します。Snykを使えば、Pythonプロジェクトで起こりうる問題を特定して修正できます。プロジェクトの安全性を高められるだけでなく、アプリケーションのバグや機能上の問題も減らせます。

まとめ

このチュートリアルでは、Pythonパッケージの作成方法を紹介しました。また、パッケージがコードを整理するうえで役立つ理由や、コードの再利用性と持続可能性を高める方法についても説明しました。

このチュートリアルでは、プロジェクトを損なうさまざまな脅威や脆弱性を把握することがいかに重要かについても解説しました。コードを追加して、脆弱性や脅威の一部を自分で対処する方法も学びました。しかし、すべての組織やチームにそれを実行する余力があるわけではなく、たとえ余力があっても、自分たちで対処すべきではありません。脆弱性や脅威に継続的に対応し、作業を効率化する、標準的で包括的な製品を使うのが効果的です。

Snykrのような製品は、コードを徹底的にスキャンして脆弱性を特定し、一部の問題を自動修正することで、こうした脅威の軽減に役立ちます。

Pythonアプリのセキュリティ対策を始めましょう

Snykを使って、Pythonの脆弱性を無料で検出・修正できます。

クレジットカードの登録は不要です。

またはAzure AD、Docker ID、Bitbucketで登録

Snykを利用すると、利用規約およびプライバシーポリシーを含む各種ポリシーに同意したものとみなされます。

続きを読む

illustration hero ai
Blog

PHPが重大な未認証RCEの脆弱性2件を修正(CVE-2026-109618、CVE-2026-109619):今すぐアップデート

PHPのリクエスト処理に重大な脆弱性が2件あり、未認証のリモートコード実行につながるおそれがあります。修正版にアップグレードするか、旧バージョンにはベンダー提供のバックポートを適用してください。

blog feature ai lilac
Blog

エージェント型修正で脆弱性バックログをプルリクエストに変える

Evo Remediation Agentがオープンプレビューで利用可能になり、Evo Agentic AppSecに依存関係の自動解決機能が加わりました。AppSecチームは継続的な修正キャンペーンを実施できるようになりました

AI Background min crop
Blog

AIが、あなたが受け入れてきたあらゆるリスクを一気に顕在化させる:AI Security Panel: Unfilteredから得た7つの洞察

AIによって攻撃はより安価かつ高速になり、組織がすでに抱えているリスクが露呈しています。4人のセキュリティリーダーが、機械の速度に対応する防御と迅速な修復が今不可欠である理由を語ります。