実践編
前編では、bashスクリプトをpythonで書き換えた経緯と、実装した機能(サブコマンド)について記載した。しかし、このツールの目的は、Pythonで可搬的なスクリプトを作成する際にいつも行う定型作業を自動化するというところにある。基本的には、initサブコマンドを実行すればスクリプトにやりたいことを実装する最低限の準備作業が完了できるようにしている。以下、想定される利用例をいくつか挙げてみる
ファイル置き場
可搬的なスクリプトのセットアップ
新たにmy-new-toolsという名前のツールセットを作成してみます。実行可能なPythonスクリプト(my_new_work_tool.py)と、それに必要なライブラリクラス/関数は別のmy_utils.pyというファイルに実装していくようにしたいとします。また、これらに機能を実装する際にPythonの非標準モジュールのdateutilsを使うことが想定されているとします。
- 作業ディレクトリ(
${workdir})の下のpy_sandboxにpy_encaseをダウンロードする - それを使って"${workdir}"/my-new-toolsというプロジェクトディレクトリを作成し、その下にファイルを作成
-
README.mdのスケルトンを作成 -
gitのレポジトリとして初期化。ローカルレポジトリとしてだけでなく、remotehost.remotedomainというホストにマスターレポジトリも作成。(ssh経由)
これらは
% pip3 install --target "${workdir}"/py_sandbox py-encase
% env PYTHONPATH="${workdir}"/py_sandbox: \
"${workdir}"/py_sandbox/bin/py_encase \
--manage init --verbose \
--prefix "${workdir}"/my-new-tools \
--readme --title 'Tools for my work ....... ' \
--app-framework \
--module dateutils \
--required-module \
--script-lib 'my_utils.py' \
--setup-git \
--git-user-name 'my_git_account' \
--git-user-email 'my_git_account@my_git_host.domain' \
--git-set-upstream \
--git-remote-setup \
--git-remote-account remote_account \
--git-remote-host remotehost.remotedomain \
--git-remote-path '~/git_repositories/' \
--git-remote-share group \
--git-protocol ssh \
my_new_work_tool
長いですが、コマンド2つで準備完了です。
--git-remote-account,--git-remote-host,--git-remote-pathの3つのコマンドラインオプションは、それぞれ環境変数GIT_REMOTE_USER,GIT_REMOTE_HOST,GIT_REMOTE_PATHでも指定可能です。
もしGitHubのCLI(gh)もしくはGitLabのCLI(glab)で、ユーザー情報の登録がセットアップされていれば、 --git-user-nameと--git-user-emailの2つのオプションの代わりに、--github-userinfoもしくは--gitlab-userinfoというオプションが指定可能です。また、リモートレポジトリとしてGitHubもしくはGitLabを使う場合には、--git-remote-account,--git-remote-host,--git-remote-path,--git-remote-share,--git-protocolの5つのオプションの代わりに、--git-hosting githubもしくは--git-hosting gitlabの1つのオプションで指定可能です。
[.....] mkdir -p : '${workdir}/my-new-tools'
[.....] mkdir -p : '${workdir}/my-new-tools/bin'
[.....] mkdir -p : '${workdir}/my-new-tools/var'
[.....] mkdir -p : '${workdir}/my-new-tools/src'
[.....] mkdir -p : '${workdir}/my-new-tools/var/tmp/python/packages/3.12.11'
[.....] mkdir -p : '${workdir}/my-new-tools/var/log'
[.....] mkdir -p : '${workdir}/my-new-tools/lib/python'
[.....] mkdir -p : '${workdir}/my-new-tools/lib/python/site-packages/3.12.11'
[.....] mkdir -p : '${workdir}/my-new-tools/var/cache/python/packages/3.12.11'
[.....] mkdir -p : '${workdir}/my-new-tools/src/python/packages/3.12.11'
[.....] mkdir -p : '${workdir}/my-new-tools/var/log/pip/25.1.1'
[.....] cp -ai '${workdir}/py_sandbox/py_encase/py_encase.py' '${workdir}/my-new-tools/bin/py_encase.py'
[.....] make symbolic link : '${workdir}/my-new-tools/bin/mng_encase' --> 'py_encase.py'
[.....] gitignore : '${workdir}/my-new-tools/.gitignore'
[.....] put .gitkeep in '${workdir}/my-new-tools/lib/python/site-packages'
[.....] put .gitkeep in '${workdir}/my-new-tools/var/cache/python/packages'
[.....] put .gitkeep in '${workdir}/my-new-tools/src/python/packages'
[.....] put .gitkeep in '${workdir}/my-new-tools/var/log/pip'
[.....] put .gitkeep in '${workdir}/my-new-tools/var/tmp/python/packages'
[.....] Exec : '/usr/bin/git init ${workdir}/my-new-tools'
[.....] Return code(/usr/bin/git): 0
[.....] STDOUT(/usr/bin/git) : 'Initialized empty Git repository in ${workdir}/my-new-tools/.git/'
[.....] Exec : '/usr/bin/git config --file ${workdir}/my-new-tools/.git/config user.name my_git_account'
[.....] Return code(/usr/bin/git): 0
[.....] Exec : '/usr/bin/git config --file ${workdir}/my-new-tools/.git/config user.email my_git_account@my_git_host.domain'
[.....] Return code(/usr/bin/git): 0
[.....] Exec : '/usr/bin/ssh remotehost.remotedomain ( test -d ~/git_repositories/my-new-tools.git ||
git init --bare ~/git_repositories/my-new-tools.git )'
[.....] Return code(/usr/bin/ssh): 0
[.....] STDOUT(/usr/bin/ssh) : 'Initialized empty Git repository in ...../git_repositories/my-new-tools.git/'
[.....] Exec : '/usr/bin/git --git-dir ${workdir}/my-new-tools/.git
--work-tree ${workdir}/my-new-tools remote add origin
ssh://remote_account@remotehost.remotedomain/...../git_repositories/my-new-tools.git'
[.....] Return code(/usr/bin/git): 0
[.....] Exec : '/usr/bin/git --git-dir ${workdir}/my-new-tools/.git
--work-tree ${workdir}/my-new-tools commit
--allow-empty -m Initialize Repository'
[.....] Return code(/usr/bin/git): 0
[.....] STDOUT(/usr/bin/git) : '[main (root-commit) 2c0d6bb] Initialize Repository'
[.....] Exec : '/usr/bin/git --git-dir ${workdir}/my-new-tools/.git
--work-tree ${workdir}/my-new-tools push -u origin main'
[.....] Return code(/usr/bin/git): 0
[.....] STDOUT(/usr/bin/git) : 'branch 'main' set up to track 'origin/main'.'
[.....] STDERR(/usr/bin/git) : 'To ssh://remotehost.remotedomain/...../git_repositories/my-new-tools.git
* [new branch] main -> main'
[.....] Save Readme file : '${workdir}/my-new-tools/README.md'
[.....] Preparing python library file from template : '${workdir}/my-new-tools/lib/python/my_new_work_tool.py'
[.....] make symbolic link : '${workdir}/my-new-tools/bin/my_new_work_tool' --> 'py_encase.py'
[.....] Preparing python library file from template : '${workdir}/my-new-tools/lib/python/my_utils.py'
[.....] Preparing python library file from template : '${workdir}/my-new-tools/lib/python/streamextd.py'
[.....] KV file: '${workdir}/my-new-tools/share/my-new-tools/my_new_work_tool.kv'
[.....] Exec: '.../bin/pip-3.12 --python .../bin/python3.12 install
--cache-dir ${workdir}/my-new-tools/var/cache/python/packages/3.12.11
--log ${workdir}/my-new-tools/var/log/pip/25.1.1/pip-log.txt
--target ${workdir}/my-new-tools/lib/python/site-packages/3.12.11
--src ${workdir}/my-new-tools/src/python/packages/3.12.11
dateutils pytz tzlocal pkgstruct argparse_extd
psutil sshkeyring enc_ds plyer pyobjus kivy'
Collecting dateutils
Downloading dateutils-0.6.12-py2.py3-none-any.whl.metadata (1.3 kB)
Collecting pytz
Downloading pytz-2025.2-py2.py3-none-any.whl.metadata (22 kB)
.....
このようにコマンド2つを実行すれば、
- すぐに
${workdir}/my-new-tools/lib/python/my_new_work_tool.py、${workdir}/my-new-tools/lib/python/my_utils.pyに実際のコードを実装していくことが可能、 -
${workdir}/my-new-tools/bin/my_new_work_toolで実行可能、 - 編集後はすぐに
git add ...,git commit ...,git push ...できる
ので、アイデアをコードに実装することに精神を集中できます。
Pythonモジュールの開発環境のセットアップ
新たにmy_module_devというディレクトリ以下にPythonモジュール(とそれを使う)の開発環境セットアップしてみます。たとえば、これらに機能を実装する際にPythonの非標準モジュールのdateutilsを使うことが想定されているとします。
- 作業ディレクトリ(
${workdir})の下のpy_sandboxにpy_encaseをダウンロードする - それを使って"${workdir}"/my_module_devというプロジェクトディレクトリを作成し、その配下のサブディレクトリ
src以下にpythonモジュールに必要なファイルのテンプレートを作成 -
gitのレポジトリとして初期化。ローカルレポジトリとしてだけでなく、remotehost.remotedomainというホストにマスターレポジトリも作成。(ssh経由)
% pip3 install --target "${workdir}"/py_sandbox py-encase
% env PYTHONPATH=$workdir/py_sandbox: \
"${workdir}"/py_sandbox/bin/py_encase \
--manage init --verbose \
--prefix "${workdir}"/my_module_dev \
--readme \
--title 'Modules Development ....... ' \
% "${workdir}"/my_module_dev/bin/mng_encase \
newmodule --verbose \
--title 'My New Work Utils' \
--description 'Utility classes for ....' \
--module-website 'https://github.com/ACOUNT/REPOSITORY' \
--class-name 'MyNewWorkUtils' \
--keywords 'utility' \
--classifiers 'Programming Language :: Python :: 3' \
--author-name 'my_name' \
--author-email 'my_email@myhost.domain' \
--maintainer-name 'my_name' \
--maintainer-email 'my_email@myhost.domain' \
--module dateutils \
--git-user-name 'my_git_account' \
--git-user-email 'my_git_account@my_git_host.domain' \
--git-set-upstream \
--git-remote-setup \
--git-remote-account remote_account \
--git-remote-host remotehost.remotedomain \
--git-remote-path '~/git_repositories' \
--git-remote-share group \
--git-protocol ssh \
my_new_work_utils
前の例と同じく、GitHub CLI(gh)もしはGitLab CLI(glab)のアカウント設定が完了していれば、git-remote関係のオプションは簡略化可能です。
[.....] mkdir -p : '${workdir}/my_module_dev'
[.....] mkdir -p : '${workdir}/my_module_dev/bin'
[.....] mkdir -p : '${workdir}/my_module_dev/var'
[.....] mkdir -p : '${workdir}/my_module_dev/src'
[.....] mkdir -p : '${workdir}/my_module_dev/var/tmp/python/packages/3.12.11'
[.....] mkdir -p : '${workdir}/my_module_dev/var/log'
[.....] mkdir -p : '${workdir}/my_module_dev/lib/python'
[.....] mkdir -p : '${workdir}/my_module_dev/lib/python/site-packages/3.12.11'
[.....] mkdir -p : '${workdir}/my_module_dev/var/cache/python/packages/3.12.11'
[.....] mkdir -p : '${workdir}/my_module_dev/src/python/packages/3.12.11'
[.....] mkdir -p : '${workdir}/my_module_dev/var/log/pip/25.1.1'
[.....] cp -ai '${workdir}/py_sandbox/py_encase/py_encase.py' '${workdir}/my_module_dev/bin/py_encase.py'
[.....] make symbolic link : '${workdir}/my_module_dev/bin/mng_encase' --> 'py_encase.py'
[.....] Save Readme file : '${workdir}/my_module_dev/README.md'
[.....] mkdir -p : '${workdir}/my_module_dev/src/my-new-work-utils'
[.....] mkdir -p : '${workdir}/my_module_dev/src/my-new-work-utils/test'
[.....] mkdir -p : '${workdir}/my_module_dev/src/my-new-work-utils/src'
[.....] mkdir -p : '${workdir}/my_module_dev/src/my-new-work-utils/src/my_new_work_utils'
[.....] put .gitkeep in '${workdir}/my_module_dev/src/my-new-work-utils/test'
[.....] gitignore : '${workdir}/my_module_dev/src/my-new-work-utils/.gitignore'
[.....] Exec : '/usr/bin/git init ${workdir}/my_module_dev/src/my-new-work-utils'
[.....] Return code(/usr/bin/git): 0
[.....] STDOUT(/usr/bin/git) : 'Initialized empty Git repository in ${workdir}/my_module_dev/src/my-new-work-utils/.git/
'
[.....] Exec : '/usr/bin/git config --file ${workdir}/my_module_dev/src/my-new-work-utils/.git/config user.name my_git_account'
[.....] Return code(/usr/bin/git): 0
[.....] Exec : '/usr/bin/git config --file ${workdir}/my_module_dev/src/my-new-work-utils/.git/config user.email my_git_account@my_git_host.domain'
[.....] Return code(/usr/bin/git): 0
[.....] Exec : '/usr/bin/ssh remotehost.remotedomain ( test -d ~/git_repositories/my-new-work-utils.git ||
git init --bare ~/git_repositories/my-new-work-utils.git )'
[.....] Return code(/usr/bin/ssh): 0
[.....] STDOUT(/usr/bin/ssh) : 'Initialized empty Git repository in ...../git_repositories/my-new-work-utils.git/'
[.....] Exec : '/usr/bin/git --git-dir ${workdir}/my_module_dev/src/my-new-work-utils/.git
--work-tree ${workdir}/my_module_dev/src/my-new-work-utils remote add origin
ssh://remote_account@remotehost.remotedomain/...../git_repositories/my-new-work-utils.git'
[.....] Return code(/usr/bin/git): 0
[.....] Exec : '/usr/bin/git --git-dir ${workdir}/my_module_dev/src/my-new-work-utils/.git
--work-tree ${workdir}/my_module_dev/src/my-new-work-utils commit
--allow-empty -m Initialize Repository'
[.....] Return code(/usr/bin/git): 0
[.....] STDOUT(/usr/bin/git) : '[main (root-commit) e8c8f5e] Initialize Repository'
[.....] Exec : '/usr/bin/git --git-dir ${workdir}/my_module_dev/src/my-new-work-utils/.git
--work-tree ${workdir}/my_module_dev/src/my-new-work-utils push -u origin main'
[.....] Return code(/usr/bin/git): 0
[.....] STDOUT(/usr/bin/git) : 'branch 'main' set up to track 'origin/main'.'
[.....] STDERR(/usr/bin/git) : 'To ssh://remotehost.remotedomain/...../git_repositories/my-new-work-utils.git
* [new branch] main -> main'
[.....] Preparing README from template : '${workdir}/my_module_dev/src/my-new-work-utils/README.md'
[.....] Preparing LICENSE from template : '${workdir}/my_module_dev/src/my-new-work-utils/LICENSE'
[.....] Preparing Makefile from template : '${workdir}/my_module_dev/src/my-new-work-utils/Makefile'
[.....] Preparing pyproject.toml from template : '${workdir}/my_module_dev/src/my-new-work-utils/pyproject.toml'
[.....] Preparing __init__.py from template : '${workdir}/my_module_dev/src/my-new-work-utils/src/my_new_work_utils/__init__.py'
[.....] Preparing my_new_work_utils.py from template : '${workdir}/my_module_dev/src/my-new-work-utils/src/my_new_work_utils/my_new_work_utils.py'
このようにコマンド3つを実行すれば、
- すぐに
${workdir}/my_module_dev/src/my-new-work-utils/以下のモジュールに必要なファイルを編集していくことが可能、 -
${workdir}/my_module_dev/src/my-new-work-utils/Makefileには、install_localというターゲットが定義されているので、make -C ${workdir}/my_module_dev/src/my-new-work-utils/ install_localを実行すれば、${workdir}/my_module_dev/lib/python/site-packages/以下にインストールされるので、このプロジェクトですぐに利用可能。 -
twineが適切にセットアップされていれば、同じMakefileで容易にPyPIに登録可能 - 編集後はすぐに
git add ...,git commit ...,git push ...できる
ので、アイデアをコードに実装することに精神を集中できます。
まとめ
1ファイルで可搬的なPythonスクリプトの実行/開発環境を作成するPythonスクリプトpy_encase.pyを作りました。小規模なプロジェクトがコマンド1つで作成可能です。PyPIから容易にダウンロード可能で、セルフアップデート機能も実装してあります。
まだまだバグがありそうなので、注意してご利用ください。(とくにサブコマンドcleanとdistcleanについては、あらかじめ--dry-runオプションで動作を確認していただければ、と思います。). バグを見つけた場合には教えていただけると助かります。