Not pickable object using multiprocessing in SAP GUI with Python

89 Views Asked by At

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!!

2

There are 2 best solutions below

0
Luiz Carlos On BEST ANSWER

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.

0
Cleydson Roque S. R. On

Thank you very much for sharing, I did a test exactly as you said and it worked the first time, now with this concept I will be able to speed up my RPAs.

from sap import SAPConnector
import threading
import pythoncom
import win32com.client

SAP = SAPConnector()
sap_sessions = SAP.session(total_sessions=6)

def serializeSessions(sap_sessions):
    serial_sessions = []
    for session in sap_sessions:
        serial_sessions.append(
            pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch,session)
            )
    return serial_sessions

def deserializeSession(serial_session):
    pythoncom.CoInitialize()
    session = win32com.client.Dispatch(
        pythoncom.CoGetInterfaceAndReleaseStream(serial_session, pythoncom.IID_IDispatch)
    )
    return session

def xk03(serial_session):
    session = deserializeSession(serial_session)
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nxk03"
    session.findById("wnd[0]").sendVKey(0)

def me21n(serial_session):
    session = deserializeSession(serial_session)
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nme21n"
    session.findById("wnd[0]").sendVKey(0)

def me33k(serial_session):
    session = deserializeSession(serial_session)
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nme33k"
    session.findById("wnd[0]").sendVKey(0)

def xk03(serial_session):
    session = deserializeSession(serial_session)
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nxk03"
    session.findById("wnd[0]").sendVKey(0)

def ie03(serial_session):
    session = deserializeSession(serial_session)
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nie03"
    session.findById("wnd[0]").sendVKey(0)

def ml81n(serial_session):
    session = deserializeSession(serial_session)
    session.findById("wnd[0]/tbar[0]/okcd").text = "/nml81n"
    session.findById("wnd[0]").sendVKey(0)

def run_parallel(list_sessions):
    t1 = threading.Thread(target=xk03, args=(list_sessions[0], ))
    t2 = threading.Thread(target=me21n, args=(list_sessions[1], ))
    t3 = threading.Thread(target=me33k, args=(list_sessions[2], ))
    t4 = threading.Thread(target=xk03, args=(list_sessions[3], ))
    t5 = threading.Thread(target=ie03, args=(list_sessions[4], ))
    t6 = threading.Thread(target=ml81n, args=(list_sessions[5], ))
    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t5.start()
    t6.start()
    t1.join()
    t2.join()
    t3.join()
    t4.join()
    t5.join()
    t6.join()

list_serial_sessions = serializeSessions(sap_sessions)
run_parallel(list_serial_sessions)