Coder Social home page Coder Social logo

hasenpfote / python-poetry-example Goto Github PK

View Code? Open in Web Editor NEW
3.0 1.0 0.0 392 KB

A simple example of how to use pyenv + poetry + tox + pytest.

License: Creative Commons Zero v1.0 Universal

Python 100.00%
black flake8 isort mypy pdoc poetry pytest python python3 tox poetry-dynamic-versioning github-actions

python-poetry-example's Introduction

Lint Test codecov

pyenv + poetry + tox + pytest 環境構築例

要約

python で pyenv + poetry + tox + pytest に optional で

  • pytest の拡張

    pytest-mock + pytest-cov

  • linter/formatter

    black + isort + flake8 + mypy

  • API の文書化

    pdoc

  • 動的バージョニング

    poetry-dynamic-versioning

  • CI/CD

    gh-actions

特に注釈がなければ, 各種情報の背景は 2022年12月あたり.
本例では python 3.7 環境でエラーが発生するようにしている.

前提条件

  • OS は Linux または Windows を想定
  • 任意の python 環境は pyenv で構築
    Windows の場合は pyenv-win で代替
  • 仮想環境は poetry で構築
    グループ機能が便利なのでバージョンは 1.2 以上.
  • 本例では python 3.7 / 3.8 を想定
    3.8 は現行バージョンで, 3.7 は後方互換の最小バージョンとした開発を行っている状況など.
    ※ 3.7 未満は依存関係がカオスなので全力回避
  • テストは tox + pytest
  • 面倒なことはしない
flowchart TD

  subgraph system
    id11(python >= 3.7)
    id12(poetry >= 1.2)

    subgraph pyenv
      id21(python = 3.7)
      id22(python = 3.8)
    end

    subgraph project
      id31(.venv)
      subgraph .tox
        id41(py37)
        id42(py38)
      end
    end
  end

  id12 .-> id11
  id12 --> |poetry run python| id31
  id12 --> |poetry run tox| .tox
  id41 .-> id21
  id31 & id42 .-> id22
Loading

基本

準備

pyenv

インストール
for Linux
for Windows

選択肢が多いので Installation からお好みで.

Chocolatey 以外は環境変数の設定が必要になるので詳細を参考に.

python

インストール

候補を確認

$ pyenv install -l

とある環境での最新は 3.7.9 / 3.8.10 だったのでインストール

$ pyenv install 3.7.9
$ pyenv install 3.8.10

インストール済みバージョンの確認

$ pyenv versions

poetry

インストール

[2022/12/07] この時点での最新は 1.2.2 で ドキュメント より

System requirements

Poetry requires Python 3.7+. It is multi-platform and the goal is to make it work equally well on Linux, macOS and Windows.

予めシステムに python 3.7 以上をインストール要.

poetry を動作させるために必要であって, プロジェクトへの制約ではない.

Installation を参考にお好みで.

設定
プロジェクト下に仮想環境を構築

設定を確認

$ poetry config --list

virtualenvs.in-project = null なら下記を一度だけ実行

$ poetry config virtualenvs.in-project true

もしもプロジェクト毎に有効にしたいのであれば

$ poetry config virtualenvs.in-project true --local

好みの問題ではあるが, 本例では全体設定としておく.

手順

1. 新規プロジェクトを作成

任意の場所で

$ poetry new --src python-poetry-example

ディレクトリ

python-poetry-example
├─ pyproject.toml
├─ README.md
├─ src
│  └─ python_poetry_example
│      └─ __init__.py
└─tests
   └─ __init__.py

2. 使用する python を指定

プロジェクトルートに移動

$ cd python-poetry-example

以降は特に注釈がない限りはプロジェクトルートでコマンドを実行している

使用する python を指定

$ pyenv local 3.8.10 3.7.9

有効になっているバージョンを確認

$ python -V
Python 3.8.10

本例では 3.8.10 を現行バージョンと想定している.

特記事項

動作確認を行った環境では,

  • pyenv local 3.8.10 3.7.9 なら python -V3.8.10
  • pyenv local 3.7.9 3.8.10 なら python -V3.7.9

最初に指定をしたバージョンが有効になる.

仮想環境の構築にも影響があるので要注意.

ディレクトリ

python-poetry-example
├─ .python-version (New!)
├─ pyproject.toml
├─ README.md
├─ src
│  └─ python_poetry_example
│      └─ __init__.py
└─tests
   └─ __init__.py

