2021年2月28日日曜日

53: 任意のモジュールをあなたのLibreOffice Pythonマクロへインポートする、Part 1

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

または、あなたのマクロによってインポートされた任意のモジュールへ。本パートは、ドキュメント内モジュールをインポートすること以外のためのものです。

話題


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: Pythonプログラミング言語

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、任意のモジュール(ドキュメント内モジュールを除く、本パートでは)を自らのLibreOfficeまたはApache OpenOffice Pythonマクロモジュールにインポートする方法を知る。

オリエンテーション


外部フルPythonを使用することは、以前のある記事にて取り扱われました。.

ユーザー所有またはアプリケーション所有のPythonマクロを作成することは、ある以前の記事にて取り扱われました。

ドキュメント内Pythonマクロを作成することは、ある以前の記事にて取り扱われました。.

拡張機能内Pythonマクロを作成することは、ある以前の記事にて取り扱われました。.

Pythonマクロプログラミングの詳細は、いくつかの以降の記事群にて探求されます。


本体

ト書き
Hypothesizer 7、Objector 53A、Objector 53Bが、コンピューターの前にいる。


1: 恣意的なモジュールをPythonマクロモジュールにインポートしようと試みる


Hypothesizer 7
ある恣意的なモジュールをあなたのPythonマクロモジュールにただインポートするというわけにはいきません。

Objector 53A
そのとおりだ!私は'SciPy'をインポートできない!

Hypothesizer 7
サー、実のところ、私はその件について話しているのではありません、ここでは。

Objector 53A
でも、できないんだ!

Hypothesizer 7
その理由は、そのライブラリを、当該LibreOfficeまたはApache OpenOfficeに使われているPythonにインストールされていないことです。

Objector 53A
もちろん、したとも!

Hypothesizer 7
. . .あなたが意図したPythonがあなたのLibreOfficeまたはApache OpenOfficeによって本当に使用されているのかをチェックされる必要があるかもしれません。 .

Objector 53A
その必要はない。されていると私は強く感じている。

ト書き
Objector 53Aは、確信をもって頷く。

Hypothesizer 7
. . .えーと、本記事は、Pythonに組み込まれていないモジュールたちについてのものです、ここで、「Pythonに組み込まれてい」るというのは、典型的には(かならずしもではないが)、'pip'によってインストールされたことを意味しています。ご存知のとおり、Pythonに組み込まれた任意のモジュールは、何の手間もなくインポートすることができます、なぜなら、それは、そのPythonの一部だからです、言わば。

Objector 53A
私は、Pythonに組み込まれていないモジュールをインポートしたりしない。

Hypothesizer 7
. . .ご自分のプロジェクトでいくつかモジュールを作成して、それらの内の1つをそれらの内の別のものにインポートするなどされますよね?

Objector 53A
複数のモジュールを作成するなどという無意味なことを私は決してしない。

ト書き
Objector 53Aは、相手を哀れむように頷く。

Hypothesizer 7
. . .コード全体を単一のモジュールにごちゃ混ぜにされるのですか?

Objector 53A
複数ファイルを編集しなくてすむからな。

Hypothesizer 7
しかし、その単一ファイルが、長いごちゃ混ぜになりかねません。

Objector 53A
ある作成物が、'Module1'に属するか、'Module2'に属するか、思い惑わなくてすむ。

Hypothesizer 7
思い惑わなければならなくなるのは、ただ、あなたのモジュール化が悪かったからにすぎません。'Module1'のようなモジュール名は悪いです、なぜなら、それは、ある作成物がそこに属するのか否かの何らの基準も表わしていないからです。

Objector 53A
シンプルがベストだ。単一ファイルを持つのがもっともシンプルだ。したがって、単一ファイルを持つのがベストだ。

ト書き
Objector 53Aは、相手をいたわるように頷く。

Hypothesizer 7
. . . あなたの3段論法のどのステップにも私は不賛成なのですが

それに、複数プロジェクトによって使われれるライブラリプロジェクトを持ったりはしないのですか?

Objector 53A
はあ?何のために?

Hypothesizer 7
. . . 同じロジックを複数回書いたりしたくありませんよね?

Objector 53A
複数回書いたりしない。コピーするだけだ。

