Skip to main content

My python code is working fine outside of FME but it fails inside of a PythonCaller. The documentation doesn't give any information about the scope or execution context in which the code inside a PythonCaller is run.

 

What are the restrictions when running python code inside a PythonCaller? Why does it not run the same code that you can run outside of FME? (while using the same installation of python in FME and on the command line)

I'm trying to use concurrent.futures.ProcessPoolExecutor for parallel processing in Python inside a PythonCaller but I'm getting this error:

from concurrent.futures import ProcessPoolExecutor

def test(x):
return x

with ProcessPoolExecutor(16) as executor:
result = executor.map(test, range(0,10))
next(result)
Python Exception <PicklingError>: Can't pickle <function test at 0x000000000F6A8F70>: attribute lookup test on __main__ failed
concurrent.futures.process._RemoteTraceback:
"""
Traceback (most recent call last):
File "multiprocessing\queues.py", line 244, in _feed
File "multiprocessing\reduction.py", line 51, in dumps
_pickle.PicklingError: Can't pickle <function test at 0x000000000F6A8F70>: attribute lookup test on __main__ failed
"""

ProcessPoolExecutor relies on the pickle module so I ran this code and got the same error:

import pickle

def test():
pass

pickle.dumps(test)
Python Exception <PicklingError>: Can't pickle <function test at 0x000000000F698F70>: attribute lookup test on __main__ failed
Traceback (most recent call last):
File "<string>", line 7, in <module>
_pickle.PicklingError: Can't pickle <function test at 0x000000000F698F70>: attribute lookup test on __main__ failed

 

This problem only occurs in FME, not if I run the same Python script outside of FME. I did a lot of research on the internet and it essentially always leads me to this section in the docs: What can be pickled and unpickled? It says functions accessible from the top level of a module can be pickled. And test is a function at the top level of the module. You can check by printing globals().

 

Why does the attribute lookup for test on __main__ fail when running my code in FME?

I'm going to reply to this question, but will link to your other one here: https://community.safe.com/s/question/0D5Dm0000187DPNKA2/multicore-processing-in-python

 

I'm not saying this is necessarily an answer, but may help guide someone to an answer.  This is getting out of my area of knowledge.

 

I believe this 'problem' to be down to how FME initiates Python. Whist you have what appears to be a 'blank slate' when you open up the python caller, there is actually level above it that you can't control, hence getting that error around pickling. 

 

What does appear to work however is multi-threading. The below example works. Of course you'd then need to figure out how it can be dynamically adapted to your process, but it proves the functionality is there

 

import fme
import fmeobjects
import threading
import time
import random
 
 
def thread_function_1():
    for i in range(5):
        print("Thread 1: Working...")
        print("Run: "+ str(i))
        time.sleep(random.randint(0, 5))
 
def thread_function_2():
    for i in range(5):
        print("Thread 2: Working...")
        print("Run: "+ str(i))
        time.sleep(random.randint(0, 5))
 
def test(f):
    # Create two thread objects
    thread1 = threading.Thread(target=thread_function_1)
    thread2 = threading.Thread(target=thread_function_2)
 
    # Start the threads
    thread1.start()
    thread2.start()
 
    # Wait for both threads to finish
    thread1.join()
    thread2.join()
 
    print("Both threads have finished.")

 


I'm going to reply to this question, but will link to your other one here: https://community.safe.com/s/question/0D5Dm0000187DPNKA2/multicore-processing-in-python

 

I'm not saying this is necessarily an answer, but may help guide someone to an answer.  This is getting out of my area of knowledge.

 

I believe this 'problem' to be down to how FME initiates Python. Whist you have what appears to be a 'blank slate' when you open up the python caller, there is actually level above it that you can't control, hence getting that error around pickling. 

 

What does appear to work however is multi-threading. The below example works. Of course you'd then need to figure out how it can be dynamically adapted to your process, but it proves the functionality is there

 

import fme
import fmeobjects
import threading
import time
import random
 
 
def thread_function_1():
    for i in range(5):
        print("Thread 1: Working...")
        print("Run: "+ str(i))
        time.sleep(random.randint(0, 5))
 
def thread_function_2():
    for i in range(5):
        print("Thread 2: Working...")
        print("Run: "+ str(i))
        time.sleep(random.randint(0, 5))
 
def test(f):
    # Create two thread objects
    thread1 = threading.Thread(target=thread_function_1)
    thread2 = threading.Thread(target=thread_function_2)
 
    # Start the threads
    thread1.start()
    thread2.start()
 
    # Wait for both threads to finish
    thread1.join()
    thread2.join()
 
    print("Both threads have finished.")

 

> I believe this 'problem' to be down to how FME initiates Python. Whist you have what appears to be a 'blank slate' when you open up the python caller, there is actually level above it that you can't control, hence getting that error around pickling.

 

Thanks for your reply. I think you're right. FME loads the python310.dll dynamically and runs python inside of the FME.exe process. We don't know what exactly is happening there. I can't find any documentation in which context the code inside the PythonCaller is actually executed. That's why I'm asking what exact restrictions exist when running Python code inside of the PythonCaller?

 

Your multi-threading example works fine, but I'm looking for parallel processing on multiple CPU cores because it makes FME (or at least the PythonCaller part of my workspace) run 16 times faster. The reason why multi-threading works is probably because pickling is not necesarry since there is no inter-process communication.


I'm going to reply to this question, but will link to your other one here: https://community.safe.com/s/question/0D5Dm0000187DPNKA2/multicore-processing-in-python

 

I'm not saying this is necessarily an answer, but may help guide someone to an answer.  This is getting out of my area of knowledge.

 

I believe this 'problem' to be down to how FME initiates Python. Whist you have what appears to be a 'blank slate' when you open up the python caller, there is actually level above it that you can't control, hence getting that error around pickling. 

 

What does appear to work however is multi-threading. The below example works. Of course you'd then need to figure out how it can be dynamically adapted to your process, but it proves the functionality is there

 

import fme
import fmeobjects
import threading
import time
import random
 
 
def thread_function_1():
    for i in range(5):
        print("Thread 1: Working...")
        print("Run: "+ str(i))
        time.sleep(random.randint(0, 5))
 
def thread_function_2():
    for i in range(5):
        print("Thread 2: Working...")
        print("Run: "+ str(i))
        time.sleep(random.randint(0, 5))
 
def test(f):
    # Create two thread objects
    thread1 = threading.Thread(target=thread_function_1)
    thread2 = threading.Thread(target=thread_function_2)
 
    # Start the threads
    thread1.start()
    thread2.start()
 
    # Wait for both threads to finish
    thread1.join()
    thread2.join()
 
    print("Both threads have finished.")

 

Thank you sir for answering.


Reply