コンテンツエディタがdefs.pyのコンテンツからキャリッジリターンを削除し、次のように表示されていることに注意してください。
def f(x):
return x*x
休暇中、私はPythonをいじくり回す時間がありました。 Python / JSLインターフェースからの並列処理で遭遇した問題は、Pythonは通常、PythonではなくJMPの複数のコピーを起動することです。 この理由は、PythonをJMPに埋め込んでいるため、実行可能ファイルはPythonではなくJMPです。 マルチプロセッシングパッケージを使用すると、並列コードの実行時に実行可能ファイルを起動するように設定できることがわかりました。 これはパズルに欠けている部分です。
以下のスクリプトは、Python3.9を搭載したMacとWindowsの両方のJMP15.2.1でテストしました。私のWindowsマシンでは、Python Init()へのPath()引数を使用する必要がありました。 Pythonには複数のバージョンがあり、正しいバージョンが選択されていることを確認したいと思います。 私のMacでは、引数のないPython Init()が私の構成で正常に機能します。 Windowsで遭遇した問題が1つあります。それは、pythonw.exeへのパスをハードコーディングする必要があったことです。 Pythonの場所に一致するように変更する必要があります。 FixMeを検索します。
Python Init()パラメータとWindowsでのpythonw.exeへのパスを除いて、これはMacまたはWindowsのいずれかでJMP15.2.1以降で問題なく実行されるはずです。 Pythonをサポートしている古いバージョンのJMPで実行できますが、テストされていません。
このスクリプトと同じディレクトリにdefs.pyファイルを作成する必要があります。 マルチプロセッシングパッケージでは、並行して実行するコードが、スクリプトに対してローカルではなく、インポート可能なコードからのものである必要があります。 したがって、このスクリプトでのほとんどの作業は、sys.pathが適切に設定されていることを確認して、defs.pyファイルをスクリプトで簡単にインポートできるようにすることでした。
defs.pyファイルの内容全体がファイルのコメントに含まれているため、新しいファイルに簡単に貼り付けることができます。 以下に複製された内容は次のとおりです。
def f(x):
return x*x
スクリプトは数値のリスト[1,2,3]を取り、並行してf(x)を呼び出して値を二乗し、結果[1,4,9]を返します。
pyParallel.jsl
Names Default To Here( 1 );
/* Description: Python mulltiprocessing from JMP JSL
* File: pyParallel.jsl, and defs.py
* Author: Paul Nelson
* JMP Statistical Discovery, LLC
*
* Python multiprocessing from an embedded context adds additional challenges.
* Just attempting to run Python parallel processing modules, typically launches
* multiple copies of JMP, not executing the Python code in parallel.
*
* The multiprocessing module allows setting the executable to launch, since we
* are loading Python into JMP, it's an embedded case thus sys.executable => JMP, not Python.
*
*
* Note: Functions to be run in parallel must come from importable code, not local
* code definitions.
* A multiprocessing 'Feature', not bug, See: https://bugs.python.org/issue25053
*
* Known Bugs:
* On Windows within JMP, sys.exec_prefix is empty, so path to pythonw.exe has to be set explicitly
*
* Create a defs.py file with the contents below. Place in the same directory as this JSL file.
def f(x):
return x*x
*/
// On my windows machine since I have multiple versions of Python, I have to specify the
// full path to the python39.dll, on my Mac it just works.
If( Host Is("Windows"),
Python Init(Path("C:\\Users\\panels\\AppData\\Local\\Programs\\Python\\Python39\\python39.dll")),
Python Init()
);
// send the current working directory where we loaded this script, down to Python so we can find defs.py
script_path = Get Default Directory();
Python Send(script_path);
Python Submit("\[
import sys
import os
import multiprocessing
from multiprocessing import Pool
import platform
# The loaded Python does not have the current working directory initialized, so change it
# to the directory of the script, as sent in from JSL
print('script_path: ' + script_path)
hostPlatform = platform.system()
print(hostPlatform)
try:
if 'Windows' == hostPlatform:
# strip leading / off from path if it exists
if '/' == script_path[0]:
script_path = script_path[1:]
norm_path = os.path.normpath(script_path)
print(norm_path)
os.chdir(norm_path)
else:
os.chdir(script_path)
except:
print("Unable to change current working directory", sys.exc_info())
finally:
print(os.getcwd())
# if '.' is not at the beginning of sys.path, prepend so we can look for modules within CWD
if sys.path[0] != '.':
sys.path.insert(0, '.')
print( sys.path )
# here argv is [''] empty string, doesn't seem to bother us in this use case.
#print( 'argv: ', sys.argv )
# prints the path to JMP, not Python
print('sys.executable: ' + sys.executable)
# build up path prefix to python3 exectable
if 'Darwin' == hostPlatform:
pyPath = os.path.join(sys.exec_prefix, 'bin', 'python3')
elif 'Windows' == hostPlatform:
# Bug - on windows sys.exec_prefix is empty in Python within JMP!
pyPath = None
if sys.exec_prefix:
pyPath = os.path.join(sys.exec_prefix, 'pythonw.exe')
else:
# FixMe: hardcode for now
pyPath = 'C:\\Users\\panels\\AppData\\Local\\Programs\\Python\\Python39\\pythonw.exe'
print ('Python Executable: ' + pyPath)
# Finally down to running something in parallel.
#
# Import the function code that will be executed in parallel.
# JMP is not reporting ImportError or ModuleNotFound errors, the scripting just returns -1
# on an import failure, so wrap it within our own try: except: block
try:
import defs as d
# print( d.f(2) )
# Tell multiprocessing to use the Python executable, not JMP to spawn the workers.
multiprocessing.set_executable(pyPath)
# Run the tasks in parallel - (separate Python instances)
with Pool(5) as p:
print(p.map(d.f, [1,2,3])) # Results in [1,4,9] the square of the numbers
except ModuleNotFoundError as mnf:
print(mnf)
print('Check that the moudule is located on your sys.path:')
print( sys.path )
except ImportError as error:
print(error)
except Exception as ex:
print(ex)
]\");