@@ -600,6 +600,7 @@ def __init__(self, future: Future, eventLoop, devCtrl, returnClusterObject: bool
600
600
self ._changedPathSet = set ()
601
601
self ._pReadClient = None
602
602
self ._pReadCallback = None
603
+ self ._resultError = None
603
604
604
605
def SetClientObjPointers (self , pReadClient , pReadCallback ):
605
606
self ._pReadClient = pReadClient
@@ -608,7 +609,7 @@ def SetClientObjPointers(self, pReadClient, pReadCallback):
608
609
def GetAllEventValues (self ):
609
610
return self ._events
610
611
611
- def _handleAttributeData (self , path : AttributePathWithListIndex , dataVersion : int , status : int , data : bytes ):
612
+ def handleAttributeData (self , path : AttributePathWithListIndex , dataVersion : int , status : int , data : bytes ):
612
613
try :
613
614
imStatus = status
614
615
try :
@@ -629,10 +630,7 @@ def _handleAttributeData(self, path: AttributePathWithListIndex, dataVersion: in
629
630
except Exception as ex :
630
631
logging .exception (ex )
631
632
632
- def handleAttributeData (self , path : AttributePath , dataVersion : int , status : int , data : bytes ):
633
- self ._handleAttributeData (path , dataVersion , status , data )
634
-
635
- def _handleEventData (self , header : EventHeader , path : EventPath , data : bytes , status : int ):
633
+ def handleEventData (self , header : EventHeader , path : EventPath , data : bytes , status : int ):
636
634
try :
637
635
eventType = _EventIndex .get (str (path ), None )
638
636
eventValue = None
@@ -671,19 +669,8 @@ def _handleEventData(self, header: EventHeader, path: EventPath, data: bytes, st
671
669
except Exception as ex :
672
670
logging .exception (ex )
673
671
674
- def handleEventData (self , header : EventHeader , path : EventPath , data : bytes , status : int ):
675
- self ._handleEventData (header , path , data , status )
676
-
677
- def _handleError (self , chipError : int ):
678
- if not self ._future .done ():
679
- self ._future .set_exception (
680
- chip .exceptions .ChipStackError (chipError ))
681
- self ._subscription_handler .OnErrorCb (chipError , self ._subscription_handler )
682
-
683
672
def handleError (self , chipError : int ):
684
- self ._event_loop .call_soon_threadsafe (
685
- self ._handleError , chipError
686
- )
673
+ self ._resultError = chipError
687
674
688
675
def _handleSubscriptionEstablished (self , subscriptionId ):
689
676
if not self ._future .done ():
@@ -713,9 +700,28 @@ def _handleReportEnd(self):
713
700
self ._changedPathSet = set ()
714
701
715
702
def _handleDone (self ):
703
+ #
704
+ # We only set the exception/result on the future in this _handleDone call (if it hasn't
705
+ # already been set yet, which can be in the case of subscriptions) since doing so earlier
706
+ # would result in the callers awaiting the result to
707
+ # move on, possibly invalidating the provided _event_loop.
708
+ #
716
709
if not self ._future .done ():
717
- self ._future .set_result (AsyncReadTransaction .ReadResponse (
718
- attributes = self ._cache .attributeCache , events = self ._events ))
710
+ if self ._resultError :
711
+ if self ._subscription_handler :
712
+ self ._subscription_handler .OnErrorCb (chipError , self ._subscription_handler )
713
+ else :
714
+ self ._future .set_exception (chip .exceptions .ChipStackError (chipError ))
715
+ else :
716
+ self ._future .set_result (AsyncReadTransaction .ReadResponse (
717
+ attributes = self ._cache .attributeCache , events = self ._events ))
718
+
719
+ #
720
+ # Decrement the ref on ourselves to match the increment that happened at allocation.
721
+ # This happens synchronously as part of handling done to ensure the object remains valid
722
+ # right till the very end.
723
+ #
724
+ ctypes .pythonapi .Py_DecRef (ctypes .py_object (self ))
719
725
720
726
def handleDone (self ):
721
727
self ._event_loop .call_soon_threadsafe (self ._handleDone )
@@ -732,31 +738,36 @@ class AsyncWriteTransaction:
732
738
def __init__ (self , future : Future , eventLoop ):
733
739
self ._event_loop = eventLoop
734
740
self ._future = future
735
- self ._res = []
741
+ self ._resultData = []
742
+ self ._resultError = None
736
743
737
- def _handleResponse (self , path : AttributePath , status : int ):
744
+ def handleResponse (self , path : AttributePath , status : int ):
738
745
try :
739
746
imStatus = chip .interaction_model .Status (status )
740
- self ._res .append (AttributeWriteResult (Path = path , Status = imStatus ))
747
+ self ._resultData .append (AttributeWriteResult (Path = path , Status = imStatus ))
741
748
except :
742
- self ._res .append (AttributeWriteResult (Path = path , Status = status ))
743
-
744
- def handleResponse (self , path : AttributePath , status : int ):
745
- self ._event_loop .call_soon_threadsafe (
746
- self ._handleResponse , path , status )
747
-
748
- def _handleError (self , chipError : int ):
749
- self ._future .set_exception (
750
- chip .exceptions .ChipStackError (chipError ))
749
+ self ._resultData .append (AttributeWriteResult (Path = path , Status = status ))
751
750
752
751
def handleError (self , chipError : int ):
753
- self ._event_loop .call_soon_threadsafe (
754
- self ._handleError , chipError
755
- )
752
+ self ._resultError = chipError
756
753
757
754
def _handleDone (self ):
758
- if not self ._future .done ():
759
- self ._future .set_result (self ._res )
755
+ #
756
+ # We only set the exception/result on the future in this _handleDone call,
757
+ # since doing so earlier would result in the callers awaiting the result to
758
+ # move on, possibly invalidating the provided _event_loop.
759
+ #
760
+ if self ._resultError is not None :
761
+ self ._future .set_exception (chip .exceptions .ChipStackError (self ._resultError ))
762
+ else :
763
+ self ._future .set_result (self ._resultData )
764
+
765
+ #
766
+ # Decrement the ref on ourselves to match the increment that happened at allocation.
767
+ # This happens synchronously as part of handling done to ensure the object remains valid
768
+ # right till the very end.
769
+ #
770
+ ctypes .pythonapi .Py_DecRef (ctypes .py_object (self ))
760
771
761
772
def handleDone (self ):
762
773
self ._event_loop .call_soon_threadsafe (self ._handleDone )
@@ -821,7 +832,6 @@ def _OnReportEndCallback(closure):
821
832
@_OnReadDoneCallbackFunct
822
833
def _OnReadDoneCallback (closure ):
823
834
closure .handleDone ()
824
- ctypes .pythonapi .Py_DecRef (ctypes .py_object (closure ))
825
835
826
836
827
837
_OnWriteResponseCallbackFunct = CFUNCTYPE (
@@ -846,7 +856,6 @@ def _OnWriteErrorCallback(closure, chiperror: int):
846
856
@_OnWriteDoneCallbackFunct
847
857
def _OnWriteDoneCallback (closure ):
848
858
closure .handleDone ()
849
- ctypes .pythonapi .Py_DecRef (ctypes .py_object (closure ))
850
859
851
860
852
861
def WriteAttributes (future : Future , eventLoop , device , attributes : List [AttributeWriteRequest ], timedRequestTimeoutMs : int = None ) -> int :
0 commit comments