{"id":20717963,"url":"https://github.com/hasenpfote/python-poetry-example","last_synced_at":"2026-04-17T16:33:09.681Z","repository":{"id":64644991,"uuid":"576285679","full_name":"Hasenpfote/python-poetry-example","owner":"Hasenpfote","description":"A simple example of how to use pyenv + poetry + tox + pytest.","archived":false,"fork":false,"pushed_at":"2023-06-26T09:03:44.000Z","size":392,"stargazers_count":3,"open_issues_count":7,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-17T22:22:03.014Z","etag":null,"topics":["black","flake8","github-actions","isort","mypy","pdoc","poetry","poetry-dynamic-versioning","pytest","python","python3","tox"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hasenpfote.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-09T13:11:34.000Z","updated_at":"2024-07-23T19:06:45.000Z","dependencies_parsed_at":"2025-01-17T22:33:38.129Z","dependency_job_id":null,"html_url":"https://github.com/Hasenpfote/python-poetry-example","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hasenpfote%2Fpython-poetry-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hasenpfote%2Fpython-poetry-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hasenpfote%2Fpython-poetry-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hasenpfote%2Fpython-poetry-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hasenpfote","download_url":"https://codeload.github.com/Hasenpfote/python-poetry-example/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242990612,"owners_count":20218016,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["black","flake8","github-actions","isort","mypy","pdoc","poetry","poetry-dynamic-versioning","pytest","python","python3","tox"],"created_at":"2024-11-17T03:11:11.078Z","updated_at":"2026-04-17T16:33:04.659Z","avatar_url":"https://github.com/Hasenpfote.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Lint](https://github.com/Hasenpfote/python-poetry-example/actions/workflows/lint.yml/badge.svg)](https://github.com/Hasenpfote/python-poetry-example/actions/workflows/lint.yml)\n[![Test](https://github.com/Hasenpfote/python-poetry-example/actions/workflows/test.yml/badge.svg)](https://github.com/Hasenpfote/python-poetry-example/actions/workflows/test.yml)\n[![codecov](https://codecov.io/github/Hasenpfote/python-poetry-example/branch/main/graph/badge.svg?token=8BDD1K119N)](https://codecov.io/github/Hasenpfote/python-poetry-example)\n\n# pyenv + poetry + tox + pytest 環境構築例\n\n## 要約\n\npython で pyenv + poetry + tox + pytest に optional で\n\n- pytest の拡張\n\n  pytest-mock + pytest-cov\n\n- linter/formatter\n\n  black + isort + flake8 + mypy\n\n- API の文書化\n\n  pdoc\n\n- 動的バージョニング\n\n  poetry-dynamic-versioning\n\n- CI/CD\n\n  gh-actions\n\n特に注釈がなければ, 各種情報の背景は 2022年12月あたり.  \n**本例では python 3.7 環境でエラーが発生するようにしている.**  \n\n## 前提条件\n\n- OS は Linux または Windows を想定\n- 任意の python 環境は [pyenv](https://github.com/pyenv/pyenv) で構築  \n  Windows の場合は [pyenv-win](https://github.com/pyenv-win/pyenv-win) で代替\n- 仮想環境は [poetry](https://github.com/python-poetry/poetry) で構築  \n  グループ機能が便利なのでバージョンは 1.2 以上.\n- 本例では python 3.7 / 3.8 を想定  \n  3.8 は現行バージョンで, 3.7 は後方互換の最小バージョンとした開発を行っている状況など.  \n  **※ 3.7 未満は依存関係がカオスなので全力回避**\n- テストは [tox](https://github.com/tox-dev/tox) + [pytest](https://github.com/pytest-dev/pytest)\n- 面倒なことはしない\n\n```mermaid\nflowchart TD\n\n  subgraph system\n    id11(python \u003e= 3.7)\n    id12(poetry \u003e= 1.2)\n\n    subgraph pyenv\n      id21(python = 3.7)\n      id22(python = 3.8)\n    end\n\n    subgraph project\n      id31(.venv)\n      subgraph .tox\n        id41(py37)\n        id42(py38)\n      end\n    end\n  end\n\n  id12 .-\u003e id11\n  id12 --\u003e |poetry run python| id31\n  id12 --\u003e |poetry run tox| .tox\n  id41 .-\u003e id21\n  id31 \u0026 id42 .-\u003e id22\n```\n\n## 基本\n\n### 準備\n\n#### pyenv\n\n##### インストール\n\n###### for Linux\n\n- [Automatic installer](https://github.com/pyenv/pyenv#automatic-installer) を利用\n- 詳細は [pyenv installer](https://github.com/pyenv/pyenv-installer)\n- 環境毎の違いは [Troubleshooting / FAQ](https://github.com/pyenv/pyenv/wiki#troubleshooting--faq)\n\n###### for Windows\n\n選択肢が多いので [Installation](https://github.com/pyenv-win/pyenv-win#installation) からお好みで.\n\nChocolatey 以外は環境変数の設定が必要になるので[詳細](https://github.com/pyenv-win/pyenv-win/blob/master/docs/installation.md)を参考に.\n\n#### python\n\n##### インストール\n\n候補を確認\n\n```bash\n$ pyenv install -l\n```\n\nとある環境での最新は 3.7.9 / 3.8.10 だったのでインストール\n\n```bash\n$ pyenv install 3.7.9\n$ pyenv install 3.8.10\n```\n\nインストール済みバージョンの確認\n\n```bash\n$ pyenv versions\n```\n\n#### poetry\n\n##### インストール\n\n[2022/12/07] この時点での最新は 1.2.2 で [ドキュメント](https://python-poetry.org/docs/#system-requirements) より\n\n\u003e ## System requirements\n\u003e\n\u003e Poetry requires **Python 3.7+**. It is multi-platform and the goal is to make it work equally well on Linux, macOS and Windows.\n\n予め**システム**に python 3.7 以上をインストール要.\n\n**poetry を動作させるために必要であって, プロジェクトへの制約ではない.**\n\n[Installation](https://python-poetry.org/docs/#installation) を参考にお好みで.\n\n##### 設定\n\n###### プロジェクト下に仮想環境を構築\n\n設定を確認\n\n```bash\n$ poetry config --list\n```\n\n`virtualenvs.in-project = null` なら下記を一度だけ実行\n\n```bash\n$ poetry config virtualenvs.in-project true\n```\n\nもしもプロジェクト毎に有効にしたいのであれば\n\n```bash\n$ poetry config virtualenvs.in-project true --local\n```\n\n好みの問題ではあるが, 本例では全体設定としておく.\n\n### 手順\n\n#### 1. 新規プロジェクトを作成\n\n任意の場所で\n\n```bash\n$ poetry new --src python-poetry-example\n```\n\nディレクトリ\n\n```bash\npython-poetry-example\n├─ pyproject.toml\n├─ README.md\n├─ src\n│  └─ python_poetry_example\n│      └─ __init__.py\n└─tests\n   └─ __init__.py\n```\n\n- [Choosing a test layout / import rules](https://docs.pytest.org/en/latest/explanation/goodpractices.html#tests-outside-application-code)\n- [Packaging a python library](https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure)\n\n#### 2. 使用する python を指定\n\nプロジェクトルートに移動\n\n```bash\n$ cd python-poetry-example\n```\n\n**以降は特に注釈がない限りはプロジェクトルートでコマンドを実行している**\n\n使用する python を指定\n\n```bash\n$ pyenv local 3.8.10 3.7.9\n```\n\n有効になっているバージョンを確認\n\n```bash\n$ python -V\nPython 3.8.10\n```\n\n本例では 3.8.10 を現行バージョンと想定している.\n\n##### 特記事項\n\n動作確認を行った環境では,\n\n- `pyenv local 3.8.10 3.7.9` なら `python -V` で`3.8.10`\n- `pyenv local 3.7.9 3.8.10` なら `python -V` で `3.7.9`\n\n**最初に指定をしたバージョンが有効になる.**\n\n仮想環境の構築にも影響があるので要注意.\n\nディレクトリ\n\n```bash\npython-poetry-example\n├─ .python-version (New!)\n├─ pyproject.toml\n├─ README.md\n├─ src\n│  └─ python_poetry_example\n│      └─ __init__.py\n└─tests\n   └─ __init__.py\n```\n\n#### 3. `pyproject.toml` でバージョンを指定\n\n以下のように指定をしておく.\n\n```toml\n[tool.poetry.dependencies]\npython = \"^3.7\"\n```\n\n意味は 3.7 \u003c= python version \u003c 4.0 と若干緩め.\n\n依存するモジュールによっては制限を厳しくする必要もある.\n\n#### 4. 仮想環境を構築\n\n 3.8.10 ベースで構築.\n\n```bash\n$ poetry env use python\n```\n\n確認は\n\n```bash\n$ poetry env info\n```\n\nディレクトリ\n\n```bash\npython-poetry-example\n├─ .python-version\n├─ pyproject.toml\n├─ README.md\n├─ .venv (New!)\n│  └─ ...\n├─ src\n│  └─ python_poetry_example\n│      └─ __init__.py\n└─tests\n   └─ __init__.py\n```\n\n#### 5. コードを配置\n\n適当な機能をまとめた `utils.py` と, そのテストコードを配置する.\n\nディレクトリ\n\n```bash\npython-poetry-example\n├─ .python-version\n├─ pyproject.toml\n├─ README.md\n├─ .venv\n│  └─ ...\n├─ src\n│  └─ python_poetry_example\n│      ├─ __init__.py\n│      └─ utils.py (New!)\n└─tests\n   ├─ __init__.py\n   └─ test_utils.py (New!)\n```\n\n#### 6. テスト環境の準備\n\n`poetry add` でも構わないが, 本例では諸事情から `pyproject.toml` に直接記述をする.\n\n```toml\n[tool.poetry.group.dev]\noptional = true\n\n[tool.poetry.group.dev.dependencies]\ntox = \"^3.27.1\"\n\n[tool.poetry.group.test]\noptional = true\n\n[tool.poetry.group.test.dependencies]\npytest = \"^7.2.0\"\n\n[tool.pytest.ini_options]\naddopts = [\n  \"--import-mode=importlib\",\n]\npythonpath = \"src\"\n```\n\n- [2022/12/09] tox \u003e= 4.0.0 で仮想環境を認識できない不具合を確認  \n  Linux Mint 21 / Windows10 共に, アクティブになっている１つの環境しか認識されない.  \n  本例では 3.7 が認識されずスキップされる.  \n  issue では [Tox 4 breaks in CI/CD pipelines where user does not exist\n#2702](https://github.com/tox-dev/tox/issues/2702) が似ている.  \n  4系で環境変数周りが大幅に変更された影響と思われるので様子見が必要.  \n\nインストール\n\n```bash\n$ poetry install --with dev\n```\n\nインストールでエラーがでるようならロックファイルの更新で対応\n\n```bash\n$ poetry update\n```\n\n確認\n\n```bash\n$ poetry show\n```\n\n#### 7. `tox.ini` の配置\n\n```ini\n[tox]\nenvlist =\n    py{37,38}\nskipsdist = true\nisolated_build = true\n\n[testenv]\nallowlist_externals =\n    poetry\ncommands_pre =\n    poetry install --with test -v\ncommands =\n    poetry run pytest -v\n```\n\n- `py37` / `py38` は `pyenv local` で指定されたバージョンを参照する `tox` 側のキーワード.  \n  もしも 3.11.x が必要なら `py311` となる.\n\nディレクトリ\n\n```bash\npython-poetry-example\n├─ .python-version\n├─ poetry.lock\n├─ pyproject.toml\n├─ README.md\n├─ tox.ini (New!)\n├─ .venv\n│  └─ ...\n├─ src\n│  └─ python_poetry_example\n│      ├─ __init__.py\n│      └─ utils.py\n└─tests\n   ├─ __init__.py\n   └─ test_utils.py\n```\n\n#### 8. テストを実行\n\n```bash\n$ poetry run tox\n```\n\n**本例では python 3.7 環境でエラーが発生するようにしている.**\n\nディレクトリ\n\n```bash\npython-poetry-example\n├─ .python-version\n├─ poetry.lock\n├─ pyproject.toml\n├─ README.md\n├─ tox.ini\n├─ .tox (New!)\n│  └─ ...\n├─ .venv\n│  └─ ...\n├─ src\n│  └─ python_poetry_example\n│      ├─ __pycache__\n│      │  └─ ...\n│      ├─ __init__.py\n│      └─ utils.py\n└─tests\n   ├─ __pycache__\n   │  └─ ...\n   ├─ __init__.py\n   └─ test_utils.py\n```\n\n### 各種ファイル\n\n#### `pyproject.toml`\n\n```toml\n[tool.poetry]\nname = \"python-poetry-example\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"name \u003cemail\u003e\"]\nreadme = \"README.md\"\npackages = [{include = \"python_poetry_example\", from = \"src\"}]\n\n[tool.poetry.dependencies]\npython = \"^3.7\"\n\n[tool.poetry.group.dev]\noptional = true\n\n[tool.poetry.group.dev.dependencies]\ntox = \"^3.27.1\"\n\n[tool.poetry.group.test]\noptional = true\n\n[tool.poetry.group.test.dependencies]\npytest = \"^7.2.0\"\n\n[build-system]\nrequires = [\"poetry-core\u003e=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n[tool.pytest.ini_options]\naddopts = [\n  \"--import-mode=importlib\",\n]\npythonpath = \"src\"\n```\n\n#### `tox.ini`\n\n```ini\n[tox]\nenvlist =\n    py{37,38}\nskipsdist = true\nisolated_build = true\n\n[testenv]\nallowlist_externals =\n    poetry\ncommands_pre =\n    poetry install --with test -v\ncommands =\n    poetry run pytest -v\n```\n\n#### `.gitignore`\n\n```txt\n/.tox\n/.venv\n/.python-version\n/poetry.lock\n\n__pycache__/\n```\n\n- `.python-version` は環境次第でマイナーバージョンに大きな差があるため.\n- `poetry.lock` はお好みで.\n- 最終的には[Python.gitignore](https://github.com/github/gitignore/blob/main/Python.gitignore)を元に調整.\n\n## 拡張\n\n基本で目的は果たしているものの, 心許ないので一手間を加える場合もある.  \n\n好みもあるので以降は optional となる.  \n\n### 拡張 1 - pytest の拡張\n\n- mocker fixture の追加\n\n  [pytest-mock](https://github.com/pytest-dev/pytest-mock)\n\n- coverage reports の追加\n\n  [pytest-cov](https://github.com/pytest-dev/pytest-cov)\n\n#### 準備\n\n`./pyproject.toml`\n\n```toml\n...\n[tool.poetry.group.test]\noptional = true\n\n[tool.poetry.group.test.dependencies]\npytest = \"^7.2.0\"\npytest-mock = \"^3.10.0\"\npytest-cov = \"^4.0.0\"\n...\n```\n\n`./tox.ini`\n\n```ini\n[tox]\nenvlist =\n    clean\n    py{37,38}\n    report\nskipsdist = true\nisolated_build = true\n\n[testenv]\nallowlist_externals =\n    poetry\ncommands_pre =\n    poetry install --with test -v\ncommands =\n    poetry run pytest --cov --cov-append --cov-report=term-missing -v\n\n[testenv:clean]\ndeps = coverage\nskip_install = true\ncommands_pre = # nop\ncommands =\n    poetry run coverage erase\n\n[testenv:report]\ndeps = coverage\nskip_install = true\ncommands_pre = # nop\ncommands =\n    poetry run coverage report -m\n    poetry run coverage html\n\n...\n```\n\n#### 手順\n\n- インストール\n\n  ```bash\n  $ poetry install --with test\n  ```\n\n- 実行\n\n  ```bash\n  $ poetry run tox -r -e clean,py37,py38,report\n  ```\n\n  並列で行う場合は,\n\n  `./tox.ini`\n\n  ```ini\n  ...\n  [testenv]\n  ...\n  depends =\n      py{37,38}: clean\n      report: py{37,38}\n  # Required if -p is enabled on Windows.\n  # https://github.com/tox-dev/tox/pull/2641\n  setenv =\n      PYTHONIOENCODING=utf-8\n  ...\n  ```\n\n  を追記し\n\n  ```bash\n  $ poetry run tox -r -e clean,py37,py38,report -p all\n  ```\n\n  順序は `depends` で解決するので適当でもよい.  \n\n### 拡張 2 - linter/formatter\n\n#### 構成\n\n##### solution\n\n- [black](https://github.com/psf/black)\n\n  コードの整形に利用.\n\n- [isort](https://github.com/PyCQA/isort)\n\n  `import` の並び替えに利用.\n\n- [flake8p](https://github.com/john-hen/Flake8-pyproject)\n\n  構文チェックに利用.\n\n  `poetry` 経由で利用するので[オリジナル](https://github.com/PyCQA/flake8)は使えない.\n\n  [2022/12/11] [pflake8](https://github.com/csachs/pyproject-flake8) は `max-line-length` が反映されなかったので選択肢を変更した.\n\n  79 / 88 / 120 宗派のため.\n\n- [mypy](https://github.com/python/mypy)\n\n  型ヒントに利用.\n\n  取り敢えず組み込んでおき, いつでも optional から変更できるように配慮.\n\n```mermaid\nflowchart LR\n\n  subgraph solution\n    subgraph formatter\n      black\n      isort\n      black .- isort\n    end\n    subgraph linter\n      flake8\n      mypy\n    end\n  end\n```\n\n##### 環境\n\n`vscode` 環境とそれ以外を考慮しつつ, 最終的にリポジトリへのコミットで同一性を担保する方向で.\n\n- `solution` の設定は `pyproject.toml` で一元管理\n- `vscode` は `poetry` で作成した仮想環境を利用するように設定\n- `tox` に確認用の環境を仕込んでおく\n- [pre-commit](https://github.com/pre-commit/pre-commit) でコミットをフックする\n\n```mermaid\nflowchart LR\n\n  subgraph tox\n    id_tox(solution)\n  end\n\n  subgraph poetry\n    id_poetry(solution)\n  end\n\n  subgraph vscode\n    id_vscode(solution)\n  end\n\n  subgraph pre-commit\n    id_pre-commit(solution)\n  end\n\n  tox .-o poetry\n\n  id_terminal(terminal)\n  id_py(*.py)\n  id_repo[(repo)]\n  id_git(git)\n\n  id_terminal .-\u003e id_git\n  vscode .-\u003e id_git\n\n  id_terminal --\u003e |poetry run tox solution| id_tox --\u003e |dry-run| id_py\n  id_terminal ---\u003e |poetry run solution| id_poetry --\u003e |run| id_py\n  id_vscode --\u003e id_py\n\n  id_py .-o id_repo\n  id_pre-commit --\u003e id_py\n  id_git .-\u003e |git commit| pre-commit .-\u003e id_repo\n```\n\n#### 準備\n\n`./pyproject.toml`\n\n```toml\n...\n[tool.poetry.group.dev.dependencies]\nblack = \"^22.10.0\"\nisort = \"^5.10.1\"\nFlake8-pyproject = \"^1.2.2\"\nmypy = \"^0.991\"\npre-commit = \"^2.20.0\"\ntox = \"^3.27.1\"\n\n...\n[tool.black]\nline-length = 88\nskip-string-normalization = true\n\n[tool.isort]\nprofile = \"black\"\nline_length = 88\nmulti_line_output = 3\ninclude_trailing_comma = true\n\n[tool.flake8]\nmax-line-length = 88\nextend-ignore = [\"E203\", \"E266\", \"W503\",]\nmax-complexity = 10\nextend-exclude = [\".venv\", \"dist\", \".github\",]\n\n[tool.mypy]\nignore_errors = true\n#disallow_untyped_defs = true\n#ignore_missing_imports = true\n#no_implicit_optional = true\n#show_error_context = true\n#show_column_numbers = true\n#warn_return_any = true\n#warn_unused_ignores = true\n#warn_redundant_casts = true\nexclude = [\"dist/\",]\n\n...\n```\n\n**`mypy` を `ignore_errors = true` で実質的に無効にしているので注意.**\n\n`./tox.ini`\n\n```ini\n[tox]\nenvlist =\n    py{37,38}\n    black\n    isort\n    flake8\n    mypy\nskipsdist = true\nisolated_build = true\n\n...\n\n[testenv:black]\ndeps = black\ncommands_pre = # nop\ncommands =\n    poetry run black . --check --diff --color\n\n[testenv:isort]\ndeps =\n    isort\n    colorama\ncommands_pre = # nop\ncommands =\n    poetry run isort . --check --diff --color\n\n[testenv:flake8]\ndeps = Flake8-pyproject\ncommands_pre = # nop\ncommands =\n    poetry run flake8p .\n\n[testenv:mypy]\ndeps = mypy\ncommands_pre = # nop\ncommands =\n    poetry run mypy .\n```\n\n`./.pre-commit-config.yaml`\n\n```yaml\nrepos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.4.0\n    hooks:\n      - id: check-added-large-files\n      - id: check-toml\n      - id: check-yaml\n      - id: end-of-file-fixer\n      - id: mixed-line-ending\n        args: [--fix=lf]\n      - id: trailing-whitespace\n        args: [--markdown-linebreak-ext=md]\n  - repo: https://github.com/psf/black\n    rev: 22.12.0\n    hooks:\n      - id: black\n        language_version: python3\n  - repo: https://github.com/pycqa/isort\n    rev: 5.10.1\n    hooks:\n      - id: isort\n  - repo: https://github.com/pycqa/flake8\n    rev: 6.0.0\n    hooks:\n      - id: flake8\n        additional_dependencies: [Flake8-pyproject]\n  - repo: https://github.com/pre-commit/mirrors-mypy\n    rev: v0.991\n    hooks:\n      - id: mypy\n```\n\n#### 手順\n\n- インストール\n\n  ```bash\n  $ poetry install --with dev\n  ```\n\n- `vscode` 環境以外で直接実行\n\n  ```bash\n  $ poetry run black .\n  $ poetry run isort .\n  $ poetry run flake8 .\n  $ poetry run mypy .\n  ```\n\n- `vscode` 環境以外で tox 経由で実行\n\n  ```bash\n  $ poetry run tox black\n  $ poetry run tox isort\n  $ poetry run tox flake8\n  $ poetry run tox mypy\n  ```\n\n- `pre-commit` を編集時に適用する\n\n  ```bash\n  $ poetry run pre-commit install\n  ```\n\n#### 補足\n\n- 実行パス `.` の由来や除外するディレクトリ\n  - black  \n    デフォルト動作は `.gitignore` を利用する.  \n    思想が垣間見れるのは, [ココ](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#next-steps) や [ココ](https://black.readthedocs.io/en/stable/usage_and_configuration/file_collection_and_discovery.html#gitignore).  \n  - isort  \n    [Compatibility with black](https://pycqa.github.io/isort/docs/configuration/black_compatibility.html) を謳っているので追従している.  \n  - flake8  \n    何もしない.  \n  - mypy  \n    デフォルト動作は隠しディレクトリを除外する.\n\n### 拡張 3 - API の文書化\n\nライブラリ色の強いプロジェクトでは API の文書化が必要なので, ここでは [pdoc](https://github.com/mitmproxy/pdoc) を利用した方法を示す.  \n\n[sphinx](https://github.com/sphinx-doc/sphinx) でも大差はない.  \n\n#### 準備\n\n`./pyproject.toml`\n\n```toml\n...\n[tool.poetry.group.docs]\noptional = true\n\n[tool.poetry.group.docs.dependencies]\npdoc = \"^12.3.0\"\ntomli = {version = \"^2.0.1\", python = \"\u003c3.11\"}\n...\n```\n\n`./docs/make.py`\n\n```python\nfrom pathlib import Path\n\nimport pdoc\n\ntry:\n    import tomllib\nexcept ModuleNotFoundError:\n    import tomli as tomllib\n\nif __name__ == '__main__':\n    here = Path(__file__).parent\n    project_root_dir = here / '..'\n\n    toml_path = project_root_dir / 'pyproject.toml'\n    with open(toml_path, mode='rb') as f:\n        toml_dict = tomllib.load(f)\n        project_name = toml_dict['tool']['poetry']['name']\n        module_name = toml_dict['tool']['poetry']['packages'][0]['include']\n        version = toml_dict['tool']['poetry']['version']\n\n    # Render docs\n    pdoc.render.configure(\n        docformat='google',\n        footer_text=f'{project_name} {version}',\n    )\n\n    pdoc.pdoc(\n        project_root_dir / 'src' / module_name,\n        output_directory=project_root_dir / 'docs/build',\n    )\n```\n\n宗教上の理由がある場合は下記を参照.  \n\n[...use numpydoc or Google docstrings?](https://pdoc.dev/docs/pdoc.html#use-numpydoc-or-google-docstrings)\n\n#### 手順\n\n- インストール\n\n  ```bash\n  $ poetry install --with docs\n  ```\n\n- 実行\n\n  ```bash\n  $ poetry run python ./docs/make.py\n  ```\n\n### 拡張 4 - 動的バージョニング\n\nVCS を基点に動的なバージョン管理を行う.  \n[poetry-dynamic-versioning](https://github.com/mtkennerly/poetry-dynamic-versioning) を利用する.  \n\n利用するのはデフォルト動作で, `poetry build` を実行している間だけ有効であることに注意.  \n\n```mermaid\nflowchart LR\n\n  id_1(start)\n  id_2(end)\n\n  subgraph sdist/wheel\n    subgraph targets\n      id_t1[pyproject.toml]\n      id_t2[__init__.py]\n      id_t3[__version__.py]\n      id_t3 .-o id_t2\n    end\n  end\n\n  subgraph versioning\n    id_v1[git tag v0.1.0]\n    subgraph build\n      id_b1[poetry build]\n    end\n    id_v1 --\u003e build\n  end\n\n  build .-\u003e sdist/wheel\n  id_1 ==\u003e versioning ==\u003e id_2\n```\n\n#### 準備\n\n[Installation](https://github.com/mtkennerly/poetry-dynamic-versioning#installation)  \n\n\"0.0.0\" の由来は\n\n\u003e Poetry's typical `version` setting is still required in `[tool.poetry]`, but you are encouraged to use `version = \"0.0.0\"` as a standard placeholder.\n\n`./pyproject.toml`\n\n```toml\n[tool.poetry]\n...\nversion = \"0.0.0\"\n...\n[build-system]\nrequires = [\"poetry-core\u003e=1.0.0\", \"poetry-dynamic-versioning\"]\nbuild-backend = \"poetry_dynamic_versioning.backend\"\n\n[tool.poetry-dynamic-versioning]\nenable = true\nvcs = \"git\"\nstyle = \"pep440\"\n\n[tool.poetry-dynamic-versioning.substitution]\nfolders = [\n  { path = \"src\" }\n]\n...\n```\n\n`./src/python_poetry_example/__version__.py`\n\n```python\n__version__ = '0.0.0'\n```\n\n`./src/python_poetry_example/__init__.py`\n\n```python\nfrom .__version__ import __version__\n```\n\n#### 手順\n\n- プラグインのインストール\n\n  ```bash\n  $ poetry self add \"poetry-dynamic-versioning[plugin]\"\n  ```\n\n- プラグインのアンインストール\n\n  ```bash\n  $ poetry self remove poetry-dynamic-versioning\n  ```\n\n- バージョニング\n\n  1. タグをつける\n\n     `v0.0.0` から `v0.1.0` にする場合\n\n     ```bash\n     $ git tag v0.1.0\n     ```\n\n  2. ビルドを実行する\n\n     ```bash\n     $ poetry build\n     ```\n\n  `./dist` 内に含まれる対象/関連ファイルのみ影響を受ける.  \n\n  よってリリース後の `python_poetry_example.__version__` などは意図した動作になる.  \n\n#### 連携\n\n- **拡張 3 - API の文書化** と連携する場合について\n\n  `poetry-dynamic-versioning` を手動で実行する方法も考えられるがとても煩雑になる.  \n\n  よって VCS からタグを取得する方向で済ませる.  \n\n  ```mermaid\n  flowchart LR\n\n    subgraph versioning\n      id_v1[git tag v0.1.0]\n    end\n\n    subgraph documentation\n      id_d1[poetry run python ./docs/make.py]\n    end\n\n    build[(./docs/build)]\n\n    versioning ==\u003e documentation\n    id_d1 .-\u003e build\n  ```\n\n  `./pyproject.toml`\n\n  ```toml\n  ...\n  [tool.poetry.group.docs]\n  optional = true\n\n  [tool.poetry.group.docs.dependencies]\n  pdoc = \"^12.3.0\"\n  tomli = {version = \"^2.0.1\", python = \"\u003c3.11\"}\n  GitPython = \"^3.1.29\"\n  ...\n  ```\n\n  `./docs/make.py`\n\n  ```python\n  import os\n  from pathlib import Path\n\n  import git\n  import pdoc\n\n  try:\n      import tomllib\n  except ModuleNotFoundError:\n      import tomli as tomllib\n\n  if __name__ == '__main__':\n      here = Path(__file__).parent\n      project_root_dir = here / '..'\n\n      toml_path = project_root_dir / 'pyproject.toml'\n      with open(toml_path, mode='rb') as f:\n          toml_dict = tomllib.load(f)\n          project_name = toml_dict['tool']['poetry']['name']\n          module_name = toml_dict['tool']['poetry']['packages'][0]['include']\n          version = toml_dict['tool']['poetry']['version']\n\n      if version == '0.0.0':\n          repo_dir = project_root_dir / '.git'\n          if os.path.isdir(repo_dir):\n              repo = git.Repo(repo_dir)\n              if repo.tags:\n                  tags = sorted(repo.tags, key=lambda t: t.commit.committed_datetime)\n                  latest_tag = tags[-1]\n                  version = str(latest_tag)\n                  if version.startswith('v'):\n                      version = version[1:]\n\n      # Render docs\n      pdoc.render.configure(\n          footer_text=f'{project_name} {version}',\n      )\n\n      pdoc.pdoc(\n          project_root_dir / 'src' / module_name,\n          output_directory=project_root_dir / 'docs/build',\n      )\n  ```\n\n### 拡張 5 - CI/CD\n\nここでは [GitHub Actions](https://docs.github.com/en/actions) を利用した例となる.  \n\nデプロイ先は\n\n- パッケージは [TestPyPI](https://test.pypi.org)  \n- API ドキュメントは [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site)\n\n```mermaid\nflowchart LR\n\n  event_1(on: push)\n  event_2(on: pull_request)\n  event_3(on: release)\n  event_4(on: workflow_dispatch)\n\n  subgraph test.yml\n  direction LR\n    id_t1[black\\ndry-run]\n    id_t2[isort\\ndry-run]\n    lint-pr\n    lint-local\n    mypy\n    test\n    id_t1 --\u003e id_t2\n    id_t2 --\u003e lint-local --\u003e mypy\n    id_t2 --\u003e lint-pr --\u003e mypy\n    mypy --\u003e test\n  end\n\n  subgraph testpypi.yml\n    testpypi_1[build]\n    --\u003e testpypi_2[deploy]\n  end\n\n  subgraph docs.yml\n    docs_1[build]\n    --\u003e docs_2[deploy]\n  end\n\n  event_1 ==\u003e test.yml\n  event_2 ==\u003e test.yml\n  event_3 ==\u003e testpypi.yml\n  testpypi_2 .-\u003e o1[(TestPyPI)]\n  testpypi.yml ====\u003e |on: workflow_call| docs.yml\n  event_4 ==\u003e docs.yml\n  docs_2 .-\u003e o2[(gh-pages)]\n```\n\n#### 準備\n\n`./github/python-version.txt`\n\n```python\n3.8\n```\n\n各ファイルで用いる, 開発上の現行バージョンを指定.  \n\n`./github/workflows/test.yml`\n\n```python\nname: Test\n\non: [ push, pull_request ]\n\npermissions:\n  contents: read\n\njobs:\n  black:\n    if: true\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-python@v4\n        with:\n          python-version-file: .github/python-version.txt\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          python -m pip install 'poetry\u003e=1.2'\n          poetry install --with dev --no-interaction\n      - name: Lint with black\n        run: |\n          poetry run tox -e black\n\n  isort:\n    if: true\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-python@v4\n        with:\n          python-version-file: .github/python-version.txt\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          python -m pip install 'poetry\u003e=1.2'\n          poetry install --with dev --no-interaction\n      - name: Lint with isort\n        run: |\n          poetry run tox -e isort\n\n  lint-pr:\n    if: github.event_name == 'pull_request'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: TrueBrain/actions-flake8@v2\n\n  lint-local:\n    if: github.event_name == 'push'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-python@v4\n        with:\n          python-version-file: .github/python-version.txt\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          python -m pip install 'poetry\u003e=1.2'\n          poetry install --with dev --no-interaction\n      - name: Lint with flake8\n        run: |\n          poetry run tox -e flake8\n\n  mypy:\n    if: true\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-python@v4\n        with:\n          python-version-file: .github/python-version.txt\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          python -m pip install 'poetry\u003e=1.2'\n          poetry install --with dev --no-interaction\n      - name: Lint with mypy\n        run: |\n          poetry run tox -e mypy\n\n  test:\n    runs-on: ubuntu-20.04\n    strategy:\n      fail-fast: false\n      matrix:\n        python-version: ['3.7', '3.8']\n    steps:\n    - uses: actions/checkout@v3\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v4\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        python -m pip install 'poetry\u003e=1.2'\n        poetry install --with dev --no-interaction\n    - name: Test with pytest\n      run: |\n        poetry run tox -e py\n```\n\n`test` は対象範囲が変わる都度, 設定を変更する必要がある.  \n\n`./github/workflows/testpypi.yml`\n\n```python\nname: Publish the package to TestPyPI\n\non:\n  workflow_dispatch:\n  release:\n    types: [published]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        fetch-depth: 0\n    - name: Set up Python\n      uses: actions/setup-python@v4\n      with:\n        python-version-file: .github/python-version.txt\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        python -m pip install 'poetry\u003e=1.2'\n        poetry self add 'poetry-dynamic-versioning[plugin]'\n    - name: Build\n      run: |\n        poetry build\n    - name: publish\n      env:\n        TESTPYPI_API_TOKEN: ${{ secrets.TESTPYPI_API_TOKEN }}\n      run: |\n        poetry config repositories.test-pypi https://test.pypi.org/legacy/\n        poetry config pypi-token.test-pypi $TESTPYPI_API_TOKEN\n        poetry publish -r test-pypi\n\n  docs:\n    if: true # true if documentation is needed.\n    needs: deploy\n    permissions:\n      contents: read\n      pages: write\n      id-token: write\n    uses: ./.github/workflows/docs.yml\n```\n\n`docs` は API の文書化が不要なら `false` に設定.  \n\n`./github/workflows/docs.yml`\n\n```python\n# Workflow for deploying static content to GitHub Pages\nname: Deploy static content to Pages\n\n# build the documentation whenever there are new commits on main\non:\n  workflow_dispatch:\n  workflow_call:\n\n# security: restrict permissions for CI jobs.\npermissions:\n  contents: read\n\njobs:\n  # Build the documentation and upload the static HTML files as an artifact.\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n      - uses: actions/setup-python@v4\n        with:\n          python-version-file: .github/python-version.txt\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          python -m pip install 'poetry\u003e=1.2'\n          poetry install --with docs --no-interaction\n      - name: Build\n        run: |\n          poetry run python ./docs/make.py\n      - uses: actions/upload-pages-artifact@v1\n        with:\n          path: docs/build/\n\n  # Deploy the artifact to GitHub pages.\n  # This is a separate job so that only actions/deploy-pages has the necessary permissions.\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n    permissions:\n      pages: write\n      id-token: write\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    steps:\n      - id: deployment\n        uses: actions/deploy-pages@v1\n```\n\n#### 手順\n\n- `on: push`\n\n  ```bash\n  $ git push\n  ```\n\n- `on: pull_request`\n\n  PR\n\n- `on: release`\n\n  ```bash\n  $ git tag v0.1.0\n  $ git push origin v0.1.0\n  ```\n\n  `v0.1.0` をリリース  \n\n  [Managing releases in a repository](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository)\n\n- `on: workflow_dispatch`\n\n  デバッグ用に手動でイベントをキック.  \n\n#### 補足\n\n- `on: workflow_dispatch`\n\n  `docs.yml` を手動で実行すると `main` ブランチ(default)が渡される.  \n\n- `on: workflow_call`\n\n  `testpypi.yml` 経由で実行されると `on: release` で `v0.1.0` ブランチが渡される.  \n\n  gh-pages の[ブランチ保護ルール](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule)に後者が利用できるように追記が必要.  \n  例えば `v*` などとしておく.  \n\n#### キャッシュ\n\n`Poetry` や依存関係のキャッシュを行うことでアクションの実行時間を低減する.  \n分量が多いため [issue](https://github.com/Hasenpfote/python-poetry-example/issues/2) で扱う.  \n\n## 参考\n\n- [Simple Python Version Management: pyenv](https://github.com/pyenv/pyenv)\n- [pyenv for Windows](https://github.com/pyenv-win/pyenv-win)\n- [Poetry](https://python-poetry.org)\n- [tox](https://github.com/tox-dev/tox)\n- [pytest](https://github.com/pytest-dev/pytest)\n- [pytest-mock](https://github.com/pytest-dev/pytest-mock)\n- [pytest-cov](https://github.com/pytest-dev/pytest-cov)\n- [black](https://github.com/psf/black)\n- [isort](https://github.com/PyCQA/isort)\n- [flake8p](https://github.com/john-hen/Flake8-pyproject)\n- [mypy](https://github.com/python/mypy)\n- [pre-commit](https://github.com/pre-commit/pre-commit)\n- [pdoc](https://github.com/mitmproxy/pdoc)\n- [poetry-dynamic-versioning](https://github.com/mtkennerly/poetry-dynamic-versioning)\n- [GitHub Actions](https://docs.github.com/en/actions)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhasenpfote%2Fpython-poetry-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhasenpfote%2Fpython-poetry-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhasenpfote%2Fpython-poetry-example/lists"}