내용 편집기는 defs.py의 내용에서 캐리지 리턴을 제거했으며 다음과 같이 표시되어야 합니다.
def f(x):
return x*x
휴일 방학 동안 나는 파이썬으로 땜질하는 시간을 가졌습니다. Python/JSL 인터페이스의 병렬 처리와 관련하여 직면한 문제는 Python이 일반적으로 Python이 아닌 JMP의 여러 복사본을 시작한다는 것입니다. 그 이유는 Python을 JMP에 포함하여 실행 파일이 Python이 아닌 JMP이기 때문입니다. 병렬 코드를 실행할 때 실행 파일을 실행하도록 설정하는 멀티프로세싱 패키지를 발견했습니다. 이것은 퍼즐에서 누락된 부분입니다.
아래 스크립트는 Python 3.9가 설치된 Mac 및 Windows 모두에서 JMP 15.2.1에서 테스트했습니다. 내 Windows 컴퓨터에서 Python Init()에 대한 Path() 인수를 사용해야 했습니다. 여러 버전의 Python이 있고 올바른 버전이 선택되었는지 확인하고 싶습니다. 내 Mac에서 인수가 없는 Python Init()은 내 구성에 대해 잘 작동합니다. Windows에서 한 가지 문제가 발생했습니다. pythonw.exe의 경로를 하드코딩해야 했습니다. Python 위치와 일치하도록 변경해야 합니다. FixMe 검색:
Python Init() 매개변수 및 Windows의 pythonw.exe 경로 외에는 JMP 15.2.1 이상에서 Mac 또는 Windows에서 문제 없이 실행되어야 합니다. 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)
]\");