前の関連記事:LibreOffice5(115)イベントが呼ばれる順を調べるマクロ
httpプロトコールからモジュールをロードするPython Cookbookの例
Python Cookbookの10.11.loading_modules_from_a_remote_machine_using_import_hooksの例をまずやります。
最初に読みとられる側のhttpサーバーを起動します。
python3 -m http.server 15000
Terminalからtestcodeディレクトリに移動してそこでこのコマンドを実行します。
pq@pq-VirtualBox:~/git/python-cookbook/PythonCookbook/src/10/10.11.loading_modules_from_a_remote_machine_using_import_hooks/testcode$ python3 -m http.server 15000 Serving HTTP on 0.0.0.0 port 15000 ... 127.0.0.1 - - [24/Dec/2017 13:06:00] "GET /fib.py HTTP/1.1" 200 - 127.0.0.1 - - [24/Dec/2017 13:06:00] "GET /spam.py HTTP/1.1" 200 -linuxBean14.04だとこのような結果になります。
これで準備完了です。
あとは別のTerminalやPyDevパッケージでexplicit_load.pyを実行すれば次のような結果になります。
I'm fib 89 I'm spam Hello Guido <module 'http://localhost:15000/fib.py' from 'http://localhost:15000/fib.py'> <module 'http://localhost:15000/spam.py' from 'http://localhost:15000/spam.py'>Python 3.4.3での実行結果です。
httpプロトコール越しに取得したモジュールが実行されていることがわかります。
Python Cookbookの例のimpモジュールを置き換える
Python Cookbookの例のexplicit_load.pyで使っているimpモジュールはPython3.4で撤廃と書いてあるので代わりのモジュールに置き換えます。
from types import ModuleType
import urllib.request
import sys
def load_module(url):
u = urllib.request.urlopen(url)
source = u.read().decode('utf-8') # モジュールのソースをテキストで取得。
mod = sys.modules.setdefault(url, ModuleType(url)) # 新規モジュールをsys.modulesに挿入。
code = compile(source, url, 'exec') # urlを呼び出し元としてソースコードをコンパイルする。
mod.__file__ = url # モジュールの__file__を設定。
mod.__package__ = '' # モジュールの__package__を設定。
exec(code, mod.__dict__) # モジュールの名前空間を設定する。
return mod
if __name__ == '__main__':
fib = load_module('http://localhost:15000/fib.py')
print(fib.fib(10))
spam = load_module('http://localhost:15000/spam.py')
spam.hello('Guido')
print(fib)
print(spam)
imp.new_module(name)に書いてある通りに、class types.ModuleType(name, doc=None)で置き換えるとうまくいきました。7行目で辞書sys.modulesにurlの名前のモジュールを挿入しています。
sys.modulesの辞書の項目が一つ増えているのはわかりましたが、PyDevのデバッガでなぜかキーをみつけることができませんでした。
この手法は単純なモジュールだけでしか機能しないようですが、とりあえずこの方法で埋め込みマクロからモジュールのインポートを実践してみます。
ドキュメント内のモジュールをロードするマクロ
import sys
from types import ModuleType
consts = None # ドキュメントに埋め込んだモジュール。
def macro(documentevent=None): # 引数は文書のイベント駆動用。
doc = XSCRIPTCONTEXT.getDocument() if documentevent is None else documentevent.Source # ドキュメントのモデルを取得。
controller = doc.getCurrentController() # コントローラの取得。
ctx = XSCRIPTCONTEXT.getComponentContext() # コンポーネントコンテクストの取得。
smgr = ctx.getServiceManager() # サービスマネージャーの取得。
simplefileaccess = smgr.createInstanceWithContext("com.sun.star.ucb.SimpleFileAccess", ctx) # SimpleFileAccess
modulefolderpath = getModuleFolderPath(ctx, smgr, doc) # 埋め込みモジュールフォルダへのURLを取得。
global consts
if consts is None:
consts = load_module(simplefileaccess, "/".join((modulefolderpath, "consts.py"))) # import constsと同等。
s = consts.LISTSHEET["name"]
sheet = controller.getActiveSheet()
sheet["A1"].setString(s)
def load_module(simplefileaccess, modulepath): # modulepathのモジュールを取得。
inputstream = simplefileaccess.openFileRead(modulepath) # モジュールファイルからインプットストリームを取得。
dummy, b = inputstream.readBytes([], inputstream.available()) # simplefileaccess.getSize(module_tdocurl)は0が返る。
source = bytes(b).decode("utf-8") # モジュールのソースをテキストで取得。
mod = sys.modules.setdefault(modulepath, ModuleType(modulepath)) # 新規モジュールをsys.modulesに挿入。
code = compile(source, modulepath, 'exec') # urlを呼び出し元としてソースコードをコンパイルする。
mod.__file__ = modulepath # モジュールの__file__を設定。
mod.__package__ = '' # モジュールの__package__を設定。
exec(code, mod.__dict__) # 実行してモジュールの名前空間を取得。
return mod
def getModuleFolderPath(ctx, smgr, doc): # 埋め込みモジュールフォルダへのURLを取得。
transientdocumentsdocumentcontentfactory = smgr.createInstanceWithContext("com.sun.star.frame.TransientDocumentsDocumentContentFactory", ctx)
transientdocumentsdocumentcontent = transientdocumentsdocumentcontentfactory.createDocumentContent(doc)
tdocurl = transientdocumentsdocumentcontent.getIdentifier().getContentIdentifier() # ex. vnd.sun.star.tdoc:/1
return "/".join((tdocurl, "Scripts/python/pythonpath")) # 開いているドキュメント内の埋め込みマクロフォルダへのパス。
g_exportedScripts = macro, #マクロセレクターに限定表示させる関数をタプルで指定。
18行目でロードしたいモジュールのファイルのインプットストリームを取得してそこから内容をテキストにしてコンパイルして実行しています。Embeddedmodule.ods
このマクロを埋め込んだCalcドキュメントです。
ドキュメント内のフォルダ構成はこうなっています。
embeddedmacro.pyが埋め込みマクロファイルです。
このマクロからconsts.pyをロードしています。
埋め込みマクロを実行するにはマクロセレクターからEmbeddedmodule.ods内のembeddedmacroのmacroを実行します。
うまくいくとconsts.pyから辞書をインポートしてA1セルに値を出力します。
マクロセレクターからはpythonpathフォルダは見えません。
この方法はimport文でロードする方法と違って重複ロードなどはチェックしていないので、使い方に気をつけないといけません。
import文でロードする方法はうまくいかず
(2018.1.13追記。LibreOffice5(120)ドキュメントに埋め込んだモジュールをインポートするでうまくできました。)
Python Cookbookにはimport文でロードできるようにする方法が二つ載っていました。
urlimport.pyはその二つともに対応できるようになっています。
埋め込みマクロからこのモジュールをインポートすることはできないので、sys.meta_pathを変更する方法をマクロファイルに詰め込んで実行してみましたがうまくいきませんでした。
pydevdのデバッグすらできず、PyUNO自体が動かなくなってOSを再起動しないといけませんでした。

0 件のコメント:
コメントを投稿