How can I select an SAP GuiGridView row via Python scripting - python

I am trying to use Python for SAP Scripting, specifically to select a row in a GuiGridView object. It works just fine in VB Script, but in Python I get an exception: -2147352562, 'Invalid number of parameters.', which doesn't make sense to me since I'm using the same number of parameters as for VB Script.
I am trying to get away from VB Script since it doesn't have proper error handling.
Here is the documentation (https://help.sap.com/doc/9215986e54174174854b0af6bb14305a/760.01/en-US/sap_gui_scripting_api_761.pdf ), but even with that I haven't been able to resolve this:
Page 128
SelectedRows (Read-write)
Public Property SelectedRows As String
The string is a comma separated list of row index numbers or index ranges, such as “1,2,4-8,10”.Setting this property to an invalid string or a string containing invalid row indices will raise an exception.
The following VB Script code selects the GuiGridRow as expected, but when I attempt to convert that to Python it won't work.
If Not IsObject(application) Then
Set SapGuiAuto = GetObject("SAPGUI")
Set application = SapGuiAuto.GetScriptingEngine
End If
If Not IsObject(connection) Then
Set connection = application.Children(0)
End If
If Not IsObject(session) Then
Set session = connection.Children(0)
End If
If IsObject(WScript) Then
WScript.ConnectObject session, "on"
WScript.ConnectObject application, "on"
End If
Set GridView = session.findById("wnd[0]/usr/cntlGRID1/shellcont/shell")
GridView.SelectedRows = 1
Here is my Python 3.9 attempt using Jupyter Notebook and the results:
+*In[1]:*+
import win32com.client
+*In[2]:*+
SapGuiAuto = win32com.client.GetObject('SAPGUI')
application = SapGuiAuto.GetScriptingEngine
connection = application.Children(0)
session = connection.Children(0)
type(session)
+*Out[2]:*+
----win32com.client.CDispatch----
+*In[3]:*+
grid_view = session.findById('wnd[0]/usr/cntlGRID1/shellcont/shell')
print(grid_view)
+*Out[3]:*+
<COMObject <unknown>>
+*In[4]:*+
# to show that grid_view is a valid GuiGridView object
grid_view.RowCount
+*Out[4]:*+
----2----
+*In[5]:*+
# reading the SelectedRows works
grid_view.SelectedRows
+*Out[5]:*+
----'1'----
+*In[6]:*+
# setting SelectedRows as number fails
# note: I had manually unselected the row in SAP before running the rest of the code
grid_view.SelectedRows = 1
+*Out[6]:*+
---------------------------------------------------------------------------
com_error Traceback (most recent call last)
~\AppData\Local\Temp\20/ipykernel_21344/1273355717.py in <module>
----> 1 grid_view.SelectedRows = 1
E:\Anaconda\Lib\site-packages\win32com\client\dynamic.py in __setattr__(self, attr, value)
541 entry = self._olerepr_.propMap[attr]
542 invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
--> 543 self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
544 return
545 # Check the specific "put" map.
com_error: (-2147352562, 'Invalid number of parameters.', None, None)
----
+*In[7]:*+
# setting SelectedRows as string fails
grid_view.SelectedRows = '1'
+*Out[7]:*+
---------------------------------------------------------------------------
com_error Traceback (most recent call last)
~\AppData\Local\Temp\20/ipykernel_21344/1222153478.py in <module>
----> 1 grid_view.SelectedRows = '1'
E:\Anaconda\Lib\site-packages\win32com\client\dynamic.py in __setattr__(self, attr, value)
541 entry = self._olerepr_.propMap[attr]
542 invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
--> 543 self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
544 return
545 # Check the specific "put" map.
com_error: (-2147352562, 'Invalid number of parameters.', None, None)
----
+*In[8]:*+
# setting SelectedRows as string with multiple rows fails
grid_view.SelectedRows = '0,1'
----
+*Out[8]:*+
---------------------------------------------------------------------------
com_error Traceback (most recent call last)
~\AppData\Local\Temp\20/ipykernel_21344/3316141255.py in <module>
----> 1 grid_view.SelectedRows = '0,1'
E:\Anaconda\Lib\site-packages\win32com\client\dynamic.py in __setattr__(self, attr, value)
541 entry = self._olerepr_.propMap[attr]
542 invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
--> 543 self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
544 return
545 # Check the specific "put" map.
com_error: (-2147352562, 'Invalid number of parameters.', None, None)
----
+*In[9]:*+
# setting SelectedRows as string with multiple rows in a method-type notation fails
grid_view.SelectedRows('0,1')
----
+*Out[9]:*+
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp\20/ipykernel_21344/2186236324.py in <module>
----> 1 grid_view.SelectedRows('0,1')
TypeError: 'str' object is not callable
----

Ok, after digging around a lot more, patching to the latest version of win32com worked:
https://github.com/mhammond/pywin32/tree/main/com/win32com
Once I did that all of the following worked to select rows:
grid_view.SelectedRows = 1
grid_view.SelectedRows = '1'
grid_view.SelectedRows = '0,1'
grid_view.SelectedRows = '0-1'
The following did not work (type error), which makes sense since it would imply tuple packing/unpacking:
grid_view.SelectedRows = 0,1
This results in unexpected rows being selected since it is performing subtraction rather than indicating a range of rows:
grid_view.SelectedRows = 0-1

Related

Issues publishing to device shadow using the aws-iot-device-sdk-python-v2

In a python application that uses the aws iot device sdk for python v2 (v1.7.1) I am running into an issue where I cannot update the device shadow.
After starting the program, the DeviceShadowManager will attempt to get the latest shadow state and set it locally.
If a delta state is present the DeviceShadowManager will merge the last reported state and delta state and publish it.
That works. However, when the manager subscribes for updates, after the initial setup, I am running into an error,
where when the desired state changes, the manager cannot update the reported state. Here is the error:
Exception ignored in: <class 'TypeError'>
Traceback (most recent call last):
File "/Users/tom/.../lib/python3.9/site-packages/awscrt/mqtt.py", line 506, in callback_wrapper
callback(topic=topic, payload=payload)
TypeError: callback_wrapper() missing 3 required positional arguments: 'dup', 'qos', and 'retain'
I looked at the source, but just do not understand why a TypeError is raised,
especially because this exact scenario seems to be handled by the try and except block or am I getting it all wrong?
The source of the error:
if callback:
def callback_wrapper(topic, payload, dup, qos, retain):
try:
callback(topic=topic, payload=payload, dup=dup, qos=QoS(qos), retain=retain)
except TypeError:
# This callback used to have fewer args.
# Try again, passing only those those args, to cover case where
# user function failed to take forward-compatibility **kwargs.
callback(topic=topic, payload=payload) # this is line 506
Below you can find my code and the log of the program.
This dataclass represents the shadow:
from dataclasses import dataclass
#dataclass
class DeviceShadow:
score_threshold: float = 0.6
minimum_distance: int = 150
The shadow is managed by the DeviceShadowManager. Most of this is based on the shadow sample from the aforementioned repository.
from dataclasses import asdict
from queue import Queue
from threading import Lock
from awscrt import mqtt
from awsiot import iotshadow
from awsiot.iotshadow import IotShadowClient
from app.device_shadow.device_shadow import DeviceShadow, from_json as device_shadow_from_json
from app.models import log
SHADOW_VALUE_DEFAULT = DeviceShadow()
class DeviceShadowManager:
_shadow_client: IotShadowClient
shadow_value: DeviceShadow = DeviceShadow()
_lock = Lock()
_thing_name: str
def __init__(self, thing_name: str, mqtt_connection: mqtt.Connection):
self._thing_name = thing_name
self._shadow_client = iotshadow.IotShadowClient(mqtt_connection)
update_accepted_subscribed_future, _ = self._shadow_client.subscribe_to_update_shadow_accepted(
request=iotshadow.UpdateShadowSubscriptionRequest(thing_name=self._thing_name),
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=self.on_update_shadow_accepted # omitted
)
update_rejected_subscribed_future, _ = self._shadow_client.subscribe_to_update_shadow_rejected(
request=iotshadow.UpdateShadowSubscriptionRequest(thing_name=self._thing_name),
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=self.on_update_shadow_rejected # omitted
)
# Wait for subscriptions to succeed
update_accepted_subscribed_future.result(60)
update_rejected_subscribed_future.result(60)
log.info("Subscribing to Get responses...")
get_accepted_subscribed_future, _ = self._shadow_client.subscribe_to_get_shadow_accepted(
request=iotshadow.GetShadowSubscriptionRequest(thing_name=self._thing_name),
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=self.on_get_shadow_accepted)
get_rejected_subscribed_future, _ = self._shadow_client.subscribe_to_get_shadow_rejected(
request=iotshadow.GetShadowSubscriptionRequest(thing_name=self._thing_name),
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=self.on_get_shadow_rejected) # omitted
# Wait for subscriptions to succeed
get_accepted_subscribed_future.result()
get_rejected_subscribed_future.result()
log.info("Subscribing to Delta events...")
delta_subscribed_future, _ = self._shadow_client.subscribe_to_shadow_delta_updated_events(
request=iotshadow.ShadowDeltaUpdatedSubscriptionRequest(
thing_name=self._thing_name
),
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=self.on_shadow_delta_updated)
# Wait for subscription to succeed
delta_subscribed_future.result()
# From here on out the rest runs asynchronously.
# Issue request for shadow's current value.
# The response will be received by the on_get_accepted() callback
with self._lock:
publish_get_future = self._shadow_client.publish_get_shadow(
request=iotshadow.GetShadowRequest(
thing_name=self._thing_name,
),
qos=mqtt.QoS.AT_LEAST_ONCE
)
# Ensure that publish succeeds
publish_get_future.result()
def on_get_shadow_accepted(self, response: iotshadow.GetShadowResponse) -> None:
log.info("Finished getting initial shadow value.")
if response.state and response.state.delta:
if not response.state.reported:
response.state.reported = {}
merged_state = self.merge_states(response.state.delta, response.state.desired)
return self.set_desired(device_shadow_from_json(merged_state))
if response.state and response.state.reported:
return self.set_local(device_shadow_from_json(response.state.reported))
self.set_desired(SHADOW_VALUE_DEFAULT)
return
def on_shadow_delta_updated(self, delta: iotshadow.ShadowDeltaUpdatedEvent) -> None:
if delta.state:
if delta.state is None:
log.info("Delta reports that nothing is set. Setting defaults...")
self.set_desired(SHADOW_VALUE_DEFAULT)
return
log.info("Delta reports that desired shadow is '{}'. Changing local shadow...".format(delta.state))
self.set_desired(self.merge_states(delta.state, self.shadow_value))
else:
log.info("Delta did not report a change")
#staticmethod
def merge_states(delta: dict, reported: DeviceShadow):
for key, value in delta.items():
reported[key] = value
return reported
def set_local(self, value: DeviceShadow) -> None:
with self._lock:
self.shadow_value = value
def set_desired(self, new_value: DeviceShadow) -> None:
with self._lock:
if self.shadow_value == new_value:
log.debug("Local shadow is already '{}'.".format(new_value))
return
log.debug("Changing local shadow to '{}'.".format(new_value))
self.shadow_value = new_value
log.debug("Updating reported shadow to '{}'...".format(new_value))
request = iotshadow.UpdateShadowRequest(
thing_name=self._thing_name,
state=iotshadow.ShadowState(
desired=asdict(new_value),
reported=asdict(new_value),
),
)
self._shadow_client.publish_update_shadow(request, mqtt.QoS.AT_LEAST_ONCE)
Below you will find the log:
DEBUG:app.mqtt:Connecting to xxxxxxxxxxxxxx-ats.iot.eu-central-1.amazonaws.com with client ID '80d8bc54-971e-0e65-a537-37d14a3cb630'...
INFO:app.models:Subscribing to Get responses...
INFO:app.models:Subscribing to Delta events...
INFO:app.models:Finished getting initial shadow value.
DEBUG:app.models:Changed local shadow to 'DeviceShadow(score_threshold=0.7, minimum_distance=1503)'.
DEBUG:app.models:Updating reported shadow to 'DeviceShadow(score_threshold=0.7, minimum_distance=1503)'...
INFO:app.models:Update request published.
DEBUG:app.models:Finished updating reported shadow to '{'score_threshold': 0.7, 'minimum_distance': 1503}'.
INFO:app.models:Delta reports that desired shadow is '{'minimum_distance': 15035}'. Changing local shadow...
Exception ignored in: <class 'TypeError'>
Traceback (most recent call last):
File "/Users/tom/.../lib/python3.9/site-packages/awscrt/mqtt.py", line 506, in callback_wrapper
callback(topic=topic, payload=payload)
TypeError: callback_wrapper() missing 3 required positional arguments: 'dup', 'qos', and 'retain'
DEBUG:app.models:Finished updating reported shadow to '{'score_threshold': 0.7, 'minimum_distance': 1503}'.
As you can see the stacktrace is pretty short, is there a way to debug this better?
Any ideas to why it is giving me this particular error and maybe how to solve it?
All help is appreciated!
I am pretty sure the problem lies within
#staticmethod
def merge_states(delta: dict, reported: DeviceShadow):
for key, value in delta.items():
reported[key] = value
return reported
where the __setitem__ call on the reported argument raises a TypeError because the reported argument is a DeviceShadow dataclass object that doesn't support item assignment.
If you want to set fields of a dataclass where you have a string of the field name, you can use setattr(reported, key, value).

How to capture an error from the library python litex.Regon?

I want Handling Exceptions from litex Regon (polish library with information about company). I don't know how? When i try with
When i try with this code it's ok... so i don't know why?
while True:
try:
x = int(input("Please enter a number: "))
break
except ValueError:
print("Oops! That was no valid number. Try again...")
But my code is doesn't work
## EXAMPLE
api = REGONAPI('https://wyszukiwarkaregon.stat.gov.pl/wsBIR/UslugaBIRzewnPubl.svc')
USER_KEY = 'my_key'
api.login(USER_KEY)
entities = api.search(nip='9222976976')
c = etree.tostring(entities[0], pretty_print=True)
c
## MY CODE
REGONAPI('https://wyszukiwarkaregon.stat.gov.pl/wsBIR/UslugaBIRzewnPubl.svc')
USER_KEY = 'my_key'
api.login(USER_KEY)
from litex.regon import REGONAPI
lista = []
for i in np.arange(280, 300):
try:
entities = api.search(nip=dane_do_przeszukwania['NIP'][i])
c = etree.tostring(entities[0], pretty_print=True)
lista.append(c)
except REGONAPIError:
None
I have a error
EGONAPIError Traceback (most recent call last)
<ipython-input-7-f2653b5824bc> in <module>
4 try:
----> 5 entities = api.search(nip=dane_do_przeszukwania['NIP'][i])
6 c = etree.tostring(entities[0], pretty_print=True)
~\Anaconda3\lib\site-packages\litex\regon\__init__.py in search(self, nip, regon, krs, nips, regons, krss, detailed)
210 if not result:
--> 211 raise REGONAPIError('Search failed.')
212
REGONAPIError: Search failed.
During handling of the above exception, another exception occurred:
NameError Traceback (most recent call last)
<ipython-input-7-f2653b5824bc> in <module>
6 c = etree.tostring(entities[0], pretty_print=True)
7 lista.append(c)
----> 8 except REGONAPIError:
9 None
10
NameError: name 'REGONAPIError' is not defined
if I do not find it number hand error and next row in iterate.
REGONAPIError is not defined because you probably didn't import it.
Try adding
from litex.regon import REGONAPIError
in your file, this should work.

ValueError when opening Word file with comtypes in Python

I'm trying to port some working VBS code into Python, to analyze a collection of Word files. I was hoping that the comtypes would allow me to reuse most of my code, but I get an error when a Word instance opens a file:
ValueError: NULL COM pointer access
In [2]: from comtypes.client import CreateObject
In [3]: objWord = CreateObject("Word.Application")
In [4]: objWord.Visible = False
In [5]: objDoc = objWord.Documents.Open('my_file.docx')
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-5-c1e34bdd2b13> in <module>
----> 1 objDoc = objWord.Documents.Open('my_file.docx')
c:\program files\python37\lib\site-packages\comtypes\_meta.py in _wrap_coclass(self)
11 itf = self._com_interfaces_[0]
12 punk = cast(self, POINTER(itf))
---> 13 result = punk.QueryInterface(itf)
14 result.__dict__["__clsid"] = str(self._reg_clsid_)
15 return result
c:\program files\python37\lib\site-packages\comtypes\__init__.py in QueryInterface(self, interface, iid)
1156 if iid is None:
1157 iid = interface._iid_
-> 1158 self.__com_QueryInterface(byref(iid), byref(p))
1159 clsid = self.__dict__.get('__clsid')
1160 if clsid is not None:
ValueError: NULL COM pointer access
I would expect to get a document object that I can then read:
nbpages = objDoc.Range.Information(4)
Seems like I needed to provide the full, absolute path to the file. Maybe the Python working folder isn't passed on to the COM object.

com object instance cannot be reconverted to com object

I am trying to automate Microstation using python and win32com but I get the following error trying to resend an object created through the COM interface. Can anyone help me troubleshoot?
import win32com.client
MS = win32com.client.Dispatch("MicroStationDGN.Application")
MS.Visible = 1
DF = MS.OpenDesignFile("C:\\mydgn.dgn", False)
print(DF.Models.Count)
startPoint = MS.Point3dFromXYZ(2.0,2.0,0.0)
endPoint = MS.Point3dFromXYZ(4.0,4.0,0.0)
line = MS.CreateLineElement2(None, startPoint, endPoint)
print(line)
MS.ActiveModelReference.AddElement(line)
line.redraw()
on the AddElement line i get the following error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-54f93cd6e4bc> in <module>()
16 line = MS.CreateLineElement2(None, startPoint, endPoint)
17 print(line)
---> 18 MS.ActiveModelReference.AddElement(line)
19 line.redraw()
20
C:\Users\user\AppData\Local\Temp\gen_py\3.6\CF9F97BF-39F2-4B8E-835C-8BE9E99DAF5Bx0x8x0.py in AddElement(self, Element)
16408
16409 def AddElement(self, Element=defaultNamedNotOptArg):
> 16410 return self._oleobj_.InvokeTypes(1610743813, LCID, 1, (24, 0), ((9, 1),),Element
16411 )
16412
TypeError: The Python instance can not be converted to a COM object
but the print gives me a valid com object...
(<win32com.gen_py.Bentley MicroStation DGN 8.9 Object
Library._LineElement instance at 0x119452224>, com_struct(X=2.0,
Y=2.0, Z=0.0), com_struct(X=4.0, Y=4.0, Z=0.0))
The equivalent in ExcelVBA would be the following assuming Microstation DGN Library is loaded as a reference module.
Sub MSFromXlReference()
Dim MS As MicroStationDGN.Application
Dim DesignFile As DesignFile
Dim Line As LineElement
Dim startPoint As Point3d
Dim endPoint As Point3d
Set MS = New MicroStationDGN.Application
MS.Visible = 1
Set DesignFile =
MS.OpenDesignFile("C:\\mydgn.dgn", False)
startPoint = MS.Point3dFromXYZ(2#, 2#, 0#)
endPoint = MS.Point3dFromXYZ(4#, 4#, 0#)
Set Line = MS.CreateLineElement2(Nothing, startPoint, endPoint)
MS.ActiveModelReference.AddElement Line
Line.redraw
End Sub

traceback.format_exc/print_exc returns None when expecting traceback

I can't figure out why traceback.format_exc() is returning "None" in the following example:
#!/usr/bin/env python
import sys
import traceback
def my_excepthook(type, value, tb):
print type.__name__
print value
# the problem: why does this return "None"?
print traceback.format_exc(tb) # see http://docs.python.org/library/traceback.html#traceback.format_exc
sys.excepthook = my_excepthook # see http://docs.python.org/library/sys.html#sys.excepthook
# some code to generate a naturalistic exception
a = "text"
b = 5
error = a + b
Using Python 2.7.1, I get the following output:
TypeError
cannot concatenate 'str' and 'int' objects
None
Instead of "None" on the 3rd line, I'd expect to get what happens when I comment out the sys.excepthook line:
Traceback (most recent call last):
File "log-test.py", line 17, in <module>
error = a+b
Try to change like this in my_excepthook:
print "".join(traceback.format_exception(type, value, tb))

Categories