Skip to content

Commit a1d223a

Browse files
authored
[SchemaRegistry] Avro API changes (Azure#23573)
* combine into one exception * update version + changelog * remove preamble back compat * add details ivar to exception * log all cache info * add py.typed file * manifest file * update to beta version * fix SRAvro mypy * update EH version + return type of __message_content__ * remove callbacks * rename auto_register_schemas * remove exceptions module * pylint * errors inherit from ValueError * remove typos * fix MessageContent samples and return value * mypy/lint * add InvalidContentError * adams comments * lint
1 parent 75b4544 commit a1d223a

30 files changed

+672
-663
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.9.0b3 (Unreleased)
4+
5+
### Other Changes
6+
7+
- Updated `EventData` internals for interoperability with the Schema Registry Avro Encoder library.
8+
39
## 5.9.0b2 (2022-03-09)
410

511
### Breaking Changes

sdk/eventhub/azure-eventhub/azure/eventhub/_common.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
TYPE_CHECKING,
1919
cast,
2020
)
21+
from typing_extensions import TypedDict
2122

2223
import six
2324

@@ -60,6 +61,7 @@
6061
if TYPE_CHECKING:
6162
import datetime
6263

64+
MessageContent = TypedDict("MessageContent", {"content": bytes, "content_type": str})
6365
PrimitiveTypes = Optional[Union[
6466
int,
6567
float,
@@ -182,16 +184,17 @@ def __str__(self):
182184
event_str += " }"
183185
return event_str
184186

185-
def __message_content__(self) -> Dict:
187+
def __message_content__(self) -> MessageContent:
186188
if self.body_type != AmqpMessageBodyType.DATA:
187189
raise TypeError('`body_type` must be `AmqpMessageBodyType.DATA`.')
188190
content = bytearray()
189191
for c in self.body: # type: ignore
190192
content += c # type: ignore
191-
return {"content": bytes(content), "content_type": self.content_type}
193+
content_type = cast(str, self.content_type)
194+
return {"content": bytes(content), "content_type": content_type}
192195

193196
@classmethod
194-
def from_message_content(cls, content: bytes, content_type: str) -> "EventData":
197+
def from_message_content(cls, content: bytes, content_type: str, **kwargs: Any) -> "EventData": # pylint: disable=unused-argument
195198
"""
196199
Creates an EventData object given content type and a content value to be set as body.
197200

sdk/eventhub/azure-eventhub/azure/eventhub/_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 = "5.9.0b2"
6+
VERSION = "5.9.0b3"

sdk/schemaregistry/azure-schemaregistry-avroencoder/CHANGELOG.md

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

3+
## 1.0.0b3 (Unreleased)
4+
5+
### Breaking Changes
6+
7+
- `auto_register_schemas` keyword in the sync and async `AvroEncoder` constructors has been renamed `auto_register`.
8+
- `SchemaParseError`, `SchemaEncodeError`, and `SchemaDecodeError` have been replaced with `InvalidContentError` and `InvalidSchemaError`. The errors have been added under the `azure.schemaregistry.encoder.avroencoder` namespace.
9+
- The `exceptions` module in `azure.schemaregistry.encoder.avroencoder` has been removed.
10+
- The `encode` method on the sync and async `AvroEncoder` only allows subtypes of the `MessageType` protocol as values to the `message_type` optional parameter, rather than any callable that has the method signature `(content: bytes, content_type: str, **kwargs: Any)`.
11+
12+
### Other Changes
13+
14+
- This release and future releases will not backward compatibility support for decoding data that was encoded with the AvroSerializer.
15+
- The `encode` and `decode` methods on `AvroEncoder` support the following message models:
16+
- `azure.eventhub.EventData` in `azure-eventhub==5.9.0b3`
17+
318
## 1.0.0b2 (2022-03-09)
419

520
### Features Added

sdk/schemaregistry/azure-schemaregistry-avroencoder/MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ include azure/__init__.py
44
include azure/schemaregistry/__init__.py
55
recursive-include tests *.py
66
recursive-include samples *.py
7+
include azure/schemaregistry/encoder/avroencoder/py.typed

sdk/schemaregistry/azure-schemaregistry-avroencoder/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ content type with schema ID. Uses [SchemaRegistryClient][schemaregistry_client]
7070

7171
Support has been added to certain Azure Messaging SDK model classes for interoperability with the `AvroEncoder`. These models are subtypes of the `MessageType` protocol defined under the `azure.schemaregistry.encoder.avroencoder` namespace. Currently, the supported model classes are:
7272

73-
- `azure.eventhub.EventData` for `azure-eventhub==5.9.0b2`
73+
- `azure.eventhub.EventData` for `azure-eventhub==5.9.0b3`
7474

7575
### Message format
7676

@@ -86,7 +86,7 @@ If a message type that follows the MessageType protocol is provided to the encod
8686
- `avro/binary` is the format indicator
8787
- `<schema ID>` is the hexadecimal representation of GUID, same format and byte order as the string from the Schema Registry service.
8888

89-
If message type or callback function is not provided, and by default, the encoder will create the following dict:
89+
If message type is not provided, and by default, the encoder will create the following dict:
9090
`{"content": <Avro encoded payload>, "content_type": 'avro/binary+<schema ID>' }`
9191

9292
## Examples
@@ -101,7 +101,7 @@ The following sections provide several code snippets covering some of the most c
101101
### Encoding
102102

103103
Use `AvroEncoder.encode` method to encode dict content with the given Avro schema.
104-
The method will use a schema previously registered to the Schema Registry service and keep the schema cached for future encoding usage. It is also possible to avoid pre-registering the schema to the service and automatically register with the `encode` method by instantiating the `AvroEncoder` with the keyword argument `auto_register_schemas=True`.
104+
The method will use a schema previously registered to the Schema Registry service and keep the schema cached for future encoding usage. It is also possible to avoid pre-registering the schema to the service and automatically register with the `encode` method by instantiating the `AvroEncoder` with the keyword argument `auto_register=True`.
105105

106106
```python
107107
import os
@@ -202,7 +202,7 @@ definition = """
202202
}"""
203203

204204
schema_registry_client = SchemaRegistryClient(fully_qualified_namespace, token_credential)
205-
avro_encoder = AvroEncoder(client=schema_registry_client, group_name=group_name, auto_register_schemas=True)
205+
avro_encoder = AvroEncoder(client=schema_registry_client, group_name=group_name, auto_register=True)
206206

207207
eventhub_producer = EventHubProducerClient.from_connection_string(
208208
conn_str=eventhub_connection_str,

sdk/schemaregistry/azure-schemaregistry-avroencoder/azure/schemaregistry/encoder/avroencoder/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@
2929

3030
from ._schema_registry_avro_encoder import AvroEncoder # pylint: disable=import-error
3131
from ._message_protocol import MessageType, MessageContent # pylint: disable=import-error
32+
from ._exceptions import InvalidContentError, InvalidSchemaError # pylint: disable=import-error
3233

3334

3435
__all__ = [
3536
"AvroEncoder",
3637
"MessageType",
37-
"MessageContent"
38+
"MessageContent",
39+
"InvalidContentError",
40+
"InvalidSchemaError"
3841
]

sdk/schemaregistry/azure-schemaregistry-avroencoder/azure/schemaregistry/encoder/avroencoder/_abstract_avro_encoder.py

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

6-
from typing import BinaryIO, TypeVar, Union, Optional
6+
from typing import BinaryIO, TypeVar, Union, Any
77
from abc import abstractmethod
88

99
ObjectType = TypeVar("ObjectType")
@@ -51,9 +51,7 @@ def encode(
5151
def decode(
5252
self,
5353
content: Union[bytes, BinaryIO],
54-
schema: str,
55-
*,
56-
readers_schema: Optional[str]
54+
reader: Any
5755
):
5856
"""Read the binary representation into a specific type.
5957
Return type will be ignored, since the schema is deduced from the provided bytes.

sdk/schemaregistry/azure-schemaregistry-avroencoder/azure/schemaregistry/encoder/avroencoder/_apache_avro_encoder.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
# --------------------------------------------------------------------------------------------
55

66
from functools import lru_cache
7-
from typing import BinaryIO, Union, TypeVar
7+
from typing import BinaryIO, Union, TypeVar, cast
88
from io import BytesIO
9-
import avro
10-
from avro.io import DatumWriter, DatumReader, BinaryDecoder, BinaryEncoder
9+
import avro # type: ignore
10+
from avro.io import DatumWriter, DatumReader, BinaryDecoder, BinaryEncoder # type: ignore
1111

1212
from ._abstract_avro_encoder import AbstractAvroObjectEncoder # pylint: disable=import-error
1313

@@ -36,7 +36,7 @@ def get_schema_writer(self, schema): # pylint: disable=no-self-use
3636
return DatumWriter(schema)
3737

3838
@lru_cache(maxsize=128)
39-
def get_schema_reader(self, schema, readers_schema): # pylint: disable=no-self-use
39+
def get_schema_reader(self, schema, readers_schema=None): # pylint: disable=no-self-use
4040
schema = self.parse_schema(schema)
4141
if readers_schema:
4242
readers_schema = self.parse_schema(readers_schema)
@@ -73,9 +73,7 @@ def encode(
7373
def decode(
7474
self,
7575
content, # type: Union[bytes, BinaryIO]
76-
schema, # type: Union[str, bytes, avro.schema.Schema]
77-
*,
78-
readers_schema=None, # type: Optional[Union[str, bytes, avro.schema.Schema]]
76+
reader, # type: DatumReader
7977
) -> ObjectType:
8078
"""Read the binary representation into a specific type.
8179
Return type will be ignored, since the schema is deduced from the provided bytes.
@@ -89,11 +87,10 @@ def decode(
8987
:rtype: ObjectType
9088
"""
9189
if not hasattr(content, 'read'):
90+
content = cast(bytes, content)
9291
content = BytesIO(content)
9392

94-
reader = self.get_schema_reader(schema, readers_schema)
95-
96-
with content:
93+
with content: # type: ignore
9794
bin_decoder = BinaryDecoder(content)
9895
decoded_content = reader.read(bin_decoder)
9996

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# --------------------------------------------------------------------------
2+
#
3+
# Copyright (c) Microsoft Corporation. All rights reserved.
4+
#
5+
# The MIT License (MIT)
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the ""Software""), to
9+
# deal in the Software without restriction, including without limitation the
10+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11+
# sell copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in
15+
# all copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23+
# IN THE SOFTWARE.
24+
#
25+
# --------------------------------------------------------------------------
26+
27+
class InvalidSchemaError(ValueError):
28+
"""Error during schema validation.
29+
:param str message: The message object stringified as 'message' attribute
30+
:keyword error: The original exception, if any.
31+
32+
:ivar str message: A stringified version of the message parameter
33+
:ivar dict details: The error details related to the schema. Depending on the error,
34+
this may include information like: `schema_id`, `schema_definition`, `message_content`.
35+
"""
36+
def __init__(self, message, *args, **kwargs):
37+
self.message = str(message)
38+
self.details = kwargs.pop("details", {})
39+
super(InvalidSchemaError, self).__init__(self.message, *args)
40+
41+
class InvalidContentError(ValueError):
42+
"""Error during encoding or decoding content with a schema.
43+
:param str message: The message object stringified as 'message' attribute
44+
:keyword error: The original exception, if any.
45+
46+
:ivar str message: A stringified version of the message parameter
47+
:ivar dict details: The error details. Depending on the error, this may include information like:
48+
`schema_id`, `schema_definition`, `message_content`.
49+
"""
50+
def __init__(self, message, *args, **kwargs):
51+
self.message = str(message)
52+
self.details = kwargs.pop("details", {})
53+
super(InvalidContentError, self).__init__(self.message, *args)

0 commit comments

Comments
 (0)