You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by ko...@apache.org on 2021/07/13 01:56:39 UTC
[avro] branch master updated: AVRO-2720: Enhance AvroTypeException
message to include field name (#1287)
This is an automated email from the ASF dual-hosted git repository.
kojiromike pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/master by this push:
new f468930 AVRO-2720: Enhance AvroTypeException message to include field name (#1287)
f468930 is described below
commit f468930a1e119517491ed4e64b1bbd2906446b49
Author: Subhash Bhushan <su...@gmail.com>
AuthorDate: Mon Jul 12 18:56:31 2021 -0700
AVRO-2720: Enhance AvroTypeException message to include field name (#1287)
* AVRO-2720: Enhance AvroTypeException message to include field name
The exception message now includes the field name on which the type exception
was raised.
Closes: AVRO-2720
* Increase Pypy 3.6 minimum speed to 5 secs
* Increase benchmark timings for both READ and WRITE
* Remove unnecessary sys import
* Change the global warnings filter to catch IgnoredLogicalType: With this change, the ResourceWarning would just get messaged, and not raised as an error, but the IgnoredLogicalType would get raised and then caught, and we can test for it.
---
lang/py/avro/errors.py | 5 +++--
lang/py/avro/io.py | 2 +-
lang/py/avro/test/test_bench.py | 4 ++--
lang/py/avro/test/test_io.py | 21 +++++++++++++++++++--
lang/py/avro/test/test_schema.py | 26 ++++++++++++++------------
5 files changed, 39 insertions(+), 19 deletions(-)
diff --git a/lang/py/avro/errors.py b/lang/py/avro/errors.py
index 1723b6c..5ff3603 100644
--- a/lang/py/avro/errors.py
+++ b/lang/py/avro/errors.py
@@ -53,10 +53,11 @@ class AvroTypeException(AvroException):
def __init__(self, *args):
try:
- expected_schema, datum = args[:2]
+ expected_schema, name, datum = args[:3]
except (IndexError, ValueError):
return super().__init__(*args)
- return super().__init__(f"The datum {datum} of the type {type(datum)} is not an example of the schema {_safe_pretty(expected_schema)}")
+ pretty_expected = json.dumps(json.loads(str(expected_schema)), indent=2)
+ return super().__init__(f'The datum "{datum}" provided for "{name}" is not an example of the schema {pretty_expected}')
class InvalidDefaultException(AvroTypeException):
diff --git a/lang/py/avro/io.py b/lang/py/avro/io.py
index 161f739..d8b0f94 100644
--- a/lang/py/avro/io.py
+++ b/lang/py/avro/io.py
@@ -144,7 +144,7 @@ def validate(expected_schema: avro.schema.Schema, datum: object, raise_on_error:
if valid_node is None:
if raise_on_error:
- raise avro.errors.AvroTypeException(current_node.schema, current_node.datum)
+ raise avro.errors.AvroTypeException(current_node.schema, current_node.name, current_node.datum)
return False # preserve the prior validation behavior of returning false when there are problems.
# if there are children of this node to append, do so.
for child_node in _iterate_node(valid_node):
diff --git a/lang/py/avro/test/test_bench.py b/lang/py/avro/test/test_bench.py
index e9edd08..eec667f 100644
--- a/lang/py/avro/test/test_bench.py
+++ b/lang/py/avro/test/test_bench.py
@@ -51,8 +51,8 @@ SCHEMA: avro.schema.RecordSchema = avro.schema.parse(
READER = avro.io.DatumReader(SCHEMA)
WRITER = avro.io.DatumWriter(SCHEMA)
NUMBER_OF_TESTS = 10000
-MAX_WRITE_SECONDS = 3 if platform.python_implementation() == "PyPy" else 1
-MAX_READ_SECONDS = 3 if platform.python_implementation() == "PyPy" else 1
+MAX_WRITE_SECONDS = 5 if platform.python_implementation() == "PyPy" else 3
+MAX_READ_SECONDS = 5 if platform.python_implementation() == "PyPy" else 3
class TestBench(unittest.TestCase):
diff --git a/lang/py/avro/test/test_io.py b/lang/py/avro/test/test_io.py
index 3ef5c2b..a07d122 100644
--- a/lang/py/avro/test/test_io.py
+++ b/lang/py/avro/test/test_io.py
@@ -538,7 +538,7 @@ class TestMisc(unittest.TestCase):
datum_read = read_datum(writer, writers_schema, readers_schema)
self.assertEqual(datum_to_read, datum_read)
- def test_type_exception(self) -> None:
+ def test_type_exception_int(self) -> None:
writers_schema = avro.schema.parse(
json.dumps(
{
@@ -552,7 +552,24 @@ class TestMisc(unittest.TestCase):
)
)
datum_to_write = {"E": 5, "F": "Bad"}
- self.assertRaises(avro.errors.AvroTypeException, write_datum, datum_to_write, writers_schema)
+ with self.assertRaises(avro.errors.AvroTypeException) as exc:
+ write_datum(datum_to_write, writers_schema)
+ assert str(exc.exception) == 'The datum "Bad" provided for "F" is not an example of the schema "int"'
+
+ def test_type_exception_long(self) -> None:
+ writers_schema = avro.schema.parse(json.dumps({"type": "record", "name": "Test", "fields": [{"name": "foo", "type": "long"}]}))
+ datum_to_write = {"foo": 5.0}
+
+ with self.assertRaises(avro.errors.AvroTypeException) as exc:
+ write_datum(datum_to_write, writers_schema)
+ assert str(exc.exception) == 'The datum "5.0" provided for "foo" is not an example of the schema "long"'
+
+ def test_type_exception_record(self) -> None:
+ writers_schema = avro.schema.parse(json.dumps({"type": "record", "name": "Test", "fields": [{"name": "foo", "type": "long"}]}))
+ datum_to_write = ("foo", 5.0)
+
+ with self.assertRaisesRegex(avro.errors.AvroTypeException, r"The datum \".*\" provided for \".*\" is not an example of the schema [\s\S]*"):
+ write_datum(datum_to_write, writers_schema)
def load_tests(loader: unittest.TestLoader, default_tests: None, pattern: None) -> unittest.TestSuite:
diff --git a/lang/py/avro/test/test_schema.py b/lang/py/avro/test/test_schema.py
index 1a8e96a..4506744 100644
--- a/lang/py/avro/test/test_schema.py
+++ b/lang/py/avro/test/test_schema.py
@@ -713,19 +713,21 @@ class SchemaParseTestCase(unittest.TestCase):
# Never hide repeated warnings when running this test case.
warnings.simplefilter("always")
- def parse_valid(self):
+ def parse_valid(self) -> None:
"""Parsing a valid schema should not error, but may contain warnings."""
- with warnings.catch_warnings(record=True) as actual_warnings:
- try:
- self.test_schema.parse()
- except (avro.errors.AvroException, avro.errors.SchemaParseException): # pragma: no coverage
- self.fail(f"Valid schema failed to parse: {self.test_schema!s}")
- actual_messages = [str(wmsg.message) for wmsg in actual_warnings]
- if self.test_schema.warnings:
- expected_messages = [str(w) for w in self.test_schema.warnings]
- self.assertEqual(actual_messages, expected_messages)
- else:
- self.assertEqual(actual_messages, [])
+ test_warnings = self.test_schema.warnings or []
+ try:
+ warnings.filterwarnings(action="error", category=avro.errors.IgnoredLogicalType)
+ self.test_schema.parse()
+ except (avro.errors.IgnoredLogicalType) as e:
+ self.assertIn(type(e), (type(w) for w in test_warnings))
+ self.assertIn(str(e), (str(w) for w in test_warnings))
+ except (avro.errors.AvroException, avro.errors.SchemaParseException): # pragma: no coverage
+ self.fail(f"Valid schema failed to parse: {self.test_schema!s}")
+ else:
+ self.assertEqual([], test_warnings)
+ finally:
+ warnings.filterwarnings(action="default", category=avro.errors.IgnoredLogicalType)
def parse_invalid(self):
"""Parsing an invalid schema should error."""