7rikazhexde’s tech log

技術的な興味関心、備忘録、アウトプットなどを書いています。

既存プロジェクトをPoetryで管理する方法

以前、以下の記事でMacでPoetryをインストールする方法を紹介しました。

7rikazhexde-techlog.hatenablog.com

今回はGitHubで管理する既存プロジェクトをPoetryで管理する方法を紹介します。

対象プロジェクトについて

以下リポジトリで管理するプロジェクトを対象として以下の2つの方法について確認しました。

  1. 既存のプロジェクトにpoetry環境を構築するpoetry initの例
  2. poetry newでひな形から新規作成する例

github.com

1. 既存のプロジェクトにpoetry環境を構築するpoetry initの例

これまで上記リポジトリでは以下のパッケージをrequirements.txtで管理していましたが、

  • PySimpleGUI
  • pandas
  • requests

Poetryを使用してプロジェクトの依存関係を管理するpyproject.tomlファイルを作成していきます。

既存のプロジェクトをgit cloneしてpoetry initを実行する

新規のプロジェクトを作るのではなく、既存のプロジェクトにpyproject.tomlファイルを作成するためにpoetry initコマンドを実行して初期化処理を実行します。

% git clone https://github.com/7rikazhexde/dlSubscanStakingRewardsHistory.git

dlSubscanStakingRewardsHistory % poetry init
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.

This command will guide you through creating your pyproject.toml config.

