PythonでCLIを含んだパッケージをhatchで作る
記事の概要
PythonでCLIを作ろうと思った時、rubyでいうところの bundle gem
的なものがないかと探していたところ、hatchに辿り着いたので使い方をメモしておきます。
この記事で取り扱うのは下記の内容です。
- 環境準備
- パッケージの雛形生成
- ローカル環境での動作確認
- Lintチェック
- テスト
- カバレッジ
- パッケージング
- 依存ライブラリの管理
0. 環境準備
まずは作業用のフォルダと仮想環境を準備する。
$ mkdir sandbox_hatch
$ cd sandbox_hatch
$ python3.11 -m venv venv
$ source venv/bin/activate
次に仮想環境のpipをアップデートする
(venv) $ pip install --upgrade pip
すでに最新のバージョンが入っていた場合、下記のような表示になります。
Requirement already satisfied: pip in ./venv/lib/python3.11/site-packages (23.2.1)
そして肝腎要のhatchをインストールします。
(venv) $ pip install hatch
Collecting hatch
Using cached hatch-1.7.0-py3-none-any.whl (90 kB)
Collecting click>=8.0.3 (from hatch)
〜〜〜〜〜(中略)〜〜〜〜〜
Successfully installed anyio-4.0.0 certifi-2023.7.22 click-8.1.7 distlib-0.3.7 editables-0.5 filelock-3.12.4 h11-0.14.0 hatch-1.7.0 hatchling-1.18.0 httpcore-0.18.0 httpx-0.25.0 hyperlink-21.0.0 idna-3.4 importlib-metadata-6.8.0 jaraco.classes-3.3.0 keyring-24.2.0 markdown-it-py-3.0.0 mdurl-0.1.2 more-itertools-10.1.0 packaging-23.2 pathspec-0.11.2 pexpect-4.8.0 platformdirs-3.11.0 pluggy-1.3.0 ptyprocess-0.7.0 pygments-2.16.1 pyperclip-1.8.2 rich-13.6.0 shellingham-1.5.3 sniffio-1.3.0 tomli-w-1.0.0 tomlkit-0.12.1 trove-classifiers-2023.9.19 userpath-1.9.1 virtualenv-20.24.5 zipp-3.17.0
1. hatchプロジェクトを作成する
hatch new
でプロジェクトを作成する。
--cli
オプションを付けることで、CLI用のファイルも作成されます。
(venv) $ hatch new --cli sandbox_hatch
sandbox-hatch
├── src
│ └── sandbox_hatch
│ ├── cli
│ │ └── __init__.py
│ ├── __about__.py
│ ├── __init__.py
│ └── __main__.py
├── tests
│ └── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml
引数で指定したフォルダ内にファイルが作成されるので、
カレントディレクトリに引き上げます。
(venv) $ mv sandbox-hatch/* .
(venv) $ rmdir sandbox-hatch
改めてカレントディレクトリのファイル構成を確認してみます。
(venv) $ ls
LICENSE.txt README.md pyproject.toml sandbox-hatch src tests venv
venvと同じフォルダに作成したものが引き上げられていることを確認します。
今回は --cli
オプション付きで hatch new
しているので、下記のコマンドを使って実行結果を試すことができます。
(venv) $ hatch run sandbox-hatch
Hello world!
さて、今回は hatch new --cli sandbox_hatch
としてプロジェクトを作成したにもかかわらず、何故 hatch run sandbox-hatch
とアンダースコアがハイフンに変わっているのか、その理由は次章で触れていきます。
2. 作成したプロジェクトをインストールする
作成したプロジェクトを、編集可能フラグを立ててインストールしてみます。
(venv) $ pip install -e .
Obtaining file://.
Installing build dependencies ... done
Checking if build backend supports build_editable ... done
Getting requirements to build editable ... done
Preparing editable metadata (pyproject.toml) ... done
Requirement already satisfied: click in ./venv/lib/python3.11/site-packages (from sandbox-hatch==0.0.1) (8.1.7)
Building wheels for collected packages: sandbox-hatch
Building editable for sandbox-hatch (pyproject.toml) ... done
Created wheel for sandbox-hatch: filename=sandbox_hatch-0.0.1-py3-none-any.whl size=2614 sha256=391e8d22fa94dc69cdb4de88f4188fdd55b5d92902e117ae4205b7c490466a18
Stored in directory: /private/var/folders/wr/ly12hvsn5qgg50t8z492xfcw0000gn/T/pip-ephem-wheel-cache-zg7e7rd4/wheels/ce/2d/1e/a52e5683b9e64b8d761557282658c1fc98c8fd170a2e4a0e9a
Successfully built sandbox-hatch
Installing collected packages: sandbox-hatch
Successfully installed sandbox-hatch-0.0.1
これで仮想環境内にプロジェクト名と同じCLIがインストールされました。
実行する際には1つ注意が必要です。
pythonの世界ではパッケージ名にアンダースコアを使うことはPEP8により推奨されていません。
その影響で今回の sandbox_hatch
のようなプロジェクト名は、 sandbox-hatch
のように、 アンダースコアがハイフンに置き換えられたもの になります。
今回の実行コマンドも同様に sandbox-hatch
に変換されています。
そのため実行は下記のように行います。
(venv) $ sandbox-hatch
Hello world!
また、パッケージ名とは逆に、 モジュール名ではハイフンの利用は非推奨 となっています。
そのため、モジュール名として使用される src
配下のフォルダ名は sandbox_hatch
になります。
hatchはこのハイフンとアンダースコアの変換を自動でやってくれるのが便利なポイントだなぁ。
ちなみにこのCLIは編集可能モードでインストールしているので、変更が都度反映されます。
例えばCLIのファイル src/sandbox_hatch/cli/__init__.py
を、下記のように修正します。
〜〜〜〜〜(前略)〜〜〜〜〜
@click.group(context_settings={"help_option_names": ["-h", "--help"]}, invoke_without_command=True)
@click.version_option(version=__version__, prog_name="sandbox_hatch")
def sandbox_hatch():
- click.echo("Hello world!")
+ click.echo("Hello world??")
編集後に保存し、再度コマンドを実行すると下記のように出力が変化します。
(venv) $ sandbox-hatch
Hello world??
このように編集可能フラグを有効にしておくことで、再インストールをすることなく、編集したプログラムをすぐ試すことができるようになります。
3. プロジェクト内ファイルの構文チェックをする
hatchはデフォルトでLintチェックツールもインストールしてくれます。
実行する場合は下記のように hatch run lint:all
コマンドを実行します。
(venv) $ hatch run lint:all
cmd [1] | ruff .
cmd [2] | black --check --diff .
All done! ✨ 🍰 ✨
5 files would be left unchanged.
cmd [3] | mypy --install-types --non-interactive src/sandbox_hatch tests
Success: no issues found in 5 source files
ここで、あえてチェックに引っ掛かるようにファイルを修正してみます。
先ほどのCLIのファイル src/sandbox_hatch/cli/__init__.py
を、下記のように修正します。
(def sandbox():
と click.echo()
の間に、空白行を追加します。)
〜〜〜〜〜(前略)〜〜〜〜〜
@click.group(context_settings={"help_option_names": ["-h", "--help"]}, invoke_without_command=True)
@click.version_option(version=__version__, prog_name="sandbox_hatch")
def sandbox_hatch():
+
click.echo("Hello world??")
この状態でファイルを保存して、コマンド hatch run lint:all
を実行してみると下記の通りになります。
(venv) $ hatch run lint:all
cmd [1] | ruff .
cmd [2] | black --check --diff .
--- ./src/sandbox_hatch/cli/__init__.py2023-10-09 10:02:41.192685+00:00
+++ ./src/sandbox_hatch/cli/__init__.py2023-10-09 10:02:49.692162+00:00
@@ -7,7 +7,6 @@
@click.group(context_settings={"help_option_names": ["-h", "--help"]}, invoke_without_command=True)
@click.version_option(version=__version__, prog_name="sandbox_hatch")
def sandbox_hatch():
-
click.echo("Hello world??")
would reformat ./src/sandbox_hatch/cli/__init__.py
Oh no! 💥 💔 💥
1 file would be reformatted, 4 files would be left unchanged.
構文エラーとして先ほどの修正が発見、指摘されました。
これは手で直すこともできますが、hatchの場合はコマンドである程度自動修正することもできます。
コマンドで自動修正を行う場合は hatch run lint:fmt
を実行します。
(venv) $ hatch run lint:fmt
cmd [1] | black .
reformatted ./src/sandbox_hatch/cli/__init__.py
All done! ✨ 🍰 ✨
1 file reformatted, 4 files left unchanged.
cmd [2] | ruff --fix .
cmd [3] | ruff .
cmd [4] | black --check --diff .
All done! ✨ 🍰 ✨
5 files would be left unchanged.
これで再度ファイル src/sandbox_hatch/cli/__init__.py
を開いてみると、先程の構文エラーが修正されていることが確認できます。
〜〜〜〜〜(前略)〜〜〜〜〜
@click.group(context_settings={"help_option_names": ["-h", "--help"]}, invoke_without_command=True)
@click.version_option(version=__version__, prog_name="sandbox_hatch")
def sandbox_hatch():
click.echo("Hello world??")
mypyの設定を変更したい場合は、下記のページを参考に設定することができます。
- [公式] The mypy configuration file: https://mypy.readthedocs.io/en/stable/config_file.html
4. テストを実行する
hatchで作成されたプロジェクトには、pytestが含まれています。
テストは、コマンド hatch run test
で実行できます。
(venv) $ hatch run test
========================= test session starts ==========================
platform darwin -- Python 3.11.5, pytest-7.4.2, pluggy-1.3.0
rootdir: .
collected 0 items
======================== no tests ran in 0.00s =========================
デフォルトでは何もテストが記載されていません。
試しにテストを実装してみます。
今回は Hello World!
を、固定文字列ではなくメソッドから返された値に変更する想定で書き進めます。
まずはテストコードを置くフォルダを作成し、テストファイルを編集します。
(venv) $ mkdir -p tests/sandbox_hatch/cli
(venv) $ vi tests/sandbox_hatch/cli/test_message_generator.py
テストファイル tests/sandbox_hatch/cli/test_message_generator.py
は、下記の内容で作成します。
from sandbox_hatch.cli.message_generator import message
def test_message():
assert message() == "Hoge!"
次にプロダクトコードを実装します。
(venv) $ vi src/sandbox_hatch/cli/message_generator.py
ファイル src/sandbox_hatch/cli/message_generator.py
に、下記のコードを記述します。
def message() -> str:
return "Hoge!"
改めてテストを実行します。
(venv) $ hatch run test
========================= test session starts ==========================
platform darwin -- Python 3.11.5, pytest-7.4.2, pluggy-1.3.0
rootdir: .
collected 1 item
tests/sandbox_hatch/cli/test_message_generator.py . [100%]
========================== 1 passed in 0.10s ===========================
テストが認識され、正常に終了しました。
これをCLIの実行結果に出力するように修正します。
〜〜〜〜〜(前略)〜〜〜〜〜
@click.group(context_settings={"help_option_names": ["-h", "--help"]}, invoke_without_command=True)
@click.version_option(version=__version__, prog_name="sandbox_hatch")
def sandbox_hatch():
- click.echo("Hello world??")
+ click.echo(message())
これで再度コマンドを実装すると、上記で実装した Hoge!
が表示されるようになっています。
(venv) $ sandbox-hatch
Hoge!
その他詳細なテストの書き方については下記を参照。
- [公式] How to write and report assertions in tests: https://docs.pytest.org/en/7.4.x/how-to/assert.html
5. テストカバレッジを確認する
hatchはテストの実行だけでなく、カバレッジを表示することもできます。
(venv) $ hatch run cov
cmd [1] | coverage run -m pytest tests
========================= test session starts ==========================
platform darwin -- Python 3.11.5, pytest-7.4.2, pluggy-1.3.0
rootdir: .
collected 1 item
tests/sandbox_hatch/cli/test_message_generator.py . [100%]
========================== 1 passed in 0.03s ===========================
CoverageWarning: Module tests was never imported. (module-not-imported)
self.warn(f"Module {pkg} was never imported.", slug="module-not-imported")
cmd [2] | - coverage combine
Combined data file .coverage.local.35102.864206
cmd [3] | coverage report
Name Stmts Miss Branch BrPart Cover
------------------------------------------------------------------------------
src/sandbox_hatch/__init__.py 0 0 0 0 100%
src/sandbox_hatch/__main__.py 1 1 0 0 0%
src/sandbox_hatch/cli/__init__.py 7 1 6 0 92%
src/sandbox_hatch/cli/message_generator.py 2 0 0 0 100%
------------------------------------------------------------------------------
TOTAL 10 2 6 0 88%
これを見ると、 src/sandbox_hatch/cli/__init__.py
と src/sandbox_hatch/__main__.py
に対するテストが足りていない事がわかります。
ではどこが足りていないのか。コマンドラインでファイルのカバレッジのみを出力しましたが、何行目かを追加で表示させてみます。
カバレッジも、前記したLintチェックでのmypyやテストでのpytestのようにライブラリを組み合わせて使っています。
カバレッジの場合はその名の通り coverage
が使われており、それは pyproject.toml
の50行目付近にある、 [tool.hatch.envs.default.scripts]
に定義されています。
〜〜〜〜〜(前略)〜〜〜〜〜
[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
cov-report = [
"- coverage combine",
"coverage report",
]
cov = [
"test-cov",
"cov-report",
]
〜〜〜〜〜(後略)〜〜〜〜〜
coverage report
コマンドは、 -m
オプションで行を表示できるので、上記で設定されている coverage report
を coverage report -m
のように修正します。
〜〜〜〜〜(前略)〜〜〜〜〜
[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
cov-report = [
"- coverage combine",
- "coverage report",
+ "coverage report -m",
]
cov = [
"test-cov",
"cov-report",
]
〜〜〜〜〜(後略)〜〜〜〜〜
そうすると、出力が下記のように変化します。
(venv) $ hatch run cov
cmd [1] | coverage run -m pytest tests
========================= test session starts ==========================
platform darwin -- Python 3.11.5, pytest-7.4.2, pluggy-1.3.0
rootdir: .
collected 1 item
tests/sandbox_hatch/cli/test_message_generator.py . [100%]
========================== 1 passed in 0.04s ===========================
CoverageWarning: Module tests was never imported. (module-not-imported)
self.warn(f"Module {pkg} was never imported.", slug="module-not-imported")
cmd [2] | - coverage combine
Combined data file .coverage.local.36972.967737
cmd [3] | coverage report -m
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------
src/sandbox_hatch/__init__.py 0 0 0 0 100%
src/sandbox_hatch/__main__.py 1 1 0 0 0% 4
src/sandbox_hatch/cli/__init__.py 7 1 6 0 92% 13
src/sandbox_hatch/cli/message_generator.py 2 0 0 0 100%
----------------------------------------------------------------------------------------
TOTAL 10 2 6 0 88%
出力内容を確認すると、 Cover
の右側に Missing
という列が追加されています。
ここに表示されている数字が、当該ファイルでテストされていない行を表しています。
さて、カバレッジの詳細化に併せて、hatchのコマンド内容が簡単に書き換えられることがわかりました。
先程記述を変更したところを少し解説します。
[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
cov-report = [
"- coverage combine",
"coverage report -m",
]
cov = [
"test-cov",
"cov-report",
]
カバレッジを実行するとき、 hatch run cov
というコマンドを実行しました。これは cov = [
という記載とリンクしています。
cov = [
には test-cov
と cov-report
が含まれています。これらはその上にある test-cov = [
と cov-report = [
にリンクしています。
このうち cov-report = [
に coverage report
コマンドが含まれていたので、そこに -m
を加えることで hatch run cov
の挙動を変えた。
・・・というのが先程の顛末でした。
これを応用すると、例えばこういうこともできます。
pyproject.toml
に下記を追加します。
〜〜〜〜〜(前略)〜〜〜〜〜
[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
cov-report = [
"- coverage combine",
"coverage report -m",
]
cov = [
"test-cov",
"cov-report",
]
+ cov-html = [
+ "- coverage combine",
+ "coverage html",
+ ]
〜〜〜〜〜(後略)〜〜〜〜〜
これで新たに cov-html
を追加できました。
実行してみると下記のようになります。
(venv) $ hatch run cov-html
cmd [1] | - coverage combine
No data to combine
cmd [2] | coverage html
Wrote HTML report to htmlcov/index.html
これは、記載の通り coverage html
コマンドを実行するhatchコマンドです。
これにより生成された htmlcov/index.html
をブラウザで開くことで、より詳細なカバレッジ情報を確認することができるようになりました。
上記で指摘されているところは下記の2箇所でした。
-
src/sandbox_hatch/__main__.py
の4行目 -
src/sandbox_hatch/cli/__init__.py
の13行目
これを直していきます。
1つ目の src/sandbox_hatch/__main__.py
の4行目については、 import sys
にマークが付いています。
これはテストファイルを作ることで解消はできますが、importのみが書かれているだけのテストファイルに価値がないと考えるので、この行をカバレッジ対象外にするようにします。
カバレッジの測定対象外とするためには、下記のコメントを対象となる行の末尾に追記します。
# pragma: no cover
# SPDX-FileCopyrightText: 2023-present
#
# SPDX-License-Identifier: MIT
- import sys
+ import sys # pragma: no cover
if __name__ == "__main__":
from sandbox_hatch.cli import sandbox_hatch
sys.exit(sandbox_hatch())
これで1つ目は警告が出なくなりました。
次に2つ目、 src/sandbox_hatch/cli/__init__.py
の13行目について見ていくと、リファクタリングしたポイントが対象になっています。
そのため、ファイル tests/sandbox_hatch/cli/test___init__.py
を作成し、下記のテストを追記します。
import click # noqa: F401
from click.testing import CliRunner
from sandbox_hatch.cli import sandbox_hatch
def test_sandbox_hatch():
runner = CliRunner()
result = runner.invoke(sandbox_hatch, [])
assert result.exit_code == 0
assert result.output == "Hoge!\n"
hatchが生成するCLIはclickを使っているため、これはclickに対応したテストになります。
clickに対応するために click.testing
を読み込みますが、その前段で click
をimportするのですが、これは直接使わないためLintエラーになります。その対応のため、lintチェックを無視するための noqa
を追記しています。
カバレッジの無視は no cover
、Lintチェックの無視は noqa
を使います。
6. プロジェクト全体のチェックをする
ここで改めてチェックしてみます。
(venv) $ hatch run lint:all
cmd [1] | ruff .
cmd [2] | black --check --diff .
All done! ✨ 🍰 ✨
8 files would be left unchanged.
cmd [3] | mypy --install-types --non-interactive src/sandbox_hatch tests
Success: no issues found in 8 source files
(venv) $ hatch run cov
cmd [1] | coverage run -m pytest tests
========================= test session starts ==========================
platform darwin -- Python 3.11.5, pytest-7.4.2, pluggy-1.3.0
rootdir: .
collected 2 items
tests/sandbox_hatch/cli/test___init__.py . [ 50%]
tests/sandbox_hatch/cli/test_message_generator.py . [100%]
========================== 2 passed in 0.02s ===========================
CoverageWarning: Module tests was never imported. (module-not-imported)
self.warn(f"Module {pkg} was never imported.", slug="module-not-imported")
cmd [2] | - coverage combine
Combined data file .coverage.local.47437.317629
cmd [3] | coverage report -m
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------------------
src/sandbox_hatch/__init__.py 0 0 0 0 100%
src/sandbox_hatch/__main__.py 0 0 0 0 100%
src/sandbox_hatch/cli/__init__.py 7 0 6 0 100%
src/sandbox_hatch/cli/message_generator.py 2 0 0 0 100%
----------------------------------------------------------------------------------------
TOTAL 9 0 6 0 100%
ここまでの修正でLintチェック、テスト、カバレッジの全てが通ったhatchプロジェクトを作成することができました。
7. パッケージをビルドする
これまで作ったプロジェクトをビルドして、パッケージを作成してみます。
コマンド hatch build
を実行することで、ビルドすることができます。
(venv) $ hatch build
[sdist]
dist/sandbox_hatch-0.0.1.tar.gz
[wheel]
dist/sandbox_hatch-0.0.1-py3-none-any.whl
ビルドが成功すると dist
フォルダ内にtar.gzとwhlファイルが各々生成されます。
(venv) $ ls dist
sandbox_hatch-0.0.1-py3-none-any.whl sandbox_hatch-0.0.1.tar.gz
生成されたパッケージをインストールしてみます。
今の仮想環境にはすでに開発中の sandbox-hatch
がインストールされているので、一度仮想環境を抜けます。
(venv) $ deactivate
次に作成したパッケージをインストールして動作環境を行うための、仮想環境を作成します。
$ python3.11 -m venv test_venv
$ source test_venv/bin/activate
この環境には sandbox-hatch
がインストールされていないので、 sandbox-hatch
コマンドは失敗します。
(test_venv) $ sandbox-hatch
zsh: command not found: sandbox-hatch
tar.gzファイル版のパッケージをインストールしてみる。
(test_venv) $ pip install --no-index --find-links=./dist/ sandbox-hatch
Looking in links: ./dist/
Processing ./dist/sandbox_hatch-0.0.1-py3-none-any.whl
Requirement already satisfied: click in ./test_venv/lib/python3.11/site-packages (from sandbox-hatch) (8.1.7)
Installing collected packages: sandbox-hatch
Successfully installed sandbox-hatch-0.0.1
インストールが成功した場合、開発環境と同様の出力が表示される。
(test_venv) $ sandbox-hatch
Hoge!
パッケージをアンインストールする場合は下記のコマンドを実行します。
(test_venv) $ pip uninstall sandbox-hatch -y
Found existing installation: sandbox-hatch 0.0.1
Uninstalling sandbox-hatch-0.0.1:
Successfully uninstalled sandbox-hatch-0.0.1
これでこの仮想環境からは sandbox-hatch
が削除されてたので、 sandbox-hatch
コマンドは失敗します。
(test_venv) $ sandbox-hatch
zsh: command not found: sandbox-hatch
次はwhlファイル版のパッケージをインストールしてみる。
(test_venv) $ pip install ./dist/sandbox_hatch-0.0.1-py3-none-any.whl
Processing ./dist/sandbox_hatch-0.0.1-py3-none-any.whl
Collecting click (from sandbox-hatch==0.0.1)
Obtaining dependency information for click from https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl.metadata
Using cached click-8.1.7-py3-none-any.whl.metadata (3.0 kB)
Using cached click-8.1.7-py3-none-any.whl (97 kB)
Installing collected packages: click, sandbox-hatch
Successfully installed click-8.1.7 sandbox-hatch-0.0.1
これでwhlファイル版もパッケージのインストールも完了しました。
(test_venv) $ sandbox-hatch
Hoge!
動作確認が完了したら、インストールして動作環境を行うための仮想環境を抜けておきます。
(test_venv) $ deactivate
開発を再開する場合は再度開発用の仮想環境を有効にすることで、開発を進めることができます。
$ source venv/bin/activate
8. 依存ライブラリを追加する
CLIが参照する依存ライブラリは、 pyproject.toml
で管理します。
通常使用するライブラリは [project]
の dependencies = [
に、テストで使用するライブラリは [tool.hatch.envs.default]
の dependencies = [
に追記します。
〜〜〜〜〜(前略)〜〜〜〜〜
[project]
〜〜〜〜〜(中略)〜〜〜〜〜
dependencies = [
"click",
# ここに通常使用するライブラリを追加する
]
〜〜〜〜〜(中略)〜〜〜〜〜
[tool.hatch.envs.default]
dependencies = [
"coverage[toml]>=6.5",
"pytest",
# ここにテストで使用するライブラリを追加する
]
〜〜〜〜〜(前略)〜〜〜〜〜
まとめ
以上がhatchを使ってPythonでCLIを作るための各種手順のメモでした。
rubyでいうところの bundle gem
的なものがないかと探していたところ、見つけたhatchでしたが、思った以上に色々サポートされていて使いやすいイメージでした。
パッケージをpublishすることもできそうですが、まずはちょっとしたコードをまとめておくことを目的に評価していたので、一旦はここまでで止めておこうと思います。
しばらくはhatchを使ってみよう。
Discussion