2021年3月7日日曜日

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

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

任意のドキュメント内モジュールもあなたのマクロまたはあなたのマクロによってインポートされた任意のモジュールへインポートできます。以下はその方法です。

話題


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

この記事の目次


開始コンテキスト



ターゲットコンテキスト



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

オリエンテーション


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

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

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

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

任意の非ドキュメント内モジュールをインポートすることは、本記事の第1パートにて取り扱われました。

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


本体

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


1: ドキュメント内モジュールは別問題です、なぜならば . . .


Hypothesizer 7
もしも、インポートされるべきモジュールがドキュメント内でないのであれば、本記事の第1パートだけで十分なはずです。

任意のドキュメント内モジュールは別問題ですが、その理由は、それがオペレーティングシステムファイル内にあるのではないことです。

Objector 54A
ドキュメント・オペレーティングシステムファイル内にあるがね。

Hypothesizer 7
確かに、サー、しかし、私が申し上げているのは、ドキュメント・オペレーティングシステムファイルは、モジュールファイルではなく、モジュールファイルを格納しているZIPファイルであるということです。

Objector 54A
じゃあ、私が正しかったわけだ。

Hypothesizer 7
えーと、厳密に言うと、任意のドキュメント内モジュールは、オペレーティングシステムファイル内に直接にはありません。

とにかく、問題は、そうしたモジュール位置は'PYUNO_LOADER_PYTHONPATH'や'sys.path'にはセットできないということです。


2: モジュールはインポートすることができる、もしも、モジュールのコンテンツが得られたならば


Hypothesizer 7
実のところ、もしも、モジュールのコンテンツが得られたのであれば、そのモジュールはインポートすることができます

Objector 54B
. . . それじゃあ、ソースローダーを作ればよいだけだと?

Hypothesizer 7
もしも、当該モジュールコンテンツの取得がおできになるのであれば、そうです、マダム。

Objector 54B
ZIPファイルからモジュールファイルを抽出すればいいんでしょ(ドキュメントファイルはZIPファイルですよね)?

Hypothesizer 7
それは1つのオプションかもしれません、もしも、当該ドキュメントファイルが暗号化されていないのであれば。

Objector 54B
えーと、もしされてたらどうなの?

Hypothesizer 7
そのモジュールファイルのコンテンツは暗号化されているでしょう。

Objector 54B
. . . コンテンツは復号化できるでしょう?

Hypothesizer 7
おできになりますか?

Objector 54B
. . . できない?

Hypothesizer 7
えーと、論理的に言えば、可能なはずです、もしも、当該ロジックをLibreOfficeまたはApache OpenOfficeのソースファイルから学ばれて、また、勿論、ユーザーにパスワードを提供させるのであれば。

Objector 54B
. . .


3: モジュールコンテンツはオープン済みのドキュメントから取得できる、もしもそのURLが知られていれば


Hypothesizer 7
実のところ、モジュールコンテンツは、オープン済みのドキュメントから取得することができます、以下のようにして、ここで、'l_remoteUnoObjectsContextInXComponentContext'は、LibreOfficeまたはApache OpenOfficeインスタンスへのUNOオブジェクト群コンテキスト、'a_url'は、モジュールURL、リターンは、'bytes'データです。

@Python ソースコード
from uno import ByteSequence
from com.sun.star.io import XInputStream
from com.sun.star.ucb import XSimpleFileAccess

		l_simpleFilesAccessUnoService: XSimpleFileAccess = cast (XSimpleFileAccess, l_remoteUnoObjectsContextInXComponentContext.getServiceManager ().createInstanceWithContext ("com.sun.star.ucb.SimpleFileAccess", l_remoteUnoObjectsContextInXComponentContext))
		try:
			l_inputStream: "XInputStream" = l_simpleFilesAccessUnoService.openFileRead (a_url)
			l_readLength: int = 0
			l_inputDatum: ByteSequence = ByteSequence (b"")
			l_inputBuffer: ByteSequence = None
			while True:
				l_readLength, l_inputBuffer = l_inputStream.readBytes (None, 1024)
				l_inputDatum = l_inputDatum + l_inputBuffer
				if l_readLength < 1024:
					break
		finally:
			if l_inputStream is not None:
				l_inputStream.closeInput
		return l_inputDatum.value

