0
0

More than 3 years have passed since last update.

比較的小規模で可搬的なPython toolのスケルトン

Last updated at Posted at 2021-06-24

Python scriptの可搬性

2021/8/31 Python2とPython3のスクリプトが混在していても使えるようにバージョンアップした

ちょっとした作業のためにPythonでひとまとりのスクリプトを書くことが多々ある。Pythonを利用する利点は、既存のライブラリ/モジュールが豊富で、OSのパッケージシステム(rpm,yum,apt,..)やそれに準じたもの(Macports, Homebrew)で配布されていたり、PyPI(pipコマンド一つ)によって容易に導入して利用できる点にある。

ただし、いろいろな計算機で可搬的に利用しようとすると、環境依存を気にしなくてはいけなくなる。モジュールがOSの配布物より導入済みの場合とそうでない場合、システム管理者としてpipを使える環境とそうでない場合、一般ユーザーとしてPyPIの設定をカスタマイズしている場合とそうでない場合、virtualenvを使っている場合とそうでない場合、などなど。ちょっとしたツールをパッと使いたいのに、各環境でPythonのセットアップがどうなっていたかを気にしないといけないのは非常に煩わしい。

それを回避するための処方箋をテンプレート化した。

方針

  • 利用する環境ですでに導入済みかどうかは気にせず、とにかくローカルにPyPIを利用してインストールして利用する。そのためのシェルスプリプトを用意する。(内部的ではpip install --target ./lib/python/site-packages some-moduleを実行する)

  • 利用するPythonを実行する前に、PyPIでインストールしたローカルのディレクトリを環境変数PYTHONPATHを加えて実行する必要があるので、そのためのラッパーシェルスクリプトを用意する。

  • ただし、Pythonスクリプト毎にラッパーシェルスクリプトを書くのは不毛な作業なので、一つのシェルスクリプトに対してシンボリックリンクを張り、シンボリック名に応じて実行するPythonスクリプトを切り替える。

実装 (含むサンプルpythonスクリプト)

ファイルの一覧としては、以下。 PyPIモジュールをインストールするbashスクリプトと、実際にPythonスクリプト実行するラッパースクリプトでは、ディレクトリ名などで整合性をとる必要がある。そのため実態としては1つのファイルとして、シンボリックリンクを使って実行時のコマンド名で動作を切り替える。それにより、内部のシェル変数の定義の記述は一箇所にまとめている。

結果的に、Pythonスクリプト以外の実体としては1つのシェルスクリプトに収まっている。

ファイル構成
bin/run_pyscript
bin/mng_pymodule -> run_pyscript
bin/ex_greeting -> run_pyscript
lib/python/ex_greeting.py
lib/python/site-packages/
  • bin/mng_pymodule: 指定したPyPIモジュールのインストール/一括アンインストールをする。
  • bin/run_pyscript: シェルスクリプトの実体。この名前で実行したときは、第一引数に指定した名前でlib/python以下に置かれたpython scriptを、環境変数PYTHONPATHlib/pythonlib/python/site-packagesを加えたうえで実行する。

  • lib/python: Pythonスクリプトを置くディレクトリ

  • lib/python/site-packages/: PyPIでPythonモジュールがローカルにインストールされるディレクトリ

以下は、サンプル。

  • lib/python/ex_greeting.py: サンプルスクリプト("Hello, world!"+時刻表示)。標準モジュール以外に、pytz, tzlocalを使う。

  • bin/ex_greeting: 上記サンプルを呼び出すためのコマンドとして使うためのbin/run_pyscriptへのシンボリックリンク

2021/8/31追記

Python2とPython3の混在に対応するため、下記のシンボリックリンクとサンプル例を追加した

追加ファイル構成
bin/mng_pymodule2 -> run_pyscript
bin/mng_pymodule3 -> run_pyscript
bin/ex_greeting2 -> run_pyscript
bin/ex_greeting3 -> run_pyscript
lib/python/ex_greeting2.py
lib/python/ex_greeting3.py

ファイル置き場

使い方

  • Pythonスクリプトを書き、lib/python/以下に置く。
% emacs lib/python/ex_greeting.py
  • bin以下に、Pythonスクリプト名から拡張子(.py)をとった名前、もしくは同名でbin/run_pyscriptへのシンボリックを作成する。
Pythonスクリプト作成
% (cd bin ; ln -s run_pyscript ex_greeting)

