Skip to content

Commit 9b74efb

Browse files
richardpark-msftkashifkhanswathipil
authored
pyamqp - description and info are optional fields, even if they're often returned. (Azure#39195)
* escription and info are optional fields, even if they're often returned. * bring over changes to async link * bring over changes to EH * unit tests for link detach error scenarios * fix index in test * fix field names in test * sync/async error should be similar --------- Co-authored-by: Kashif Khan <[email protected]> Co-authored-by: swathipil <[email protected]>
1 parent 9da43f7 commit 9b74efb

File tree

10 files changed

+123
-10
lines changed

10 files changed

+123
-10
lines changed

sdk/eventhub/azure-eventhub/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Release History
22

3+
## 5.14.1 (Unreleased)
4+
5+
### Bugs Fixed
6+
7+
- Fixed a bug where service errors were incorrectly required and expected to have info/description fields.
8+
39
## 5.14.0 (2025-02-13)
410

511
### Features Added

sdk/eventhub/azure-eventhub/azure/eventhub/_pyamqp/aio/_link_async.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,13 @@ async def _incoming_detach(self, frame) -> None:
237237
# TODO: on_detach_hook
238238
if frame[2]: # error
239239
# frame[2][0] is condition, frame[2][1] is description, frame[2][2] is info
240-
error_cls = AMQPLinkRedirect if frame[2][0] == ErrorCondition.LinkRedirect else AMQPLinkError
241-
self._error = error_cls(condition=frame[2][0], description=frame[2][1], info=frame[2][2])
240+
condition = frame[2][0]
241+
error_cls = AMQPLinkRedirect if condition == ErrorCondition.LinkRedirect else AMQPLinkError
242+
# description and info are optional fields, from the AMQP spec.
243+
# https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-error
244+
description = None if len(frame[2]) < 2 else frame[2][1]
245+
info = None if len(frame[2]) < 3 else frame[2][2]
246+
self._error = error_cls(condition=condition, description=description, info=info)
242247
await self._set_state(LinkState.ERROR)
243248
else:
244249
if self.state != LinkState.DETACH_SENT:

sdk/eventhub/azure-eventhub/azure/eventhub/_pyamqp/link.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,15 @@ def _incoming_detach(self, frame) -> None:
232232
# TODO: on_detach_hook
233233
if frame[2]: # error
234234
# frame[2][0] is condition, frame[2][1] is description, frame[2][2] is info
235-
error_cls = AMQPLinkRedirect if frame[2][0] == ErrorCondition.LinkRedirect else AMQPLinkError
236-
self._error = error_cls(condition=frame[2][0], description=frame[2][1], info=frame[2][2])
235+
condition = frame[2][0]
236+
error_cls = AMQPLinkRedirect if condition == ErrorCondition.LinkRedirect else AMQPLinkError
237+
238+
# description and info are optional fields, from the AMQP spec.
239+
# https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-error
240+
description = None if len(frame[2]) < 2 else frame[2][1]
241+
info = None if len(frame[2]) < 3 else frame[2][2]
242+
243+
self._error = error_cls(condition=condition, description=description, info=info)
237244
self._set_state(LinkState.ERROR)
238245
else:
239246
if self.state != LinkState.DETACH_SENT:

sdk/eventhub/azure-eventhub/azure/eventhub/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
# ------------------------------------
55

66

7-
VERSION = "5.14.0"
7+
VERSION = "5.14.1"

sdk/eventhub/azure-eventhub/tests/pyamqp_tests/unittest/asynctests/test_link_async.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import asyncio
22
import sys
33

4+
from azure.eventhub._pyamqp.error import AMQPLinkError
5+
46
if sys.version_info >= (3, 8, 0):
57
from unittest.mock import AsyncMock
68
from azure.eventhub._pyamqp.aio import Link
@@ -67,3 +69,39 @@ async def test_link_should_not_detach(state):
6769
link._outgoing_detach = AsyncMock(return_value=None)
6870
await link.detach()
6971
link._outgoing_detach.assert_not_called()
72+
73+
@pytest.mark.asyncio
74+
@pytest.mark.parametrize(
75+
"frame",
76+
[
77+
[2, True, [b'amqp:link:detach-forced', b"The link is force detached. Code: publisher(link3006875). Details: AmqpMessagePublisher.IdleTimerExpired: Idle timeout: 00:10:00.", None]],
78+
[2, True, [b'amqp:link:detach-forced', None, b'something random']],
79+
[2, True, [b'amqp:link:detach-forced', None, None]],
80+
[2, True, [b'amqp:link:detach-forced']],
81+
82+
],
83+
ids=["description and info", "info only", "description only", "no info or description"],
84+
)
85+
async def test_detach_with_error(frame):
86+
'''
87+
A detach can optionally include an description and info field.
88+
https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-error
89+
'''
90+
session = None
91+
link = Link(
92+
session,
93+
3,
94+
name="test_link",
95+
role=True,
96+
source_address="test_source",
97+
target_address="test_target",
98+
network_trace=False,
99+
network_trace_params={},
100+
)
101+
await link._set_state(LinkState.DETACH_RCVD)
102+
await link._incoming_detach(frame)
103+
104+
with pytest.raises(AMQPLinkError) as ae:
105+
await link.get_state()
106+
assert ae.description == frame[2][1]
107+
assert ae.info == frame[2][2]