Package name [dlsubscanstakingrewardshistory]:  
Version [0.1.0]:  
Description []:  GUI application to save Reward&Slash data as csv file using PySimpleGUI and Subscan API
Author [[[user.name(git config)] <[user.email(git config)>, n to skip]:  
License []:  MIT
Compatible Python versions [^3.10]:  

main dependencies向けのパッケージ追加

次にプロジェクトで使用するパッケージを追加します。
main dependenciesで入力した情報は[tool.poetry.dependencies]の項目に追加されます。
パッケージを入力すると候補となるパッケージを検索してくれるので、[数字]の記載の番号を入力すると追加されます。

Would you like to define your main dependencies interactively? (yes/no) [yes] yes
You can specify a package in the following forms:
  - A single name (requests): this will search for matches on PyPI
  - A name and a constraint (requests@^2.23.0)
  - A git url (git+https://github.com/python-poetry/poetry.git)
  - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop)
  - A file path (../my-package/my-package.whl)
  - A directory (../my-package/)
  - A url (https://example.com/packages/my-package-0.1.0.tar.gz)

Package to add or search for (leave blank to skip): pysimplegui
Found 20 packages matching pysimplegui
Showing the first 10 matches

Enter package # to add, or the complete package name if it is not listed []:
 [ 0] PySimpleGUI
 [ 1] PySimpleGUI27
 [ 2] PySimpleGUI-Events
 [ 3] pysimplegui-exemaker
 [ 4] PySimpleGUI-chess
 [ 5] pysimplegui-howdoi
 [ 6] PySimpleGuy
 [ 7] psgresizer
 [ 8] psgdemos
 [ 9] psgcompiler
 [10] 
 > 0
Enter the version constraint to require (or leave blank to use the latest version): 
Using version ^4.60.4 for PySimpleGUI

Add a package (leave blank to skip): pandas
Found 20 packages matching pandas
Showing the first 10 matches

Enter package # to add, or the complete package name if it is not listed []:
 [ 0] pandas
 [ 1] pandas2
 [ 2] Pandas3
 [ 3] Pint-Pandas
 [ 4] pandas-ui
 [ 5] clustergrammer-pandas2
 [ 6] pandas-jsonlines
 [ 7] pandas-or
 [ 8] h3pandas
 [ 9] pandas-utility
 [10] 
 > 0
Enter the version constraint to require (or leave blank to use the latest version): 
Using version ^1.5.1 for pandas

Add a package (leave blank to skip): requests
Found 20 packages matching requests
Showing the first 10 matches

Enter package # to add, or the complete package name if it is not listed []:
 [ 0] requests
 [ 1] requests5
 [ 2] requests3
 [ 3] requests2
 [ 4] requests_spider
 [ 5] requests-kerberos
 [ 6] fed-requests
 [ 7] requests_gpgauthlib
 [ 8] jupyter-requests
 [ 9] ks33requests
 [10] 
 > 0
Enter the version constraint to require (or leave blank to use the latest version): 
Using version ^2.28.1 for requests

Add a package (leave blank to skip): 

development dependencies向けのパッケージ登録

本番動作には必要ないが、開発する時に必要なパッケージは
development dependencies で[tool.poetry.group.dev.dependencies]の項目に追加されます。

Would you like to define your development dependencies interactively? (yes/no) [yes] yes
Package to add or search for (leave blank to skip): pytest
Found 20 packages matching pytest
Showing the first 10 matches

Enter package # to add, or the complete package name if it is not listed []:
 [ 0] pytest
 [ 1] pytest123
 [ 2] 131228_pytest_1
 [ 3] pytest-pingguo-pytest-plugin
 [ 4] pytest-parallel
 [ 5] pytest-circleci
 [ 6] exgrex-pytest
 [ 7] pytest-grpc
 [ 8] pytest-pythonpath
 [ 9] pytest-vnc
 [10] 
 > 0
Enter the version constraint to require (or leave blank to use the latest version): 
Using version ^7.2.0 for pytest

Add a package (leave blank to skip): 

Generated file

[tool.poetry]
name = "dlsubscanstakingrewardshistory"
version = "0.1.0"
description = "GUI application to save Reward&Slash data as csv file using PySimpleGUI and Subscan API"
authors = ["[[user.name(git config)] <[user.email(git config)>"]
license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
PySimpleGUI = "^4.60.4"
pandas = "^1.5.1"
requests = "^2.28.1"

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

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


Do you confirm generation? (yes/no) [yes] yes

依存関係の確認

作成されたpyproject.tomlを見ると実際にプロジェクトの情報とパッケージの情報が書き込まれていることが確認できます。

dlSubscanStakingRewardsHistory % cat pyproject.toml 
[tool.poetry]
name = "dlsubscanstakingrewardshistory"
version = "0.1.0"
description = "GUI application to save Reward&Slash data as csv file using PySimpleGUI and Subscan API"
authors = ["[[user.name(git config)] <[user.email(git config)>"]
license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
PySimpleGUI = "^4.60.4"
pandas = "^1.5.1"
requests = "^2.28.1"

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

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

仮想環境のセットアップ

ここまででプロジェクトの準備ができたので仮想環境をセットアップします。

dlSubscanStakingRewardsHistory % poetry install
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Creating virtualenv dlsubscanstakingrewardshistory in $HOME/develop/python/local_test1/dlSubscanStakingRewardsHistory/.venv
Updating dependencies
Resolving dependencies... (4.0s)

Writing lock file

Package operations: 19 installs, 0 updates, 0 removals

  • Installing pyparsing (3.0.9)
  • Installing six (1.16.0)
  • Installing attrs (22.1.0)
  • Installing certifi (2022.9.24)
  • Installing charset-normalizer (2.1.1)
  • Installing exceptiongroup (1.0.0)
  • Installing idna (3.4)
  • Installing iniconfig (1.1.1)
  • Installing numpy (1.23.4)
  • Installing packaging (21.3)
  • Installing pluggy (1.0.0)
  • Installing python-dateutil (2.8.2)
  • Installing pytz (2022.5)
  • Installing tomli (2.0.1)
  • Installing urllib3 (1.26.12)
  • Installing pandas (1.5.1)
  • Installing pysimplegui (4.60.4)
  • Installing pytest (7.2.0)
  • Installing requests (2.28.1)

ファイルツリーの確認

dlSubscanStakingRewardsHistory % tree
.
├── LICENSE
├── README.md
├── csv_sample
│   ├── 1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih_CryptactCustom_300_20220605.csv
│   └── 1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih_Reward&Slash_300_20220605.csv
├── png
│   ├── dlSubscanStakingRewardsHistory_CryptactCustom.png
│   ├── dlSubscanStakingRewardsHistory_Reward&Slash.png
│   ├── dlSubscanStakingRewardsHistory_setting.png
│   └── dlSubscanStakingRewardsHistory_startup.png
├── poetry.lock # 追加
├── pyproject.toml  #追加
├── requirements.txt
├── src
│   ├── __pycache__
│   │   ├── cryptact.cpython-310.pyc
│   │   ├── gui.cpython-310.pyc
│   │   └── subscan.cpython-310.pyc
│   ├── config.ini
│   ├── cryptact.py
│   ├── gui.py
│   ├── main.py
│   └── subscan.py
└── uml
    ├── classes_dlSubscanStakingRewardsHistory
    │   ├── classes_dlSubscanStakingRewardsHistory.png
    │   └── classes_dlSubscanStakingRewardsHistory.puml
    └── packages_dlSubscanStakingRewardsHistory
        ├── packages_dlSubscanStakingRewardsHistory.png
        └── packages_dlSubscanStakingRewardsHistory.puml

poetry runコマンドによる仮想環境の有効化とプログラムの実行

仮想環境でプログラムを実行するためにはpoetry runコマンドを使用します。
コマンドを実行したところ問題なく起動確認できました。

src % python --version
Python 3.10.7
[ユーザー名]@[コンピューター名] src % poetry run python main.py
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Mac OS Version is 12.6 and patch enabled so applying the patch
Applyting Mac OS 12.3+ Alpha Channel fix.  Your default Alpha Channel is now 0.99

poetry run python main.py 実行結果

poetry shellコマンドによる仮想環境の有効化とプログラムの実行

poetry shellコマンドを使用することで仮想環境を有効化できます。
有効化した状態でプログラムを実行した場合でも問題なく起動確認できました。

  • 仮想環境の有効化: poetry shell
  • 仮想環境の無効化: exit
[ユーザー名]@[コンピューター名] src % python --version
Python 3.10.7
[ユーザー名]@[コンピューター名] src % poetry shell    
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Spawning shell within $HOME/develop/python/local_test1/dlSubscanStakingRewardsHistory_poetry/.venv
Restored session: 2022年 10月29日 土曜日 19時39分38秒 JST
[ユーザー名]@[コンピューター名] src % . $HOME/develop/python/local_test1/dlSubscanStakingRewardsHistory_poetry/.venv/bin/activate
(dlsubscanstakingrewardshistory-py3.10) [ユーザー名]@[コンピューター名] src % python --version
Python 3.10.4
(dlsubscanstakingrewardshistory-py3.10) [ユーザー名]@[コンピューター名] src % python main.py
Mac OS Version is 12.6 and patch enabled so applying the patch
Applyting Mac OS 12.3+ Alpha Channel fix.  Your default Alpha Channel is now 0.99
(dlsubscanstakingrewardshistory-py3.10) [ユーザー名]@[コンピューター名] src % exit

Saving session...completed.
[ユーザー名]@[コンピューター名] src % python --version
Python 3.10.7

2. poetry newでひな形から新規作成する例

プロジェクトを新規作成する場合poetry new コマンドを実行します。

poetry newの指定

  • プロジェクト名: dlSubscanStakingRewardsHistory
  • パッケージ内のディレクトリ名: app --name オプションを指定することでproject以下のプロジェクトフォルダ名を指定できます。
% mkdir poetry_project
% cd poetry_project
poetry_project % poetry new dlSubscanStakingRewardsHistory --name app

Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Created package app in dlSubscanStakingRewardsHistory

ファイルツリーを確認

% cd dlSubscanStakingRewardsHistory 
dlSubscanStakingRewardsHistory % tree
.
├── README.md
├── app #appとなっています。
│   └── __init__.py
├── pyproject.toml
└── tests
    └── __init__.py

tomlファイルの確認

dlSubscanStakingRewardsHistory % cat pyproject.toml
[tool.poetry]
name = "app" #appとなっています。
version = "0.1.0"
description = ""
authors = ["[[user.name(git config)] <[user.email(git config)>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10" #Version指定がなければ3.10で追加される


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

<補足>明示的にPythonのバージョンを指定した場合

コマンド実行した2022/08/21時点では3.10.6は非サポートのため3.10.4で仮想環境が作成されました。

[ユーザー名]@[コンピューター名] poetry_test3 % poetry init --python 3.10.6 #明示的にバージョンを指定

This command will guid[user.email(git config)e you through creating your pyproject.toml config.

Package name [poetry_test3]:  
Version [0.1.0]:  
Description []:  
Author [[[user.name(git config)] <[user.email(git config)>, n to skip]:  
License []:  

Would you like to define your main dependencies interactively? (yes/no) [yes] no
Would you like to define your development dependencies interactively? (yes/no) [yes] no
Generated file

[tool.poetry]
name = "poetry_test3"
version = "0.1.0"
description = ""
authors = ["[[user.name(git config)] <[user.email(git config)>"]

[tool.poetry.dependencies]
python = "3.10.6"

[tool.poetry.dev-dependencies]

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


Do you confirm generation? (yes/no) [yes] yes
[ユーザー名]@[コンピューター名] poetry_test3 % ls
pyproject.toml
[ユーザー名]@[コンピューター名] poetry_test3 % poetry shell
The currently activated Python version 3.10.4 is not supported by the project (3.10.6).
Trying to find and use a compatible version. 
Using python3 (3.10.6)
Creating virtualenv poetry-test3 in $HOME/develop/python/poetry_test3/.venv
Spawning shell within $HOME/develop/python/poetry_test3/.venv
Restored session: 2022年 8月21日 日曜日 19時12分37秒 JST
[ユーザー名]@[コンピューター名] poetry_test3 % . $HOME/develop/python/poetry_test3/.venv/bin/activate
(.venv) [ユーザー名]@[コンピューター名] poetry_test3 % python -V
Python 3.10.4

仮想環境のセットアップ

dlSubscanStakingRewardsHistory % poetry install    
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Creating virtualenv app in $HOME/develop/python/poetry_project/dlSubscanStakingRewardsHistory/.venv
Updating dependencies
Resolving dependencies... (0.1s)

Writing lock file

Installing the current project: app (0.1.0)

ファイルツリーを再度確認

poetry installを実行したことで、Poetryは pyproject.tomlに並べられた全ての依存関係を解決し、それらのファイルの最新バージョンをダウンロードされます。
Poetryがインストールを完了すると、ダウンロードしたパッケージとその正確なバージョンを poetry.lockへ書き込み、その特定のバージョンでプロジェクトを固定します。

dlSubscanStakingRewardsHistory % tree
.
├── README.md
├── app
│   └── __init__.py
├── poetry.lock     #lockファイルが追加された
├── pyproject.toml
└── tests
    └── __init__.py

lockファイル確認

dlSubscanStakingRewardsHistory % cat poetry.lock           
package = []

[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "53f2eabc9c26446fbcc00d348c47878e118afc2054778c3c803a0a8028af27d9"

パッケージ追加

dlSubscanStakingRewardsHistory % poetry add pysimplegui
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Using version ^4.60.4 for pysimplegui

Updating dependencies
Resolving dependencies... (0.5s)

Writing lock file

Package operations: 1 install, 0 updates, 0 removals

  • Installing pysimplegui (4.60.4)
[ユーザー名]@[コンピューター名] dlSubscanStakingRewardsHistory % poetry add pandas     
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Using version ^1.5.1 for pandas

Updating dependencies
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/b5/d7/91fd8911d22e7fac794803095dd192bf1ebd70c7603272085230d915e738/pytz-2022.5-py2Resolving dependencies... Downloading https://files.pythonhosted.org/packages/64/8e/9929b64e146d240507edaac2185cd5516f00b133be5b39250d253be25a64/numpy-1.23.4.taResolving dependencies... Downloading https://files.pythonhosted.org/packages/64/8e/9929b64e146d240507edaac2185cd5516f00b133be5b39250d253be25a64/numpy-1.23.4.taResolving dependencies... Downloading https://files.pythonhosted.org/packages/64/8e/9929b64e146d240507edaac2185cd5516f00b133be5b39250d253be25a64/numpy-1.23.4.taResolving dependencies... Downloading https://files.pythonhosted.org/packages/64/8e/9929b64e146d240507edaac2185cd5516f00b133be5b39250d253be25a64/numpy-1.23.4.taResolving dependencies... (21.5s)

Writing lock file

Package operations: 5 installs, 0 updates, 0 removals

  • Installing six (1.16.0)
  • Installing numpy (1.23.4)
  • Installing python-dateutil (2.8.2)
  • Installing pytz (2022.5)
  • Installing pandas (1.5.1)
[ユーザー名]@[コンピューター名] dlSubscanStakingRewardsHistory % poetry add requests
Configuration file exists at $HOME/Library/Application Support/pypoetry, reusing this directory.

Consider moving configuration to $HOME/Library/Preferences/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Using version ^2.28.1 for requests

Updating dependencies
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-noResolving dependencies... Downloading https://files.pythonhosted.org/packages/1d/38/fa96a426e0c0e68aabc68e896584b83ad1eec779265a028e156ce509630e/certifi-2022.9.Resolving dependencies... (0.6s)

Writing lock file

Package operations: 5 installs, 0 updates, 0 removals

  • Installing certifi (2022.9.24)
  • Installing charset-normalizer (2.1.1)
  • Installing idna (3.4)
  • Installing urllib3 (1.26.12)
  • Installing requests (2.28.1)

まとめ

実際のプロジェクトを使用してpoetry環境を構築する方法を紹介しました。 1. 既存のプロジェクトにpoetry環境を構築するpoetry initの例 2. poetry newでひな形から新規作成する例

コマンドはPoetry公式のBasic Usageに記載されています。
紹介した内容は基本的な部分であり、使用していないオプション指定やパッケージの更新などはドキュメントを参照しながら確認していきたいと思います。 また、今後はdevelopment dependenciesやpoetry.lockファイルを活用して、
機能追加やテストコードなどを考慮したプロジェクト管理ができるようにしていきたいと考えています。

参考

以下の記事を参考にさせていただきました。

Poetryをサクッと使い始めてみる - Qiita

Basic usage | Documentation | Poetry - Python dependency management and packaging made easy

MacでPoetryをインストールする

Pythonではこれまでrequirements.txtでパッケージ管理していましたが、プロジェクト管理を1パッケージずつバージョンアップ管理するのが手間に感じたので、
Node.jsで使用されているnpmのpackage.json によるパッケージ管理と同様のことができるPoetryというプロジェクトを知り導入することにしました。

本記事ではMacにPoetryをインストールした際の手順や確認したことをまとめます。
また以下の記事ではGitHubリポジトリで管理している既存のプロジェクトに適用した例も紹介しています。

7rikazhexde-techlog.hatenablog.com

Poetryのインストール

Poetryをサクッと使い始めてみるを参考にさせていただいたところ、
インストール時に非推奨と警告が表示されたため一度アンインストールし、公式の手順を確認しました。
本指定ではself updateコマンドで 1.2.0a1 以降にアップグレードすることができないようです。
※一部記載を環境変数に置き換えていますのでご注意ください。

% curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
Retrieving Poetry metadata

This installer is deprecated. Poetry versions installed using this script will not be able to use 'self update' command to upgrade to 1.2.0a1 or later. It is recommended to use https://install.python-poetry.org instead. Instructions are available at https://python-poetry.org/docs/#installation
# Welcome to Poetry!

This will download and install the latest version of Poetry,
a dependency and package manager for Python.

It will add the `poetry` command to Poetry's bin directory, located at:

$HOME/.poetry/bin

This path will then be added to your `PATH` environment variable by
modifying the profile files located at:

$HOME/.profile
$HOME/.zshrc
$HOME/.bash_profile

You can uninstall at any time by executing this script with the --uninstall option,
and these changes will be reverted.

Installing version: 1.1.14
  - Downloading poetry-1.1.14-darwin.tar.gz (70.76MB)

Poetry (1.1.14) is installed now. Great!

To get started you need Poetry's bin directory ($HOME/.poetry/bin) in your `PATH`
environment variable. Next time you log in this will be done
automatically.

To configure your current shell run `source $HOME/.poetry/env`

アンインストール

curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - --uninstall

公式のインストール手順

Installation -> With the official installerに従いインストールしました。
https://python-poetry.org/docs/master/#installing-with-the-official-installer
※一部記載を環境変数に置き換えていますのでご注意ください。

% curl -sSL https://install.python-poetry.org | python3 -
Retrieving Poetry metadata

# Welcome to Poetry!

This will download and install the latest version of Poetry,
a dependency and package manager for Python.

It will add the `poetry` command to Poetry's bin directory, located at:

$HOME/Library/Python/3.10/bin

You can uninstall at any time by executing this script with the --uninstall option,
and these changes will be reverted.

Installing Poetry (1.1.14): Done

Poetry (1.1.14) is installed now. Great!

To get started you need Poetry's bin directory ($HOME/Library/Python/3.10/bin) in your `PATH`
environment variable.

Add `export PATH="$HOME/Library/Python/3.10/bin:$PATH"` to your shell configuration file.

Alternatively, you can call Poetry explicitly with `$HOME/Library/Python/3.10/bin/poetry`.

You can test that everything is set up by executing:

`poetry --version`

poetryコマンドの実行パスをzprofileに環境変数$PATHを追加する

# poetry
export PATH="$HOME/Library/Python/3.10/bin:$PATH"

コマンド実行

設定ファイル確認

% poetry config --list
cache-dir = "$HOME/Library/Caches/pypoetry"
experimental.new-installer = true
installer.parallel = true
virtualenvs.create = true
virtualenvs.in-project = null
virtualenvs.path = "{cache-dir}/virtualenvs"  # $HOME/Library/Caches/pypoetry/virtualenvs

仮想環境の設定変更

virtualenvs.in-projectの設定を変更して各プロジェクト配下に仮想環境を作るようにしました。
これで仮想環境の実体が入ったフォルダ(python.exe, 各パッケージ)を、 各プロジェクト直下の .venv に作成されるようになります。

% poetry config virtualenvs.in-project true

% poetry config --list  
cache-dir = "$HOME/Library/Caches/pypoetry"
experimental.new-installer = true
installer.parallel = true
virtualenvs.create = true
virtualenvs.in-project = true # 変更された
virtualenvs.path = "{cache-dir}/virtualenvs"  # $HOME/Library/Caches/pypoetry/virtualenvs

プロジェクトのセットアップ

以下記事で分かりやすく紹介されていますので紹介させていただきます。

仮想環境のセットアップ

上記記事を参考にさせていただき、poetry_testというプロジェクト名で仮想環境をセットアップしました。

% poetry install
Updating dependencies
Resolving dependencies... (9.8s)

Writing lock file

Package operations: 4 installs, 2 updates, 0 removals

  • Updating pyparsing (3.0.4 -> 3.0.9)
  • Updating attrs (21.4.0 -> 22.1.0)
  • Installing more-itertools (8.14.0)
  • Installing pluggy (0.13.1)
  • Installing py (1.11.0)
  • Installing pytest (5.4.3)

Installing the current project: poetry_test (0.1.0)

パッケージ追加

パッケージとして、requestsとPySimpleGUIしてみます。

% poetry add requests
Using version ^2.28.1 for requests

Updating dependencies
Resolving dependencies... (0.9s)

Writing lock file

Package operations: 4 installs, 1 update, 0 removals

  • Updating pyparsing (3.0.4 -> 3.0.9)
  • Installing charset-normalizer (2.1.1)
  • Installing idna (3.3)
  • Installing urllib3 (1.26.11)
  • Installing requests (2.28.1)

% poetry add pysimplegui
Using version ^4.60.3 for PySimpleGUI

Updating dependencies
Resolving dependencies... (0.3s)

Writing lock file

Package operations: 1 install, 1 update, 0 removals

  • Updating pyparsing (3.0.4 -> 3.0.9)
  • Installing pysimplegui (4.60.3)

% cat pyproject.toml 
[tool.poetry]
name = "poetry_test"
version = "0.1.0"
description = ""
authors = ["[user.name] <[user.mail]>"]

[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.28.1" # 追加された
PySimpleGUI = "^4.60.3" # 追加された

[tool.poetry.dev-dependencies]
pytest = "^5.2"

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

<補足>パッケージ追加/削除

  • poetry addで追加すると(.venv) pip freezeで表示される
  • poetry removeで削除すると(.venv) pip freezeから表示されなくなる
  • (.venv)pip installでもパッケージをインストールできるが、pyproject.tomlには記録されない
[ユーザー名]@[コンピューター名] poetry_test % poetry remove pysimplegui
Updating dependencies
Resolving dependencies... (0.1s)

Writing lock file

Package operations: 0 installs, 0 updates, 1 removal

  • Removing pysimplegui (4.60.3)
[ユーザー名]@[コンピューター名] poetry_test % poetry shell             
Spawning shell within $HOME/develop/python/poetry_test/.venv
Restored session: 2022年 8月21日 日曜日 18時55分31秒 JST
[ユーザー名]@[コンピューター名] poetry_test % . $HOME/develop/python/poetry_test/.venv/bin/activate
(.venv) [ユーザー名]@[コンピューター名] poetry_test % pip freeze                                                        
(.venv) [ユーザー名]@[コンピューター名] poetry_test % pip install pysimplegui
Collecting pysimplegui
  Using cached PySimpleGUI-4.60.3-py3-none-any.whl (509 kB)
Installing collected packages: pysimplegui
Successfully installed pysimplegui-4.60.3
(.venv) [ユーザー名]@[コンピューター名] poetry_test % exit

Saving session...completed.
[ユーザー名]@[コンピューター名] poetry_test % cat pyproject.toml       
[tool.poetry]
name = "poetry_test"
version = "0.1.0"
description = ""
authors = ["[[user.name(git config)] <[user.email(git config)>"]

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

[tool.poetry.dev-dependencies]

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

poetry addでパッケージ追加後、(.venv)でpipコマンドで削除してもpyproject.tomlには残る

[ユーザー名]@[コンピューター名] poetry_test % poetry add pysimplegui         
Using version ^4.60.3 for PySimpleGUI

Updating dependencies
Resolving dependencies... (0.1s)

Writing lock file

No dependencies to install or update
[ユーザー名]@[コンピューター名] poetry_test % poetry shell             
Spawning shell within $HOME/develop/python/poetry_test/.venv
Restored session: 2022年 8月21日 日曜日 18時56分37秒 JST
[ユーザー名]@[コンピューター名] poetry_test % . $HOME/develop/python/poetry_test/.venv/bin/activate
(.venv) [ユーザー名]@[コンピューター名] poetry_test % pip freeze
PySimpleGUI==4.60.3 #存在する
(.venv) [ユーザー名]@[コンピューター名] poetry_test % pip uninstall pysimplegui
Found existing installation: PySimpleGUI 4.60.3
Uninstalling PySimpleGUI-4.60.3:
  Would remove:
    $HOME/develop/python/poetry_test/.venv/bin/psghelp
    $HOME/develop/python/poetry_test/.venv/bin/psgissue
    $HOME/develop/python/poetry_test/.venv/bin/psgmain
    $HOME/develop/python/poetry_test/.venv/bin/psgsettings
    $HOME/develop/python/poetry_test/.venv/bin/psgupgrade
    $HOME/develop/python/poetry_test/.venv/bin/psgver
    $HOME/develop/python/poetry_test/.venv/lib/python3.10/site-packages/PySimpleGUI-4.60.3.dist-info/*
    $HOME/develop/python/poetry_test/.venv/lib/python3.10/site-packages/PySimpleGUI/*
Proceed (Y/n)? Y
  Successfully uninstalled PySimpleGUI-4.60.3 # pipコマンドでuninstallした
(.venv) [ユーザー名]@[コンピューター名] poetry_test % exit

Saving session...completed.

[ユーザー名]@[コンピューター名] poetry_test % cat pyproject.toml       
[tool.poetry]
name = "poetry_test"
version = "0.1.0"
description = ""
authors = ["[[user.name(git config)] <[user.email(git config)>"]

[tool.poetry.dependencies]
python = "^3.10"
PySimpleGUI = "^4.60.3" #消えていない

[tool.poetry.dev-dependencies]

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

Poetryのバージョンアップデート

% poetry self update
Updating Poetry to 1.2.2

Updating dependencies
Resolving dependencies... (15.2s)

Package operations: 10 installs, 12 updates, 0 removals

  - Updating certifi (2022.6.15 -> 2022.9.24)
  - Updating idna (3.3 -> 3.4)
  - Installing more-itertools (9.0.0)
  - Installing pycparser (2.21)
  - Updating urllib3 (1.26.11 -> 1.26.12)
  - Installing attrs (22.1.0)
  - Installing cffi (1.15.1)
  - Updating distlib (0.3.5 -> 0.3.6)
  - Installing jaraco.classes (3.2.3)
  - Updating poetry-core (1.0.8 -> 1.3.2)
  - Installing pyrsistent (0.18.1)
  - Updating cachecontrol (0.12.11 -> 0.12.12)
  - Updating cleo (0.8.1 -> 1.0.0a5)
  - Installing dulwich (0.20.46)
  - Installing jsonschema (4.16.0)
  - Updating keyring (23.8.2 -> 23.9.3)
  - Updating packaging (20.9 -> 21.3)
  - Installing poetry-plugin-export (1.1.2)
  - Updating tomlkit (0.11.4 -> 0.11.6)
  - Updating virtualenv (20.16.3 -> 20.16.6)
  - Installing xattr (0.9.9)
  - Updating poetry (1.1.14 -> 1.2.2)

Updating the poetry script

Poetry (1.2.2) is installed now. Great!

【Mac + Shortcuts App】シェルスクリプトアクションを使用して指定フォルダ内のビデオファイルを1ファイルずつエンコード処理するショートカットについて

私は普段、QuickTimePlayerによる収録、Youtubeの動画保存、デジカメやiPhoneによる動画撮影等をしているのですが、
解像度がFullHD以上で固定されている場合が多く、録画時間が長くなるとファイルサイズも大きくなり、ストレージの空き容量も少なくなります。
一方でファイル内容や動画をスマホで見る場合など、FullHDまで必要ないケースもあるため、Macエンコード機能を使用してファイルサイズを小さくして管理しています。

ビデオファイルのエンコード方法
ファイルで右クリック > 選択したビデオファイルをエンコード > 設定でエンコード指定

Mac メディアエンコード画面

エンコードする際は複数ファイルを選択して実行することで1ファイルずつ実行されるのですが、ファイル選択とエンコード指示が手間(実際そこまでではないですが)なので、
1アクションで実行する方法として、ショートカットAppを使用して指定フォルダ内のビデオファイルを1ファイルずつエンコード処理するショートカットを作成したので紹介します。

作成したショートカットについて

以下で公開しています。使用する際は自己責任の範囲でリンクより取得して使用してください。
※リンクから追加画面に遷移しない場合はメモにURLを貼り付けてからアクセスしてください。

www.icloud.com

使い方、注意事項についてはショートカットの先頭にコメントで記載しているため参照してください。
以下では使用アクションと処理のポイントについて記載します。

指定フォルダに格納されたファイル一覧の取得処理として、エンコード処理はファイル単位で実行するため、lsコマンドを使用してファイルの一覧を.tmpファイルに書き出しています。

次にフォルダからファイルを取得アクションを使用して改行で分割指定することにより、.tmpファイルからファイル単位の情報に分割しています。

分割した情報はリストから項目を取得アクションを使用してRangeの項目指定で開始位置から終了位置まで取得しています。

フォルダ内のファイル一覧を取得するアクションがあるとよかったのですが、単体では存在しないようです 。

videoFileAvconvert-1

次に.tmpファイルは不要になるためrmコマンドで削除しています。

その後、各項目を繰り返すアクションでリストからの項目を指定しています。

リストからの項目ではビデオファイル名(ファイル名.拡張子)を取得できるため、ビデオファイル名をシェルスクリプト引数として与えてavconvertコマンドを実行しています。

ここでポイントとして、ファイル名を出力ファイル名に使用したかったので、${source%.*}でファイル名部分を変数filenameに代入しています。シェルスクリプトに渡す変数は調べた範囲では複数指定できないようだったのでシェルの中で処理しました。

videoFileAvconvert-2

最後に完了メッセージを表示して、ショートカットを停止しています。

videoFileAvconvert-3

まとめ

ショートカットAppを使用して指定フォルダ内のビデオファイルを1ファイルずつエンコード処理するショートカットを作成したので紹介しました。

ショートカットを作成していて使いたいアクションがないケースがあり、作りづらいと感じる部分もありました。

正直、エンコード処理であればシェルスクリプトで書けばいいのではと思うこともありましたが、今回はショートカットで作成しました。

ショートカットでは共有する都合上、表示アクションを追加していますが、削除すればショートカットの実行のみで各ファイルがエンコードされるため、やりたいことは実現することができました。

また、ファイルの取得処理などは動画ファイル以外でも使用できるため、何かで他でも活用できるかと思います。

参考

以下の記事を参考にさせていただきました。
MacOS コマンドラインで「選択したビデオファイルをエンコード」 - vague memory

文字列と変数の結合 | Linuxシェルスクリプト | shiraberu.tech

シェルスクリプトの特殊変数$@と$*の違い - ハックノート

Qiita API v2を使用して定期実行または手動実行(スマートフォン指示)でQiitaのデータ(view, like, stock)を取得するGoogle Apps Script(GAS)

下記記事を参考にQiita API v2を使用してQiitaのデータ(view, like, stock)を取得するGoogle Apps Script(GAS)を作成しました。
【Qiita API】いろんな方法で Views、Likes、Stocksを取得(JavaScript、Google Script、Python, Vue.js)

GASではHTTP GETリクエストを送信してスプレッドシートに追加する関数を時間ベース(1日1回)でトリガーに設定して実行していました。 しかし、しばらく運用していたところレスポンスで403 Forbiddenエラーになることがあり、データを取得できないことが多々ありました。
参考として私の環境ではエラー率が60%を超えていました。(2022年9月25日時点)
ただ、早朝の時間に変更するとエラーはほぼ起きていないため、同じようにエラーが起きる場合はまずは時間帯の変更を試してみるのが良いかと思います。

実行数画面(エラー時のログ)

<実行数>定期実行トリガー:myFunction()エラー

トリガー画面

<トリガー>結果:myFunction()

また、エラー情報(Exception)はエラー発生日の翌日にgmailで送信される仕様のためエラーが発生してスレッドシートへのデータ追加が失敗したことに気づけないことがありました。
そこで機能改善として以下の機能に対応したので紹介します。

  1. エラー発生時のメール送信
  2. スマートフォンからの実行指示

仕様

上記改善と参考にさせていただいた記事との差異に関する仕様は下記の通りです。

  1. 初回起動時を想定し、関数実行でcontrol,stock,like,view,sumシートを追加する。
  2. スプレッドシートにはAPIリクエスト時の日付データ(YYYY/MM/DD)と取得したデータをセットにして、view,like,stockシートに追加する。
  3. APIリクエストはGASの関数実行(手動、定期、チェックボックス操作)で送信する。
  4. チェックボックス操作はPC、スマートフォンに対応し、controlシートのチェックボックスを有効(TRUE)にするとスマホからAPIリクエストを送信し、正常終了、異常終了時に無効(FALSE)に変更する。
  5. 複数回データ取得を行う場合は日付データを基準に重複する日付を特定して、行単位で最新データのみ残す。
  6. エラー発生時は設定したメールアドレス宛にエラー内容を送信する。

ソースコード

Githubにもコミットしています。

github.com

1.初回起動時を想定し、関数実行でcontrol,stock,like,view,sumシートを追加する。
initSheet()で実行します。
ポイントとしてはisExistingSheet(sheetName)です。シート名を特定するためにはスプレッドシートIDを指定する必要があるため個別に関数化して対応しています。

2.スプレッドシートにはAPIリクエスト時の日付データ(YYYY/MM/DD)と取得したデータをセットにして、view,like,stockシートに追加する。

myFunction()の以下で対応しています。Dateオブジェクトを整形しているだけですが、月と日は3桁になることがあるため.slice(-2)で調整しています。

3.APIリクエストはGASの関数実行(手動、定期、チェックボックス操作)で送信する。
4.チェックボックス操作はPC、スマートフォンに対応し、controlシートのチェックボックスを有効(TRUE)にするとスマホからAPIリクエストを送信し、正常終了、異常終了時に無効(FALSE)に変更する。
定期実行ではトリガーにmyFunctionを登録して実行します。
手動実行ではmyFunction()を直接実行する方法とチェックボックスからのmyFunction()を実行する方法に対応しています。

チェックボックスからのmyFunction()に関連する処理は以下の通りです。 初期値(チェックボックス)をinitUniqueSheet()で追加します。

HTTP Responseを受けて受信処理をした後にチェックボックスをFALSEに変更して処理を完了しています。

チェックボックス操作ではシートを更新(チェックボックスの変更)するためにイベントハンドラー(InstallableTriggers)を実行しています。

初めにスプレッドシートのセルを更新時に実行されるonEdit()を実装しましたが、
Exception: SpreadsheetApp.openById を呼び出す権限がありません。 必要な権限: https://www.googleapis.com/auth/spreadsheetsエラーとなり実行できませんでした。

エラー内容の通り、スコープ(appsscript.json)にhttps://www.googleapis.com/auth/spreadsheetsを追加しましたがエラー内容は解消されませんでした。

こちらの原因を調べたところ、SimpleTriggersには制限があることがわかりました。
また、こちらの記事の通りSimpleTriggersで実行されるメソッドでは他のスプレッドシートを参照することができないため、createEditTrigger()によりInstallableTriggers(onEditCell(e))を追加して、チェックボックス操作でmyFunction()を実行する形式にしました。

<補足>InstallableTriggersも他のトリガー同様にトリガーの一覧に表示されます。削除する際はメニューから削除することで対応できます。

トリガー一覧

5.複数回データ取得を行う場合は日付データを基準に重複する日付を特定して、行単位で最新データのみ残す。
checkSheetDateAndDeleteRowDuplicateDate(spreadsheet)deleteRowDuplicateDate(sheet,val)で対応します。
checkSheetDateAndDeleteRowDuplicateDate(spreadsheet)ではシート情報と日付を作成し、その値をdeleteRowDuplicateDate(sheet,val)の引数に指定して実行します。

deleteRowDuplicateDate(sheet,val)ではシートから重複する日付データを取得してその行数を配列に詰めます。 重複する日付データは要素数1以下の場合は以降処理しないで終了します。 配列には重複する行数が格納されているため、deleteRow(要素数)で指定して削除します。 なお、2行以上の重複がある場合は一番後ろのデータは残すためにduplicateDateList.length-2からデクリメントして削除します。

onEditCellトリガー実行ログ

6.エラー発生時は設定したメールアドレス宛にエラー内容を送信します。
エラー処理はmyFuction()でコールするfetch関数実行時のHTTP リクエスト送信時および レスポンスを受信時に発生するため、
try~catch分でエラー情報取得し、エラーログの出力及び、メール送信を実行します。

qiitaV2API.gs(エラー情報取得)

erros.gs

sendMail.gs

<注意事項>
エラー情報(Exception)発生時にログを出力したことにより、トリガー上のエラー率には反映されなくなります。
これはエラー時にcatch文が実行されることでスクリプトとしては正常終了とカウントされるためと考えられます。
エラー率を集計したい場合は個別に計算する必要があると思います。エラー率としてカウントできる仕組みや関数があるかもしれませんが、未確認です。

エラー通知メール例

取得結果

まとめ

参考記事のコードをベースに以下機能に対応したGASを紹介しました。
1. エラー発生時のメール送信
2. スマートフォンからの実行指示

本対応によりエラーの有無をメールから確認することができること、また、チェックボックス操作による関数実行スマートフォンから操作することができました。
これで漏れなくデータの集計が可能になると思います。

参考

以下の記事を参考にさせていただきました。
1. 【Qiita API】いろんな方法で Views、Likes、Stocksを取得(JavaScript、Google Script、Python, Vue.js) 2. 【解決】SpreadsheetApp.openById を呼び出す権限がありません。というエラー 3. スマホからGAS起動

Pythonのprint関数の形式

Pythonで小数点を含む実数の表示形式で悩むことがあったので、PEP仕様と実際の例を確認して内容をまとめました。

Pythonのprint関数の形式は基本はC言語の文字列フォーマットと同じですが、一部仕様(参考1)により異なります。

形式

[%] (マップキー) (変換フラグ)(最小フィールド幅)(.精度)[フォーマット指定子]

※()はオプションで省略化

構成要素

  • [%]
指定子の開始を示す文字  
  • (マップキー)
丸括弧で囲った文字列で表し、フォーマット化したい値をマップから選び出す  

print('%(language)s has %(number)03d quote types.' %{'language': "Python", "number": 2})  
Python has 002 quote types.  
  • (変換フラグ)
    一部の変換型の結果に影響
'#' : 値の変換に (下で定義されている) "別の形式" を使う。  
'0' : 数値型に対してゼロによるパディングを行う。  
'-' : 変換された値を左寄せにします ('0' と同時に与えた場合、 '0' を上書きする) 。  
' ' : (スペース) 符号付きの変換で正の数の場合、前に一つスペースを空ける (そうでない場合は空文字になる) 。  
'+' : 変換の先頭に符号文字 ('+' または '-') を付ける("スペース" フラグを上書きする) 。  
  • (最小フィールド幅)
・全体の文字数(実数の場合は小数点も含む)  
・最小フィールド幅よりも値が大きい桁数だった場合はそのまま表示される。  
・'*' (アスタリスク) を指定した場合、実際の文字列幅が values タプルの次の要素から読み出されれる。  
・タプルには最小フィールド幅やオプションの精度指定の後に変換したいオブジェクトがくるようにする。  
  • (.精度)
・効数字桁数  
・%.3fの場合、12.3567→12.357となる(小数第4位は四捨五入)  
・指定しない場合は6  
・'.' (ドット) とその後に続く精度で与えられる。 
・'*' (アスタリスク) を指定した場合、精度の桁数は values タプルの次の要素から読み出される。
・タプルには精度指定の後に変換したい値がくるようにする。
  • [フォーマット指定子]
・(精度長変換子:l)と変換型:dで表現される。  
・フォーマット指定子によりその指定子に対応する引数の表示の仕方が決まる。  
・精度長変換子(h, l,または L) を使うことができますが、 Python では必要ないため無視される。  
・つまり、例えば %ld は %d と等価である。  
  • 指定子
'd': 符号付き 10 進整数。  
'i': 符号付き 10 進整数。  
'o': 符号付き 8 進数。(1)  
'u': 旧式の型 -- 'd' と同じです。(6)  
'x': 符号付き 16 進数 (小文字)。(2)  
'X': 符号付き 16 進数 (大文字)。(2)  
'e': 指数表記の浮動小数点数 (小文字)。(3)  
'E': 指数表記の浮動小数点数 (大文字)。(3)  
'f': 10 進浮動小数点数。(3)  
'F': 10 進浮動小数点数。(3)  
'g': 浮動小数点数。指数部が -4 以上または精度以下の場合には小文字指数表記、それ以外の場合には10進表記。(4)  
'G': 浮動小数点数。指数部が -4 以上または精度以下の場合には大文字指数表記、それ以外の場合には10進表記。(4)  
'c': 文字一文字 (整数または一文字からなる文字列を受理する)。  
'r': 文字列 (Python オブジェクトを repr() で変換する)。(5)  
's': 文字列 (Python オブジェクトを str() で変換する)。(5)  
'a': 文字列 (Python オブジェクトを ascii() で変換する)。(5)  
'%': 引数を変換せず、返される文字列中では文字 '%' になる。  
     別の形式を指定(訳注: 変換フラグ # を使用)すると 8 進数を表す接頭辞 ('0o') が最初の数字の前に挿入される。  
  • 注釈
(1)別の形式を指定(訳注: 変換フラグ # を使用)すると 16 進数を表す接頭辞 '0x' または
   '0X' (使用するフォーマット文字が 'x' か 'X' に依存します) が最初の数字の前に挿入される。  
(2)この形式にした場合、変換結果には常に小数点が含まれ、それはその後ろに数字が続かない場合にも適用される。  
(3)指定精度は小数点の後の桁数を決定し、そのデフォルトは 6 。  
(4)この形式にした場合、変換結果には常に小数点が含まれ他の形式とは違って末尾の 0 は取り除かれない。  
   指定精度は小数点の前後の有効桁数を決定し、そのデフォルトは 6 。  
(5)精度が N なら、出力は N 文字に切り詰めらる。  
(6)PEP 237 を参照。

a = 0.12345678
print('----- start a='+str(0.12345678)+'の例 start -----') 
print('%.1f' + 'の値は%.1fです。'%a)
print('%.2f' + 'の値は%.2fです。'%a)
print('%.3f' + 'の値は%.3fです。'%a)
print('%.4f' + 'の値は%.4fです。'%a)
print('%.5f' + 'の値は%.5fです。'%a)
print('%.6f' + 'の値は%.6fです。'%a)
print('%.7f' + 'の値は%.7fです。'%a)
print('%.8f' + 'の値は%.8fです。'%a)
print('%.9f' + 'の値は%.9fです。'%a)

print('%1f' + 'の値は%1fです。'%a)
print('%2f' + 'の値は%2fです。'%a)
print('%3f' + 'の値は%3fです。'%a)
print('%4f' + 'の値は%4fです。'%a)
print('%5f' + 'の値は%5fです。'%a)
print('%6f' + 'の値は%6fです。'%a)
print('%7f' + 'の値は%7fです。'%a)
print('%8f' + 'の値は%8fです。'%a)
print('%9f' + 'の値は%9fです。'%a)
print('%10f' + 'の値は%10fです。'%a)
print('%11f' + 'の値は%11fです。'%a)
print('%12f' + 'の値は%12fです。'%a)

print('%20.1f' + 'の値は%20.1fです。'%a)
print('%20.2f' + 'の値は%20.2fです。'%a)
print('%20.3f' + 'の値は%20.3fです。'%a)
print('%20.4f' + 'の値は%20.4fです。'%a)
print('%20.5f' + 'の値は%20.5fです。'%a)
print('%20.6f' + 'の値は%20.6fです。'%a)
print('%20.7f' + 'の値は%20.7fです。'%a)
print('%20.8f' + 'の値は%20.8fです。'%a)
print('%20.9f' + 'の値は%20.9fです。'%a)
print('%20.10f' + 'の値は%20.10fです。'%a)

print('%-20.1f' + 'の値は%-20.1fです。'%a)
print('%-20.2f' + 'の値は%-20.2fです。'%a)
print('%-20.3f' + 'の値は%-20.3fです。'%a)
print('%-20.4f' + 'の値は%-20.4fです。'%a)
print('%-20.5f' + 'の値は%-20.5fです。'%a)
print('%-20.6f' + 'の値は%-20.6fです。'%a)
print('%-20.7f' + 'の値は%-20.7fです。'%a)
print('%-20.8f' + 'の値は%-20.8fです。'%a)
print('%-20.9f' + 'の値は%-20.9fです。'%a)
print('%-20.10f' + 'の値は%-20.10fです。'%a)
print('----- end a='+str(0.12345678)+'の例 end -----') 

b = 12345678.12345678
print('----- start b='+str(12345678.12345678)+'の例 start -----') 
print('%.1f' + 'の値は%.1fです。'%b)
print('%.2f' + 'の値は%.2fです。'%b)
print('%.3f' + 'の値は%.3fです。'%b)
print('%.4f' + 'の値は%.4fです。'%b)
print('%.5f' + 'の値は%.5fです。'%b)
print('%.6f' + 'の値は%.6fです。'%b)
print('%.7f' + 'の値は%.7fです。'%b)
print('%.8f' + 'の値は%.8fです。'%b)
print('%.9f' + 'の値は%.9fです。'%b)

print('%1f' + 'の値は%1fです。'%b)
print('%2f' + 'の値は%2fです。'%b)
print('%3f' + 'の値は%3fです。'%b)
print('%4f' + 'の値は%4fです。'%b)
print('%5f' + 'の値は%5fです。'%b)
print('%6f' + 'の値は%6fです。'%b)
print('%7f' + 'の値は%7fです。'%b)
print('%8f' + 'の値は%8fです。'%b)
print('%9f' + 'の値は%9fです。'%b)
print('%10f' + 'の値は%10fです。'%b)
print('%11f' + 'の値は%11fです。'%b)
print('%12f' + 'の値は%12fです。'%b)

print('%20.1f' + 'の値は%20.1fです。'%b)
print('%20.2f' + 'の値は%20.2fです。'%b)
print('%20.3f' + 'の値は%20.3fです。'%b)
print('%20.4f' + 'の値は%20.4fです。'%b)
print('%20.5f' + 'の値は%20.5fです。'%b)
print('%20.6f' + 'の値は%20.6fです。'%b)
print('%20.7f' + 'の値は%20.7fです。'%b)
print('%20.8f' + 'の値は%20.8fです。'%b)
print('%20.9f' + 'の値は%20.9fです。'%b)
print('%20.10f' + 'の値は%20.10fです。'%b)

print('%-20.1f' + 'の値は%-20.1fです。'%a)
print('%-20.2f' + 'の値は%-20.2fです。'%a)
print('%-20.3f' + 'の値は%-20.3fです。'%a)
print('%-20.4f' + 'の値は%-20.4fです。'%a)
print('%-20.5f' + 'の値は%-20.5fです。'%a)
print('%-20.6f' + 'の値は%-20.6fです。'%a)
print('%-20.7f' + 'の値は%-20.7fです。'%a)
print('%-20.8f' + 'の値は%-20.8fです。'%a)
print('%-20.9f' + 'の値は%-20.9fです。'%a)
print('%-20.10f' + 'の値は%-20.10fです。'%a)

print('%1.8f' + 'の値は%1.8fです。'%b)
print('%2.8f' + 'の値は%2.8fです。'%b)
print('%3.8f' + 'の値は%3.8fです。'%b)
print('%4.8f' + 'の値は%4.8fです。'%b)
print('%5.8f' + 'の値は%5.8fです。'%b)
print('%6.8f' + 'の値は%6.8fです。'%b)
print('%7.8f' + 'の値は%7.8fです。'%b)
print('%8.8f' + 'の値は%8.8fです。'%b)
print('%9.8f' + 'の値は%9.8fです。'%b)
print('----- end b='+str(12345678.12345678)+'の例 end -----') 
----- start a=0.12345678の例 start -----
%.1fの値は0.1です。
%.2fの値は0.12です。
%.3fの値は0.123です。
%.4fの値は0.1235です。#小数第4位が後ろの5で四捨五入されて桁上がり
%.5fの値は0.12346です。
%.6fの値は0.123457です。
%.7fの値は0.1234568です。
%.8fの値は0.12345678です。
%.9fの値は0.123456780です。#小数第9位は0埋め
%1fの値は0.123457です。
%2fの値は0.123457です。
%3fの値は0.123457です。
%4fの値は0.123457です。
%5fの値は0.123457です。
%6fの値は0.123457です。
%7fの値は0.123457です。
%8fの値は0.123457です。
%9fの値は 0.123457です。
%10fの値は  0.123457です。#10桁なので0埋め
%11fの値は   0.123457です。
%12fの値は    0.123457です。
%20.1fの値は                 0.1です。
%20.2fの値は                0.12です。
%20.3fの値は               0.123です。
%20.4fの値は              0.1235です。
%20.5fの値は             0.12346です。
%20.6fの値は            0.123457です。
%20.7fの値は           0.1234568です。
%20.8fの値は          0.12345678です。
%20.9fの値は         0.123456780です。
%20.10fの値は        0.1234567800です。
%-20.1fの値は0.1                 です。#20文字分で0埋めされて左詰め
%-20.2fの値は0.12                です。
%-20.3fの値は0.123               です。
%-20.4fの値は0.1235              です。
%-20.5fの値は0.12346             です。
%-20.6fの値は0.123457            です。
%-20.7fの値は0.1234568           です。
%-20.8fの値は0.12345678          です。
%-20.9fの値は0.123456780         です。
%-20.10fの値は0.1234567800        です。
----- end a=0.12345678の例 end -----
----- start b=12345678.12345678の例 start -----
%.1fの値は12345678.1です。
%.2fの値は12345678.12です。
%.3fの値は12345678.123です。
%.4fの値は12345678.1235です。
%.5fの値は12345678.12346です。
%.6fの値は12345678.123457です。
%.7fの値は12345678.1234568です。
%.8fの値は12345678.12345678です。
%.9fの値は12345678.123456780です。
%1fの値は12345678.123457です。
%2fの値は12345678.123457です。
%3fの値は12345678.123457です。
%4fの値は12345678.123457です。
%5fの値は12345678.123457です。
%6fの値は12345678.123457です。
%7fの値は12345678.123457です。
%8fの値は12345678.123457です。
%9fの値は12345678.123457です。
%10fの値は12345678.123457です。
%11fの値は12345678.123457です。
%12fの値は12345678.123457です。
%20.1fの値は          12345678.1です。
%20.2fの値は         12345678.12です。
%20.3fの値は        12345678.123です。
%20.4fの値は       12345678.1235です。
%20.5fの値は      12345678.12346です。
%20.6fの値は     12345678.123457です。
%20.7fの値は    12345678.1234568です。
%20.8fの値は   12345678.12345678です。
%20.9fの値は  12345678.123456780です。
%20.10fの値は 12345678.1234567799です。
%-20.1fの値は0.1                 です。
%-20.2fの値は0.12                です。
%-20.3fの値は0.123               です。
%-20.4fの値は0.1235              です。
%-20.5fの値は0.12346             です。
%-20.6fの値は0.123457            です。
%-20.7fの値は0.1234568           です。
%-20.8fの値は0.12345678          です。
%-20.9fの値は0.123456780         です。
%-20.10fの値は0.1234567800        です。
%1.8fの値は12345678.12345678です。
%2.8fの値は12345678.12345678です。
%3.8fの値は12345678.12345678です。
%4.8fの値は12345678.12345678です。
%5.8fの値は12345678.12345678です。
%6.8fの値は12345678.12345678です。
%7.8fの値は12345678.12345678です。
%8.8fの値は12345678.12345678です。
%9.8fの値は12345678.12345678です。
----- end b=12345678.12345678の例 end -----

小数点を含む実数ならば「%.[小数点以下の桁数分]f」にしておけば良さそうです。

参考

Macでnodenvをインストールする

Node.jsのバージョン管理ツールとしてnを使用していましたが、
brew doctorを実行したところ以下のwarningが出ました。

% brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: You have unlinked kegs in your Cellar.
Leaving kegs unlinked can lead to build-trouble and cause formulae that depend on
those kegs to fail to run properly once built. Run `brew link` on these:
  node

nとbrewコマンドでインストールしたnodeの設定に問題があり、
nodeコマンドの実体についてリンクを設定し直せばwarningは解消できそうでしたが、
Pythonのバージョン管理としてpyenvを使用していることもあり、同様に管理可能なNode.js向けに開発されたもバージョン管理ツールであるnodenvをインストールすることにしました。
本記事ではインストール時に確認した内容のまとめになります。

nとnode(npm)を削除する

以下の記事を参考にさせていただきました。
手順は同じですが、./zshrcを変えていたので関連する箇所は個別に修正しました。
nパッケージを完全にアンインストールする

削除後にbrewコマンドではnode,npmがインストールしていないことを確認します。

% brew node
Error: Unknown command: node
% brew npm
Error: Unknown command: npm

nodenvをインストールする

nodenvのインストールは以下の記事を参考にさせていただきました。
nodenvでNode.jsのバージョンを切り替える

Homebrewでnodenvをインストールする

# https://github.com/nodenv/nodenv#homebrew-on-macos
# Homebrew on macOS
% brew install nodenv
Running `brew update --auto-update`...
==> Downloading https://ghcr.io/v2/homebrew/core/node-build/manifests/4.9.87
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/node-build/blobs/sha256:4187ec6
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/nodenv/manifests/1.4.0
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/nodenv/blobs/sha256:a20f7a9c8ce
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
######################################################################## 100.0%
==> Installing dependencies for nodenv: node-build
==> Installing nodenv dependency: node-build
==> Pouring node-build--4.9.87.all.bottle.tar.gz
🍺  /usr/local/Cellar/node-build/4.9.87: 775 files, 928.6KB
==> Installing nodenv
==> Pouring nodenv--1.4.0.monterey.bottle.tar.gz
🍺  /usr/local/Cellar/nodenv/1.4.0: 34 files, 106.4KB
==> Running `brew cleanup nodenv`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

zshrcに以下を追加

% vi ~/.zshrc
eval "$(nodenv init -)"

nodenv-doctor スクリプトを使用して、nodenv が適切に設定されていることを確認する

# https://github.com/nodenv/nodenv#homebrew-on-macos
# 4. Verify that nodenv is properly set up using this nodenv-doctor script:
% curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-doctor | bash
Checking for `nodenv' in PATH: /usr/local/bin/nodenv
# nodenvコマンド(エイリアス)のインストール先
Checking for nodenv shims in PATH: OK
Checking `nodenv install' support: /usr/local/bin/nodenv-install (node-build 4.9.87)
# nodenv-installコマンド(エイリアス)のインストール先
Counting installed Node versions: none
  There aren't any Node versions installed under `$HOME/.nodenv/versions'.
  You can install Node versions like so: nodenv install 2.2.4
Auditing installed plugins: OK

<補足> brewコマンドで更新する場合:nodenv,node-buildそれぞれ指定する

# https://github.com/nodenv/nodenv#homebrew-on-macos
# Upgrading with Homebrew
% brew upgrade nodenv node-build
Warning: nodenv 1.4.0 already installed
Warning: node-build 4.9.87 already installed

<補足> nodenvコマンド(実体)の確認

% cd /usr/local/bin/      
% ls -lt nodenv
lrwxr-xr-x  1 [computer_name]  admin  33  8  4 21:04 nodenv -> ../Cellar/nodenv/1.4.0/bin/nodenv

% cd ../Cellar/nodenv/1.4.0/bin/

% pwd
/usr/local/Cellar/nodenv/1.4.0/bin

% ls
nodenv

<補足> nodenv-installコマンド(実体)の確認

% ls -lt nodenv-install
lrwxr-xr-x  1 [computer_name]  admin  46  8  4 21:04 nodenv-install -> ../Cellar/node-build/4.9.87/bin/nodenv-install

% cd ../Cellar/node-build/4.9.87/bin/

% pwd
/usr/local/Cellar/node-build/4.9.87/bin

% ls
node-build      nodenv-install      nodenv-uninstall
# nodenv-install以外も追加されている

nodenv-package-rehashプラグインをインストールする

nodenv-package-rehashプラグインはグローバルパッケージをインストールまたはアンインストールするたびに、nodenv rehash を自動的に実行されるようになります。

nodenv init - --no-rehashによる設定について

https://github.com/nodenv/nodenv-package-rehash
Configuration (optional)
With this plugin, rehashing will happen on-demand (when global npm modules are installed/uninstalled). You can take advantage of this and remove nodenv's automatic hashing upon shell initialization. In your shell startup file (.bash_profile, .bashrc, or .zshrc), add the --no-rehash flag to the nodenv init - invocation: eval "$(nodenv init - --no-rehash)" This will speed up your shell initialization since nodenv will no longer need to rehash on every startup.

シェルの起動ファイル (.bash_profile, .bashrc, .zshrc) で、
nodenv init - invocation に --no-rehashフラグを追加することにより、オンデマンドでリハッシュが行われます。
(グローバルなnpmモジュールがインストール/アンインストールされたとき)
これを利用して、シェル初期化時のnodenvの自動ハッシュ化を解除することができます。
nodenvが起動のたびにリハッシュする必要がなくなるので、シェルの初期化が高速化されます。

# install via git (recommended)
% git clone https://github.com/nodenv/nodenv-package-rehash.git "$(nodenv root)"/plugins/nodenv-package-rehash
Cloning into '$HOME/.nodenv/plugins/nodenv-package-rehash'...
remote: Enumerating objects: 820, done.
remote: Total 820 (delta 0), reused 0 (delta 0), pack-reused 820
Receiving objects: 100% (820/820), 111.61 KiB | 4.65 MiB/s, done.
Resolving deltas: 100% (374/374), done.

zshrcを以下に変更

% vi ~/.zshrc
- eval "$(nodenv init -)"
+ eval "$(nodenv init - --no-rehash)"

nodenvコマンドの使い方

指定バージョンのインストール方法

% nodenv install -l

0.1.14
# 省略
18.4.0
18.5.0
18.6.0
18.7.0
chakracore-dev
chakracore-nightly
chakracore-8.1.2
# 省略
iojs-3.3.1
nightly
node-dev
rc
v8-canary

% nodenv install 17.9.1
Downloading node-v17.9.1-darwin-x64.tar.gz...
-> https://nodejs.org/dist/v17.9.1/node-v17.9.1-darwin-x64.tar.gz

WARNING: node-v17.9.1-darwin-x64 is past its end of life and is now unsupported.
It no longer receives bug fixes or security updates.

Installing node-v17.9.1-darwin-x64...
Installed node-v17.9.1-darwin-x64 to $HOME/.nodenv/versions/17.9.1

Installed postinstall/postuninstall package hooks for 17.9.1

% nodenv install 18.6.0
Downloading node-v18.6.0-darwin-x64.tar.gz...
-> https://nodejs.org/dist/v18.6.0/node-v18.6.0-darwin-x64.tar.gz
Installing node-v18.6.0-darwin-x64...
Installed node-v18.6.0-darwin-x64 to $HOME/.nodenv/versions/18.6.0

Installed postinstall/postuninstall package hooks for 18.6.0

% nodenv install 18.7.0 
Downloading node-v18.7.0-darwin-x64.tar.gz...
-> https://nodejs.org/dist/v18.7.0/node-v18.7.0-darwin-x64.tar.gz
Installing node-v18.7.0-darwin-x64...
Installed node-v18.7.0-darwin-x64 to $HOME/.nodenv/versions/18.7.0

Installed postinstall/postuninstall package hooks for 18.7.0

#インストールされているNode.jsのバージョン一覧
% nodenv versions      
  17.9.1
  18.6.0
  18.7.0

# インストールされているNode.jsをアンインストール方法
% nodenv uninstall 17.9.1
nodenv: remove $HOME/.nodenv/versions/17.9.1? [yN] y
% nodenv versions        
  18.6.0
  18.7.0

バージョン変更

% node -v                
nodenv: node: command not found

The `node' command exists in these Node versions:
  18.6.0
  18.7.0

# グローバル(システム全体)で利用するNode.jsのバージョンを設定する
% nodenv global 18.7.0
% node -v             
v18.7.0

% nodenv versions               
  18.6.0
* 18.7.0 (set by $HOME/.nodenv/version)

# ローカル(カレントディレクトリ配下)で利用するNode.jsのバージョンを設定する
node % mkdir nodenvtest
node % cd nodenvtest 
nodenvtest % nodenv local 18.6.0
nodenvtest % node -v            
v18.6.0

% cat .node-version
# nodenv localコマンドで.node-versionが作成されバージョン情報が書き込まれる 
18.6.0

<補足> バージョンの切り替えについて
nodenvはrbenvからフォークされたプロジェクトです。
https://github.com/nodenv/nodenv/wiki/Why-nodenv%3F

バージョン管理構造はrbenvと同じでshimsコマンドを使用しています。 概念と仕組みについては下記記事でわかりやすく記載されていますので、共有させていただきます。
https://mogulla3.tech/articles/2021-07-10-understanding-rbenv-1/

コマンドの実行ファイル(パス)

% which node
$HOME/.nodenv/shims/node
% which npm 
$HOME/.nodenv/shims/npm

% cd $HOME/.nodenv/shims/
% ls
corepack    node        npm     npx

shims % ls -lt
total 32
-rwxr-xr-x  1 [computer_name]  staff  421  8  4 21:18 npx
-rwxr-xr-x  1 [computer_name]  staff  421  8  4 21:18 npm
-rwxr-xr-x  1 [computer_name]  staff  421  8  4 21:18 node
-rwxr-xr-x  1 [computer_name]  staff  421  8  4 21:18 corepack

バージョン毎のnodeファイル

% ls ~/.nodenv/
plugins     shims       version     versions

% ls ~/.nodenv/versions/   
18.6.0  18.7.0

% ls ~/.nodenv/versions/18.6.0/
CHANGELOG.md    README.md   include     share
LICENSE     bin     lib

シェル固有の Node バージョンを設定する方法 このバージョンはアプリケーション固有のバージョンとグローバルバージョンをオーバーライドする。

% nodenv shell 18.7.0   
% nodenv versions    
  18.6.0
* 18.7.0 (set by NODENV_VERSION environment variable)
% echo $NODENV_VERSION
18.7.0
% env | grep NODENV_VERSION
NODENV_VERSION=18.7.0
# 環境変数としてnodeのバージョンが設定されている

# ただしこのバージョンはshell instanceごとにアクティブなnodeバージョンのためshellを再起動すると削除される。
% env | grep NODENV_VERSION
% nodenv versions          
  18.6.0
* 18.7.0 (set by $HOME/.nodenv/version)

# シェル固有の Node バージョンを未設定にする
% nodenv shell --unset
% nodenv versions          
  18.6.0
* 18.7.0 (set by $HOME/.nodenv/version)
% env | grep NODENV_VERSION

まとめ

参考記事を元にnodenvをインストールしました。特に悩む点はなくインストールできました。
n以外にもnodeのパッケージツールはいくつかありますが、それぞれ設定が必要な場合もあるため少し抵抗がありました。 MacではHomebrewで他のスクリプトを管理しているため、nodenv+brewでシンプルになり、フォルダごとにバージョンを区別できるのでより管理がしやすくなるかなと思います。

iPhoneのショートカットアプリで土日、祝日、平日を考慮してアラームを指定時間で設定するオートメーションについて

iPhoneの時計アプリではアラームを設定することができますが、設定を自動化したいと思いショートカットアプリで作成することにしました。
iPhoneでは「ヘルスケア」の睡眠スケジュール機能として曜日ごとにアラームを設定できる機能がありますが、アラーム設定は平日と休日で異なる場合があります。例えば祝日の場合は休日のアラームを設定することがあると思います。
しかし、現状のヘルスケアの睡眠スケジュール機能では祝日の判定ができないため、希望通りの設定ができません。 また、睡眠スケジュールアラームには曲を選択することはできたいため、曲を指定したい場合は時計アプリを使用する必要があります。 そこで本記事ではカレンダーの情報から平日と祝日の情報を取得してアラームを設定するショートカットアプリを作成したので紹介します。

作成したもの

指定時間(デフォルトでは0時)に土、日、祝日用と平日用に2種類のアラームを設定するショートカットアプリです。

動作確認環境

iOS 13.x以降

使用手順

  1. 下記リンクよりアラーム設定のショートカットをショートカットアプリに追加する。
    ※リンクから追加画面に遷移しない場合はメモにURLを貼り付けてからアクセスしてください。 www.icloud.com
  2. 時計アプリで土、日、祝日用と平日用のアラームを作成する。
  3. アラーム設定のショートカットに2.で作成した時間を土、日、祝日用と平日用のアラームについて、アラームの切り替えアクションでオン、オフをそれぞれ設定する。
  4. 新規オートメーション画面で個人用オートメーションを作成して時刻を00:00、繰り返しを毎日に設定し、アクションを追加ボタンから1.で追加したアラーム設定のショートカットを設定する。

注意事項

土、日、祝日は同じ時間のアラームを設定するようにしています。(個人的に土、日、祝日で分ける必要はなかったためです。)
それぞれ設定する場合は後述するアラーム設定用の変数と判定処理を追加すれば対応可能です。
実行可能な端末はiPhoneiPadです。Macでもショートカットアプリはサポートしていますが、時計アプリとオートメーションが未サポート(※)のため実行できません。
MacOS バージョン12.5現在

ショートカットの処理について

以下アクティビティ図に従い処理します。
※アラーム設定のショートカットにもコメントを追加していますので合わせて確認ください。

autoAlarmSettingShortcutAutomation.png

<補足>アクティビティ図のソースコード(.puml)

まとめ

iPhoneの時計アプリのアラームを自動で設定するショートカットアプリを作成しました。
カレンダーから土日、祝日、平日の情報を取得、判定することでアラームを設定しました。 気になった方は是非使ってみてください。
本来は寝る時間も起きる時間も固定化して規則正しい睡眠を取ることが理想ですが、寝る時間はバラついているので寝るまでの時間も意識して習慣化したいなと思っています。