Hypothesizer 7
. . . えーと、もしも、それがあなたのスタイルなのであれば、本記事はあなたには無意味でしょう。


2: インポートされるモジュールがドキュメント内のものである場合を除き、問題は、モジュール群パスをセットするというだけのことです、しかし、. . .


Hypothesizer 7
あるべき姿どおり、インポートされるモジュールがドキュメント内のものである場合を除き、問題は、モジュール群パスをセットするというだけのことです: もしも、当該パスがセットされていれば、当該モジュールはインポートできるはずです。

Objector 53B
私は'PYTHONPATH'環境変数をセットしたけど、うまくいかなかったわよ!

Hypothesizer 7
マダム、それは上手くいきません。そこで、何が上手くいくかを見てみましょう。


3: 予測可能なモジュール群パスをセットする


Hypothesizer 7
任意の予測可能なモジュール群パスは、LibreOfficeまたはApache OpenOffice 'program'ディレクトリ内の'pythonloader.unorc'または'pythonloader.uno.ini'ファイル内の'PYUNO_LOADER_PYTHONPATH'変数内にセットできます。

Objector 53B
「予測可能」ってどういう意味よ?あるものは予言者によって予測可能で、私にはそうではないかもしれないじゃないの。

Hypothesizer 7
それはいい質問です。実のところ、問題は、そのパスがあなたによって予測可能かどうかということです。

Objector 53B
はあ?あなた馬鹿?それが私によって予測可能かどうか、あなたはなんで知ってるわけ?

Hypothesizer 7
私は知りませんし、知る必要もありません: もしも、あなたがそれを予測可能だとみなすのであれば、本方法を用いることが推奨されるということです。

Objector 53B
はあ?なんか、とてもなげやりに聞こえるけど . . .

Hypothesizer 7
あなたは、定数を、先程申し上げた変数にセットすることになります。もしも、それがあなたにとってオーケーでしたら、それはあなたにとってオーケーでしょう。

Objector 53B
はあ?じゃあ、どんなパスが非「定数」でありうるの?

Hypothesizer 7
あなたがこれらを非定数とみなすかどうかを私は知りませんが、例えば、ユーザー所有マクロ群ベースパスは、オペレーティングシステム、LibreOfficeまたはApache OpenOfficeのバージョン、ユーザーに依存します。

Objector 53B
ああ、Linuxにおける'/home/objectror53B/.config/libreoffice/4/user/Scripts/python'のようなパスね . . .

Hypothesizer 7
もしも、そのパスを当該変数にセットすることにあなたがオーケーなのであれば、それはあなたにとってオーケーでしょう。

Objector 53B
たぶんね。

Hypothesizer 7
別の例として、ある拡張機能のPythonマクロ群ベースパスは、'/home/objectror53B/.config/libreoffice/4/user/uno_packages/cache/uno_packages/lu49122vpj5e.tmp_/theBiasPlanet.pythonEnvironmentChecker.unoExtension.oxt/Scripts/python'のようなものになります。

Objector 53B
. . . その「~.tmp_」ってやつは何なの?

Hypothesizer 7
それは、その拡張機能がインストールされた際に自動的に作成されたディレクトリです。名前は、インストール実行毎に変化します。それは予測可能でしょうか?それはあなたが決めることです。

Objector 53B
うーん . . .

Hypothesizer 7
いずれにせよ、注意すべきは、その変数にセットするパスは実際にはURLであるということです。


4: 任意のモジュール群パスを動的にセットする


Hypothesizer 7
実のところ、ご存知かもしれませんが、任意のモジュール群パスは、動的に、'sys.path'変数へ設定できます。

Objector 53B
知ってますよ . . .、それじゃあ、「予測不能」なパス群を探してセットするロジックを作れっていうこと?

Hypothesizer 7
もしも、そうされるおつもりでしたら、あなたはそうできます。

しかしながら、セットすべきパスが、あるマクロ群ベースパスでしたら、次セクションにて紹介される方法のほうがよいかもしれません。


5: アクセスされたモジュール群ベースパスが自動的にセットされるようにする


Hypothesizer 7
これは役に立つ情報です: 任意のマクロモジュールは、LibreOfficeまたはApache OpenOfficeのプロダクトディレクトリ内の'program'ディレクトリ内の'pythonscript.py'によってロードされます。

