As several users of the PyRFC Python bindings might have noticed, SAP decided to discontinue the project due to lack of internal resources and the impossibility of finding a new maintainer who might have access to NetWeaver RFC SDK source code to keep the bindings up to date, mainly due to the fact the SDK itself is not free & open software like its bindings.
Needless to say this is a very sad news for me, who is an avid user of the PyRFC package, but also for the Free & Open Software community who recorded another casualty due to non permissive licenses despite SAP believes in Free & Open Source. I hope this paradox will become clear, one day.
Moving (back) to C/C++
Python bindings were not just the only affected project, also Node.js and other bindings were discontinued as well, leaving no other choice of moving to either C or C++ to get official support from SAP, so I decided to try to convert one of my sample PyRFC scripts into a C program to be executed either in Linux or Windows. This is the original script in Python, which queries RFC_GET_SYSTEM_INFO to get system details:
from pyrfc import Connection
from pyrfc._exception import LogonError
conn_params = {
‘user’: ‘***’,
‘passwd’: ‘***’,
‘mshost’: ‘***’,
‘msserv’: ‘***’,
‘r3name’: ‘***’,
‘group’: ‘***’,
‘client’: ‘***’,
‘lang’: ‘***’
}
try:
conn = Connection(**conn_params)
result = conn.call(‘RFC_GET_SYSTEM_INFO’)
except LogonError as e:
print(e)
exit(1)
rfcsi = result[‘RFCSI_EXPORT’]
print(f’RFCDEST: {rfcsi[‘RFCDEST’]}’)
print(f’PFCSYSID: {rfcsi[‘RFCSYSID’]}’)
print(f’RFCDBSYS: {rfcsi[‘RFCDBSYS’]}’)
conn.close()
This is the equivalent written in C, which is significantly larger in size and much more complex to read and understand, although it’s been a while since I last touched the language and it could be there are easier ways to achieve the same results:
#include <stdlib.h>
#include <stdio.h>
#include “sapnwrfc.h”
#define NUM_PARAMS 8
int main(int argc, SAP_UC** argv){
RFC_CONNECTION_PARAMETER loginParams[NUM_PARAMS];
loginParams[0].name = cU(“MSHOST”);
loginParams[0].value = cU(“***”);
loginParams[1].name = cU(“MSSERV”);
loginParams[1].value = cU(“***”);
loginParams[2].name = cU(“R3NAME”);
loginParams[2].value = cU(“***”);
loginParams[3].name = cU(“GROUP”);
loginParams[3].value = cU(“***”);
loginParams[4].name = cU(“CLIENT”);
loginParams[4].value = cU(“***”);
loginParams[5].name = cU(“USER”);
loginParams[5].value = cU(“***”);
loginParams[6].name = cU(“PASSWD”);
loginParams[6].value = cU(“***”);
loginParams[7].name = cU(“LANG”);
loginParams[7].value = cU(“***”);
RFC_ERROR_INFO errorInfo;
RFC_CONNECTION_HANDLE conn;
RFC_FUNCTION_HANDLE funcHandle;
RFC_FUNCTION_DESC_HANDLE funcDescHandle;
RFC_STRUCTURE_HANDLE sysInfoHandle;
RFC_RC rc;
SAP_UC buffer[100];
conn = RfcOpenConnection(loginParams, NUM_PARAMS, &errorInfo);
if (errorInfo.code != RFC_OK) {
printfU(cU(“Connection failed: %sn”), errorInfo.key);
}
funcDescHandle = RfcGetFunctionDesc(conn, cU(“RFC_GET_SYSTEM_INFO”), &errorInfo);
if (funcDescHandle == NULL) {
printfU(cU(“Error getting function description: %sn”), errorInfo.message);
exit(1);
}
funcHandle = RfcCreateFunction(funcDescHandle, &errorInfo);
if (funcHandle == NULL) {
printfU(cU(“Error creating function: %sn”), errorInfo.message);
exit(1);
}
rc = RfcInvoke(conn, funcHandle, &errorInfo);
if (rc != RFC_OK) {
printfU(cU(“Error invoking function: %sn”), errorInfo.message);
exit(1);
}
rc = RfcGetStructure(funcHandle, cU(“RFCSI_EXPORT”), &sysInfoHandle, &errorInfo);
if (rc != RFC_OK) {
printfU(cU(“Error getting RFCSI_EXPORT: %sn”), errorInfo.message);
exit(1);
}
RfcGetString(sysInfoHandle, cU(“RFCDEST”), buffer, sizeof(buffer), NULL, &errorInfo);
printfU(cU(“RFCDEST: %sn”), &buffer);
RfcGetString(sysInfoHandle, cU(“RFCSYSID”), buffer, sizeof(buffer), NULL, &errorInfo);
printfU(cU(“RFCSYSID: %sn”), &buffer);
RfcGetString(sysInfoHandle, cU(“RFCDBSYS”), buffer, sizeof(buffer), NULL, &errorInfo);
printfU(cU(“RFCDBSYS: %sn”), &buffer);
RfcDestroyFunction(funcHandle, &errorInfo);
RfcCloseConnection(conn, &errorInfo);
return 0;
}
Quite surprisingly, the execution time is taking longer for the ELF binary derived from the C source rather than the interpreted Python script (0,8 seconds on average for the ELF binary against 0,7 seconds for the Python script), while normally ELF binaries are orders of magnitude faster than their equivalent in Python. Trying to analyze where the biggest performance impact is located, it seems the Unicode functions are taking a big portion of the load, which surprises me even more…
Final considerations
Converting my scripts from Python to C seems feasible, there’s a learning curve to master as I need to familiarize again with C after many years of Python, but little by little I should be able to get there.
I would like to hear from you whether there are solutions for the Unicode inefficiency, some linking parameters I somehow missed perhaps?
As several users of the PyRFC Python bindings might have noticed, SAP decided to discontinue the project due to lack of internal resources and the impossibility of finding a new maintainer who might have access to NetWeaver RFC SDK source code to keep the bindings up to date, mainly due to the fact the SDK itself is not free & open software like its bindings.Needless to say this is a very sad news for me, who is an avid user of the PyRFC package, but also for the Free & Open Software community who recorded another casualty due to non permissive licenses despite SAP believes in Free & Open Source. I hope this paradox will become clear, one day. Moving (back) to C/C++Python bindings were not just the only affected project, also Node.js and other bindings were discontinued as well, leaving no other choice of moving to either C or C++ to get official support from SAP, so I decided to try to convert one of my sample PyRFC scripts into a C program to be executed either in Linux or Windows. This is the original script in Python, which queries RFC_GET_SYSTEM_INFO to get system details: from pyrfc import Connection
from pyrfc._exception import LogonError
conn_params = {
‘user’: ‘***’,
‘passwd’: ‘***’,
‘mshost’: ‘***’,
‘msserv’: ‘***’,
‘r3name’: ‘***’,
‘group’: ‘***’,
‘client’: ‘***’,
‘lang’: ‘***’
}
try:
conn = Connection(**conn_params)
result = conn.call(‘RFC_GET_SYSTEM_INFO’)
except LogonError as e:
print(e)
exit(1)
rfcsi = result[‘RFCSI_EXPORT’]
print(f’RFCDEST: {rfcsi[‘RFCDEST’]}’)
print(f’PFCSYSID: {rfcsi[‘RFCSYSID’]}’)
print(f’RFCDBSYS: {rfcsi[‘RFCDBSYS’]}’)
conn.close() This is the equivalent written in C, which is significantly larger in size and much more complex to read and understand, although it’s been a while since I last touched the language and it could be there are easier ways to achieve the same results: #include <stdlib.h>
#include <stdio.h>
#include “sapnwrfc.h”
#define NUM_PARAMS 8
int main(int argc, SAP_UC** argv){
RFC_CONNECTION_PARAMETER loginParams[NUM_PARAMS];
loginParams[0].name = cU(“MSHOST”);
loginParams[0].value = cU(“***”);
loginParams[1].name = cU(“MSSERV”);
loginParams[1].value = cU(“***”);
loginParams[2].name = cU(“R3NAME”);
loginParams[2].value = cU(“***”);
loginParams[3].name = cU(“GROUP”);
loginParams[3].value = cU(“***”);
loginParams[4].name = cU(“CLIENT”);
loginParams[4].value = cU(“***”);
loginParams[5].name = cU(“USER”);
loginParams[5].value = cU(“***”);
loginParams[6].name = cU(“PASSWD”);
loginParams[6].value = cU(“***”);
loginParams[7].name = cU(“LANG”);
loginParams[7].value = cU(“***”);
RFC_ERROR_INFO errorInfo;
RFC_CONNECTION_HANDLE conn;
RFC_FUNCTION_HANDLE funcHandle;
RFC_FUNCTION_DESC_HANDLE funcDescHandle;
RFC_STRUCTURE_HANDLE sysInfoHandle;
RFC_RC rc;
SAP_UC buffer[100];
conn = RfcOpenConnection(loginParams, NUM_PARAMS, &errorInfo);
if (errorInfo.code != RFC_OK) {
printfU(cU(“Connection failed: %sn”), errorInfo.key);
}
funcDescHandle = RfcGetFunctionDesc(conn, cU(“RFC_GET_SYSTEM_INFO”), &errorInfo);
if (funcDescHandle == NULL) {
printfU(cU(“Error getting function description: %sn”), errorInfo.message);
exit(1);
}
funcHandle = RfcCreateFunction(funcDescHandle, &errorInfo);
if (funcHandle == NULL) {
printfU(cU(“Error creating function: %sn”), errorInfo.message);
exit(1);
}
rc = RfcInvoke(conn, funcHandle, &errorInfo);
if (rc != RFC_OK) {
printfU(cU(“Error invoking function: %sn”), errorInfo.message);
exit(1);
}
rc = RfcGetStructure(funcHandle, cU(“RFCSI_EXPORT”), &sysInfoHandle, &errorInfo);
if (rc != RFC_OK) {
printfU(cU(“Error getting RFCSI_EXPORT: %sn”), errorInfo.message);
exit(1);
}
RfcGetString(sysInfoHandle, cU(“RFCDEST”), buffer, sizeof(buffer), NULL, &errorInfo);
printfU(cU(“RFCDEST: %sn”), &buffer);
RfcGetString(sysInfoHandle, cU(“RFCSYSID”), buffer, sizeof(buffer), NULL, &errorInfo);
printfU(cU(“RFCSYSID: %sn”), &buffer);
RfcGetString(sysInfoHandle, cU(“RFCDBSYS”), buffer, sizeof(buffer), NULL, &errorInfo);
printfU(cU(“RFCDBSYS: %sn”), &buffer);
RfcDestroyFunction(funcHandle, &errorInfo);
RfcCloseConnection(conn, &errorInfo);
return 0;
} Quite surprisingly, the execution time is taking longer for the ELF binary derived from the C source rather than the interpreted Python script (0,8 seconds on average for the ELF binary against 0,7 seconds for the Python script), while normally ELF binaries are orders of magnitude faster than their equivalent in Python. Trying to analyze where the biggest performance impact is located, it seems the Unicode functions are taking a big portion of the load, which surprises me even more… Final considerationsConverting my scripts from Python to C seems feasible, there’s a learning curve to master as I need to familiarize again with C after many years of Python, but little by little I should be able to get there.I would like to hear from you whether there are solutions for the Unicode inefficiency, some linking parameters I somehow missed perhaps? Read More Technology Blogs by Members articles
#SAP
#SAPTechnologyblog