Objector 54A
. . . はあ?「UNOオブジェクト群コンテキスト」?何だ、それは?

Hypothesizer 7
. . . 基本を学ばずに済まそうとしないでください。

Objector 54A
「基本」だと?ふざけてるのか?そんなのを学ぶのは私のこ券に関わる。

Hypothesizer 7
. . . もしも、あなたのプログラムがマクロであれば、'XSCRIPTCONTEXT.getComponentContext ()'がそれをリターンします。

Objector 54A
それで、URLは何だ?

Hypothesizer 7
URLは、'vnd.sun.star.tdoc:/1/Scripts/python/theBiasPlanet/pythonEnvironmentChecker/InDocumentModuleTest.py'のようなものです。

Objector 54A
それじゃあ、'Scripts/python'の後ろの部分を置き換えればいいのか?

Hypothesizer 7
実は、そこの数字、'1'は、ドキュメントロードナンバーです。

Objector 54A
どういう意味だ?

Hypothesizer 7
1番目にロードされたドキュメントには'1'が与えられ、2番目のものには'2'が、等になります、インスタンスが開始されてから。

Objector 54A
一体全体、特定のドキュメントの番号をどうやって私が知るように想定されているのだ?

Hypothesizer 7
えーと、いくつかの方法がありえます。


4: 対象モジュールが、同一ドキュメント内のマクロにインポートされると仮定すると


Hypothesizer 7
対象モジュールが、同一ドキュメント内のマクロにインポートされると仮定しましょう、それは、私が想像するに、普通のケースでしょう。

Objector 54B
他にどうありえると言うの?

Hypothesizer 7
そのモジュールは、別のドキュメント内のマクロ、または、ユーザ所有、アプリケーション所有、拡張機能内のいずれかのマクロにインポートされるかもしれません。

Objector 54B
それは考えにくい、と私は思うけど。

Hypothesizer 7
もしもそうであれば、それはとても好都合です、私にとっても、あなたにとっても。

Objector 54B
じゃあ、そうだと仮定して . . .

Hypothesizer 7
実は、本記事の第1パートのあるセクションにて、当該マクロモジュールのURLをそのマクロモジュールのある変数('s_sourceFileUrl')にセットするロジックを私は実装済みです。

Objector 54B
. . . ふーむ、あなたが言ってるのは、インポートするマクロのURLのことでしょ、私が本当に必要としている、インポートされるモジュールのURLのことじゃなく?

Hypothesizer 7
はい、しかし、あなたが必要とする、ドキュメントロードナンバーは、同じものです。

Objector 54B
確かに。


5: しかし、もしも、対象モジュールが、同一ドキュメント内のあるマクロにインポートされたあるモジュールへインポートされるならば?


Objector 54B
でも、もしも、対象モジュールが、同一ドキュメント内のあるマクロにインポートされたあるモジュール(勿論、同一ドキュメント内にある)へインポートされるとしたら?

Hypothesizer 7
そこで、当該マクロに、インポートされたモジュールのURLをインポートされたモジュールへセットさせましょう。

Objector 54B
ああ、もちろん、それじゃあ、URLたちが順次セットされていくわけね。

Hypothesizer 7
実装上は、ソースローダーが、URLを、インポートされるモジュールへセットできます。


6: 対象モジュールが、同一ドキュメント内にないマクロまたはモジュールへインポートされると仮定すると


Objector 54A
それじゃあ、対象モジュールが、同一ドキュメント内にないマクロまたはモジュールへインポートされると仮定しろ。

Hypothesizer 7
えーと、本当にそれを必要とされますか、サー?

Objector 54A
実際にはしない、少なくとも、今のところはな、しかし、とにかくそうしろ。

Hypothesizer 7
なぜでございますか、サー?

Objector 54A
なぜなら、任意のモジュールがインポートできると言った約束をお前が果たさずに済ますことを、私は許さんからだ。

Hypothesizer 7
結構でございます。いくつか方法がありえますが、私であれば、当該ドキュメント内に1つマクロを用意して、そのマクロに、そのドキュメントのPythonモジュール群ベースURLをリターンさせるでしょう。