3. pyproject.toml でバージョンを指定

以下のように指定をしておく.

[tool.poetry.dependencies]
python = "^3.7"

意味は 3.7 <= python version < 4.0 と若干緩め.

依存するモジュールによっては制限を厳しくする必要もある.

4. 仮想環境を構築

3.8.10 ベースで構築.

$ poetry env use python

確認は

$ poetry env info

ディレクトリ

python-poetry-example
├─ .python-version
├─ pyproject.toml
├─ README.md
├─ .venv (New!)
│  └─ ...
├─ src
│  └─ python_poetry_example
│      └─ __init__.py
└─tests
   └─ __init__.py

5. コードを配置

適当な機能をまとめた utils.py と, そのテストコードを配置する.

ディレクトリ

python-poetry-example
├─ .python-version
├─ pyproject.toml
├─ README.md
├─ .venv
│  └─ ...
├─ src
│  └─ python_poetry_example
│      ├─ __init__.py
│      └─ utils.py (New!)
└─tests
   ├─ __init__.py
   └─ test_utils.py (New!)

6. テスト環境の準備

poetry add でも構わないが, 本例では諸事情から pyproject.toml に直接記述をする.

[tool.poetry.group.dev]
optional = true

[tool.poetry.group.dev.dependencies]
tox = "^3.27.1"

[tool.poetry.group.test]
optional = true

[tool.poetry.group.test.dependencies]
pytest = "^7.2.0"

[tool.pytest.ini_options]
addopts = [
  "--import-mode=importlib",
]
pythonpath = "src"
  • [2022/12/09] tox >= 4.0.0 で仮想環境を認識できない不具合を確認
    Linux Mint 21 / Windows10 共に, アクティブになっている1つの環境しか認識されない.
    本例では 3.7 が認識されずスキップされる.
    issue では Tox 4 breaks in CI/CD pipelines where user does not exist #2702 が似ている.
    4系で環境変数周りが大幅に変更された影響と思われるので様子見が必要.

インストール

$ poetry install --with dev

インストールでエラーがでるようならロックファイルの更新で対応

$ poetry update

確認

$ poetry show

7. tox.ini の配置

[tox]
envlist =
    py{37,38}
skipsdist = true
isolated_build = true

[testenv]
allowlist_externals =
    poetry
commands_pre =
    poetry install --with test -v
commands =
    poetry run pytest -v
  • py37 / py38pyenv local で指定されたバージョンを参照する tox 側のキーワード.
    もしも 3.11.x が必要なら py311 となる.

ディレクトリ

python-poetry-example
├─ .python-version
├─ poetry.lock
├─ pyproject.toml
├─ README.md
├─ tox.ini (New!)
├─ .venv
│  └─ ...
├─ src
│  └─ python_poetry_example
│      ├─ __init__.py
│      └─ utils.py
└─tests
   ├─ __init__.py
   └─ test_utils.py

8. テストを実行

$ poetry run tox

本例では python 3.7 環境でエラーが発生するようにしている.

ディレクトリ

python-poetry-example
├─ .python-version
├─ poetry.lock
├─ pyproject.toml
├─ README.md
├─ tox.ini
├─ .tox (New!)
│  └─ ...
├─ .venv
│  └─ ...
├─ src
│  └─ python_poetry_example
│      ├─ __pycache__
│      │  └─ ...
│      ├─ __init__.py
│      └─ utils.py
└─tests
   ├─ __pycache__
   │  └─ ...
   ├─ __init__.py
   └─ test_utils.py

各種ファイル

pyproject.toml

[tool.poetry]
name = "python-poetry-example"
version = "0.1.0"
description = ""
authors = ["name <email>"]
readme = "README.md"
packages = [{include = "python_poetry_example", from = "src"}]

[tool.poetry.dependencies]
python = "^3.7"

[tool.poetry.group.dev]
optional = true

[tool.poetry.group.dev.dependencies]
tox = "^3.27.1"

[tool.poetry.group.test]
optional = true

[tool.poetry.group.test.dependencies]
pytest = "^7.2.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.pytest.ini_options]
addopts = [
  "--import-mode=importlib",
]
pythonpath = "src"

tox.ini

[tox]
envlist =
    py{37,38}
skipsdist = true
isolated_build = true

[testenv]
allowlist_externals =
    poetry
commands_pre =
    poetry install --with test -v
commands =
    poetry run pytest -v

.gitignore

