pythonpicklepywin32sap-guidill

Not pickable object using multiprocessing in SAP GUI with Python


there.

I have two daily extractions using Python in SAP GUI that consume a lot of time, and this must be done faster. So, i am trying to run in parallel two transactions for solve this issue.

I am stuck in the same problem occurred here Multiprocessing in SAP GUI using Python , but i cannot find the way this it was solve. Basically, it is a problem when i am trying to acess SAP GUI using multiprocessing library for Python and one of internal modules try pickle the SAP GUI session object.

This is the error: TypeError: cannot pickle 'PyIDispatch' object

I am trying to acess SAP GUI version 740 for extract two transactions report at the same time, and like my friend

above i cannot use RFC tools. I kwnow that is non-maintainable solution, but it will be a temporary solution for this problem.

I have two files in my code - sap_novo:

import time, win32com.client
from subprocess import Popen
class SAP:
def __init__(self, site): # Função que inicia a classe SAP
     self.path = 'C:\\Program Files (x86)\\SAP\\FrontEnd\\SAPgui\\saplgpad.exe'
     self.site = site
     self.SSO = SSO
     self.con = None
     self.appl = None
     self.sap_gui = None
     self.session = None

    @property
    def session(self):
        return self._session
    
    @session.setter
    def session(self, value):
        self._session = value
    
    def connectToSAP(self): 
        Popen(self.path)
        time.sleep(15)
        SapGui = win32com.client.GetObject("SAPGUI")
        Appl = SapGui.GetScriptingEngine
        con = Appl.OpenConnection(self.site, True)
        self.session = con.Children(0)
        self.con = Appl.Connections(0)

main

from multiprocess import Process
import sap_novo
import dill

def CX34(con_base, id_session):

    sap_session = con_base.Sessions(id_session)
    
    sap_session.findById("wnd[0]").maximize()
    sap_session.findById("wnd[0]/tbar[0]/okcd").text = "cx34"
    sap_session.findById("wnd[0]").sendVKey(0)

def FS10N(con_base, id_session):

    sap_session = con_base.Sessions(id_session)
    
    sap_session.findById("wnd[0]").maximize()
    sap_session.findById("wnd[0]/tbar[0]/okcd").text = "FS10N"
    sap_session.findById("wnd[0]").sendVKey(0)

def run_parallel(connection):
    p1 = Process(target=CX34, args=(connection, 0))
    p2 = Process(target=FS10N, args=(connection, 1))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

def main():

    sap_obj = sap_novo.SAP('Environment')
    sap_obj.connectToSAP()
    sap_obj.checkLogin(sap_obj.session,"wnd[1]")
    run_parallel(sap_obj.con)

if __name__ == '__main__':
    main()

I use dill package for understand what is happening using this

dill.detect.trace(True)
dill.pickles(sap_obj.session)
print(dill.dumps(sap_obj.session))

, and i got:

T4: \<class 'win32com.client.CDispatch'\>
 # T4 \[32 B\]
┬ D2: \<dict object at 0x20ba608a500\>
┬ T4: \<class 'win32com.client.CDispatch'\>
└ # T4 \[32 B\]
┬ D2: \<dict object at 0x20ba608a500\>

Then, it looks like a problem with serialization/pickle in win32com.client.CDispatch objects.

Anyone could help me this?

Thanks!!


Solution

  • guys.

    I achieved my goal using python threading package. I could not make it work using multiprocess.

    Basicaly, i am making two threads, one of each transaction code, and passing a session id for each function.

    However, it is necessary a intermediary step for make this work. I had to use pythoncom library for pass a session object between the threads, because there is some limitation in working with COM objects and threads together in python. This topic help me to do that Using win32com with multithreading.

    In this sense, i had to create two functions to serialize and deserialize sessions objects.

        def serializeSessions(sap_sessions):
        sessions = []
        for session in sap_sessions:
            sessions.append(pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch,
                                                                            session))
        return sessions
    
    def deserializeSession(session_id):
        pythoncom.CoInitialize()
        #
        # Get instance from the id
        session = win32com.client.Dispatch(
            pythoncom.CoGetInterfaceAndReleaseStream(session_id, pythoncom.IID_IDispatch)
        )
    
        return session
    

    With this, i am doing a serialize before instantiate a thread, then i pass the serialized session object and run a deserialization inside the transaction function.