Objector 54A
. . . どうすれば、そのマクロをコールできるんだ?

Hypothesizer 7
ある以前の記事にて紹介された方法でできるでしょう。

Objector 54A
. . . えーと、他にどんな方法がある?

Hypothesizer 7
えーと、より推奨できる方法を私は知りませんが、多分、ドキュメントオープンイベント群ハンドラを作成してドキュメントのオープン群を記録させるとか、単に力ずくで試行するとか、することができるでしょう、例えば。


7: あるソースローダーおよびあるモジュールインポーターのコード


Hypothesizer 7
以下は、あるソースローダーおよびあるモジュールインポーターのコードです。

theBiasPlanet/unoUtilities/pythonSourceLoader/UnoExtendedPythonSourceLoader.py

@Python ソースコード
from typing import Union
from typing import cast
from importlib.abc import SourceLoader
import uno
from uno import ByteSequence
from com.sun.star.io import XInputStream
from com.sun.star.ucb import XSimpleFileAccess
from com.sun.star.uno import XComponentContext

class UnoExtendedPythonSourceLoader (SourceLoader):
	c_readingBlockSize: int = 1024
	
	def __init__ (a_this: "UnoExtendedPythonSourceLoader", a_remoteUnoObjectsContextInXComponentContext: "XComponentContext", a_uriPrefix: str) -> None:
		a_this.i_simpleFilesAccessUnoService: XSimpleFileAccess = None
		a_this.i_uriPrefix: str = a_uriPrefix
		
		a_this.i_simpleFilesAccessUnoService = cast (XSimpleFileAccess, a_remoteUnoObjectsContextInXComponentContext.getServiceManager ().createInstanceWithContext ("com.sun.star.ucb.SimpleFileAccess", a_remoteUnoObjectsContextInXComponentContext))
	
	def get_filename (a_this: "UnoExtendedPythonSourceLoader", a_moduleName: str) -> str:
		return "{0:s}{1:s}.{2:s}".format (a_this.i_uriPrefix, a_moduleName.replace (".", "/"), "py")
	
	def get_data (a_this: "UnoExtendedPythonSourceLoader", a_url: Union [bytes, str]) -> bytes:
		try:
			l_inputStream: "XInputStream" = a_this.i_simpleFilesAccessUnoService.openFileRead (a_url)
			l_readLength: int = 0
			l_inputDatum: ByteSequence = ByteSequence (b"")
			l_inputBuffer: ByteSequence = None
			while True:
				l_readLength, l_inputBuffer = l_inputStream.readBytes (None, 1024)
				l_inputDatum = l_inputDatum + l_inputBuffer
				if l_readLength < 1024:
					break
		finally:
			if l_inputStream is not None:
				l_inputStream.closeInput
		return l_inputDatum.value

theBiasPlanet/unoUtilities/pythonModuleImporter/UnoExtendedPythonModuleImporter.py

@Python ソースコード
from typing import Optional
from collections import OrderedDict
import sys
from types import ModuleType
from theBiasPlanet.unoUtilities.pythonSourceLoader.UnoExtendedPythonSourceLoader import UnoExtendedPythonSourceLoader

class UnoExtendedPythonModuleImporter:
	c_pythonModulesBaseDirectoryIndicator: str = "/Scripts/python/"
	c_sourceFileUrlModulePropertyName: str = "s_sourceFileUrl"
	
	@staticmethod
	def getUriPrefix (a_sourceFileUrl: str) -> str:
		l_uriPrefix: str = a_sourceFileUrl [0: a_sourceFileUrl.find (UnoExtendedPythonModuleImporter.c_pythonModulesBaseDirectoryIndicator) + len (UnoExtendedPythonModuleImporter.c_pythonModulesBaseDirectoryIndicator)]
		return l_uriPrefix
	
	@staticmethod
	def importModule (a_unoExtendedPythonSourceLoader: "UnoExtendedPythonSourceLoader", a_moduleName: str, a_setModuleProperties: "Optional [OrderedDict [str, object]]") -> ModuleType:
		l_pythonModule = ModuleType (a_moduleName)
		l_pythonModule.__dict__ [UnoExtendedPythonModuleImporter.c_sourceFileUrlModulePropertyName] = a_unoExtendedPythonSourceLoader.get_filename (a_moduleName)
		l_propertyName: str
		l_propertyValue: object
		for l_propertyName, l_propertyValue in a_setModuleProperties.items ():
			l_pythonModule.__dict__ [l_propertyName] = l_propertyValue
		a_unoExtendedPythonSourceLoader.exec_module (l_pythonModule)
		sys.modules [a_moduleName] = l_pythonModule
		return l_pythonModule

