(20216/22 修正)
普通のPythonであれば,pipというパッケージ管理ツールで,サードパーティのモジュールやパッケージを使うことができるようになりますが,VRMNXに搭載されているPythonエンジンではそういう便利なことはできません。撮る夫くん などのVRMNX用汎用モジュールは、レイアウトファイルと同じ場所にpyファイルを保存することで,importできるようになります。しかし、撮る夫くんを多数のレイアウトで使うたびにいろんなフォルダにtoruo.pyをコピーするのは面倒です。
これを回避して楽をする方法も存在します。Pythonのインポートシステムの解説も交えながらその方法を説明するとともに、Python拡張を利用するVRMNXレイアウトの配布や利用の指針を提案したいと思います。
Pythonのインポートシステム
例えば,よく使われる標準モジュールに,乱数を扱うrandom
があります。Pythonがimport random
という文を実行する際は、モジュール検索パスの場所を順番に探して、見つかったrandom.pyをインポートしています。
このあたりの細かい仕様は,Python公式マニュアルに記述があるので興味のある方はご覧ください。以下では,利用者目線で必要な事柄をかいつまんで紹介します。
モジュール検索パス
さて、Pythonがモジュールを探しに行く場所を、 モジュール検索パス といいます。 これを確認してみましょう。
import sys for p in sys.path: vrmapi.LOG(p) #VRMNXのPythonエンジンの場合 print(p) # 普通のPythonの場合
sys.path
にはリスト形式でいくつかのパスが収められており,このリストの先頭からモジュールを検索します。同名のモジュールがあった場合には,最初に見つかったモジュールがimportされ,それ以降のパスの検索は中止されます。
VRMNXのPythonエンジンの場合,最初の3つは
C:\\Program Files\\I.MAGIC\\鉄道模型シミュレーターオンラインNX\\python\\Lib C:\\Program Files\\I.MAGIC\\鉄道模型シミュレーターオンラインNX\\python\\Lib\\site-packages C:\\Users\\yourname\\AppData\\Roaming\\imagic\\vrmonline\\python\\Lib
のようになっているはずです(VRMNX Version 6.0.0.210)。このうち1つ目は,先程のrandom.pyなどの標準モジュールを収めておくディレクトリであり,先程のrandom
モジュールはここから読み出されます。
そして,どこかに保存済みのレイアウトでこれを実行していれば,4つ目に,レイアウトを保存したディレクトリが来ていると思います。したがって,レイアウトと同じ場所にモジュール(またはパッケージ)を保存しておけば,Pythonエンジンがそのモジュールを見つけ出すことができ,無事importが行われるわけです。
しかし当然,モジュール検索の仕様上,リストにある他のフォルダにモジュールやパッケージを置いておくことでも,Pythonエンジンからimportできるようになります。ただし,python/Libとpython/Lib/site-packagesの違いについて留意しておくことが必要です。python/LibにはPythonの標準モジュールの置き場なので,サードパーティのモジュールやライブラリ(当然,我々が作る自作モジュールも含む)はsite-packagesに収めるべきです。
site-packagesにモジュールをインストールしたら,配布時のバージョン管理に注意
pythonパッケージには作者によってアップデートが入る可能性があります。例えば,私が新バージョンの撮る夫くんを同梱してレイアウトを配布したとして,それをダウンロードしたユーザの C:\Program Files(中略)\Lib\site-packages に旧バージョンの撮る夫くんがインストールされていた場合,VRMNXシステムは私のレイアウトからでも旧バージョンの撮る夫くんを優先的にインポートします。Pythonのimport文は,モジュール検索パスの最初から順番に探していき,見つけたところで検索を打ち切る仕様だからです。これでは不都合が生じる可能性があります。
このことを回避するためには,システムのモジュール検索パスの先頭にレイアウトのディレクトリを強制的に指定してしまえばよく,次のような記述をレイアウトスクリプトの先頭に書き加えることで対応できます。
#LAYOUT import os, sys import vrmapi sys.path.insert(0, vrmapi.SYSTEM().GetLayoutDir())
外部モジュールを同梱してレイアウトを配布する際には,この一手間を加えてやるのが安心です。
なお, sys.path.insert(0, <somewhere>)
というイディオムは,Pythonモジュールのリファレンス文書を自動生成してくれるSphinx-apidocのconf.pyや,pipの内部仕様にも見られ,割とポピュラーなものです。