この時点でも、必要なモジュールがpython実行環境に揃っていれば問題なく実行できるはずだが、揃ってない場合には、モジュールがないというエラーがでる。

モジュールがインストールされていない場合の実行例
% ./bin/ex_greeting --date
Traceback (most recent call last):
  File "......../lib/python/ex_greeting.py", line 8, in <module>
    import tzlocal
ImportError: No module named tzlocal
  • Pythonスクリプトで使用しているPyPIのモジュール(以下の例ではtzlocal)をインストールする。
実行例
% ./bin/mng_pymodule install tzlocal pytz
Collecting tzlocal
  Using cached tzlocal-2.1-py2.py3-none-any.whl (16 kB)
Collecting pytz
  Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Installing collected packages: pytz, tzlocal
Successfully installed pytz-2021.1 tzlocal-2.1
% ls
./lib/python/site-packages/pytz  pytz-2021.1.dist-info  tzlocal  tzlocal-2.1.dist-info
%

bin/mng_pymoduleの第一引数のinstallは省略可能である。実行後はPythonスクリプトが正常に実行できるようになる。

2021/8/31追記

python2python3のファイル/モジュールを混在して使用するために、mng_pymodule2,mng_pymodule3というシンボリックリンクを用意した。mng_pymodule2pip2があればそれを使い、なければpipを実行する。mng_pymodule3,mng_pymoduleは、pip3があればpip3を使い、なければpipを使う。ただし、どちらも環境変数PIPで使いたいpipが指定されている場合には、そちらが優先して使われる。 モジュールがインストールされる場所は、
./lib/python/site-packages/X.Y/ (X,Yはそれぞれpythonのメジャーバージョン,マイナーバージョン)となる。

モジュールをインストールした後の実行例
% ./bin/ex_greeting --date
./bin/ex_greeting --date
Hello, World! It is "Thu Jun 23 21:01:45 2021."

[2021/8/31追記] Python2とPython3の使い分け

スクリプトを実行する際に、どのバージョンのpythonを用いるかは、スクリプトのShebang(シェバン)に従う動作とした。サンプルファイルでは

サンプルの比較
% diff3 lib/python/ex_greeting.py lib/python/ex_greeting2.py lib/python/ex_greeting3.py
====
1:1c
  #!/usr/bin/env python
2:1c
  #!/usr/bin/env python2
3:1c
  #!/usr/bin/env python3

違いはShebangだけである。これらを(呼ぶシンボリックリンクを)実行すると、

実行例
% env ./bin/ex_greeting -d | head -2
Hello, World! It is "Tue Aug 31 20:49:16 2021."
Python : 2.7.18 (....)

% env ./bin/ex_greeting2 -d | head -2
Hello, World! It is "Tue Aug 31 20:49:19 2021."
Python : 2.7.18 (....)

% env ./bin/ex_greeting3 -d | head -2
Hello, World! It is "Tue Aug 31 20:49:21 2021."
Python : 3.8.2 (....)

といったようにPythonが使い分けられる。ただし環境変数PYTHONが指定された場合には、そちらを優先的に使う。下の2番目の実行例のように、環境変数PYTHONとスクリプトファイルのShebangのpythonのメジャーバージョンが違っている場合には警告を出すようにした。

% env PYTHON=python3 ./bin/ex_greeting -d | head -2
Hello, World! It is "Tue Aug 31 20:49:37 2021."
Python : 3.8.2 (....)

% env PYTHON=python3 ./bin/ex_greeting2 -d | head -3
Warning: Python version (3.8) mismatch?  (Version 2) is expexted by /..../lib/python/ex_greeting2.py)
Hello, World! It is "Tue Aug 31 20:50:07 2021."
Python : 3.8.2 (....)

おまけ

bin/mng_pymoduleの第一引数をdistcleanとすると、lib/python/site-packages/以下にあるものを全て消去する。

実行例
% ./bin/mng_pymodule distclean
% ls ./lib/python/site-packages/
%

自分で作成したスクリプトなど消してはいけないファイルをlib/python/site-packages/以下に置いてはいけない

[2021/8/31追記]

さらに、

実行例
% ./bin/mng_pymodule2 clean
% ./bin/mng_pymodule3 clean

と実行した場合には、対応するpythonバージョンのモジュールのみ(対応する./lib/python/site-packages/X.Y/以下のみ)をクリーンナップする動作とした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0