Objector 54B
えーと . . .

Hypothesizer 7
そのソースローダのコンストラクタは、LibreOfficeまたはApache OpenOfficeインスタンスへのUNOオブジェクト群コンテキスト、および'vnd.sun.star.tdoc:/1/Scripts/python/'のようなURLプリフィックスを取ります。

そのモジュールインポーターのインポート実行メソッドは、モジュール名、およびインポートされたモジュールへセットされるプロパティ群を取ります。


8: 使用例


Hypothesizer 7
以下は、同一ドキュメント内モジュール、'theBiasPlanet.pythonEnvironmentChecker.InDocumentModuleTest'を、あるモジュール(それはマクロモジュールであるかもないかもしれない)インポートする使用例です。

@Python ソースコード
from collections import OrderedDict
~
import sys
~
from types import ModuleType
~
from com.sun.star.script.provider import XScriptContext
~
from theBiasPlanet.unoUtilities.pythonModuleImporter.UnoExtendedPythonModuleImporter import UnoExtendedPythonModuleImporter
from theBiasPlanet.unoUtilities.pythonSourceLoader.UnoExtendedPythonSourceLoader import UnoExtendedPythonSourceLoader

XSCRIPTCONTEXT: XScriptContext

s_unoExtendedPythonSourceLoader: "UnoExtendedPythonSourceLoader" = UnoExtendedPythonSourceLoader (XSCRIPTCONTEXT.getComponentContext (), UnoExtendedPythonModuleImporter.getUriPrefix (s_sourceFileUrl))
s_pythonModule: ModuleType = UnoExtendedPythonModuleImporter.importModule (s_unoExtendedPythonSourceLoader, "theBiasPlanet.pythonEnvironmentChecker.InDocumentModuleTest", OrderedDict ( [ ("XSCRIPTCONTEXT", XSCRIPTCONTEXT)]))
InDocumentModuleTest = s_pythonModule.InDocumentModuleTest

~

def checkPythonEnvironment2 (a_message: str) -> str:
		return "The Python environment: version -> {0:s}, paths -> {1:s}\n".format (sys.version, str (sys.path)) + ", " + InDocumentModuleTest.c_test + ", " + s_sourceFileUrl

Objector 54A
. . . それは、なんかごてごてしてるぞ。

Hypothesizer 7
そうですか?どう見えるかはとにかくとして、あなたが変更しなければならないのは、モジュール名だけです。

Objector 54A
うーん . . .

Hypothesizer 7
ご注意いただきたいのですが、そこの's_sourceFileUrl'がそこに存在するのは、'pythonscript.py'(本記事の第1パートにおけるように変更された)またはそのモジュールインポーターがそこにセットしたからにほかなりません。

Objector 54A
同一ドキュメント内にないモジュールはどうなる?

Hypothesizer 7
えーと、唯一の課題は、's_sourceFileUrl'の代わりに適切な値を用いなければならないということだけです。

Objector 54A
どのように適切にしろと?

Hypothesizer 7
とても適切に。

Objector 54A
. . . 「そのドキュメントのPythonモジュール群ベースURLをリターンさせる」当該ドキュメント内マクロ」なるものが具体的にどのようであるべきなのか、私には分からん。

Hypothesizer 7
お分かりになりませんか?そのマクロは、単に、'UnoExtendedPythonModuleImporter.getUriPrefix (s_sourceFileUrl)'をリターンすることになります、以下のように。

@Python ソースコード
def getBaseUrlOfThisDocumentPythonModules () -> str:
	return UnoExtendedPythonModuleImporter.getUriPrefix (s_sourceFileUrl)

既に申し上げたとおり、そのマクロをどのようにコールするかは、ある以前の記事に記述されております。


参考資料


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