/.tox
/.venv
/.python-version
/poetry.lock

__pycache__/
  • .python-version は環境次第でマイナーバージョンに大きな差があるため.
  • poetry.lock はお好みで.
  • 最終的にはPython.gitignoreを元に調整.

拡張

基本で目的は果たしているものの, 心許ないので一手間を加える場合もある.

好みもあるので以降は optional となる.

拡張 1 - pytest の拡張

準備

./pyproject.toml

...
[tool.poetry.group.test]
optional = true

[tool.poetry.group.test.dependencies]
pytest = "^7.2.0"
pytest-mock = "^3.10.0"
pytest-cov = "^4.0.0"
...

./tox.ini

[tox]
envlist =
    clean
    py{37,38}
    report
skipsdist = true
isolated_build = true

[testenv]
allowlist_externals =
    poetry
commands_pre =
    poetry install --with test -v
commands =
    poetry run pytest --cov --cov-append --cov-report=term-missing -v

[testenv:clean]
deps = coverage
skip_install = true
commands_pre = # nop
commands =
    poetry run coverage erase

[testenv:report]
deps = coverage
skip_install = true
commands_pre = # nop
commands =
    poetry run coverage report -m
    poetry run coverage html

...

手順

  • インストール

    $ poetry install --with test
  • 実行

    $ poetry run tox -r -e clean,py37,py38,report

    並列で行う場合は,

    ./tox.ini

    ...
    [testenv]
    ...
    depends =
        py{37,38}: clean
        report: py{37,38}
    # Required if -p is enabled on Windows.
    # https://github.com/tox-dev/tox/pull/2641
    setenv =
        PYTHONIOENCODING=utf-8
    ...

    を追記し

    $ poetry run tox -r -e clean,py37,py38,report -p all

    順序は depends で解決するので適当でもよい.

拡張 2 - linter/formatter

構成

solution
  • black

    コードの整形に利用.

  • isort

    import の並び替えに利用.

  • flake8p

    構文チェックに利用.

    poetry 経由で利用するのでオリジナルは使えない.

    [2022/12/11] pflake8max-line-length が反映されなかったので選択肢を変更した.

    79 / 88 / 120 宗派のため.

  • mypy

    型ヒントに利用.

    取り敢えず組み込んでおき, いつでも optional から変更できるように配慮.

flowchart LR

  subgraph solution
    subgraph formatter
      black
      isort
      black .- isort
    end
    subgraph linter
      flake8
      mypy
    end
  end
Loading
環境

vscode 環境とそれ以外を考慮しつつ, 最終的にリポジトリへのコミットで同一性を担保する方向で.

  • solution の設定は pyproject.toml で一元管理
  • vscodepoetry で作成した仮想環境を利用するように設定
  • tox に確認用の環境を仕込んでおく
  • pre-commit でコミットをフックする
flowchart LR

  subgraph tox
    id_tox(solution)
  end

  subgraph poetry
    id_poetry(solution)
  end

  subgraph vscode
    id_vscode(solution)
  end

  subgraph pre-commit
    id_pre-commit(solution)
  end

  tox .-o poetry

  id_terminal(terminal)
  id_py(*.py)
  id_repo[(repo)]
  id_git(git)

  id_terminal .-> id_git
  vscode .-> id_git

  id_terminal --> |poetry run tox solution| id_tox --> |dry-run| id_py
  id_terminal ---> |poetry run solution| id_poetry --> |run| id_py
  id_vscode --> id_py

  id_py .-o id_repo
  id_pre-commit --> id_py
  id_git .-> |git commit| pre-commit .-> id_repo
Loading

準備

./pyproject.toml

...
[tool.poetry.group.dev.dependencies]
black = "^22.10.0"
isort = "^5.10.1"
Flake8-pyproject = "^1.2.2"
mypy = "^0.991"
pre-commit = "^2.20.0"
tox = "^3.27.1"

...
[tool.black]
line-length = 88
skip-string-normalization = true

[tool.isort]
profile = "black"
line_length = 88
multi_line_output = 3
include_trailing_comma = true

[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "E266", "W503",]
max-complexity = 10
extend-exclude = [".venv", "dist", ".github",]

[tool.mypy]
ignore_errors = true
#disallow_untyped_defs = true
#ignore_missing_imports = true
#no_implicit_optional = true
#show_error_context = true
#show_column_numbers = true
#warn_return_any = true
#warn_unused_ignores = true
#warn_redundant_casts = true
exclude = ["dist/",]