sdk/eventhub/azure-eventhub/tests/pyamqp_tests/unittest/test_link.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from unittest.mock import Mock
2+
from azure.eventhub._pyamqp.error import AMQPLinkError
23
from azure.eventhub._pyamqp.link import Link
34
from azure.eventhub._pyamqp.receiver import ReceiverLink
45
from azure.eventhub._pyamqp.constants import LinkState
@@ -155,3 +156,41 @@ def mock_outgoing():
155156
link._incoming_transfer(transfer_frame_three)
156157
assert link.current_link_credit == -1
157158
assert link.total_link_credit == 98
159+
160+
@pytest.mark.parametrize(
161+
"frame",
162+
[
163+
[2, True, [b'amqp:link:detach-forced', b"The link is force detached. Code: publisher(link3006875). Details: AmqpMessagePublisher.IdleTimerExpired: Idle timeout: 00:10:00.", None]],
164+
[2, True, [b'amqp:link:detach-forced', None, b'something random']],
165+
[2, True, [b'amqp:link:detach-forced', None, None]],
166+
[2, True, [b'amqp:link:detach-forced']],
167+
168+
],
169+
ids=["description and info", "info only", "description only", "no info or description"],
170+
)
171+
def test_detach_with_error(frame):
172+
'''
173+
A detach can optionally include an description and info field.
174+
https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-error
175+
'''
176+
session = None
177+
link = Link(
178+
session,
179+
3,
180+
name="test_link",
181+
role=True,
182+
source_address="test_source",
183+
target_address="test_target",
184+
network_trace=False,
185+
network_trace_params={},
186+
)
187+
link._set_state(LinkState.DETACH_RCVD)
188+
link._incoming_detach(frame)
189+
190+
with pytest.raises(AMQPLinkError) as ae:
191+
link.get_state()
192+
assert ae.description == frame[2][1]
193+
assert ae.info == frame[2][2]
194+
195+
196+

sdk/servicebus/azure-servicebus/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Release History
22

3+
## 7.14.1 (Unreleased)
4+
5+
### Bugs Fixed
6+
7+
- Fixed a bug where service errors were incorrectly required and expected to have info/description fields.
8+
39
## 7.14.0 (2025-02-13)
410

511
### Features Added

sdk/servicebus/azure-servicebus/azure/servicebus/_pyamqp/aio/_link_async.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,13 @@ async def _incoming_detach(self, frame) -> None:
237237
# TODO: on_detach_hook
238238
if frame[2]: # error
239239
# frame[2][0] is condition, frame[2][1] is description, frame[2][2] is info
240-
error_cls = AMQPLinkRedirect if frame[2][0] == ErrorCondition.LinkRedirect else AMQPLinkError
241-
self._error = error_cls(condition=frame[2][0], description=frame[2][1], info=frame[2][2])
240+
condition = frame[2][0]
241+
error_cls = AMQPLinkRedirect if condition == ErrorCondition.LinkRedirect else AMQPLinkError
242+
# description and info are optional fields, from the AMQP spec.
243+
# https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-error
244+
description = None if len(frame[2]) < 2 else frame[2][1]
245+
info = None if len(frame[2]) < 3 else frame[2][2]
246+
self._error = error_cls(condition=condition, description=description, info=info)
242247
await self._set_state(LinkState.ERROR)
243248
else:
244249
if self.state != LinkState.DETACH_SENT:

sdk/servicebus/azure-servicebus/azure/servicebus/_pyamqp/link.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,15 @@ def _incoming_detach(self, frame) -> None:
232232
# TODO: on_detach_hook
233233
if frame[2]: # error
234234
# frame[2][0] is condition, frame[2][1] is description, frame[2][2] is info
235-
error_cls = AMQPLinkRedirect if frame[2][0] == ErrorCondition.LinkRedirect else AMQPLinkError
236-
self._error = error_cls(condition=frame[2][0], description=frame[2][1], info=frame[2][2])
235+
condition = frame[2][0]
236+
error_cls = AMQPLinkRedirect if condition == ErrorCondition.LinkRedirect else AMQPLinkError
237+
238+
# description and info are optional fields, from the AMQP spec.
239+
# https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-error
240+
description = None if len(frame[2]) < 2 else frame[2][1]
241+
info = None if len(frame[2]) < 3 else frame[2][2]
242+
243+
self._error = error_cls(condition=condition, description=description, info=info)
237244
self._set_state(LinkState.ERROR)
238245
else:
239246
if self.state != LinkState.DETACH_SENT:

sdk/servicebus/azure-servicebus/azure/servicebus/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# Licensed under the MIT License.
44
# ------------------------------------
55

6-
VERSION = "7.14.0"
6+
VERSION = "7.14.1"

0 commit comments

Comments
 (0)