Skip to content

Commit f8f223d

Browse files
authored
[ServiceBus] adding typing/type completeness (#33283)
* verifytypes mgmt * type more models * 81% * update dictmixin type * more typing * 96.4% * mypy * type pyamqp message backcompat * mgmt mypy errors * fix more typing * fix base handler context manager typing * update swagger to include transforms * libba * lint * fix settler message backcompat * fix admin corr filter * thanks libber * update settler pop * sync w/ eh * update mgmt internal model creation * update if_match in admin * kashifs comments * add reasons for casting * make error description optional * update min core version * remove cast in legacymessage * remove cast in legacymessage
1 parent 6c67965 commit f8f223d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1912
-1059
lines changed

sdk/servicebus/azure-servicebus/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
### Other Changes
1212

13+
- Updated minimum `azure-core` version to 1.28.0.
14+
1315
## 7.11.4 (2023-11-13)
1416

1517
### Bugs Fixed

sdk/servicebus/azure-servicebus/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/servicebus/azure-servicebus",
5-
"Tag": "python/servicebus/azure-servicebus_81b962314c"
5+
"Tag": "python/servicebus/azure-servicebus_5e25f00bf7"
66
}

sdk/servicebus/azure-servicebus/azure/servicebus/_base_handler.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def __init__(
254254
self._amqp_transport = kwargs.pop("amqp_transport", PyamqpTransport)
255255

256256
# If the user provided http:// or sb://, let's be polite and strip that.
257-
self.fully_qualified_namespace = strip_protocol_from_uri(
257+
self.fully_qualified_namespace: str = strip_protocol_from_uri(
258258
fully_qualified_namespace.strip()
259259
)
260260
self._entity_name = entity_name
@@ -330,17 +330,7 @@ def _convert_connection_string_to_kwargs(
330330

331331
return kwargs
332332

333-
def __enter__(self):
334-
if self._shutdown.is_set():
335-
raise ValueError(
336-
"The handler has already been shutdown. Please use ServiceBusClient to "
337-
"create a new instance."
338-
)
339-
340-
self._open_with_retry()
341-
return self
342-
343-
def __exit__(self, *args):
333+
def __exit__(self, *args: Any) -> None:
344334
self.close()
345335

346336
def _handle_exception(self, exception: BaseException) -> "ServiceBusError":

sdk/servicebus/azure-servicebus/azure/servicebus/_common/_configuration.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,17 @@ def __init__(self, **kwargs):
4141
self.auto_reconnect = kwargs.get("auto_reconnect", True)
4242
self.keep_alive = kwargs.get("keep_alive", 30)
4343
self.timeout: float = kwargs.get("timeout", 60)
44-
self.socket_timeout = kwargs.get("socket_timeout", 0.2)
44+
default_socket_timeout = 0.2
4545

4646
if self.http_proxy or self.transport_type.value == TransportType.AmqpOverWebsocket.value:
4747
self.transport_type = TransportType.AmqpOverWebsocket
4848
self.connection_port = DEFAULT_AMQP_WSS_PORT
49-
self.socket_timeout = kwargs.get("socket_timeout", 1)
49+
default_socket_timeout = 1
5050
if amqp_transport.KIND == "pyamqp":
5151
self.hostname += "/$servicebus/websocket"
5252

53+
self.socket_timeout = kwargs.get("socket_timeout") or default_socket_timeout
54+
5355
# custom end point
5456
if self.custom_endpoint_address:
5557
# if the custom_endpoint_address doesn't include the schema,

sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Licensed under the MIT License. See License.txt in the project root for license information.
44
# --------------------------------------------------------------------------------------------
55

6+
from typing import Optional
67
from ..management._models import DictMixin
78
from .._base_handler import _parse_conn_str
89

@@ -12,64 +13,72 @@ class ServiceBusConnectionStringProperties(DictMixin):
1213
Properties of a connection string.
1314
"""
1415

15-
def __init__(self, **kwargs):
16-
self._fully_qualified_namespace = kwargs.pop("fully_qualified_namespace", None)
17-
self._endpoint = kwargs.pop("endpoint", None)
18-
self._entity_path = kwargs.pop("entity_path", None)
19-
self._shared_access_signature = kwargs.pop("shared_access_signature", None)
20-
self._shared_access_key_name = kwargs.pop("shared_access_key_name", None)
21-
self._shared_access_key = kwargs.pop("shared_access_key", None)
16+
def __init__(
17+
self,
18+
*,
19+
fully_qualified_namespace: str,
20+
endpoint: str,
21+
entity_path: Optional[str] = None,
22+
shared_access_signature: Optional[str] = None,
23+
shared_access_key_name: Optional[str] = None,
24+
shared_access_key: Optional[str] = None
25+
):
26+
self._fully_qualified_namespace = fully_qualified_namespace
27+
self._endpoint = endpoint
28+
self._entity_path = entity_path
29+
self._shared_access_signature = shared_access_signature
30+
self._shared_access_key_name = shared_access_key_name
31+
self._shared_access_key = shared_access_key
2232

2333
@property
24-
def fully_qualified_namespace(self):
34+
def fully_qualified_namespace(self) -> str:
2535
"""The fully qualified host name for the Service Bus namespace.
2636
The namespace format is: `<yournamespace>.servicebus.windows.net`.
2737
:rtype: str
2838
"""
2939
return self._fully_qualified_namespace
3040

3141
@property
32-
def endpoint(self):
42+
def endpoint(self) -> str:
3343
"""The endpoint for the Service Bus resource. In the format sb://<FQDN>/
3444
:rtype: str
3545
"""
3646
return self._endpoint
3747

3848
@property
39-
def entity_path(self):
49+
def entity_path(self) -> Optional[str]:
4050
"""Optional. Represents the name of the queue/topic.
41-
:rtype: str
51+
:rtype: str or None
4252
"""
4353

4454
return self._entity_path
4555

4656
@property
47-
def shared_access_signature(self):
57+
def shared_access_signature(self) -> Optional[str]:
4858
"""
4959
This can be provided instead of the shared_access_key_name and the shared_access_key.
50-
:rtype: str
60+
:rtype: str or None
5161
"""
5262
return self._shared_access_signature
5363

5464
@property
55-
def shared_access_key_name(self):
65+
def shared_access_key_name(self) -> Optional[str]:
5666
"""
5767
The name of the shared_access_key. This must be used along with the shared_access_key.
58-
:rtype: str
68+
:rtype: str or None
5969
"""
6070
return self._shared_access_key_name
6171

6272
@property
63-
def shared_access_key(self):
73+
def shared_access_key(self) -> Optional[str]:
6474
"""
6575
The shared_access_key can be used along with the shared_access_key_name as a credential.
66-
:rtype: str
76+
:rtype: str or None
6777
"""
6878
return self._shared_access_key
6979

7080

71-
def parse_connection_string(conn_str):
72-
# type(str) -> ServiceBusConnectionStringProperties
81+
def parse_connection_string(conn_str: str) -> "ServiceBusConnectionStringProperties":
7382
"""Parse the connection string into a properties bag containing its component parts.
7483
7584
:param conn_str: The connection string that has to be parsed.
@@ -79,12 +88,11 @@ def parse_connection_string(conn_str):
7988
"""
8089
fully_qualified_namespace, policy, key, entity, signature = _parse_conn_str(conn_str, True)[:-1]
8190
endpoint = "sb://" + fully_qualified_namespace + "/"
82-
props = {
83-
"fully_qualified_namespace": fully_qualified_namespace,
84-
"endpoint": endpoint,
85-
"entity_path": entity,
86-
"shared_access_signature": signature,
87-
"shared_access_key_name": policy,
88-
"shared_access_key": key,
89-
}
90-
return ServiceBusConnectionStringProperties(**props)
91+
return ServiceBusConnectionStringProperties(
92+
fully_qualified_namespace=fully_qualified_namespace,
93+
endpoint=endpoint,
94+
entity_path=entity,
95+
shared_access_signature=signature,
96+
shared_access_key_name=policy,
97+
shared_access_key=key,
98+
)

sdk/servicebus/azure-servicebus/azure/servicebus/_common/auto_lock_renewer.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import time
1111
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError
1212
import queue
13-
from typing import TYPE_CHECKING, Union, Optional
13+
from typing import TYPE_CHECKING, Union, Optional, Any
1414

1515
from .._servicebus_receiver import ServiceBusReceiver
1616
from .._servicebus_session import ServiceBusSession
@@ -106,7 +106,7 @@ def __init__(
106106
self._renew_tasks = queue.Queue() # type: ignore
107107
self._infer_max_workers_time = 1
108108

109-
def __enter__(self):
109+
def __enter__(self) -> "AutoLockRenewer":
110110
if self._shutdown.is_set():
111111
raise ServiceBusError(
112112
"The AutoLockRenewer has already been shutdown. Please create a new instance for"
@@ -116,7 +116,7 @@ def __enter__(self):
116116
self._init_workers()
117117
return self
118118

119-
def __exit__(self, *args):
119+
def __exit__(self, *args: Any) -> None:
120120
self.close()
121121

122122
def _init_workers(self):
@@ -309,7 +309,7 @@ def register(
309309
)
310310
)
311311

312-
def close(self, wait=True):
312+
def close(self, wait: bool = True) -> None:
313313
"""Cease autorenewal by shutting down the thread pool to clean up any remaining lock renewal threads.
314314
315315
:param wait: Whether to block until thread pool has shutdown. Default is `True`.

sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ def __init__(
811811
message: Union["Message", "pyamqp_Message"],
812812
receive_mode: Union[ServiceBusReceiveMode, str] = ServiceBusReceiveMode.PEEK_LOCK,
813813
frame: Optional["TransferFrame"] = None,
814-
**kwargs
814+
**kwargs: Any
815815
) -> None:
816816
self._amqp_transport = kwargs.pop("amqp_transport", PyamqpTransport)
817817
super(ServiceBusReceivedMessage, self).__init__(None, message=message) # type: ignore
@@ -837,13 +837,13 @@ def __init__(
837837
) from None
838838
self._expiry: Optional[datetime.datetime] = None
839839

840-
def __getstate__(self):
840+
def __getstate__(self) -> Dict[str, Any]:
841841
state = self.__dict__.copy()
842842
state['_receiver'] = None
843843
state['_uamqp_message'] = None
844844
return state
845845

846-
def __setstate__(self, state):
846+
def __setstate__(self, state: Dict[str, Any]) -> None:
847847
self.__dict__.update(state)
848848

849849
@property

sdk/servicebus/azure-servicebus/azure/servicebus/_common/receiver_mixins.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
class ReceiverMixin(object): # pylint: disable=too-many-instance-attributes
2121
def _populate_attributes(self, **kwargs):
2222
self._amqp_transport: Union["AmqpTransport", "AmqpTransportAsync"]
23+
self.entity_path: str
2324
if kwargs.get("subscription_name"):
2425
self._subscription_name = kwargs.get("subscription_name")
2526
self._is_subscription = True
@@ -46,7 +47,7 @@ def _populate_attributes(self, **kwargs):
4647
is_session=bool(self._session_id)
4748
)
4849

49-
self._name = kwargs.get("client_identifier", "SBReceiver-{}".format(uuid.uuid4()))
50+
self._name = kwargs.get("client_identifier") or "SBReceiver-{}".format(uuid.uuid4())
5051
self._last_received_sequenced_number = None
5152
self._message_iter = None
5253
self._connection = kwargs.get("connection")

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
import struct
99
import uuid
1010
import logging
11-
from typing import List, Optional, Tuple, Dict, Callable, Any, cast, Union # pylint: disable=unused-import
11+
from typing import List, Optional, Tuple, Dict, Callable, Any, cast, Union, TYPE_CHECKING
1212

1313

1414
from .message import Message, Header, Properties
1515

16+
if TYPE_CHECKING:
17+
from .message import MessageDict
18+
1619
_LOGGER = logging.getLogger(__name__)
1720
_HEADER_PREFIX = memoryview(b'AMQP')
1821
_COMPOSITES = {
@@ -276,7 +279,9 @@ def decode_payload(buffer):
276279
message["footer"] = value
277280
# TODO: we can possibly swap out the Message construct with a TypedDict
278281
# for both input and output so we get the best of both.
279-
return Message(**message)
282+
# casting to TypedDict with named fields to allow for unpacking with **
283+
message_properties = cast("MessageDict", message)
284+
return Message(**message_properties)
280285

281286

282287
def decode_frame(data):

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
except ImportError:
3131
from typing_extensions import TypeAlias
3232

33+
from typing_extensions import Buffer
34+
3335

3436
from .types import (
3537
TYPE,
@@ -619,7 +621,7 @@ def encode_fields(value):
619621

620622

621623
def encode_annotations(value):
622-
# type: (Optional[Dict[str, Any]]) -> Dict[str, Any]
624+
# type: (Optional[Dict[Union[str, bytes] , Any]]) -> Dict[str, Any]
623625
"""The annotations type is a map where the keys are restricted to be of type symbol or of type ulong.
624626
625627
All ulong keys, and all symbolic keys except those beginning with "x-" are reserved.
@@ -650,8 +652,7 @@ def encode_annotations(value):
650652
return fields
651653

652654

653-
def encode_application_properties(value):
654-
# type: (Optional[Dict[str, Any]]) -> Dict[str, Any]
655+
def encode_application_properties(value: Optional[Dict[Union[str, bytes], Any]]) -> Dict[Union[str, bytes], Any]:
655656
"""The application-properties section is a part of the bare message used for structured application data.
656657
657658
<type name="application-properties" class="restricted" source="map" provides="section">
@@ -668,7 +669,7 @@ def encode_application_properties(value):
668669
"""
669670
if not value:
670671
return {TYPE: AMQPTypes.null, VALUE: None}
671-
fields = {TYPE: AMQPTypes.map, VALUE: cast(List, [])}
672+
fields: Dict[Union[str, bytes], Any] = {TYPE: AMQPTypes.map, VALUE: cast(List, [])}
672673
for key, data in value.items():
673674
cast(List, fields[VALUE]).append(({TYPE: AMQPTypes.string, VALUE: key}, data))
674675
return fields
@@ -876,11 +877,11 @@ def describe_performative(performative):
876877
body.append(
877878
{
878879
TYPE: AMQPTypes.array,
879-
VALUE: [_FIELD_DEFINITIONS[field.type](v) for v in value],
880+
VALUE: [_FIELD_DEFINITIONS[field.type](v) for v in value], # type: ignore
880881
}
881882
)
882883
else:
883-
body.append(_FIELD_DEFINITIONS[field.type](value))
884+
body.append(_FIELD_DEFINITIONS[field.type](value)) # type: ignore
884885
elif isinstance(field.type, ObjDefinition):
885886
body.append(describe_performative(value))
886887
else:
@@ -1033,7 +1034,8 @@ def encode_frame(frame, frame_type=_FRAME_TYPE):
10331034
frame_data = bytearray()
10341035
encode_value(frame_data, frame_description)
10351036
if isinstance(frame, performatives.TransferFrame):
1036-
frame_data += frame.payload
1037+
# casting from Optional[Buffer] since payload will not be None at this point
1038+
frame_data += cast(Buffer, frame.payload)
10371039

10381040
size = len(frame_data) + 8
10391041
header = size.to_bytes(4, "big") + _FRAME_OFFSET + frame_type

0 commit comments

Comments
 (0)