...

mypyignore_errors = true で実質的に無効にしているので注意.

./tox.ini

[tox]
envlist =
    py{37,38}
    black
    isort
    flake8
    mypy
skipsdist = true
isolated_build = true

...

[testenv:black]
deps = black
commands_pre = # nop
commands =
    poetry run black . --check --diff --color

[testenv:isort]
deps =
    isort
    colorama
commands_pre = # nop
commands =
    poetry run isort . --check --diff --color

[testenv:flake8]
deps = Flake8-pyproject
commands_pre = # nop
commands =
    poetry run flake8p .

[testenv:mypy]
deps = mypy
commands_pre = # nop
commands =
    poetry run mypy .

./.pre-commit-config.yaml

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: check-added-large-files
      - id: check-toml
      - id: check-yaml
      - id: end-of-file-fixer
      - id: mixed-line-ending
        args: [--fix=lf]
      - id: trailing-whitespace
        args: [--markdown-linebreak-ext=md]
  - repo: https://github.com/psf/black
    rev: 22.12.0
    hooks:
      - id: black
        language_version: python3
  - repo: https://github.com/pycqa/isort
    rev: 5.10.1
    hooks:
      - id: isort
  - repo: https://github.com/pycqa/flake8
    rev: 6.0.0
    hooks:
      - id: flake8
        additional_dependencies: [Flake8-pyproject]
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v0.991
    hooks:
      - id: mypy

手順

  • インストール

    $ poetry install --with dev
  • vscode 環境以外で直接実行

    $ poetry run black .
    $ poetry run isort .
    $ poetry run flake8 .
    $ poetry run mypy .
  • vscode 環境以外で tox 経由で実行

    $ poetry run tox black
    $ poetry run tox isort
    $ poetry run tox flake8
    $ poetry run tox mypy
  • pre-commit を編集時に適用する

    $ poetry run pre-commit install

補足

  • 実行パス . の由来や除外するディレクトリ
    • black
      デフォルト動作は .gitignore を利用する.
      **が垣間見れるのは, ココココ.
    • isort
      Compatibility with black を謳っているので追従している.
    • flake8
      何もしない.
    • mypy
      デフォルト動作は隠しディレクトリを除外する.

拡張 3 - API の文書化

ライブラリ色の強いプロジェクトでは API の文書化が必要なので, ここでは pdoc を利用した方法を示す.

sphinx でも大差はない.

準備

./pyproject.toml

...
[tool.poetry.group.docs]
optional = true

[tool.poetry.group.docs.dependencies]
pdoc = "^12.3.0"
tomli = {version = "^2.0.1", python = "<3.11"}
...

./docs/make.py

from pathlib import Path

import pdoc

try:
    import tomllib
except ModuleNotFoundError:
    import tomli as tomllib

if __name__ == '__main__':
    here = Path(__file__).parent
    project_root_dir = here / '..'

    toml_path = project_root_dir / 'pyproject.toml'
    with open(toml_path, mode='rb') as f:
        toml_dict = tomllib.load(f)
        project_name = toml_dict['tool']['poetry']['name']
        module_name = toml_dict['tool']['poetry']['packages'][0]['include']
        version = toml_dict['tool']['poetry']['version']

    # Render docs
    pdoc.render.configure(
        docformat='google',
        footer_text=f'{project_name} {version}',
    )

    pdoc.pdoc(
        project_root_dir / 'src' / module_name,
        output_directory=project_root_dir / 'docs/build',
    )

宗教上の理由がある場合は下記を参照.

...use numpydoc or Google docstrings?

手順

  • インストール

    $ poetry install --with docs
  • 実行

    $ poetry run python ./docs/make.py

拡張 4 - 動的バージョニング

VCS を基点に動的なバージョン管理を行う.
poetry-dynamic-versioning を利用する.

利用するのはデフォルト動作で, poetry build を実行している間だけ有効であることに注意.

flowchart LR

  id_1(start)
  id_2(end)

  subgraph sdist/wheel
    subgraph targets
      id_t1[pyproject.toml]
      id_t2[__init__.py]
      id_t3[__version__.py]
      id_t3 .-o id_t2
    end
  end

  subgraph versioning
    id_v1[git tag v0.1.0]
    subgraph build
      id_b1[poetry build]
    end
    id_v1 --> build
  end

  build .-> sdist/wheel
  id_1 ==> versioning ==> id_2
