Skip to content

Commit ab2907b

Browse files
authored
[Python] Store original PyChipError in ChipStackException (#33954)
* [Python] Drop unused ErrorToException function Also remove the now unused pychip_Stack_ErrorToString function. This is handled in pychip_FormatError today. * [Python] Cleanup PyChipError return values Use PyChipError return value where PyChipErrors are actually returned. This then also allows us to use the typical .raise_on_error() pattern. * [Python] Store original PyChipError in ChipStackException This change stores the original PyChipError in ChipStackException so that details of the original error code can still be retrieved. This is interesting to use the properties returning processed information about the original error code. It also preserves the line and code file which can be helpful. * [Python] Fix Command API argument type errors NativeLibraryHandleMethodArguments correctly setting the arguments uncovered some incorrectly set arguments. * [Python] Use to_exception() to convert PyChipError to ChipStackError * [Python] Fix Cert API argument type errors NativeLibraryHandleMethodArguments correctly setting the argument types causes argument type errors: ctypes.ArgumentError: argument 1: TypeError: expected LP_c_ubyte instance instead of bytes We can safely cast bytes as the native side marks it const.
1 parent 947fef5 commit ab2907b

File tree

11 files changed

+61
-95
lines changed

11 files changed

+61
-95
lines changed

src/controller/python/ChipDeviceController-ScriptBinding.cpp

-11
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(chip::Controller:
210210
// BLE
211211
PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::DeviceCommissioner * devCtrl);
212212

213-
const char * pychip_Stack_ErrorToString(ChipError::StorageType err);
214213
const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode);
215214

216215
PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
@@ -366,11 +365,6 @@ PyChipError pychip_DeviceController_GetNodeId(chip::Controller::DeviceCommission
366365
return ToPyChipError(CHIP_NO_ERROR);
367366
}
368367

