This is an automated email from the ASF dual-hosted git repository.

astitcher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git

commit 3670d8eba63123c7dfa9e942312e19094f746a88
Author: Andrew Stitcher <astitc...@apache.org>
AuthorDate: Mon Aug 19 14:40:15 2024 -0400

    PROTON-2873: Use the new disposition API in python
    
    This is a substantial change to the Python disposition API:
    
    * Split functionality into local and remote disposition as each
      have different purposes and access to attributes.
    
    * Allow assigning remote dispositions to local delivery
    
    * Add python tests
---
 python/cproton.h                    |   5 +
 python/proton/__init__.py           |  14 +-
 python/proton/_delivery.py          | 405 +++++++++++++++++++++++++++---------
 python/proton/_reactor.py           |  10 +-
 python/tests/proton_tests/engine.py | 100 ++++++++-
 5 files changed, 419 insertions(+), 115 deletions(-)

diff --git a/python/cproton.h b/python/cproton.h
index f98915693..59fc89944 100644
--- a/python/cproton.h
+++ b/python/cproton.h
@@ -406,14 +406,19 @@ void pn_disposition_set_section_offset(pn_disposition_t 
*disposition, uint64_t s
 void pn_disposition_set_undeliverable(pn_disposition_t *disposition, _Bool 
undeliverable);
 uint64_t pn_disposition_type(pn_disposition_t *disposition);
 
+typedef struct pn_custom_disposition_t pn_custom_disposition_t;
 typedef struct pn_received_disposition_t pn_received_disposition_t;
 typedef struct pn_rejected_disposition_t pn_rejected_disposition_t;
 typedef struct pn_modified_disposition_t pn_modified_disposition_t;
 
+pn_custom_disposition_t *pn_custom_disposition(pn_disposition_t *disposition);
 pn_received_disposition_t *pn_received_disposition(pn_disposition_t 
*disposition);
 pn_rejected_disposition_t *pn_rejected_disposition(pn_disposition_t 
*disposition);
 pn_modified_disposition_t *pn_modified_disposition(pn_disposition_t 
*disposition);
 
+void pn_custom_disposition_set_type(pn_custom_disposition_t *disposition, 
uint64_t type);
+uint64_t pn_custom_disposition_get_type(pn_custom_disposition_t *disposition);
+pn_data_t *pn_custom_disposition_data(pn_custom_disposition_t *disposition);
 pn_condition_t *pn_rejected_disposition_condition(pn_rejected_disposition_t 
*disposition);
 uint32_t pn_received_disposition_get_section_number(pn_received_disposition_t 
*disposition);
 void pn_received_disposition_set_section_number(pn_received_disposition_t 
*disposition, uint32_t section_number);
diff --git a/python/proton/__init__.py b/python/proton/__init__.py
index e63f256b3..2b7c54cca 100644
--- a/python/proton/__init__.py
+++ b/python/proton/__init__.py
@@ -36,7 +36,8 @@ from cproton import PN_VERSION_MAJOR, PN_VERSION_MINOR, 
PN_VERSION_POINT
 from ._condition import Condition
 from ._data import UNDESCRIBED, Array, Data, Described, char, symbol, 
timestamp, ubyte, ushort, uint, ulong, \
     byte, short, int32, float32, decimal32, decimal64, decimal128, 
AnnotationDict, PropertyDict, SymbolList
-from ._delivery import Delivery, Disposition, DispositionType
+from ._delivery import Delivery, Disposition, DispositionType, 
CustomDisposition, RejectedDisposition, \
+    ModifiedDisposition, ReceivedDisposition
 from ._endpoints import Endpoint, Connection, Session, Link, Receiver, Sender, 
Terminus
 from ._events import Collector, Event, EventType
 from ._exceptions import ProtonException, MessageException, DataException, 
TransportException, \
@@ -47,8 +48,6 @@ from ._transport import Transport, SASL, SSL, SSLDomain, 
SSLSessionDetails
 from ._url import Url
 
 __all__ = [
-    "API_LANGUAGE",
-    "IMPLEMENTATION_LANGUAGE",
     "UNDESCRIBED",
     "AnnotationDict",
     "Array",
@@ -56,6 +55,7 @@ __all__ = [
     "Condition",
     "Connection",
     "ConnectionException",
+    "CustomDisposition",
     "Data",
     "DataException",
     "Delivery",
@@ -70,11 +70,12 @@ __all__ = [
     "LinkException",
     "Message",
     "MessageException",
+    "ModifiedDisposition",
     "PropertyDict",
     "ProtonException",
-    "VERSION_MAJOR",
-    "VERSION_MINOR",
     "Receiver",
+    "ReceivedDisposition",
+    "RejectedDisposition",
     "SASL",
     "Sender",
     "Session",
@@ -111,9 +112,6 @@ VERSION_MAJOR = PN_VERSION_MAJOR
 VERSION_MINOR = PN_VERSION_MINOR
 VERSION_POINT = PN_VERSION_POINT
 VERSION = (VERSION_MAJOR, VERSION_MINOR, VERSION_POINT)
-API_LANGUAGE = "C"
-IMPLEMENTATION_LANGUAGE = "C"
-
 
 handler = logging.NullHandler()
 
diff --git a/python/proton/_delivery.py b/python/proton/_delivery.py
index 1929d82bc..1425b582b 100644
--- a/python/proton/_delivery.py
+++ b/python/proton/_delivery.py
@@ -17,26 +17,44 @@
 # under the License.
 #
 
-from cproton import PN_ACCEPTED, PN_MODIFIED, PN_RECEIVED, PN_REJECTED, 
PN_RELEASED, pn_delivery_abort, \
-    pn_delivery_aborted, pn_delivery_attachments, pn_delivery_link, 
pn_delivery_local, pn_delivery_local_state, \
-    pn_delivery_partial, pn_delivery_pending, pn_delivery_readable, 
pn_delivery_remote, pn_delivery_remote_state, \
-    pn_delivery_settle, pn_delivery_settled, pn_delivery_tag, 
pn_delivery_update, pn_delivery_updated, \
-    pn_delivery_writable, pn_disposition_annotations, 
pn_disposition_condition, pn_disposition_data, \
-    pn_disposition_get_section_number, pn_disposition_get_section_offset, 
pn_disposition_is_failed, \
-    pn_disposition_is_undeliverable, pn_disposition_set_failed, 
pn_disposition_set_section_number, \
-    pn_disposition_set_section_offset, pn_disposition_set_undeliverable, 
pn_disposition_type
-
-from ._condition import cond2obj, obj2cond
+from cproton import (PN_ACCEPTED, PN_MODIFIED, PN_RECEIVED, PN_REJECTED, 
PN_RELEASED, pn_delivery_abort,
+                     pn_delivery_aborted, pn_delivery_attachments, 
pn_delivery_link, pn_delivery_local,
+                     pn_delivery_local_state,
+                     pn_delivery_partial, pn_delivery_pending, 
pn_delivery_readable, pn_delivery_remote,
+                     pn_delivery_remote_state,
+                     pn_delivery_settle, pn_delivery_settled, pn_delivery_tag, 
pn_delivery_update, pn_delivery_updated,
+                     pn_delivery_writable, pn_disposition_annotations, 
pn_disposition_condition, pn_disposition_data,
+                     pn_disposition_get_section_number, 
pn_disposition_get_section_offset, pn_disposition_is_failed,
+                     pn_disposition_is_undeliverable, 
pn_disposition_set_failed, pn_disposition_set_section_number,
+                     pn_disposition_set_section_offset, 
pn_disposition_set_undeliverable, pn_disposition_type,
+                     pn_custom_disposition,
+                     pn_custom_disposition_get_type,
+                     pn_custom_disposition_set_type,
+                     pn_custom_disposition_data,
+                     pn_rejected_disposition,
+                     pn_rejected_disposition_condition,
+                     pn_received_disposition,
+                     pn_received_disposition_get_section_number,
+                     pn_received_disposition_set_section_number,
+                     pn_received_disposition_get_section_offset,
+                     pn_received_disposition_set_section_offset,
+                     pn_modified_disposition,
+                     pn_modified_disposition_is_failed,
+                     pn_modified_disposition_set_failed,
+                     pn_modified_disposition_is_undeliverable,
+                     pn_modified_disposition_set_undeliverable,
+                     pn_modified_disposition_annotations)
+
+from ._condition import cond2obj, obj2cond, Condition
 from ._data import dat2obj, obj2dat
 from ._transport import Transport
 from ._wrapper import Wrapper
 
 from enum import IntEnum
-from typing import Dict, List, Optional, Union, TYPE_CHECKING
+from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
 
 
 if TYPE_CHECKING:
-    from ._condition import Condition
     from ._data import PythonAMQPData, symbol
     from ._endpoints import Receiver, Sender  # circular import
     from ._reactor import Connection, Session
@@ -85,7 +103,7 @@ class DispositionType(IntEnum):
         return cls(i) if i in cls._value2member_map_ else i
 
 
-class Disposition(object):
+class Disposition:
     """
     A delivery state.
 
@@ -102,9 +120,127 @@ class Disposition(object):
     RELEASED = DispositionType.RELEASED
     MODIFIED = DispositionType.MODIFIED
 
-    def __init__(self, impl, local):
-        self._impl = impl
-        self.local = local
+
+class RemoteDisposition(Disposition):
+
+    def __new__(cls, delivery_impl):
+        state = DispositionType.or_int(pn_delivery_remote_state(delivery_impl))
+        if state == 0:
+            return None
+        elif state == cls.RECEIVED:
+            return super().__new__(RemoteReceivedDisposition)
+        elif state == cls.REJECTED:
+            return super().__new__(RemoteRejectedDisposition)
+        elif state == cls.MODIFIED:
+            return super().__new__(RemoteModifiedDisposition)
+        else:
+            return super().__new__(RemoteCustomDisposition)
+
+
+class RemoteCustomDisposition(RemoteDisposition):
+
+    def __init__(self, delivery_impl):
+        impl = pn_custom_disposition(pn_delivery_remote(delivery_impl))
+        self._type = 
DispositionType.or_int(pn_custom_disposition_get_type(impl))
+        self._data = dat2obj(pn_custom_disposition_data(impl))
+
+    @property
+    def type(self) -> Union[int, DispositionType]:
+        return self._type
+
+    @property
+    def data(self) -> Optional[List[Any]]:
+        """Access the disposition as a :class:`Data` object.
+
+        Dispositions are an extension point in the AMQP protocol. The
+        disposition interface provides setters/getters for those
+        dispositions that are predefined by the specification, however
+        access to the raw disposition data is provided so that other
+        dispositions can be used.
+
+        The :class:`Data` object returned by this operation is valid until
+        the parent delivery is settled.
+        """
+        r = self._data
+        return r if r != [] else None
+
+    def apply_to(self, local_disposition: 'LocalDisposition'):
+        CustomDisposition(self._type, self._data).apply_to(local_disposition)
+
+
+class RemoteReceivedDisposition(RemoteDisposition):
+
+    def __init__(self, delivery_impl):
+        impl = pn_received_disposition(pn_delivery_remote(delivery_impl))
+        self._section_number = pn_received_disposition_get_section_number(impl)
+        self._section_offset = pn_received_disposition_get_section_offset(impl)
+
+    @property
+    def type(self) -> Union[int, DispositionType]:
+        return Disposition.RECEIVED
+
+    @property
+    def section_number(self) -> int:
+        return self._section_number
+
+    @property
+    def section_offset(self) -> int:
+        return self._section_offset
+
+    def apply_to(self, local_disposition: 'LocalDisposition'):
+        ReceivedDisposition(self._section_number, 
self._section_offset).apply_to(local_disposition)
+
+
+class RemoteRejectedDisposition(RemoteDisposition):
+
+    def __init__(self, delivery_impl):
+        impl = pn_rejected_disposition(pn_delivery_remote(delivery_impl))
+        self._condition = cond2obj(pn_rejected_disposition_condition(impl))
+
+    @property
+    def type(self) -> Union[int, DispositionType]:
+        return Disposition.REJECTED
+
+    @property
+    def condition(self) -> Optional[Condition]:
+        return self._condition
+
+    def apply_to(self, local_disposition: 'LocalDisposition'):
+        RejectedDisposition(self._condition).apply_to(local_disposition)
+
+
+class RemoteModifiedDisposition(RemoteDisposition):
+
+    def __init__(self, delivery_impl):
+        impl = pn_modified_disposition(pn_delivery_remote(delivery_impl))
+        self._annotations = dat2obj(pn_modified_disposition_annotations(impl))
+        self._failed = pn_modified_disposition_is_failed(impl)
+        self._undeliverable = pn_modified_disposition_is_undeliverable(impl)
+
+    @property
+    def type(self) -> Union[int, DispositionType]:
+        return Disposition.MODIFIED
+
+    @property
+    def failed(self) -> bool:
+        return self._failed
+
+    @property
+    def undeliverable(self) -> bool:
+        return self._undeliverable
+
+    @property
+    def annotations(self) -> Optional[Dict['symbol', 'PythonAMQPData']]:
+        return self._annotations
+
+    def apply_to(self, local_disposition: 'LocalDisposition'):
+        ModifiedDisposition(self._failed, self._undeliverable, 
self._annotations).apply_to(local_disposition)
+
+
+class LocalDisposition(Disposition):
+
+    def __init__(self, delivery_impl):
+        self._impl = pn_delivery_local(delivery_impl)
         self._data = None
         self._condition = None
         self._annotations = None
@@ -124,9 +260,16 @@ class Disposition(object):
         """
         return DispositionType.or_int(pn_disposition_type(self._impl))
 
+    @property
+    def data(self) -> Optional[List[int]]:
+        return self._data
+
+    @data.setter
+    def data(self, obj: List[int]) -> None:
+        self._data = obj
+
     @property
     def section_number(self) -> int:
-        """The section number associated with a disposition."""
         return pn_disposition_get_section_number(self._impl)
 
     @section_number.setter
@@ -135,16 +278,22 @@ class Disposition(object):
 
     @property
     def section_offset(self) -> int:
-        """The section offset associated with a disposition."""
         return pn_disposition_get_section_offset(self._impl)
 
     @section_offset.setter
     def section_offset(self, n: int) -> None:
         pn_disposition_set_section_offset(self._impl, n)
 
+    @property
+    def condition(self) -> Optional[Condition]:
+        return self._condition
+
+    @condition.setter
+    def condition(self, obj: Condition) -> None:
+        self._condition = obj
+
     @property
     def failed(self) -> bool:
-        """The failed flag for this disposition."""
         return pn_disposition_is_failed(self._impl)
 
     @failed.setter
@@ -153,7 +302,6 @@ class Disposition(object):
 
     @property
     def undeliverable(self) -> bool:
-        """The undeliverable flag for this disposition."""
         return pn_disposition_is_undeliverable(self._impl)
 
     @undeliverable.setter
@@ -161,78 +309,125 @@ class Disposition(object):
         pn_disposition_set_undeliverable(self._impl, b)
 
     @property
-    def data(self) -> Optional[List[int]]:
-        """Access the disposition as a :class:`Data` object.
+    def annotations(self) -> Optional[Dict['symbol', 'PythonAMQPData']]:
+        return self._annotations
 
-        Dispositions are an extension point in the AMQP protocol. The
-        disposition interface provides setters/getters for those
-        dispositions that are predefined by the specification, however
-        access to the raw disposition data is provided so that other
-        dispositions can be used.
+    @annotations.setter
+    def annotations(self, obj: Dict['symbol', 'PythonAMQPData']) -> None:
+        self._annotations = obj
 
-        The :class:`Data` object returned by this operation is valid until
-        the parent delivery is settled.
-        """
-        if self.local:
-            return self._data
-        else:
-            r = dat2obj(pn_disposition_data(self._impl))
-            return r if r != [] else None
 
-    @data.setter
-    def data(self, obj: List[int]) -> None:
-        if self.local:
-            self._data = obj
-        else:
-            raise AttributeError("data attribute is read-only")
+class ReceivedDisposition(LocalDisposition):
+
+    def __init__(self, section_number: int = None, section_offset: int = None):
+        self._section_number = section_number
+        self._section_offset = section_offset
 
     @property
-    def annotations(self) -> Optional[Dict['symbol', 'PythonAMQPData']]:
-        """The annotations associated with a disposition.
+    def type(self) -> Union[int, DispositionType]:
+        return Disposition.RECEIVED
 
-        The :class:`Data` object retrieved by this operation may be modified
-        prior to updating a delivery. When a delivery is updated, the
-        annotations described by the :class:`Data` are reported to the peer
-        if applicable to the current delivery state, e.g. states such as
-        :const:`MODIFIED`. The :class:`Data` must be empty or contain a symbol
-        keyed map.
+    @property
+    def section_number(self) -> int:
+        return self._section_number
 
-        The :class:`Data` object returned by this operation is valid until
-        the parent delivery is settled.
-        """
-        if self.local:
-            return self._annotations
-        else:
-            return dat2obj(pn_disposition_annotations(self._impl))
+    @section_number.setter
+    def section_number(self, n: int) -> None:
+        self._section_number = n
 
-    @annotations.setter
-    def annotations(self, obj: Dict[str, 'PythonAMQPData']) -> None:
-        if self.local:
-            self._annotations = obj
-        else:
-            raise AttributeError("annotations attribute is read-only")
+    @property
+    def section_offset(self) -> int:
+        return self._section_offset
+
+    @section_offset.setter
+    def section_offset(self, n: int) -> None:
+        self._section_offset = n
+
+    def apply_to(self, local_disposition: LocalDisposition):
+        disp = pn_received_disposition(local_disposition._impl)
+        if self._section_number:
+            pn_received_disposition_set_section_number(disp, 
self._section_number)
+        if self._section_offset:
+            pn_received_disposition_set_section_offset(disp, 
self._section_offset)
+
+
+class CustomDisposition(LocalDisposition):
+
+    def __init__(self, type: int, data: Any = None):
+        self._type = type
+        self._data = data
+
+    def apply_to(self, local_disposition: LocalDisposition):
+        disp = pn_custom_disposition(local_disposition._impl)
+        pn_custom_disposition_set_type(disp, self._type)
+        obj2dat(self._data, pn_custom_disposition_data(disp))
+
+
+class RejectedDisposition(LocalDisposition):
+
+    def __init__(self, condition: Optional[Condition] = None):
+        self._condition = condition
 
     @property
-    def condition(self) -> Optional['Condition']:
-        """The condition object associated with a disposition.
+    def type(self) -> Union[int, DispositionType]:
+        return Disposition.REJECTED
 
-        The :class:`Condition` object retrieved by this operation may be
-        modified prior to updating a delivery. When a delivery is updated,
-        the condition described by the disposition is reported to the peer
-        if applicable to the current delivery state, e.g. states such as
-        :const:`REJECTED`.
-        """
-        if self.local:
-            return self._condition
-        else:
-            return cond2obj(pn_disposition_condition(self._impl))
+    @property
+    def condition(self) -> Optional[Condition]:
+        return self._condition
 
     @condition.setter
-    def condition(self, obj: 'Condition') -> None:
-        if self.local:
-            self._condition = obj
-        else:
-            raise AttributeError("condition attribute is read-only")
+    def condition(self, obj: Condition) -> None:
+        self._condition = obj
+
+    def apply_to(self, local_disposition: LocalDisposition):
+        disp = pn_rejected_disposition(local_disposition._impl)
+        obj2cond(self._condition, pn_rejected_disposition_condition(disp))
+
+
+class ModifiedDisposition(LocalDisposition):
+
+    def __init__(self, failed: bool = None, undeliverable: bool = None,
+                 annotations: Optional[Dict['symbol', 'PythonAMQPData']] = 
None):
+        self._failed = failed
+        self._undeliverable = undeliverable
+        self._annotations = annotations
+
+    @property
+    def type(self) -> Union[int, DispositionType]:
+        return Disposition.MODIFIED
+
+    @property
+    def failed(self) -> bool:
+        return self._failed
+
+    @failed.setter
+    def failed(self, b: bool) -> None:
+        self._failed = b
+
+    @property
+    def undeliverable(self) -> bool:
+        return self._undelivered
+
+    @undeliverable.setter
+    def undeliverable(self, b: bool) -> None:
+        self._undelivered = b
+
+    @property
+    def annotations(self) -> Optional[Dict['symbol', 'PythonAMQPData']]:
+        return self._annotations
+
+    @annotations.setter
+    def annotations(self, obj: Dict['symbol', 'PythonAMQPData']) -> None:
+        self._annotations = obj
+
+    def apply_to(self, local_disposition: LocalDisposition):
+        disp = pn_modified_disposition(local_disposition._impl)
+        if self._failed:
+            pn_modified_disposition_set_failed(disp, self._failed)
+        if self._undeliverable:
+            pn_modified_disposition_set_undeliverable(disp, 
self._undeliverable)
+        obj2dat(self._annotations, pn_modified_disposition_annotations(disp))
 
 
 class Delivery(Wrapper):
@@ -281,8 +476,26 @@ class Delivery(Wrapper):
 
     def __init__(self, impl):
         if self.Uninitialized():
-            self.local = Disposition(pn_delivery_local(self._impl), True)
-            self.remote = Disposition(pn_delivery_remote(self._impl), False)
+            self._local = None
+            self._remote = None
+
+    @property
+    def remote(self) -> RemoteDisposition:
+        if self._remote is None:
+            self._remote = RemoteDisposition(self._impl)
+        return self._remote
+
+    @property
+    def local(self) -> LocalDisposition:
+        if self._local is None:
+            self._local = LocalDisposition(self._impl)
+        return self._local
+
+    @local.setter
+    def local(self, local_disposition: LocalDisposition) -> None:
+        if self._local is None:
+            self._local = LocalDisposition(self._impl)
+        local_disposition.apply_to(self._local)
 
     @property
     def tag(self) -> str:
@@ -295,7 +508,7 @@ class Delivery(Wrapper):
     def writable(self) -> bool:
         """
         ``True`` for an outgoing delivery to which data can now be written,
-        ``False`` otherwise..
+        ``False`` otherwise.
         """
         return pn_delivery_writable(self._impl)
 
@@ -303,7 +516,7 @@ class Delivery(Wrapper):
     def readable(self) -> bool:
         """
         ``True`` for an incoming delivery that has data to read,
-        ``False`` otherwise..
+        ``False`` otherwise.
         """
         return pn_delivery_readable(self._impl)
 
@@ -311,25 +524,29 @@ class Delivery(Wrapper):
     def updated(self) -> bool:
         """
         ``True`` if the state of the delivery has been updated
-        (e.g. it has been settled and/or accepted, rejected etc),
+        (e.g. it has been settled and/or accepted, rejected etc.),
         ``False`` otherwise.
         """
         return pn_delivery_updated(self._impl)
 
-    def update(self, state: Union[int, DispositionType]) -> None:
+    def update(self, state: Union[int, DispositionType, None] = None) -> None:
         """
         Set the local state of the delivery e.g. :const:`ACCEPTED`,
         :const:`REJECTED`, :const:`RELEASED`.
 
-        :param state: State of delivery
-        """
-        if state == self.MODIFIED:
-            obj2dat(self.local._annotations, 
pn_disposition_annotations(self.local._impl))
-        elif state == self.REJECTED:
-            obj2cond(self.local._condition, 
pn_disposition_condition(self.local._impl))
-        elif state not in (self.ACCEPTED, self.RECEIVED, self.RELEASED):
-            obj2dat(self.local._data, pn_disposition_data(self.local._impl))
-        pn_delivery_update(self._impl, state)
+        :param state: State of delivery, if omitted we assume the delivery 
state is already set
+        by other means
+        """
+        if state:
+            if state == self.MODIFIED:
+                obj2dat(self.local._annotations, 
pn_disposition_annotations(self.local._impl))
+            elif state == self.REJECTED:
+                obj2cond(self.local._condition, 
pn_disposition_condition(self.local._impl))
+            elif state not in (self.ACCEPTED, self.RECEIVED, self.RELEASED):
+                obj2dat(self.local._data, 
pn_disposition_data(self.local._impl))
+            pn_delivery_update(self._impl, state)
+        else:
+            pn_delivery_update(self._impl, self.local_state)
 
     @property
     def pending(self) -> int:
diff --git a/python/proton/_reactor.py b/python/proton/_reactor.py
index 1f32b4a67..04ff2aac2 100644
--- a/python/proton/_reactor.py
+++ b/python/proton/_reactor.py
@@ -33,7 +33,7 @@ from functools import total_ordering
 from cproton import PN_ACCEPTED, PN_EVENT_NONE
 
 from ._data import Described, symbol, ulong
-from ._delivery import Delivery
+from ._delivery import Delivery, CustomDisposition
 from ._endpoints import Connection, Endpoint, Link, Session, Terminus
 from ._events import Collector, EventType, EventBase, Event
 from ._exceptions import SSLUnavailable
@@ -584,8 +584,8 @@ class Transaction(object):
         :return: Delivery object for this message.
         """
         dlv = sender.send(msg, tag=tag)
-        dlv.local.data = [self.id]
-        dlv.update(0x34)
+        dlv.local = CustomDisposition(0x34, [self.id])
+        dlv.update()
         return dlv
 
     def accept(self, delivery: Delivery) -> None:
@@ -602,8 +602,8 @@ class Transaction(object):
 
     def update(self, delivery: Delivery, state: Optional[ulong] = None) -> 
None:
         if state:
-            delivery.local.data = [self.id, Described(ulong(state), [])]
-            delivery.update(0x34)
+            delivery.local = GeneralDisposition(0x34, [self.id, 
Described(ulong(state), [])])
+            delivery.update()
 
     def _release_pending(self):
         for d in self._pending:
diff --git a/python/tests/proton_tests/engine.py 
b/python/tests/proton_tests/engine.py
index e753bae58..894a910ff 100644
--- a/python/tests/proton_tests/engine.py
+++ b/python/tests/proton_tests/engine.py
@@ -23,7 +23,8 @@ from time import time, sleep
 from typing import Union
 
 from proton import Array, Condition, Collector, Connection, Data, Delivery, 
Disposition, DispositionType, Endpoint, \
-    Event, Link, PropertyDict, SASL, SessionException, SymbolList, Terminus, 
Transport, UNDESCRIBED, symbol
+    Event, CustomDisposition, Link, ModifiedDisposition, PropertyDict, 
ReceivedDisposition, RejectedDisposition, \
+    SASL, SessionException, SymbolList, Terminus, Transport, UNDESCRIBED, 
symbol
 from proton.reactor import Container
 
 from . import common
@@ -2275,20 +2276,36 @@ class DispositionTester:
 
 class RejectedTester(DispositionTester):
     def __init__(self, condition: Condition):
-        self.condition = condition
+        self._condition = condition
         super().__init__(Disposition.REJECTED)
 
     def apply(self, dlv: Delivery):
-        dlv.local.condition = self.condition
+        dlv.local.condition = self._condition
         dlv.update(self._type)
 
     def check(self, dlv: Delivery):
         assert dlv.remote_state == self._type
         assert dlv.remote.type == self._type
-        assert dlv.remote.condition == self.condition, (dlv.condition, 
self.condition)
+        assert dlv.remote.condition == self._condition, (dlv.remote.condition, 
self._condition)
 
 
-class ReceivedValue(DispositionTester):
+class NewRejectedTester(DispositionTester):
+
+    def __init__(self, condition: Condition):
+        self._condition = condition
+        super().__init__(Disposition.REJECTED)
+
+    def apply(self, dlv: Delivery):
+        dlv.local = RejectedDisposition(self._condition)
+        dlv.update()
+
+    def check(self, dlv: Delivery):
+        assert dlv.remote_state == self._type
+        assert dlv.remote.type == self._type
+        assert dlv.remote.condition == self._condition, (dlv.remote.condition, 
self._condition)
+
+
+class ReceivedTester(DispositionTester):
     def __init__(self, section_number: int, section_offset: int):
         self._section_number = section_number
         self._section_offset = section_offset
@@ -2302,10 +2319,27 @@ class ReceivedValue(DispositionTester):
     def check(self, dlv: Delivery):
         assert dlv.remote_state == self._type
         assert dlv.remote.type == self._type
-        assert dlv.remote.section_number == self._section_number, 
(dlv.section_number, self._section_number)
+        assert dlv.remote.section_number == self._section_number, 
(dlv.remote.section_number, self._section_number)
         assert dlv.remote.section_offset == self._section_offset
 
 
+class NewReceivedTester(DispositionTester):
+    def __init__(self, section_number: int, section_offset: int):
+        self._section_number = section_number
+        self._section_offset = section_offset
+        super().__init__(Disposition.RECEIVED)
+
+    def apply(self, dlv: Delivery):
+        dlv.local = ReceivedDisposition(self._section_number, 
self._section_offset)
+        dlv.update()
+
+    def check(self, dlv: Delivery):
+        assert dlv.remote_state == self._type
+        assert dlv.remote.type == self._type
+        assert dlv.remote.section_number == self._section_number, 
(dlv.remote.section_number, self._section_number)
+        assert dlv.remote.section_offset == self._section_offset, 
(dlv.remote.section_offset, self._section_offset)
+
+
 class ModifiedTester(DispositionTester):
     def __init__(self, failed, undeliverable, annotations):
         self._failed = failed
@@ -2324,7 +2358,26 @@ class ModifiedTester(DispositionTester):
         assert dlv.remote.type == self._type
         assert dlv.remote.failed == self._failed
         assert dlv.remote.undeliverable == self._undeliverable
-        assert dlv.remote.annotations == self._annotations, (dlv.annotations, 
self._annotations)
+        assert dlv.remote.annotations == self._annotations, 
(dlv.remote.annotations, self._annotations)
+
+
+class NewModifiedTester(DispositionTester):
+    def __init__(self, failed, undeliverable, annotations):
+        self._failed = failed
+        self._undeliverable = undeliverable
+        self._annotations = annotations
+        super().__init__(Disposition.MODIFIED)
+
+    def apply(self, dlv: Delivery):
+        dlv.local = ModifiedDisposition(self._failed, self._undeliverable, 
self._annotations)
+        dlv.update()
+
+    def check(self, dlv: Delivery):
+        assert dlv.remote_state == self._type
+        assert dlv.remote.type == self._type
+        assert dlv.remote.failed == self._failed
+        assert dlv.remote.undeliverable == self._undeliverable
+        assert dlv.remote.annotations == self._annotations, 
(dlv.remote.annotations, self._annotations)
 
 
 class CustomTester(DispositionTester):
@@ -2342,6 +2395,21 @@ class CustomTester(DispositionTester):
         assert dlv.remote.data == self._data, (dlv.data, self._data)
 
 
+class NewCustomTester(DispositionTester):
+    def __init__(self, type, data):
+        self._data = data
+        super().__init__(type)
+
+    def apply(self, dlv: Delivery):
+        dlv.local = CustomDisposition(self._type, self._data)
+        dlv.update()
+
+    def check(self, dlv: Delivery):
+        assert dlv.remote_state == self._type
+        assert dlv.remote.type == self._type, (dlv.remote.type, self._type)
+        assert dlv.remote.data == self._data, (dlv.data, self._data)
+
+
 class DeliveryTest(Test):
 
     def tearDown(self):
@@ -2398,11 +2466,17 @@ class DeliveryTest(Test):
         self._testDisposition(Disposition.ACCEPTED)
 
     def testReceived(self):
-        self._testDisposition(ReceivedValue(1, 2))
+        self._testDisposition(ReceivedTester(1, 2))
+
+    def testNewReceived(self):
+        self._testDisposition(NewReceivedTester(1, 2))
 
     def testRejected(self):
         self._testDisposition(RejectedTester(Condition(symbol("foo"))))
 
+    def testNewRejected(self):
+        self._testDisposition(NewRejectedTester(Condition(symbol("foo"))))
+
     def testReleased(self):
         self._testDisposition(Disposition.RELEASED)
 
@@ -2410,15 +2484,25 @@ class DeliveryTest(Test):
         self._testDisposition(ModifiedTester(failed=True, undeliverable=True,
                                              annotations={"key": "value"}))
 
+    def testNewModified(self):
+        self._testDisposition(NewModifiedTester(failed=True, 
undeliverable=True,
+                                                annotations={"key": "value"}))
+
     def testCustom(self):
         self._testDisposition(CustomTester(0x12345, [1, 2, 3]))
 
+    def testNewCustom(self):
+        self._testDisposition(NewCustomTester(0x12345, [1, 2, 3]))
+
     def testEmptyCustom(self):
         self._testDisposition(0x12345)
 
     def testNullCustom(self):
         self._testDisposition(CustomTester(0x12345, None))
 
+    def testNullNewCustom(self):
+        self._testDisposition(NewCustomTester(0x12345, None))
+
 
 class CollectorTest(Test):
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org
For additional commands, e-mail: commits-h...@qpid.apache.org

Reply via email to