Loading

準備

Installation

"0.0.0" の由来は

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.

./pyproject.toml

[tool.poetry]
...
version = "0.0.0"
...
[build-system]
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
build-backend = "poetry_dynamic_versioning.backend"

[tool.poetry-dynamic-versioning]
enable = true
vcs = "git"
style = "pep440"

[tool.poetry-dynamic-versioning.substitution]
folders = [
  { path = "src" }
]
...

./src/python_poetry_example/__version__.py

__version__ = '0.0.0'

./src/python_poetry_example/__init__.py

from .__version__ import __version__

手順

  • プラグインのインストール

    $ poetry self add "poetry-dynamic-versioning[plugin]"
  • プラグインのアンインストール

    $ poetry self remove poetry-dynamic-versioning
  • バージョニング

    1. タグをつける

      v0.0.0 から v0.1.0 にする場合

      $ git tag v0.1.0
    2. ビルドを実行する

      $ poetry build

    ./dist 内に含まれる対象/関連ファイルのみ影響を受ける.

    よってリリース後の python_poetry_example.__version__ などは意図した動作になる.

連携

  • 拡張 3 - API の文書化 と連携する場合について

    poetry-dynamic-versioning を手動で実行する方法も考えられるがとても煩雑になる.

    よって VCS からタグを取得する方向で済ませる.

    flowchart LR
    
      subgraph versioning
        id_v1[git tag v0.1.0]
      end
    
      subgraph documentation
        id_d1[poetry run python ./docs/make.py]
      end
    
      build[(./docs/build)]
    
      versioning ==> documentation
      id_d1 .-> build
    
    Loading

    ./pyproject.toml

    ...
    [tool.poetry.group.docs]
    optional = true
    
    [tool.poetry.group.docs.dependencies]
    pdoc = "^12.3.0"
    tomli = {version = "^2.0.1", python = "<3.11"}
    GitPython = "^3.1.29"
    ...

    ./docs/make.py

    import os
    from pathlib import Path
    
    import git
    import pdoc
    
    try:
        import tomllib
    except ModuleNotFoundError:
        import tomli as tomllib
    
    if __name__ == '__main__':
        here = Path(__file__).parent
        project_root_dir = here / '..'
    
        toml_path = project_root_dir / 'pyproject.toml'
        with open(toml_path, mode='rb') as f:
            toml_dict = tomllib.load(f)
            project_name = toml_dict['tool']['poetry']['name']
            module_name = toml_dict['tool']['poetry']['packages'][0]['include']
            version = toml_dict['tool']['poetry']['version']
    
        if version == '0.0.0':
            repo_dir = project_root_dir / '.git'
            if os.path.isdir(repo_dir):
                repo = git.Repo(repo_dir)
                if repo.tags:
                    tags = sorted(repo.tags, key=lambda t: t.commit.committed_datetime)
                    latest_tag = tags[-1]
                    version = str(latest_tag)
                    if version.startswith('v'):
                        version = version[1:]
    
        # Render docs
        pdoc.render.configure(
            footer_text=f'{project_name} {version}',
        )
    
        pdoc.pdoc(
            project_root_dir / 'src' / module_name,
            output_directory=project_root_dir / 'docs/build',
        )

拡張 5 - CI/CD

ここでは GitHub Actions を利用した例となる.

デプロイ先は

flowchart LR

  event_1(on: push)
  event_2(on: pull_request)
  event_3(on: release)
  event_4(on: workflow_dispatch)

  subgraph test.yml
  direction LR
    id_t1[black\ndry-run]
    id_t2[isort\ndry-run]
    lint-pr
    lint-local
    mypy
    test
    id_t1 --> id_t2
    id_t2 --> lint-local --> mypy
    id_t2 --> lint-pr --> mypy
    mypy --> test
  end

  subgraph testpypi.yml
    testpypi_1[build]
    --> testpypi_2[deploy]
  end

  subgraph docs.yml
    docs_1[build]
    --> docs_2[deploy]
  end

  event_1 ==> test.yml
  event_2 ==> test.yml
  event_3 ==> testpypi.yml
  testpypi_2 .-> o1[(TestPyPI)]
  testpypi.yml ====> |on: workflow_call| docs.yml
  event_4 ==> docs.yml
  docs_2 .-> o2[(gh-pages)]
Loading

準備

./github/python-version.txt

3.8

各ファイルで用いる, 開発上の現行バージョンを指定.

