ensure all named schemas have a name field
MODIFY
fastavro/_schema.pyx
fastavro/_schema.pyx
@@ -4,7 +4,9 @@ from os import path
import json
-from ._schema_common import PRIMITIVES, SCHEMA_DEFS, UnknownType
+from ._schema_common import (
+ PRIMITIVES, NAMED_TYPES, SCHEMA_DEFS, UnknownType, SchemaParseException
+)
cpdef inline extract_record_type(schema):
@@ -29,9 +31,14 @@ cpdef inline str extract_logical_type(schema):
def schema_name(schema, parent_ns):
- name = schema.get('name')
- if not name:
- return parent_ns, None
+ try:
+ name = schema['name']
+ except KeyError:
+ msg = (
+ '"name" is a required field missing from ' +
+ 'the schema: {}'.format(schema)
+ )
+ raise SchemaParseException(msg)
namespace = schema.get('namespace', parent_ns)
if not namespace:
@@ -64,8 +71,7 @@ def extract_named_schemas_into_repo(schema, repo, transformer, parent_ns=None):
if schema not in repo:
raise UnknownType(schema)
return schema
-
- namespace, name = schema_name(schema, parent_ns)
+ return
schema_type = schema.get('type')
if schema_type == 'array':
@@ -73,7 +79,7 @@ def extract_named_schemas_into_repo(schema, repo, transformer, parent_ns=None):
schema['items'],
repo,
transformer,
- namespace,
+ parent_ns,
)
if namespaced_name:
schema['items'] = namespaced_name
@@ -83,21 +89,17 @@ def extract_named_schemas_into_repo(schema, repo, transformer, parent_ns=None):
schema['values'],
repo,
transformer,
- namespace,
+ parent_ns,
)
if namespaced_name:
schema['values'] = namespaced_name
return
else:
# dict-y type schema
- if name:
+ if schema_type in NAMED_TYPES:
+ namespace, name = schema_name(schema, parent_ns)
+
repo[name] = transformer(schema)
- elif schema_type == 'record':
- msg = (
- '"name" is a required field missing from ' +
- 'the schema: {}'.format(schema)
- )
- raise Exception(msg)
for field in schema.get('fields', []):
namespaced_name = extract_named_schemas_into_repo(

MODIFY
fastavro/_schema_common.py
fastavro/_schema_common.py
@@ -9,6 +9,13 @@ PRIMITIVES = {
'string',
}
+NAMED_TYPES = {
+ 'fixed',
+ 'enum',
+ 'record',
+ 'error',
+}
+
SCHEMA_DEFS = {
'boolean': 'boolean',
'bytes': 'bytes',
@@ -25,3 +32,7 @@ class UnknownType(ValueError):
def __init__(self, name):
super(UnknownType, self).__init__(name)
self.name = name
+
+
+class SchemaParseException(Exception):
+ pass

MODIFY
fastavro/_schema_py.py
fastavro/_schema_py.py
@@ -4,7 +4,9 @@ from os import path
import json
-from ._schema_common import PRIMITIVES, SCHEMA_DEFS, UnknownType
+from ._schema_common import (
+ PRIMITIVES, NAMED_TYPES, SCHEMA_DEFS, UnknownType, SchemaParseException
+)
def extract_record_type(schema):
@@ -30,9 +32,14 @@ def extract_logical_type(schema):
def schema_name(schema, parent_ns):
- name = schema.get('name')
- if not name:
- return parent_ns, None
+ try:
+ name = schema['name']
+ except KeyError:
+ msg = (
+ '"name" is a required field missing from ' +
+ 'the schema: {}'.format(schema)
+ )
+ raise SchemaParseException(msg)
namespace = schema.get('namespace', parent_ns)
if not namespace:
@@ -66,15 +73,13 @@ def extract_named_schemas_into_repo(schema, repo, transformer, parent_ns=None):
raise UnknownType(schema)
return schema
- namespace, name = schema_name(schema, parent_ns)
-
schema_type = schema.get('type')
if schema_type == 'array':
namespaced_name = extract_named_schemas_into_repo(
schema['items'],
repo,
transformer,
- namespace,
+ parent_ns,
)
if namespaced_name:
schema['items'] = namespaced_name
@@ -84,21 +89,17 @@ def extract_named_schemas_into_repo(schema, repo, transformer, parent_ns=None):
schema['values'],
repo,
transformer,
- namespace,
+ parent_ns,
)
if namespaced_name:
schema['values'] = namespaced_name
return
else:
# dict-y type schema
- if name:
+ if schema_type in NAMED_TYPES:
+ namespace, name = schema_name(schema, parent_ns)
+
repo[name] = transformer(schema)
- elif schema_type == 'record':
- msg = (
- '"name" is a required field missing from ' +
- 'the schema: {}'.format(schema)
- )
- raise Exception(msg)
for field in schema.get('fields', []):
namespaced_name = extract_named_schemas_into_repo(

MODIFY
fastavro/schema.py
fastavro/schema.py
@@ -3,7 +3,7 @@ try:
except ImportError:
from . import _schema_py as _schema
-from ._schema_common import UnknownType
+from ._schema_common import UnknownType, SchemaParseException
_schema_name = _schema.schema_name
load_schema = _schema.load_schema
@@ -15,4 +15,5 @@ extract_named_schemas_into_repo = _schema.extract_named_schemas_into_repo
__all__ = [
'UnknownType', 'load_schema', 'extract_record_type',
'extract_logical_type', 'extract_named_schemas_into_repo',
+ 'SchemaParseException',
]

MODIFY
tests/test_complex.py
tests/test_complex.py
@@ -29,6 +29,7 @@ schema = {
"name": "array_fixed_decimal",
"type": ["null", {"type": "array",
"items": {"type": "fixed",
+ "name": "FixedDecimal",
"size": 8,
"logicalType": "decimal",
"precision": 18,

MODIFY
tests/test_fastavro.py
tests/test_fastavro.py
@@ -204,6 +204,7 @@ def test_acquaint_schema_accepts_nested_records_from_arrays():
{
"type": {
"items": {
+ "type": "record",
"fields": [{"type": "string", "name": "text"}],
"name": "Nested"
},

ADD
tests/test_schema.py
+import pytest
+import fastavro
+from fastavro.schema import SchemaParseException
+
+pytestmark = pytest.mark.usefixtures("clean_readers_writers_and_schemas")
+
+
+def test_named_types_have_names():
+ record_schema = {
+ "type": "record",
+ "fields": [{
+ "name": "field",
+ "type": "string",
+ }],
+ }
+
+ with pytest.raises(SchemaParseException):
+ fastavro.acquaint_schema(record_schema)
+
+ error_schema = {
+ "type": "error",
+ "fields": [{
+ "name": "field",
+ "type": "string",
+ }],
+ }
+
+ with pytest.raises(SchemaParseException):
+ fastavro.acquaint_schema(error_schema)
+
+ fixed_schema = {
+ "type": "fixed",
+ "size": 1,
+ }
+
+ with pytest.raises(SchemaParseException):
+ fastavro.acquaint_schema(fixed_schema)
+
+ enum_schema = {
+ "type": "enum",
+ "symbols": ["FOO"],
+ }
+
+ with pytest.raises(SchemaParseException):
+ fastavro.acquaint_schema(enum_schema)
+
+ # Should parse with name
+ for schema in (record_schema, error_schema, fixed_schema, enum_schema):
+ schema["name"] = "test_named_types_have_names"
+ fastavro.acquaint_schema(schema)

ensure all named schemas have a name field