'pythonscript.py'は、あるマクロモジュールがロードされようとする際、当該モジュール群ベースパスを知っているので、'pythonscript.py'に少し手を加えれば、それに、そのモジュール群パスを'sys.path'へセットさせるようにできます。

Objector 53B
そんなハッキングに乗り出すわけ?

Hypothesizer 7
実のところ、そうします。以下が、その変更です('pythonscript.py'はLibreOffice 6.4.4のものです)。

以下を、「from com.sun.star.uri.RelativeUriExcessParentSegments import RETAIN」の後ろに追加します。

@Python ソースコード
from collections import OrderedDict

以下を、「iENABLE_EDIT_DIALOG=False # offers a minimal editor for editing」の後ろに追加します。

@Python ソースコード
s_additionalModulesRootUrlToDummyMap = OrderedDict ()
s_fileContentsProvider = uno.getComponentContext().ServiceManager.createInstanceWithContext ("com.sun.star.ucb.FileContentProvider", uno.getComponentContext())

以下を、'getModuleByUrl'メソッド中の「entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext」の後ろに追加します。

@Python ソースコード
            entry.module.__dict__ ["s_sourceFileUrl"] = url

以下を、'getPackageName2PathMap'ファンクション内の「ret[ lastElement( j ) ] = Package( paths, transientPathElement )」の後ろに追加します。

@Python ソースコード
            for l_modulesRootUrl in paths:
                if s_additionalModulesRootUrlToDummyMap.get (l_modulesRootUrl) is None:
                    l_modulesRootDirectoryPathString = s_fileContentsProvider.getSystemPathFromFileURL (l_modulesRootUrl)
                    if l_modulesRootDirectoryPathString != "":
                        log.debug ("### Added path: " + l_modulesRootDirectoryPathString)
                        sys.path.append (l_modulesRootDirectoryPathString)
                        s_additionalModulesRootUrlToDummyMap.update ({l_modulesRootUrl: False})
                    else:
                        log.debug ("### Unhandled URL: " + l_modulesRootUrl)
                else:
                    None

以下を、'PythonScriptProvider'ファンクション内の「self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )」の後ろに追加します。

@Python ソースコード
                l_modulesRootUrl = rootUrl
                if s_additionalModulesRootUrlToDummyMap.get (l_modulesRootUrl) is None:
                    l_modulesRootDirectoryPathString = s_fileContentsProvider.getSystemPathFromFileURL (l_modulesRootUrl)
                    if l_modulesRootDirectoryPathString != "":
                        log.debug ("### Added path: " + l_modulesRootDirectoryPathString)
                        sys.path.append (l_modulesRootDirectoryPathString)
                        s_additionalModulesRootUrlToDummyMap.update ({l_modulesRootUrl: False})
                    else:
                        log.debug ("### Unhandled URL: " + l_modulesRootUrl)
                else:
                    None

Objector 53B
. . .

Hypothesizer 7
お分かりのとおり、もちろん、ベースパスがセットされるためには、1つのマクロが1度呼び出される必要があります(ダミーとしてでも)。

例えば、あるユーザー所有マクロは、ある拡張機能のモジュールをインポートできません、その拡張機能の少なくとも1つのマクロが既に呼び出されたのでなければ。

Objector 53B
もちろん。他方では、そのユーザー所有マクロは、任意のユーザー所有モジュールをインポートできるわね、だって、それ自体が既に呼び出されているんだから。

Hypothesizer 7
そうです。


6: ドキュメント内モジュールをインポートするのは別問題です


Hypothesizer 7
ドキュメント内モジュールをインポートするのは別問題です、なぜなら、そのモジュールはオペレーティングシステムファイルとして存在するのではありませんから。

Objector 53B
もちろん。そのモジュールはドキュメントファイル内に埋め込まれているからね。 . . . それで、私はどうすればいいの?

Hypothesizer 7
それは、本記事の次パートで見ましょう。

Objector 53B
警告しておきますけどね、モジュールファイルをどこかに格納するなんていうのは受け入れませんよ、たとえ、一時的にでもね。

Hypothesizer 7
そういうことはいたしません。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>