./github/workflows/test.yml

name: Test

on: [ push, pull_request ]

permissions:
  contents: read

jobs:
  black:
    if: true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with black
        run: |
          poetry run tox -e black

  isort:
    if: true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with isort
        run: |
          poetry run tox -e isort

  lint-pr:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: TrueBrain/actions-flake8@v2

  lint-local:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with flake8
        run: |
          poetry run tox -e flake8

  mypy:
    if: true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with mypy
        run: |
          poetry run tox -e mypy

  test:
    runs-on: ubuntu-20.04
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.7', '3.8']
    steps:
    - uses: actions/checkout@v3
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        python -m pip install 'poetry>=1.2'
        poetry install --with dev --no-interaction
    - name: Test with pytest
      run: |
        poetry run tox -e py

test は対象範囲が変わる都度, 設定を変更する必要がある.

./github/workflows/testpypi.yml

name: Publish the package to TestPyPI

on:
  workflow_dispatch:
  release:
    types: [published]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        fetch-depth: 0
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version-file: .github/python-version.txt
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        python -m pip install 'poetry>=1.2'
        poetry self add 'poetry-dynamic-versioning[plugin]'
    - name: Build
      run: |
        poetry build
    - name: publish
      env:
        TESTPYPI_API_TOKEN: ${{ secrets.TESTPYPI_API_TOKEN }}
      run: |
        poetry config repositories.test-pypi https://test.pypi.org/legacy/
        poetry config pypi-token.test-pypi $TESTPYPI_API_TOKEN
        poetry publish -r test-pypi

  docs:
    if: true # true if documentation is needed.
    needs: deploy
    permissions:
      contents: read
      pages: write
      id-token: write
    uses: ./.github/workflows/docs.yml

docs は API の文書化が不要なら false に設定.

./github/workflows/docs.yml

# Workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages

# build the documentation whenever there are new commits on main
on:
  workflow_dispatch:
  workflow_call:

# security: restrict permissions for CI jobs.
permissions:
  contents: read

jobs:
  # Build the documentation and upload the static HTML files as an artifact.
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with docs --no-interaction
      - name: Build
        run: |
          poetry run python ./docs/make.py
      - uses: actions/upload-pages-artifact@v1
        with:
          path: docs/build/

  # Deploy the artifact to GitHub pages.
  # This is a separate job so that only actions/deploy-pages has the necessary permissions.
  deploy:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v1

手順

  • on: push

    $ git push
  • on: pull_request

    PR

  • on: release

    $ git tag v0.1.0
    $ git push origin v0.1.0

    v0.1.0 をリリース

    Managing releases in a repository

  • on: workflow_dispatch

    デバッグ用に手動でイベントをキック.

補足

  • on: workflow_dispatch

    docs.yml を手動で実行すると main ブランチ(default)が渡される.

  • on: workflow_call

    testpypi.yml 経由で実行されると on: releasev0.1.0 ブランチが渡される.

    gh-pages のブランチ保護ルールに後者が利用できるように追記が必要.
    例えば v* などとしておく.

キャッシュ

Poetry や依存関係のキャッシュを行うことでアクションの実行時間を低減する.
分量が多いため issue で扱う.

参考

python-poetry-example's People

Contributors

dependabot[bot] avatar hasenpfote avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

python-poetry-example's Issues

Considerations for Dynamic Versioning

目的

バージョン変数を動的に書き換える.
具体的には git tag を利用する.

対象

書き換える対象は少なくとも

  • pyproject.tomlversion
  • __init_.py または __version__.py__version__

方法 - poetry-dynamic-versioning

準備

Installation

pyproject.toml

[tool.poetry]
...
version = "0.0.0"
...
[build-system]
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
build-backend = "poetry_dynamic_versioning.backend"

[tool.poetry-dynamic-versioning]
enable = true # If manual, false.
vcs = "git"
style = "pep440"

[tool.poetry-dynamic-versioning.substitution]
folders = [
  { path = "src" }
]
...

__version__.py

__version__ = '0.0.0'

__init__.py

from .__version__ import __version__

