実行環境も一つのフォルダー以下に収められる可搬的なPythonスクリプトのスケルトン作成ユーティリティ+ついでにkivyのTips

# 動機

以前、「[可搬的なpythonツールのためのスケルトン作成ユーティリティ](https://qiita.com/Nanigashi_Uji/items/66c295485fd46f445458)」というのを作成した。これは、もともとpythonスクリプトを色々な環境に持って行ってに使うのを楽にするため作成した「[比較的小規模で可搬的なPython toolのスケルトン](https://qiita.com/Nanigashi_Uji/items/1f06060917b11244ff1d)」というのが元になっている。

本体は`bash`のシェルスクリプトで、主な機能としては、
  
  - Pythonの必要なモジュールを`pip`でローカルなディレクトリにインストールする
  - 環境変数`PYTHONPATH`にそのローカルディレクトリを加えてpythonスクリプトを実行するラッパースクリプト
  - ローカルディレクトリに`python`スクリプトなどのテンプレートを作成する
  - 自分で作成したpythonスクリプトなど他の環境にコピーするファイルは残して、`pip`でローカルにインストールしたモジュールファイルをまとめて消去する。 (配布物の作成を楽にする。)

の4つがある。個人的には大変使い勝手がよく、いろいろな環境にもっていって使うpythonスクリプトを書くさいに多用するようになった。しかしながら、さらに毎回ルーチンワーク的にやっている作業があることに気づいたので、より省力化がはかれるのではないか、と思うようになった。

また、少し前に「[よく使うシェルスクリプト(bash)のツールの保守性／汎用性を高める試み。](https://qiita.com/Nanigashi_Uji/items/5bc0b80071fe93819e15)」というのを作成した。これは、bashスクリプトを`source`(`.`)しても副作用がないようなものにする工夫である。この工夫をすれば`bash`スクリプトを`alias`的にすることもできるのでないか、と思えてきた。
(個人的には、作業用にログインシェル的には`tcsh`を使っているので、その恩恵はないと思うが......)

これらのため、シェルスクリプトとしては大幅に書き換えることになったので、名前を変えて新しいツールとすることにした。pythonツールやそれに必要なものがまとめて入った小型容器ということで、`py_canister`と命名した。

# ファイル置き場

- [nanigashi-uji/py_canister @ github](https://github.com/nanigashi-uji/py_canister)
- [nanigashi_uji/py_canister @ gitlab](https://gitlab.com/nanigashi_uji/py_canister/)

# 基本的な動作 (従前とおなじ)

まず初めに`--manage`オプションをつけて、`py_canister.sh`を`init`サブコマンドで実行すると、`python`スクリプトのスケルトンを作成できます。 下記の例だと、 新規ディレクトリ`~/tmp/py_working_tool`以下に、`show_status3.py`という名の`python3`スクリプトのスニペットが準備されるとともに、これを実行するためのラッパースクリプト(へのシンボリックリンク)が作成されます。

```bash:実行例
%  ~/somewhere/py_canister.sh --manage \
       -p ~/tmp/py_working_tool -g -r -3 \
       -t 'Working tool by Python' init show_status
```

できたファイルを確認すると、下記のようになっています。

```bash:実行結果(作成されたファイル)
% find ~/tmp/py_working_tool/ -ls
....  ~/tmp/py_working_tool/
....  ~/tmp/py_working_tool/bin
....  ~/tmp/py_working_tool/bin/py_canister.sh
....  ~/tmp/py_working_tool/bin/mng_pyenv -> py_canister.sh
....  ~/tmp/py_working_tool/bin/show_status -> py_canister.sh
....  ~/tmp/py_working_tool/bin/mng_pyenv2 -> py_canister.sh
....  ~/tmp/py_working_tool/bin/mng_pyenv3 -> py_canister.sh
....  ~/tmp/py_working_tool/bin/show_status3 -> py_canister.sh
....  ~/tmp/py_working_tool/README.md
....  ~/tmp/py_working_tool/.gitignore
....  ~/tmp/py_working_tool/lib
....  ~/tmp/py_working_tool/lib/python
....  ~/tmp/py_working_tool/lib/python/show_status3.py
....  ~/tmp/py_working_tool/lib/python/show_status.py -> show_status3.py
....  ~/tmp/py_working_tool/lib/python/site-packages
....  ~/tmp/py_working_tool/lib/python/site-packages/.gitkeep
```
ここで作成された`show_status3.py`ですが、


```python:show_status3.pyの先頭
% head  ~/tmp/py_working_tool/lib/python/show_status3.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
...
import pytz
import tzlocal
...
```

非標準のモジュール`pytz`,`tzlocal`を読み込んでいますので、このままでは実行できませんが、`mng_pyenv3`で`pip`を使ってローカルにモジュールをインストールできます。

```bash:Pythonモジュールのインストール(pip3を利用)
% ~/tmp/py_working_tool/bin/mng_pyenv3 install pytz tzlocal
Collecting pytz
  Using cached pytz-2024.1-py2.py3-none-any.whl.metadata (22 kB)
Collecting tzlocal
  Using cached tzlocal-5.2-py3-none-any.whl.metadata (7.8 kB)
Using cached pytz-2024.1-py2.py3-none-any.whl (505 kB)
Using cached tzlocal-5.2-py3-none-any.whl (17 kB)
Installing collected packages: pytz, tzlocal
Successfully installed pytz-2024.1 tzlocal-5.2
```
ファイルを確認すると、ローカルフォルダにインストールされてます。

```bash:ファイルリスト(抜粋)
% find ~/tmp/py_working_tool/ -ls
......
...... ~/tmp/py_working_tool/lib/python/site-packages/3.12
...... ~/tmp/py_working_tool/lib/python/site-packages/3.12/pytz-2024.1.dist-info
...... 
...... ~/tmp/py_working_tool/lib/python/site-packages/3.12/pytz
...... 
...... ~/tmp/py_working_tool/lib/python/site-packages/3.12/tzlocal-5.2.dist-info
...... 
...... ~/tmp/py_working_tool/lib/python/site-packages/3.12/tzlocal
......
```

`bin/show_status3`は`bin/py_canister.sh`へのシンボリックリンクですが、ここでは`py_canister.sh`は、`--manage`オプションがない場合には、シンボリック名もしくはコマンドライン第一引数のファイルを`PYTHONPATH`を設定して実行するラッパースクリプトとして動作します。

```bash:実行例(ローカルに追加したpytz,tzlocalを利用して動作)
%  ~/tmp/py_working_tool/bin/show_status3 -d
Hello, World! ......
Python : 3.12.1 (..../bin/python3)
.... (PYTHONPATH一覧を表示)
```

これで準備が完了したので、`py_working_tool/lib/python/show_status3.py`を所望の動作をするようにバリバリ書き換え、また必要に応じて`py_working_tool/bin/mng_pyenv3 install ...`として、さらに追加で必要となるpythonモジュールを追加していく、というのが作業になります。ここまでは、以前の「[可搬的なpythonツールのためのスケルトン作成ユーティリティ](https://qiita.com/Nanigashi_Uji/items/66c295485fd46f445458)」と同じです。

# 追加した機能

「可搬的なPythonスクリプトツール」を作っている時にほとんど場合に必要となることは、スクリプトやその実行に必要なモジュールファイルだけでなく、データファイルや設定ファイルの可搬性を確保することが必要になる。その際には、**要は必要なファイルを一つのディレクトリ以下に収める**というドクトリンになるわけであるが、そのために毎回同じような機能を書いていることに気づいた。そのため、`py_canister.sh`にはよく使う`python`コードをテンプレートとして埋め込んでおけるようなに実装した。例として、自分が常用するコードから、ローカルディレクトリの構造をハンドルする`pkg_structure.py`というファイルと、データキャッシュを使う`pkg_cache.py`というのを入れてみた。

使い方としては、
```bash:新機能を使う実行例
%  ~/somewhere/py_canister.sh --manage -3 -p ~/tmp/py_working_tool -g -r -t 'Working tool by Python' \
       -s pkg_structure init show_status
```
のように`-s`オプションで、埋め込んだテンプレートから実ファイル作成することを指示すると、
```bash:-sオプションで追加で作成されるファイル
% find ~/tmp/py_working_tool/ -ls
.......
....  ~/tmp/py_working_tool/lib/python/pkg_structure.py
.......
```
と、追加で`python`のモジュール的に使用できるファイルが作成される。

# ローカルディレクトリの構造の定型化(`pkg_structure.py`)

前述のとおり、pythonスクリプトやモジュールだけでなく、なんらかのデータファイルや設定ファイルなどもまとめて他の環境にもっていく必要があることも少なくない。「**可搬的なスクリプトツール**」を目指す、という観点からは、
 1. **１つのディレクトリ以下に必要なファイルが収まっている。**
 1. **そのフォルダがどこの場所に置いても必要なファイルを読み込んで動作する**
 1. **そのファイル以外の場所に意図せずファイルを作らない**

の3点を満たしたい。とくに2点目や3点目に関して、実行スクリプトの親ディレクトリを調べて相対的にデータファイルや中間ファイルや出力ファイルのpathを決める、というコードを毎回書くことになっていた。このための[`pkg_structure.py`](https://github.com/nanigashi-uji/py_canister/blob/main/input/py_canister.sh/pkg_structure.py)というコードを用意した。

`py_canister.sh`では、トップディレクトリ(上記実行例では`~/tmp/py_working_tool`)以下の`bin`ディレクトリ以下に実行ファイルを、`lib/python`以下にPythonスクリプトを配置している。

`pkg_structure.py`はこれを発展させて、トップディレクトリ以下に`var`, `share`,`etc`,...といった[GNU Coding Standard](https://www.gnu.org/prep/standards/html_node/Directory-Variables.html)や[Filesystem Hierarchy Standard](https://ja.wikipedia.org/wiki/Filesystem_Hierarchy_Standard)に準じたディレクトリ構造を持たせるのをサポートする。

## `pkg_structure.py`の利用例

具体的な例として、GUIライブラリの一つである[`kivy`](https://kivy.org/)を使ったpythonスクリプトの例を挙げる。見た目のよいマルチタッチアプリケーションが作成可能ということで`kivy`を使っている方もいると思いますが、**可搬的にスクリプトにする、という点では下記の点が問題になります。**

  - Wigetの配置などをpythonスクリプトとは別の独自言語(`kv`)のファイルに記述する。
  - 最初に起動したとき、(`~/.kivy`)という隠しディレクトリを作って、その下に設定ファイルを作成する。
  - さらに`~/.kivy/log`以下に実行ログを残していく。(**ユーザーが気づかない場所で静かにディスク容量が食われていく**)
  - これに加えて、デフォルトでは、kivyで作成したアプリ(`kivy.app.App`を継承したクラス)毎の設定ファイルを、`~/.config`(macOSの場合は`~/Library/Application Support/`)以下に作成する。

作成した`pkg_structure.py`を使って、これらを回避する例を作ってみました。全体像は最後につけますが,上記ポイントに関係する部分を抽出して説明します。

### `pkg_structure.py`の使用

まず、モジュールを`import`して、それを使う`PkgStructure`クラスのエンティティを定義します。コンストラクタの引数であたえたスクリプトから親ディレクトリを推測します。

```python:pkg_structure.pyの利用
import pkg_structure
...
    pkg_info = pkg_structure.PkgStructure(script_path=sys.argv[0])
    # pkg_info.dump(relpath=False, with_seperator=True)
...
    pkg_info.make_subdirs('pkg_sysconfdir', 0o755, True, 'kivy')
    os.environ['KIVY_HOME']     = pkg_info.concat_path('pkg_sysconfdir', 'kivy')
....
```

それによりどんなサブディレクトリが定義されるかは、メンバ関数`dump()`で表示できます。引数に与えたスクリプトのパス('base_script')が、`~/tmp/py_working_tool/lib/python/show_status3.py`の場合には、
上位の`lib/python/`のさらに親ディレクトリが、このパッケージのディレクトリと類推して、
  - そのディレクトリ名`py_working_tool`をこのパッケージの名前:'pkg_name'
  - そのディレクトリの場所`~/tmp/py_working_tool`がディレクトリ構造のトップ('prefix')
と決めます。それいかに、GNU Coding StandardやFHSで定義されている。

  - 'bindir':            `~/tmp/py_working_tool/bin`
  - 'datadir':           `~/tmp/py_working_tool/share`
  - 'sysconfdir':        `~/tmp/py_working_tool/etc`
  - 'localstatedir':     `~/tmp/py_working_tool/var`
  - 'runstatedir':       `~/tmp/py_working_tool/var/run`
  - 'tmpdir':            `~/tmp/py_working_tool/tmp`

などのサブディレクトリや、これらのディレクトリ名の直下のパッケージの名前('pkg_name')のサブディレクトリ

  - 'pkg_datadir':       `~/tmp/py_working_tool/share/py_working_tool`
  - 'pkg_sysconfdir':    `~/tmp/py_working_tool/etc/py_working_tool`
  - 'pkg_cachedir':      `~/tmp/py_working_tool/var/cache/py_working_tool`
  - 'pkg_statedatadir':  `~/tmp/py_working_tool/var/lib/py_working_tool`
  - 'pkg_logdir':        `~/tmp/py_working_tool/var/log/py_working_tool`
  - 'pkg_spooldir':      `~/tmp/py_working_tool/var/spool/py_working_tool`

に相当する文字列が定義され、PkgStructureクラスのプロパティとしてアクセスできるようになります。
どのようなプロパティ(サブディレクトリ名)が定義されるかは、`lib/python/pkg_structure.py`を単体で直接実行することでも確認できます。(`********`は実行ユーザー名になります)

```bash:pkg_structure.py実行例
'pkg_name':         py_working_tool
'pkg_path':         ~/tmp/py_working_tool
----------------------------------------------------------------------
'base_script':      ~/tmp/py_working_tool/lib/python/pkg_structure.py
----------------------------------------------------------------------
'script_mnemonic':  pkg_structure
'script_path':      ~/tmp/py_working_tool/lib/python/pkg_structure.py
'script_location':  ~/tmp/py_working_tool/lib/python
'script_basename':  pkg_structure.py
----------------------------------------------------------------------
'prefix':           ~/tmp/py_working_tool
----------------------------------------------------------------------
'exec_user':        ********
----------------------------------------------------------------------
'exec_prefix':       '${prefix}'
'bindir':            '${prefix}'/bin
'datarootdir':       '${prefix}'/share
'datadir':           '${prefix}'/share
'sysconfdir':        '${prefix}'/etc
'sharedstatedir':    '${prefix}'/com
'localstatedir':     '${prefix}'/var
'include':           '${prefix}'/include
'libdir':            '${prefix}'/lib
'srcdir':            '${prefix}'/src
'infodir':           '${prefix}'/share/info
'runstatedir':       '${prefix}'/var/run
'localedir':         '${prefix}'/share/locale
'lispdir':           '${prefix}'/emacs/lisp
'docdir':            '${prefix}'/doc/py_working_tool
'htmldir':           '${prefix}'/doc/py_working_tool
'dvidir':            '${prefix}'/doc/py_working_tool
'pdfdir':            '${prefix}'/doc/py_working_tool
'psdir':             '${prefix}'/doc/py_working_tool
'mandir':            '${prefix}'/share/man
'man0dir':           '${prefix}'/share/man/man0
'man1dir':           '${prefix}'/share/man/man1
'man2dir':           '${prefix}'/share/man/man2
'man3dir':           '${prefix}'/share/man/man3
'man4dir':           '${prefix}'/share/man/man4
'man5dir':           '${prefix}'/share/man/man5
'man6dir':           '${prefix}'/share/man/man6
'man7dir':           '${prefix}'/share/man/man7
'man8dir':           '${prefix}'/share/man/man8
'man9dir':           '${prefix}'/share/man/man9
'manndir':           '${prefix}'/share/man/mann
'sbindir':           '${prefix}'/sbin
'bootdir':           '${prefix}'/boot
'devdir':            '${prefix}'/dev
'mediadir':          '${prefix}'/media
'mntdir':            '${prefix}'/mnt
'optdir':            '${prefix}'/opt
'tmpdir':            '${prefix}'/tmp
'xmldir':            '${prefix}'/etc/xml
'etcoptdir':         '${prefix}'/etc/opt
'cachedir':          '${prefix}'/var/cache
'statedatadir':      '${prefix}'/var/lib
'lockdir':           '${prefix}'/var/lock
'logdir':            '${prefix}'/var/log
'spooldir':          '${prefix}'/var/spool
'statetmpdir':       '${prefix}'/var/tmp
'user_home':         '${prefix}'/Users/********
'home':              '${prefix}'/Users/********
'homedir':           '${prefix}'/Users
----------------------------------------------------------------------
'pkg_datadir':       '${prefix}'/share/py_working_tool
'pkg_sysconfdir':    '${prefix}'/etc/py_working_tool
'pkg_runstatedir':   '${prefix}'/var/run/py_working_tool
'pkg_include':       '${prefix}'/include/py_working_tool
'pkg_libdir':        '${prefix}'/lib/py_working_tool
'pkg_srcdir':        '${prefix}'/src/py_working_tool
'pkg_tmpdir':        '${prefix}'/tmp/py_working_tool
'pkg_xmldir':        '${prefix}'/etc/xml/py_working_tool
'pkg_cachedir':      '${prefix}'/var/cache/py_working_tool
'pkg_statedatadir':  '${prefix}'/var/lib/py_working_tool
'pkg_lockdir':       '${prefix}'/var/lock/py_working_tool
'pkg_logdir':        '${prefix}'/var/log/py_working_tool
'pkg_spooldir':      '${prefix}'/var/spool/py_working_tool
'pkg_statetmpdir':   '${prefix}'/var/tmp/py_working_tool
----------------------------------------------------------------------
```


さらに、これらの下に別のディレクトリ／ファイル名の文字列を作成するメンバ関数`concat_path()`を実装しました。実際にディレクトリを作成するためのメンバ関数`make_subdirs()`も実装しました。前記の例の最後の2行

```python:pkg_structure.pyの利用の抜粋
    pkg_info.make_subdirs('pkg_sysconfdir', 0o755, True, 'kivy')
    os.environ['KIVY_HOME']     = pkg_info.concat_path('pkg_sysconfdir', 'kivy')
```
が、`~/tmp/py_working_tool/etc/py_working_tool/kivy`というディレクトリを作成して、環境変数`KIVY_HOME`にこのディレクトリを設定するコードになります。これで、`~/.kivy`という隠しファイルが作られるのを避けられます。**これらの環境変数設定は、kivy関係のモジュールをインポートする前に実行される必要があります**

### `kivy`のログファイル出力先の変更

次に他のkivy関係のモジュールを読み込む前にログファイルの置き場をしておきます。`kivy.config`関係の設定をおこなってからkivyのメインのモジュールを読み込む順番になります。

```python:kivyのログ出力先の変更
    import kivy.config
    kivy.config.Config.set("kivy","log_dir", pkg_info.pkg_logdir)
```

`pkg_info.pkg_logdir`というところが`PkgStructure`のプロパティとしてサブディレクトリの場所を取得する例となっています。

### kivyのアプリケーション設定フォルダの指定

前述のとおりkivyの作法として、`kivy.App`のを継承してアプリケーションのクラスを構築すると、`~/.config`(macOSの場合は`~/Library/Application Support/`)以下に非明示的にディレクトリが作成されます。このディレクトリの場所を変えるには、`kivy.App`クラスのコンストラクタの引数`kv_directory`で指定します。
```python:kivyアプリの設定ファイルの置き場を変える。
class MainApp(kivy.app.App):

        def __init__(self, pkg_info, **kwargs):
            super().__init__(kv_directory=pkg_info.pkg_runstatedir, **kwargs)
            self.pkg_info      = pkg_info
```
この例では、作成する`MainApp`のコンストラクタに`PkgStructureクラス`のエンティティを渡しておき、そのプロパティ`pkg_info.pkg_runstatedir`を親クラス(`kivy.app.App`)のコンストラクタに引数で渡します。また、このクラスで`PkgStructureクラス`のエンティティを参照できるように、メンバ変数に保持しておきます。

### kivyのGUI画面設定ファイルの指定

`main.App`のGUIウィンドウ画面の定義などは別途`.kv`ファイルに記述して読み込みます。これも`kivy.app.App`のコンストラクタの引数(`kv_file`)で指定できます。あらかじめ`~/tmp/py_working_tool/share/py_working_tool/main.kv`というファイルを作成しておき、`MainApp`クラスのコンストラクタ経由で`kivy.app.App`クラスのコンストラクタに渡します。

```python:kvファイルの場所の指定
    kv_file=pkg_info.concat_path('pkg_datadir', 'main.kv')
...
    main_app = MainApp(kv_file=kv_file, pkg_info=pkg_info)
```

### この例での作成ファイル。

スクリプトの全体像としてはあとにつけますが、これを実行すると非明示的に作成されるファイルは下記の通り、トップディレクトリ以下に限られ、またログファイルもより明示的な場所に作られるので、めざしていた「可搬性」が達成できました。
```bash:作成されたファイル
% ~/somewhere/py_canister.sh --manage -3 -p ~/tmp/py_working_tool \
    -g -r -t 'Working tool by Python' -s pkg_structure init show_status
% (edit) emacs ~/tmp/py_working_tool/lib/python/show_status3.py
% (edit) emacs ~/tmp/py_working_tool/share/py_working_tool/main.kv
% ~/tmp/py_working_tool/bin/mng_pyenv3 install pytz tzlocal psutil kivy
% ~/tmp/py_working_tool/bin/show_status3
% find ~/tmp/py_working_tool/ 
...
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/config.ini
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/mods
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-32.png
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-24.png
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-64.ico
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-256.png
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-48.png
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-512.png
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-64.png
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-128.png
    ~/tmp/py_working_tool/etc/py_working_tool/kivy/icon/kivy-icon-16.png
...
    ~/tmp/py_working_tool/var/log/py_working_tool/kivy_24-07-26_0.txt
```


### スクリプト全体像

```python:~/tmp/py_working_tool/lib/python/show_status3.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Standard modules
import argparse
import datetime
import sys
import os 

# Publicly Distributed modules
import pytz
import tzlocal
import psutil

# Custom-made modules
import pkg_structure

def main():

    argpsr = argparse.ArgumentParser(description='Example: showing status')
    argpsr.add_argument('-q', '--quiet', action='store_true', help='Supress kivy log to stderr')
    cmdargs = argpsr.parse_args()

    pkg_info   = pkg_structure.PkgStructure(script_path=sys.argv[0])
    #pkg_info.dump(relpath=False, with_seperator=True)

    os.environ['KIVY_NO_ARGS'] = '1'
    pkg_info.make_subdirs('pkg_sysconfdir', 0o755, True, 'kivy')
    os.environ['KIVY_HOME']     = pkg_info.concat_path('pkg_sysconfdir', 'kivy')
    if cmdargs.quiet:
        os.environ['KIVY_NO_CONSOLELOG'] = '1'
    
    import kivy.config
    kivy.config.Config.set("kivy","log_dir", pkg_info.pkg_logdir)

    import kivy.app
    import kivy.uix.widget
    import kivy.properties

    class MainApp(kivy.app.App):

        def __init__(self, pkg_info, **kwargs):
            super().__init__(kv_directory=pkg_info.pkg_runstatedir, **kwargs)
            self.pkg_info      = pkg_info

    class MainWidget(kivy.uix.widget.Widget):

        KEYWORDS = ['pkg_name', 'pkg_path', 'base_script', 
                    'prefix', 'bindir', 'datadir', 'sysconfdir',
                    'localstatedir', 'runstatedir', 'tmpdir',
                    'pkg_datadir', 'pkg_sysconfdir', 'pkg_runstatedir', 'pkg_tmpdir']

        status_text = { x: kivy.properties.StringProperty() for x in KEYWORDS }
        status_text['MECH'] = kivy.properties.StringProperty()
        
        def __init__(self, **kwargs):
            super().__init__(**kwargs)

            tz_local   = tzlocal.get_localzone()
            self.status_text['MECH'] = (f'CPU Usage: {psutil.cpu_percent(interval=1)} %'
                                        f', Load Avg. = {psutil.getloadavg()[0]:.2f}'
                                        f' @ {datetime.datetime.now(tz=tz_local).strftime("%c")}')
            for x in self.KEYWORDS:
                self.status_text[x]      = kivy.app.App.get_running_app().pkg_info[x]

    for x in MainWidget.KEYWORDS:
        print (f"{x+':':18s} {pkg_info[x]}")

    kv_file=pkg_info.concat_path('pkg_datadir', 'main.kv')
    print ("--------------------")
    print (f"{'kv_file:':18s} {kv_file}")
    print ("--------------------")

    main_app = MainApp(kv_file=kv_file, pkg_info=pkg_info)
    main_app.run()


if __name__ == '__main__':
    main()
```

```kv:~/tmp/py_working_tool/share/py_working_tool/main.kv
MainWidget:

<MainWidget>:
    BoxLayout:
        orientation: 'vertical'
        size: self.parent.size

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_MECH
                size_hint: 0.2, 0.01
                text: 'MECH'
            Label:
                id: status_text_MECH
                size_hint: 0.8, 0.01
                text: root.status_text['MECH']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_pkg_name
                size_hint: 0.2, 0.01
                text: 'pkg_name'
            Label:
                id: status_text_pkg_name
                size_hint: 0.8, 0.01
                text: root.status_text['pkg_name']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_pkg_path
                size_hint: 0.2, 0.01
                text: 'pkg_path'
            Label:
                id: status_text_pkg_path
                size_hint: 0.8, 0.01
                text: root.status_text['pkg_path']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_base_script
                size_hint: 0.2, 0.01
                text: 'base_script'
            Label:
                id: status_text_base_script
                size_hint: 0.8, 0.01
                text: root.status_text['base_script']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_prefix
                size_hint: 0.2, 0.01
                text: 'prefix'
            Label:
                id: status_text_prefix
                size_hint: 0.8, 0.01
                text: root.status_text['prefix']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_bindir
                size_hint: 0.2, 0.01
                text: 'bindir'
            Label:
                id: status_text_bindir
                size_hint: 0.8, 0.01
                text: root.status_text['bindir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_datadir
                size_hint: 0.2, 0.01
                text: 'datadir'
            Label:
                id: status_text_datadir
                size_hint: 0.8, 0.01
                text: root.status_text['datadir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_sysconfdir
                size_hint: 0.2, 0.01
                text: 'sysconfdir'
            Label:
                id: status_text_sysconfdir
                size_hint: 0.8, 0.01
                text: root.status_text['sysconfdir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_localstatedir
                size_hint: 0.2, 0.01
                text: 'localstatedir'
            Label:
                id: status_text_localstatedir
                size_hint: 0.8, 0.01
                text: root.status_text['localstatedir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_runstatedir
                size_hint: 0.2, 0.01
                text: 'runstatedir'
            Label:
                id: status_text_runstatedir
                size_hint: 0.8, 0.01
                text: root.status_text['runstatedir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_tmpdir
                size_hint: 0.2, 0.01
                text: 'tmpdir'
            Label:
                id: status_text_tmpdir
                size_hint: 0.8, 0.01
                text: root.status_text['tmpdir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_pkg_datadir
                size_hint: 0.2, 0.01
                text: 'pkg_datadir'
            Label:
                id: status_text_pkg_datadir
                size_hint: 0.8, 0.01
                text: root.status_text['pkg_datadir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_pkg_sysconfdir
                size_hint: 0.2, 0.01
                text: 'pkg_sysconfdir'
            Label:
                id: status_text_pkg_sysconfdir
                size_hint: 0.8, 0.01
                text: root.status_text['pkg_sysconfdir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_pkg_runstatedir
                size_hint: 0.2, 0.01
                text: 'pkg_runstatedir'
            Label:
                id: status_text_pkg_runstatedir
                size_hint: 0.8, 0.01
                text: root.status_text['pkg_runstatedir']

        BoxLayout:
            size_hint: 1.0, 0.01
            Label:
                id: status_label_pkg_tmpdir
                size_hint: 0.2, 0.01
                text: 'pkg_tmpdir'
            Label:
                id: status_text_pkg_tmpdir
                size_hint: 0.8, 0.01
                text: root.status_text['pkg_tmpdir']

        Button:
            size_hint: 1.0, 0.01
            text: "Quit"
            on_press: app.stop()
```



# （参考） `py_canister.sh` の使い方

ヘルプのダンプ画面をつけておきます。

```
% ~/somewhere/py_canister.sh --manage -h

               % py_canister.sh --manage [options] sub-command [arguments]
    ---- Shell script for providing Portable 'Contained' Python environment
[sub-commands] 
               % py_canister.sh --manage [options] init [scriptnames]    ... setup directory tree,
                                                                           and prepare templates if arguments are given.
               % py_canister.sh --manage [options] add script_names      ... prepare python script templates.
               % py_canister.sh --manage [options] addlib [script_names] ... prepare python script templates.
               % py_canister.sh --manage install  module_names           ... Install python module with pip locally.
               % py_canister.sh --manage download module_names           ... Download python module with pip locally.
               % py_canister.sh --manage clean                           ... Delete local python module for corresponding python version.
               % py_canister.sh --manage distclean/cleanall/allclean     ... Delete local python module for all python version.
               % py_canister.sh --manage info                            ... Show information.
[Options]
               -h     : Show this message
               -P arg : specify python command / path
               -v     : Show verbose messages
               -q     : Supress verbose messages (default)
               -n     : Dry-run mode.
               -2     : Prioritize Python2 than Python3
               -3     : Prioritize Python3 than Python2
[Options for sub-command: setup/init ]
               -p     : prefix of the directory tree. (Default: Grandparent directory if the name of parent directory of 
                        py_canister.sh is bin, otherwise current working directory.)
               -M     : moving this script body into instead of copying
               -g     : setup files for git
               -G     : do not setup files for git (default)
               -r     : setup/update README.md
               -R     : do not setup/update README.md (default)
               -k     : keep backup file of README.md when it is updated.
               -K     : do not keep backup file of README.md when it is updated. (default)
               -t arg : Project title
               -m arg : install module by pip
               -s arg : install library script from template.
               -S     : install standard library scripts. (equivalent to '-s pkg_structure -s pkg_cache')
[Options for sub-command: install/download ]
               -i arg : specify pip command

```