PYTHONPATH変数公式のPythonパッケージガイド
Pythonのモジュールは、Pythonコードを含む単一のファイル( .py拡張機能付き)です。通常、他のPythonコードで使用できるクラス、関数、および変数で構成されています。モジュールは、コードを論理単位に整理し、コードの再利用性を促進するために使用されます。
たとえば、追加、減算、乗算などの数学操作を実行する関数を含むmath_operations.pyという名前のモジュールを検討してください。これらの機能を他のPythonスクリプトでインポートおよび使用できます。
# math_operations.py
def add ( x , y ):
return x + y
def subtract ( x , y ):
return x - yPythonのパッケージは、ディレクトリにグループ化されたモジュールのコレクションです。パッケージは通常、 __init__.pyファイル(空にすることができる)と1つ以上のPythonモジュールを含むディレクトリで表されます。 __init__.pyファイルは、ディレクトリをパッケージとして扱う必要があることをPythonに示します。
たとえば、 my_packageという名前のパッケージを検討してください。
my_package/
├── __init__.py
├── module1.py
└── module2.py
ここでは、 my_packageはmodule1.pyおよびmodule2.pyを含むパッケージで、dot notation( my_package.module1 、 my_package.module2 )を使用してインポートできます。
Pythonのサブパッケージは、別のパッケージ内にネストされたパッケージです。これは、パッケージに他のパッケージとモジュールを含めることができることを意味します。サブパッケージは、ディレクトリを整理し、パッケージ構造を定義するために__init__.pyファイルを適切に追加することによって作成されます。
例えば:
my_parent_package/
├── __init__.py
└── my_sub_package/
├── __init__.py
├── module3.py
└── module4.py
この構造では、 my_sub_package my_parent_packageのサブパッケージであり、独自のモジュール( module3.py 、 module4.py )を含めることができます。サブパッケージは、dot Notation( my_parent_package.my_sub_package.module3 )を使用してインポートできます。
Pythonの配布パッケージ(または単純な配布)とは、Pythonコードのパッケージ化されたコレクションと、インストールに利用できるリソースを指します。通常、モジュール、パッケージ、データファイル、構成ファイル、および特定の目的に必要なその他のリソース(ライブラリやアプリケーションなど)が含まれます。
配布パッケージは、多くの場合、 pipなどのPythonパッケージマネージャーを使用して配布およびインストールされ、他の開発者が簡単に配信してインストールするために、Pypi(Pythonパッケージインデックス)などのリポジトリにアップロードできます。
たとえば、一般的な配布パッケージには、 pipを使用してインストールされ、Pythonプロジェクトで使用できる機能を提供するnumpy 、 fast-api 、 pandasなどが含まれます。
sdist形式setup.pyを構成しますpython setup.py build sdist実行して、ソース分布を構築しますpip install ./dist/<package_name>.tar.gz実行して、パッケージをインストールしますpip listを使用して、パッケージがインストールされているかどうかを確認します。パッケージに変更が加えられている場合は、 pip install .最新のパッケージをその場で構築およびインストールします。
または、単に編集可能なものを使用して、パッケージを再構築する必要がないようにして、新しい変更が行われます。
pip install --editable . sdist 、「SDIST」と呼ばれるコードを含む.tarファイルのソース分布の略です。つまり、配布パッケージにはソースコードのサブセットのみが含まれているということです。
「ソース分布」は、本質的にソースコードを含むジップフォルダーです。
wheel形式ソースディストリビューション( sdist ) :
*.py )、構成ファイル、およびその他のプロジェクト関連資産が含まれます。python setup.py sdistを使用して作成できます。ホイール( bdist_wheel ) :
sdistと比較して、より高度な配布形式。.whl拡張子を備えたzippedファイルを表します。python setup.py bdist_wheelを使用して作成されました。 より速いインストール:
*.whl )からのインストールは、通常、ソース分布( *.tar.gz )からインストールするよりも速いです。ユーザーの使いやすさ:
sdistとbdist_wheel両方を構築しますsdistとbdist_wheelの両方を構築および公開することをお勧めします。python setup.py sdist bdist_wheel編集要件:
残念ながら、すべてのオペレーティングシステムのホイールの構築は、コンピレーションステップが必要な場合に困難になります。そのため、一部のOSSメンテナーは、単一のOSのホイールのみを構築します。
pip実際にSDISTをダウンロードした直後に、マシン上のsetup.py (または同等のファイル)を実行します。ホイールを構築するには、コードをコンパイルする必要がある場合があります
gccなどの依存関係が必要ですが、他の言語では独自のコンパイラが必要です。ユーザーは、これらをオンマシンまたはpip install ...単に失敗します。これは、 numpy 、 pandas 、 scipy 、 pytorch 、 tensorflowなどをインストールすると発生する可能性があります。 setup.pyは任意のコードが含まれる場合があります。これは非常に不安です。 setup.pyには悪意のあるコードが含まれている場合があります。
依存関係管理:
gccなど)をインストールして、コンパイルを必要とするパッケージを正常にインストールする必要がある場合があります。セキュリティの懸念:
setup.pyを使用するには、任意のコードを実行するため、パッケージのインストールを使用することは不安定です。wheelファイルの命名スキームホイールのファイル名は、ハイフンで区切られた部分に分解されます。
{dist}-{version}(-{build})?-{python}-{abi}-{platform}.whl {brackets}の各セクションは、タグ、またはホイール名のコンポーネントであり、ホイールに何が含まれているか、ホイールが機能するか、機能しないかについての意味があります。
例: dist/packaging-0.0.0-py3-none-any.whl
packagingはパッケージ名です0.0.0はベリソン番号ですpy3 、Python3用のビルドを示しますabiタグ。 ABIは、アプリケーションバイナリインターフェイスの略です。any 。その他の例:
cryptography-2.9.2-cp35-abi3-macosx_10_9_x86_64.whlchardet-3.0.4-py2.py3-none-any.whlPyYAML-5.3.1-cp38-cp38-win_amd64.whlnumpy-1.18.4-cp38-cp38-win32.whlscipy-1.4.1-cp36-cp36m-macosx_10_6_intel.whlその理由は、ホイールファイルには、パッケージを迅速にインストールするのに役立つ事前に複数のバイナリコードが含まれています。
パッケージメンテナンス:
sdistとbdist_wheel分布の両方を提供するよう努力する必要があります。ユーザーエクスペリエンスを検討してください:
pyproject.toml build「ビルド依存関係」とは、配信パッケージをSDISTまたはホイールに組み込むためにシステムにインストールする必要があるものです。
たとえばwheel python setup.py bdist_wheel実行するためにpip install wheel必要がありました。
setup.pyファイルは複雑になる可能性があります。
pip install ... 、複雑なビルドプロセスに対応するためにsetup.pyファイルにインポートする必要がある場合があります。
講義では、複雑なsetup.pyファイルを備えたパッケージの例として、 pytorchとairflow示しています。
どういうわけか、 setup.py以外でビルド依存関係を文書化できる必要があります。
setup.pyファイルに文書化された場合… setup.pyファイルを実行してドキュメンタリー依存関係を読み取ることができません(ファイルのlistかにそれらが指定されている場合など)。
これは、 pyproject.toml解決するための元の問題です。
# pyproject.toml
[ build-system ]
# Minimum requirements for the build system to execute.
requires = [ " setuptools>=62.0.0 " , " wheel " ] pyproject.toml 、ファイルツリーのsetup.pyに隣接しています
build CLIツール( pip install build )は、Python Packaging Authority(PYPA)による特別なプロジェクトです。
pyproject.tomlの[build-system]テーブルを読み取ります。pip install build
# both setup.py and pypproject.toml should be together, ideally in the root directory
# python -m build --sdist --wheel path/to/dir/with/setup.py/and/pyproject.toml
python -m build --sdist --wheel . setup.pyからsetup.cfg configファイルへの移動から移動します
# setup.py
from pathlib import Path
from setuptools import find_packages , setup
import wheel
# Function to read the contents of README.md
def read_file ( filename : str ) -> str :
filepath = Path ( __file__ ). resolve (). parent / filename
with open ( filepath , encoding = "utf-8" ) as file :
return file . read ()
setup (
name = "packaging-demo" ,
version = "0.0.0" ,
packages = find_packages (),
# package meta-data
author = "Amit Vikram Raj" ,
author_email = "[email protected]" ,
description = "Demo for Python Packaging" ,
license = "MIT" ,
# Set the long description from README.md
long_description = read_file ( "README.md" ),
long_description_content_type = "text/markdown" ,
# install requires: libraries that are needed for the package to work
install_requires = [
"numpy" , # our package depends on numpy
],
# setup requires: the libraries that are needed to setup/build
# the package distribution
# setup_requires=[
# "wheel", # to build the binary distribution we need wheel package
# ],
)に
# setup.py
from setuptools import setup
# Now setup.py takes it's configurations from setup.cfg file
setup () # setup.cfg
[metadata]
name = packaging-demo
version = attr: packaging_demo.VERSION
author = Amit Vikram Raj
author_email = [email protected]
description = Demo for Python Packaging
long_description = file: README.md
keywords = one, two
license = MIT
classifiers =
Framework :: Django
Programming Language :: Python :: 3
[options]
zip_safe = False
include_package_data = True
# same as find_packages() in setup()
packages = find:
python_requires = >=3.8
install_requires =
numpy
importlib-metadata ; python_version<"3.10"また、追加設定は
pyproject.tomlファイルに渡されます。ここでは、setup.pyのsetup_requiresと同様のbuild-systemを指定しました。
# pyproject.toml
[ build-system ]
# Minimum requirements for the build system to execute
requires = [ " setuptools " , " wheel " , " numpy<1.24.3 " ]
# Adding ruff.toml to pyproject.toml
[ tool . ruff ]
line-length = 99
[ tool . ruff . lint ]
# 1. Enable flake8-bugbear (`B`) rules, in addition to the defaults.
select = [ " E " , " F " , " B " , " ERA " ]
# 2. Avoid enforcing line-length violations (`E501`)
ignore = [ " E501 " ]
# 3. Avoid trying to fix flake8-bugbear (`B`) violations.
unfixable = [ " B " ]
# 4. Ignore `E402` (import violations) in all `__init__.py` files, and in select subdirectories.
[ tool . ruff . lint . per-file-ignores ]
"__init__.py" = [ " E402 " ]
"**/{tests,docs,tools}/*" = [ " E402 " ]
# copying isort configurations from .isort.cfg to pyproject.toml
[ tool . isort ]
profile = " black "
multi_line_output = " VERTICAL_HANGING_INDENT "
force_grid_wrap = 2
line_length = 99
# copying balck config from .black.toml to pyproject.toml
[ tool . black ]
line-length = 99
exclude = " .venv "
# copying flake8 config from .flake8 to pyproject.toml
[ tool . flake8 ]
docstring-convention = " all "
extend-ignore = [ " D107 " , " D212 " , " E501 " , " W503 " , " W605 " , " D203 " , " D100 " ,
" E305 " , " E701 " , " DAR101 " , " DAR201 " ]
exclude = [ " .venv " ]
max-line-length = 99
# radon
radon-max-cc = 10
# copying pylint config from .pylintrc to pyproject.toml
[ tool . pylint . "messages control" ]
disable = [
" line-too-long " ,
" trailing-whitespace " ,
" missing-function-docstring " ,
" consider-using-f-string " ,
" import-error " ,
" too-few-public-methods " ,
" redefined-outer-name " ,
] setup.py栄光の構成ファイルとして扱ってきましたが、ロジックを追加してPythonファイルであるという事実を実際に利用していません。
これはより一般的です。また、構成ファイルにPythonを使用することから一般的なシフトがありました。これにより、構成ファイルを使用することに複雑さが追加されます(構成ファイルを実行するためにライブラリをインストールするなど)。
setup.cfgは、 setup.pyのコンパニオンファイルであり、静的テキストファイル、特にINI形式ファイルでパッケージ構成を定義できるようにします。
setup()の引数として直接渡されない値は、setup.cfgファイルのsetup()の呼び出しによって探しられます。
今、私たちは多くのファイルを蓄積しています!
setup.pysetup.cfgpyproject.tomlREADME.md.pylintrc 、 .flake8 、 .blackrc 、 ruff.toml 、 .mypy 、 pre-commit-config.yamlなど。CHANGELOGまたはCHANGELOG.mdVERSIONまたはversion.txtこれらのファイルのほぼすべてをpyproject.tomlに置き換えることができます。ほぼすべてpyproject.toml糸くず /コード品質のツールは、 [tool.<name>] [tool.black]セクションを解析します。
個々のツールのドキュメントは、これを達成する方法を教えてくれるはずです。
上に示されているのは、コースで使用した多くの糸くずツールの構成を備えたpyproject.tomlです。
setup.cfgとsetup.pyも交換できますか?
setup.cfgをpyproject.tomlに移動します
setup.cfgから
# setup.cfg
[metadata]
name = packaging-demo
version = attr: packaging_demo.VERSION
author = Amit Vikram Raj
author_email = [email protected]
description = Demo for Python Packaging
long_description = file: README.md
keywords = one, two
license = MIT
classifiers =
Programming Language :: Python :: 3
[options]
zip_safe = False
include_package_data = True
# same as find_packages() in setup()
packages = find:
python_requires = >=3.8
install_requires =
numpy
importlib-metadata ; python_version<"3.10"に
# pyproject.toml
[ build-system ]
# Minimum requirements for the build system to execute
requires = [ " setuptools>=61.0.0 " , " wheel " ]
# Adding these from setup.cfg in pyproject.toml file
[ project ]
name = " packaging-demo "
authors = [{ name = " Amit Vikram Raj " , email = " [email protected] " }]
description = " Demo for Python Packaging "
readme = " README.md "
requires-python = " >=3.8 "
keywords = [ " one " , " two " ]
license = { text = " MIT " }
classifiers = [ " Programming Language :: Python :: 3 " ]
dependencies = [ " numpy " , ' importlib-metadata; python_version<"3.10" ' ]
dynamic = [ " version " ]
# version = "0.0.3"
# https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata
[ tool . setuptools . dynamic ]
# every while making changes in package, you can change the verison in one of these files
# version = {attr = "packaging_demo.VERSION"} # version read by 'packaging_demo/__init__.py' file
version = { file = [ " version.txt " ]} # version read by 'version.txt' file in root folder
python -m build --sdist --wheel .- 完全に実行すると、別の構成ファイル(setup.cfg)を削除しました
setup.py build-backendに置き換えますPEP 517は、 pyproject.tomlにbuild-backend引数を追加しました。
[ build-system ]
# Defined by PEP 518:
requires = [ " flit " ]
# Defined by this PEP:
build-backend = " flit.api:main " # The above toml config is equivalent to
import flit . api
backend = flit . api . main build-backend 、 build CLIが実際にpyproject.toml解析し、ホイールとSDISTを構築するために使用するエントリポイント(この場合は実行可能ファイルモジュール)を定義します。
これは、それを行うプログラムを書くことで本日、独自のビルドバックエンドを実装できることを意味し、 requires = [...]にパッケージを追加し、 build-backend = ...でエントリポイントを指定することで使用できます。
pyproject.tomlでbuild-backendを指定しない場合、setuptoolsが想定され、パッケージは祝福的に問題なくなります。
setup.pyを削除してpython -m build --sdist --wheel . build-systemのデフォルト値は、パッケージを構築するbuild CLIのbuild-backend = "setuptools.build_meta"として設定されているため、完全に実行されます。しかし、あなたはまだこのようなビルドバックエンドとしてsetuptools明示的に宣言することができます
# pyproject.toml
...
[ build-system ]
requires = [ " setuptools>=61.0.0 " , " wheel " ]
build-backend = " setuptools.build_meta "
...通常、各ビルドバックエンドは、独自の構成オプションを使用してpyproject.tomlファイルを拡張します。例えば、
# pyproject.toml
...
[ tool . setuptools . package-data ]
package_demo = [ " *.json " ]
[ tool . setuptools . dynamic ]
version = { file = " version.txt " }
long_description = { file = " README.md " }
...プロジェクトでsetuptoolsを使用することを選択した場合は、これらのセクションをpyproject.tomlに追加できます。これについては、 setuptoolsドキュメントで詳細を読むことができます
多くの場合、Pythonコードがこれらの非Pythonファイルに依存しているため、パッケージ内にデータファイルやバイナリファイルなどの非Pythonファイルを含めることが有益です。
そして、これらのファイルを含める場合は、インストール時にユーザーの仮想環境内に入るのはパッケージフォルダーであるため、これらのファイルをパッケージフォルダー内に登場させる必要があることがわかりました。
また、デフォルトでは、すべての非Pythonファイルがその最終的な仮想環境フォルダーにそれを作成しないことも見られました。つまり、ビルド手順中に実際にパッケージフォルダーに入らないでください。
それでは、これらのファイルがパッケージのホイール/distビルドになることをどのように確認するのでしょうか?たとえば、ここでは、 states_info.pyファイルで使用されているように、パッケージに必要なcities.jsonファイルをデモします。
データサポート用の公式
setuptoolsドキュメント
# pyprject.toml
[ tool . setuptools ]
# ...
# By default, include-package-data is true in pyproject.toml, so you do
# NOT have to specify this line.
include-package-data = trueしたがって、デフォルトでは、公式ドキュメントに表示されるように、このinclude-package-data値がtrueに設定されていることをデフォルトで搭載していますが、追加のファイルMANIFEST.inを作成して、Root Dirに存在するパッケージに積み込まれるデータを指定する必要があります。
IMP:パッケージディレクトリ内のすべてのフォルダーにインポートされている
__init__.pyファイルには、setuptoolsが行うfo; dersが__init__.pyファイルを持つfo;find_packages()ders fo; ders fo;
# MANIFEST.in
include packaging_demo/*.json
include packaging_demo/my_folder/*.json
OR
Recursive include all json files in the package directory
recursive-include packaging_demo/ *.json
Manifest.inファイルの構成に関するドキュメント
MANIFEST.inファイルを使用せずにSetUptoolsドキュメントから、これをpyproject.tomlファイルに追加できます。
# this is by default true so no need to explicitly add it
# but as mentioned in the docs, it is false for other methods like setup.py or setup.cfg
[ tool . setuptools ]
include-package-data = true
# add the data here, it's finding the files recursively
[ tool . setuptools . package-data ]
package_demo = [ " *.json " ]setuptoolsよりも他のbuild-backendシステムsetuptools以外に、これらのビルドバックエンドシステムを使用できます。注意すべき点は、他のシステムを使用する場合、 pyproject.toml cofigurationが立場に従う必要があることです。
ハッチ
[ build-system ]
requires = [ " hatchling " ]
build-backend = " hatchling.build "詩
[ build-system ]
requires = [ " poetry-core>=1.0.0 " ]
build-backend = " poetry.core.masonry.api " 依存関係の正確なバージョンとその依存関係などを文書化します。
講義で説明されているように、依存関係の地獄、または他のパッケージとの依存関係の競合につながる可能性があるため、パッケージに関連付けられた依存関係の数を少なくすることをお勧めします。
依存関係が複雑になるほど、他のライブラリの将来のバージョンと競合する可能性が高くなります。
エリックによる依存性グラフ分析
依存関係とPythonバージョンのピン留めバージョンを保持することは、トラブルシューティングの目的で向上します。
pip freeze > requirements.txt pip install pipdeptree graphviz
sudo apt-get install graphviz
# generate the dependency graph
pipdeptree -p packaging-demo --graph-output png > dependency-graph.png[ project . optional-dependencies ]
dev = [ " ruff " , " mypy " , " black " ] # installing our package with optional dependencies
pip install ' .[dev] ' [ project . optional-dependencies ]
# for developement
dev = [ " ruff " , " mypy " , " black " ]
# plugin based architecture
colors = [ " rich " ] # plugin based installation
pip install ' .[colors] '
# here we demo with rich library, if user wants the output to be
# colorized then they can install our package like this.
# we can add multiple optional dependencies like:
pip install ' .[colors, dev] ' [ project . optional-dependencies ]
# for developement
dev = [ " ruff " , " mypy " , " black " ]
# plugin based architecture
colors = [ " rich " ]
# install all dependencies
all = [ " packaging-demo[dev, colors] " ] # Installing all dependencies all at once
pip install ' .[all] ' SNYKを使用して、パッケージに使用する依存関係のために、セキュリティの問題などがどれほど安定しているか、よくサポートされているかを確認し、プロジェクトで使用する決定を下します。
公式ガイドに記載されているように、パッケージをPypi [Python Packaging Index]に吹き飛ばすために、 twine CLIツールを使用します。
pip install twine
twine upload --helpPYPIテストまたはPYPI製品用のAPIトークンを生成します
Pythonパッケージの構築: python -m build --sdist --wheel "${PACKAGE_DIR}" 、ここでは、推奨されるように、sdistとホイールの両方を構築します。
Twineツールを実行する: twine upload --repository testpypi ./dist/*へのアップレディング
CMakeとMakefile
sudo apt-get install maketaskfile
justfile
Pyinvoke
開発者
$ rightArrow $ QA/ステージング$ rightArrow $ 製品



# .github/workflows/publish.yaml
name : Build, Test, and Publish
# triggers: whenever there is new changes pulled/pushed on this
# repo under given conditions, run the below jobs
on :
pull_request :
types : [opened, synchronize]
push :
branches :
- main
# Manually trigger a workflow
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
workflow_dispatch :
jobs :
build-test-and-publish :
runs-on : ubuntu-latest
steps :
# github actions checksout, clones our repo, and checks out the branch we're working in
- uses : actions/checkout@v3
with :
# Number of commits to fetch. 0 indicates all history for all branches and tags
# fetching all tags so to aviod duplicate version tagging in 'Tag with the Release Version'
fetch-depth : 0
- name : Set up Python 3.8
uses : actions/setup-python@v3
with :
python-version : 3.8
# tagging the release version to avoid duplicate releases
- name : Tag with the Release Version
run : |
git tag $(cat version.txt)
- name : Install Python Dependencies
run : |
/bin/bash -x run.sh install
- name : Lint, Format, and Other Static Code Quality Check
run : |
/bin/bash -x run.sh lint:ci
- name : Build Python Package
run : |
/bin/bash -x run.sh build
- name : Publish to Test PyPI
# setting -x in below publish:test will not leak any secrets as they are masked in github
if : ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run : |
/bin/bash -x run.sh publish:test
env :
TEST_PYPI_TOKEN : ${{ secrets.TEST_PYPI_TOKEN }}
- name : Publish to Prod PyPI
if : ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run : |
/bin/bash -x run.sh publish:prod
env :
PROD_PYPI_TOKEN : ${{ secrets.PROD_PYPI_TOKEN }}
- name : Push Tags
if : ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run : |
git push origin --tagsロック要件
依存関係キャッシング
GitHub CIでGitHubアクションが実行されるたびに、新鮮な容器で実行されるたびに。したがって、毎回PIPから依存関係をダウンロードして再インストールする必要があります。これは、それがefficeintであり、ワークフローを遅くするため、良くありません。
したがって、ワークフローが最初に実行されたときにすべての依存関係をインストールし、新しいワーフローが実行されるたびに使用することをお勧めします。
githubアクションは、依存関係をキャッシュすることによりこの機能を提供し、インストールされた依存関係( ~/.cache/pip )を保存し、新しいワークフローが実行されるたびにダウンロードします。ドキュメント
- uses : actions/cache@v3
with :
path : ~/.cache/pip
key : ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys : |
${{ runner.os }}-pip-setup-python Githubアクションを使用したPIP依存関係キャッシュ steps :
- uses : actions/checkout@v4
- uses : actions/setup-python@v5
with :
python-version : ' 3.9 '
cache : ' pip ' # caching pip dependencies
- run : pip install -r requirements.txt並列化
# See .github/workflows/publish.yaml
jobs :
check-verison-txt :
...
lint-format-and-static-code-checks :
....
build-wheel-and-sdist :
...
publish :
needs :
- check-verison-txt
- lint-format-and-static-code-checks
- build-wheel-and-sdist
...