概要

  • 自動で書き換える場合
    vcs で管理されるファイルのバージョンは変更されない.
    厳密には一時的に変更されるが元に戻される.

    $ git tag v0.1.0
    $ poetry build

    dist 内の pyproject.toml と指定ファイルのバージョンが 0.1.0 に置き換えられる.
    元は変化しないので初期値が 0.0.0 ならそのままを維持する.
    ドキュメント生成を考慮しなければ最も楽な方法.

  • 手動で書き換える場合 - tool.poetry-dynamic-versioning.enable = false
    vcs で管理されるファイルのバージョンは変更される.

    $ git tag v0.1.0
    $ poetry dynamic-versioning

    pyproject.toml と指定ファイルのバージョンが 0.1.0 に置き換えられる.
    置き換えられたファイルをコミットした後に git tag を振り直さなけば 1 つずれるので注意.

    ドキュメント生成を poetry build 以前に済ませる場合の選択肢の 1 つ.
    sphinx なら conf.py から __version__ を参照できる.

    • dist 内にドキュメントを含める場合など
    flowchart LR
    
      id0(start)
      id1(end)
    
      subgraph versioning
        id_v1(git tag v0.1.0)
        --> id_v2(poetry dynamic-versioning)
        --> id_v3(git add ...)
        --> id_v4(git commit ...)
        subgraph re-tagging
        id_v5(git tag -d v0.1.0)
        --> id_v6(git tag v0.1.0)
        end
        id_v4 --> re-tagging
      end
    
      subgraph documentation
        id_d1(poetry run sphinx-build ...)
      end
    
      subgraph build
        id_b1(poetry build)
      end
    
      subgraph publish
        id_p1(poetry publish ...)
      end
    
      subgraph targets
        id_t1(pyproject.toml)
        id_t2(__init__.py)
        id_t3(__version__.py)
        id_t3 .-o id_t2
      end
    
      id0 ==> id_v1
      versioning ==> documentation ==> build ==> publish ==> id1
    
      id_v2 .-> |v0.0.0 to v0.1.0| targets
    
    Loading
    • dist 内にドキュメントを含めない場合など
    flowchart LR
    
      id0(start)
      id1(end)
    
      subgraph versioning
        id_v1(git tag v0.1.0)
        --> id_v2(poetry dynamic-versioning)
        --> id_v3(git add ...)
        --> id_v4(git commit ...)
        subgraph re-tagging
        id_v5(git tag -d v0.1.0)
        --> id_v6(git tag v0.1.0)
        end
        id_v4 --> re-tagging
      end
    
      subgraph documentation
        id_d1(poetry run sphinx-build ...)
      end
    
      subgraph build
        id_b1(poetry build)
      end
    
      subgraph publish
        id_p1(poetry publish ...)
      end
    
      subgraph targets
        id_t1(pyproject.toml)
        id_t2(__init__.py)
        id_t3(__version__.py)
        id_t3 .-o id_t2
      end
    
      id0 ==> id_v1
      versioning ==> build ==> publish ==> id1
      versioning ==> documentation ==> id1
    
      id_v2 .-> |v0.0.0 to v0.1.0| targets
    
    Loading

課題

  1. ドキュメント生成を考慮する場合の作法が煩わしい

  2. poetry-dynamic-versioning がファイルを書き換える際に, ファイルの改行コードではなく OS を尊重する.
    [2022/12/16] バグだったようなので修正して頂き v0.21.2 で解消.
    Windows 環境下では変換の度に LF が CRLF に置き換わるが, v0.21.1 時点では簡便な回避方法がない.
    - 自動の場合
    poetry build 時なので dist 内のファイルに CRLF が混じる
    本例なら pyproject.toml__version__.py
    - 手動の場合
    ファイル毎に手動で修正するか, pre-commit で自動化してもコミットが1度は失敗するので手間が発生する.

    本体の poetry version コマンドならば pyproject.tomltool.poetry.version を改行コードを維持したまま変更する.

Considerations for caching Poetry in GHA

GitHub ActionsPoetry 自身のキャッシュを行うための考察.

動機

  1. 時間
    GHA で Lint や Test の度に Poetry を用意する必要がある.
    準備をするのにそれなりの時間を要するため, Poetry 自身をキャッシュできるか考察をする.
    snok/install-poetry@v1 で実現できなかったことにも起因する.
  2. 制限
    根本的には Included storage and minutes

対象

  • OS は Linux / Windows / macOS
  • 特に指定がなければ Poetry (1.3.1)
  • actions/cache@v3

話題

外部の問題

  • Poetry (1.3.1) でWindows に POETRY_HOME を指定すると動作が怪しい
  • actions/cache@v3 の Windows におけるシンボリックリンク復元動作が怪しい

成果物

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.