369-
const char * pychip_DeviceController_ErrorToString(ChipError::StorageType err)
370-
{
371-
return chip::ErrorStr(CHIP_ERROR(err));
372-
}
373-
374368
const char * pychip_DeviceController_StatusReportToString(uint32_t profileId, uint16_t statusCode)
375369
{
376370
// return chip::StatusReportStr(profileId, statusCode);
@@ -769,11 +763,6 @@ pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete(chip::Controller:
769763
return ToPyChipError(CHIP_NO_ERROR);
770764
}
771765

772-
const char * pychip_Stack_ErrorToString(ChipError::StorageType err)
773-
{
774-
return chip::ErrorStr(CHIP_ERROR(err));
775-
}
776-
777766
const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode)
778767
{
779768
// return chip::StatusReportStr(profileId, statusCode);

src/controller/python/chip/ChipDeviceCtrl.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -1013,12 +1013,8 @@ def GetRemoteSessionParameters(self, nodeid) -> typing.Optional[SessionParameter
10131013
sessionParametersStruct = SessionParametersStruct.parse(b'\x00' * SessionParametersStruct.sizeof())
10141014
sessionParametersByteArray = SessionParametersStruct.build(sessionParametersStruct)
10151015
device = self.GetConnectedDeviceSync(nodeid)
1016-
res = self._ChipStack.Call(lambda: self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters(
1017-
device.deviceProxy, ctypes.c_char_p(sessionParametersByteArray)))
1018-
1019-
# 0 is CHIP_NO_ERROR
1020-
if res != 0:
1021-
return None
1016+
self._ChipStack.Call(lambda: self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters(
1017+
device.deviceProxy, ctypes.c_char_p(sessionParametersByteArray))).raise_on_error()
10221018

10231019
sessionParametersStruct = SessionParametersStruct.parse(sessionParametersByteArray)
10241020
return SessionParameters(
@@ -1030,8 +1026,6 @@ def GetRemoteSessionParameters(self, nodeid) -> typing.Optional[SessionParameter
10301026
specficiationVersion=sessionParametersStruct.SpecificationVersion if sessionParametersStruct.SpecificationVersion != 0 else None,
10311027
maxPathsPerInvoke=sessionParametersStruct.MaxPathsPerInvoke)
10321028

1033-
return res
1034-
10351029
async def TestOnlySendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo],
10361030
timedRequestTimeoutMs: typing.Optional[int] = None,
10371031
interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None,
@@ -1804,6 +1798,9 @@ def _InitLib(self):
18041798

18051799
self._dmLib.pychip_CheckInDelegate_SetOnCheckInCompleteCallback(_OnCheckInComplete)
18061800

1801+
self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters.restype = PyChipError
1802+
self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters.argtypes = [c_void_p, c_char_p]
1803+
18071804

18081805
class ChipDeviceController(ChipDeviceControllerBase):
18091806
''' The ChipDeviceCommissioner binding, named as ChipDeviceController

src/controller/python/chip/ChipStack.py

-30
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import chip.native
3636
from chip.native import PyChipError
3737

38-
from .ChipUtility import ChipUtility
3938
from .clusters import Attribute as ClusterAttribute
4039
from .clusters import Command as ClusterCommand
4140
from .exceptions import ChipStackError, ChipStackException, DeviceError
@@ -247,33 +246,6 @@ def PostTaskOnChipThread(self, callFunct) -> AsyncCallableHandle:
247246
raise res.to_exception()
248247
return callObj
249248

250-
def ErrorToException(self, err, devStatusPtr=None):
251-
if err == 0x2C and devStatusPtr:
252-
devStatus = devStatusPtr.contents
253-
msg = ChipUtility.CStringToString(
254-
(
255-
self._ChipStackLib.pychip_Stack_StatusReportToString(
256-
devStatus.ProfileId, devStatus.StatusCode
257-
)
258-
)
259-
)
260-
sysErrorCode = (
261-
devStatus.SysErrorCode if (
262-
devStatus.SysErrorCode != 0) else None
263-
)
264-
if sysErrorCode is not None:
265-
msg = msg + " (system err %d)" % (sysErrorCode)
266-
return DeviceError(
267-
devStatus.ProfileId, devStatus.StatusCode, sysErrorCode, msg
268-
)
269-
else:
270-
return ChipStackError(
271-
err,
272-
ChipUtility.CStringToString(
273-
(self._ChipStackLib.pychip_Stack_ErrorToString(err))
274-
),
275-
)
276-
277249
def LocateChipDLL(self):
278250
self._loadLib()
279251
return self._chipDLLPath
@@ -302,8 +274,6 @@ def _loadLib(self):
302274
c_uint16,
303275
]
304276
self._ChipStackLib.pychip_Stack_StatusReportToString.restype = c_char_p
305-
self._ChipStackLib.pychip_Stack_ErrorToString.argtypes = [c_uint32]
306-
self._ChipStackLib.pychip_Stack_ErrorToString.restype = c_char_p
307277

308278
self._ChipStackLib.pychip_DeviceController_PostTaskOnChipThread.argtypes = [
309279
_ChipThreadTaskRunnerFunct, py_object]

src/controller/python/chip/clusters/Attribute.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ def __init__(self, future: Future, eventLoop, devCtrl, returnClusterObject: bool
651651
self._changedPathSet = set()
652652
self._pReadClient = None
653653
self._pReadCallback = None
654-
self._resultError = None
654+
self._resultError: Optional[PyChipError] = None
655655

656656
def SetClientObjPointers(self, pReadClient, pReadCallback):
657657
self._pReadClient = pReadClient
@@ -718,7 +718,7 @@ def handleEventData(self, header: EventHeader, path: EventPath, data: bytes, sta
718718
logging.exception(ex)
719719

720720
def handleError(self, chipError: PyChipError):
721-
self._resultError = chipError.code
721+
self._resultError = chipError
722722

723723
def _handleSubscriptionEstablished(self, subscriptionId):
724724
if not self._future.done():
@@ -777,11 +777,11 @@ def _handleDone(self):
777777
# move on, possibly invalidating the provided _event_loop.
778778
#
779779
if not self._future.done():
780-
if self._resultError:
780+
if self._resultError is not None:
781781
if self._subscription_handler:
782-
self._subscription_handler.OnErrorCb(self._resultError, self._subscription_handler)
782+
self._subscription_handler.OnErrorCb(self._resultError.code, self._subscription_handler)
783783
else:
784-
self._future.set_exception(chip.exceptions.ChipStackError(self._resultError))
784+
self._future.set_exception(self._resultError.to_exception())
785785
else:
786786
self._future.set_result(AsyncReadTransaction.ReadResponse(
787787
attributes=self._cache.attributeCache, events=self._events, tlvAttributes=self._cache.attributeTLVCache))
@@ -809,7 +809,7 @@ def __init__(self, future: Future, eventLoop):
809809
self._event_loop = eventLoop
810810
self._future = future
811811
self._resultData = []
812-
self._resultError = None
812+
self._resultError: Optional[PyChipError] = None
813813

814814
def handleResponse(self, path: AttributePath, status: int):
815815
try:

src/controller/python/chip/clusters/Command.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -467,13 +467,13 @@ def Init():
467467
setter = chip.native.NativeLibraryHandleMethodArguments(handle)
468468

469469
setter.Set('pychip_CommandSender_SendCommand',
470-
PyChipError, [py_object, c_void_p, c_uint16, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_bool])
470+
PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_uint16, c_bool])
471471
setter.Set('pychip_CommandSender_SendBatchCommands',
472472
PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint16, c_bool, POINTER(PyInvokeRequestData), c_size_t])
473473
setter.Set('pychip_CommandSender_TestOnlySendBatchCommands',
474474
PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint16, c_bool, TestOnlyPyBatchCommandsOverrides, POINTER(PyInvokeRequestData), c_size_t])
475475
setter.Set('pychip_CommandSender_TestOnlySendCommandTimedRequestNoTimedInvoke',
476-
PyChipError, [py_object, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_bool])
476+
PyChipError, [py_object, c_void_p, c_uint16, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_uint16, c_bool])
477477
setter.Set('pychip_CommandSender_SendGroupCommand',
478478
PyChipError, [c_uint16, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16])
479479
setter.Set('pychip_CommandSender_InitCallbacks', None, [

src/controller/python/chip/credentials/cert.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ def convert_x509_cert_to_chip_cert(x509Cert: bytes) -> bytes:
3535
"""Converts a x509 certificate to CHIP Certificate."""
3636
output_buffer = (ctypes.c_uint8 * 1024)()
3737
output_size = ctypes.c_size_t(1024)
38+
ptr_type = ctypes.POINTER(ctypes.c_uint8)
3839

39-
_handle().pychip_ConvertX509CertToChipCert(x509Cert, len(x509Cert), output_buffer, ctypes.byref(output_size)).raise_on_error()
40+
_handle().pychip_ConvertX509CertToChipCert(ctypes.cast(x509Cert, ptr_type), len(x509Cert),
41+
ctypes.cast(output_buffer, ptr_type), ctypes.byref(output_size)).raise_on_error()
4042

4143
return bytes(output_buffer)[:output_size.value]
4244

@@ -45,7 +47,9 @@ def convert_chip_cert_to_x509_cert(chipCert: bytes) -> bytes:
4547
"""Converts a x509 certificate to CHIP Certificate."""
4648
output_buffer = (ctypes.c_byte * 1024)()
4749
output_size = ctypes.c_size_t(1024)
50+
ptr_type = ctypes.POINTER(ctypes.c_uint8)
4851

49-
_handle().pychip_ConvertChipCertToX509Cert(chipCert, len(chipCert), output_buffer, ctypes.byref(output_size)).raise_on_error()
52+
_handle().pychip_ConvertChipCertToX509Cert(ctypes.cast(chipCert, ptr_type), len(chipCert),
53+
ctypes.cast(output_buffer, ptr_type), ctypes.byref(output_size)).raise_on_error()
5054

5155
return bytes(output_buffer)[:output_size.value]

src/controller/python/chip/discovery/__init__.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,7 @@ def FindAddressAsync(fabricid: int, nodeid: int, callback, timeout_ms=1000):
236236
)
237237

238238
res = _GetDiscoveryLibraryHandle().pychip_discovery_resolve(fabricid, nodeid)
239-
if res != 0:
240-
raise Exception("Failed to start node resolution")
239+
res.raise_on_error()
241240

242241

243242
class _SyncAddressFinder:

src/controller/python/chip/exceptions/__init__.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
# limitations under the License.
1616
#
1717

18+
from __future__ import annotations
19+
1820
__all__ = [
1921
"ChipStackException",
2022
"ChipStackError",
@@ -26,15 +28,32 @@
2628
"UnknownCommand",
2729
]
2830

31+
from typing import TYPE_CHECKING
32+
33+
if TYPE_CHECKING:
34+
from chip.native import PyChipError
35+
2936

3037
class ChipStackException(Exception):
3138
pass
3239

3340

3441
class ChipStackError(ChipStackException):
35-
def __init__(self, err, msg=None):
36-
self.err = err
37-
self.msg = msg if msg else "Chip Stack Error %d" % err
42+
def __init__(self, chip_error: PyChipError, msg=None):
43+
self._chip_error = chip_error
44+
self.msg = msg if msg else "Chip Stack Error %d" % chip_error.code
45+
46+
@classmethod
47+
def from_chip_error(cls, chip_error: PyChipError) -> ChipStackError:
48+
return cls(chip_error, str(chip_error))
49+
50+
@property
51+
def chip_error(self) -> PyChipError | None:
52+
return self._chip_error
53+
54+
@property
55+
def err(self) -> int:
56+
return self._chip_error.code
3857

3958
def __str__(self):
4059
return self.msg

src/controller/python/chip/interaction_model/delegate.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ def InitIMDelegate():
330330
setter.Set("pychip_InteractionModelDelegate_SetCommandResponseErrorCallback", None, [
331331
_OnCommandResponseFunct])
332332
setter.Set("pychip_InteractionModel_GetCommandSenderHandle",
333-
c_uint32, [ctypes.POINTER(c_uint64)])
333+
chip.native.PyChipError, [ctypes.POINTER(c_uint64)])
334334
setter.Set("pychip_InteractionModelDelegate_SetOnWriteResponseStatusCallback", None, [
335335
_OnWriteResponseStatusFunct])
336336

@@ -389,10 +389,8 @@ def WaitCommandIndexStatus(commandHandle: int, commandIndex: int):
389389
def GetCommandSenderHandle() -> int:
390390
handle = chip.native.GetLibraryHandle()
391391
resPointer = c_uint64()
392-
res = handle.pychip_InteractionModel_GetCommandSenderHandle(
393-
ctypes.pointer(resPointer))
394-
if res != 0:
395-
raise chip.exceptions.ChipStackError(res)
392+
handle.pychip_InteractionModel_GetCommandSenderHandle(
393+
ctypes.pointer(resPointer)).raise_on_error()
396394
ClearCommandStatus(resPointer.value)
397395
return resPointer.value
398396

src/controller/python/chip/native/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def sdk_code(self) -> int:
116116

117117
def to_exception(self) -> typing.Union[None, chip.exceptions.ChipStackError]:
118118
if not self.is_success:
119-
return chip.exceptions.ChipStackError(self.code, str(self))
119+
return chip.exceptions.ChipStackError.from_chip_error(self)
120120

121121
def __str__(self):
122122
buf = ctypes.create_string_buffer(256)
@@ -199,7 +199,7 @@ def __init__(self, handle):
199199
def Set(self, methodName: str, resultType, argumentTypes: list):
200200
method = getattr(self.handle, methodName)
201201
method.restype = resultType
202-
method.argtype = argumentTypes
202+
method.argtypes = argumentTypes
203203

204204

205205
@dataclass

src/controller/python/chip/setup_payload/setup_payload.py

+14-24
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@
1414
# limitations under the License.
1515
#
1616

17-
from ctypes import CFUNCTYPE, c_char_p, c_int32, c_uint8, c_uint16, c_uint32
17+
from ctypes import CFUNCTYPE, c_char_p, c_uint8, c_uint16, c_uint32
1818
from typing import Optional
1919

20-
from chip.exceptions import ChipStackError
21-
from chip.native import GetLibraryHandle, NativeLibraryHandleMethodArguments
20+
from chip.native import GetLibraryHandle, NativeLibraryHandleMethodArguments, PyChipError
2221

2322

2423
class SetupPayload:
@@ -46,34 +45,25 @@ def AddVendorAttribute(tag, value):
4645

4746
def ParseQrCode(self, qrCode: str):
4847
self.Clear()
49-
err = self.chipLib.pychip_SetupPayload_ParseQrCode(qrCode.upper().encode(),
50-
self.attribute_visitor,
51-
self.vendor_attribute_visitor)
52-
53-
if err != 0:
54-
raise ChipStackError(err)
48+
self.chipLib.pychip_SetupPayload_ParseQrCode(qrCode.upper().encode(),
49+
self.attribute_visitor,
50+
self.vendor_attribute_visitor).raise_on_error()
5551

5652
return self
5753

5854
def ParseManualPairingCode(self, manualPairingCode: str):
5955
self.Clear()
60-
err = self.chipLib.pychip_SetupPayload_ParseManualPairingCode(manualPairingCode.encode(),
61-
self.attribute_visitor,
62-
self.vendor_attribute_visitor)
63-
64-
if err != 0:
65-
raise ChipStackError(err)
56+
self.chipLib.pychip_SetupPayload_ParseManualPairingCode(manualPairingCode.encode(),
57+
self.attribute_visitor,
58+
self.vendor_attribute_visitor).raise_on_error()
6659

6760
return self
6861

6962
# DEPRECATED
7063
def PrintOnboardingCodes(self, passcode, vendorId, productId, discriminator, customFlow, capabilities, version):
7164
self.Clear()
72-
err = self.chipLib.pychip_SetupPayload_PrintOnboardingCodes(
73-
passcode, vendorId, productId, discriminator, customFlow, capabilities, version)
74-
75-
if err != 0:
76-
raise ChipStackError(err)
65+
self.chipLib.pychip_SetupPayload_PrintOnboardingCodes(
66+
passcode, vendorId, productId, discriminator, customFlow, capabilities, version).raise_on_error()
7767

7868
# DEPRECATED
7969
def Print(self):
@@ -106,17 +96,17 @@ def __DecorateValue(self, name, value):
10696
return None
10797

10898
def __InitNativeFunctions(self, chipLib):
109-
if chipLib.pychip_SetupPayload_ParseQrCode is not None:
99+
if chipLib.pychip_SetupPayload_ParseQrCode.argtypes is not None:
110100
return
111101
setter = NativeLibraryHandleMethodArguments(chipLib)
112102
setter.Set("pychip_SetupPayload_ParseQrCode",
113-
c_int32,
103+
PyChipError,
114104
[c_char_p, SetupPayload.AttributeVisitor, SetupPayload.VendorAttributeVisitor])
115105
setter.Set("pychip_SetupPayload_ParseManualPairingCode",
116-
c_int32,
106+
PyChipError,
117107
[c_char_p, SetupPayload.AttributeVisitor, SetupPayload.VendorAttributeVisitor])
118108
setter.Set("pychip_SetupPayload_PrintOnboardingCodes",
119-
c_int32,
109+
PyChipError,
120110
[c_uint32, c_uint16, c_uint16, c_uint16, c_uint8, c_uint8, c_uint8])
121111

122112
# Getters from parsed contents.

0 commit comments

Comments
 (0)