resolve namespaces when fixing schemas
MODIFY
fastavro/_schema.pyx
fastavro/_schema.pyx
@@ -52,7 +52,9 @@ cpdef schema_name(schema, parent_ns):
namespace = schema.get("namespace", parent_ns)
if not namespace:
return namespace, name
-
+ elif "." in name:
+ return "", name
+ else:
return namespace, f"{namespace}.{name}"
@@ -300,7 +302,7 @@ cdef _load_schema(schema, schema_dir, named_schemas, write_hint):
return _load_schema(schema, schema_dir, schema_copy, write_hint)
-cdef _inject_schema(outer_schema, inner_schema, is_injected=False):
+cdef _inject_schema(outer_schema, inner_schema, namespace="", is_injected=False):
# Once injected, we can stop checking to see if we need to inject since it
# should only be done once at most
if is_injected is True:
@@ -314,7 +316,7 @@ cdef _inject_schema(outer_schema, inner_schema, is_injected=False):
union.append(each_schema)
else:
return_schema, injected = _inject_schema(
- each_schema, inner_schema, is_injected
+ each_schema, inner_schema, namespace, is_injected
)
union.append(return_schema)
if injected is True:
@@ -325,7 +327,11 @@ cdef _inject_schema(outer_schema, inner_schema, is_injected=False):
elif not isinstance(outer_schema, dict):
if outer_schema in PRIMITIVES:
return outer_schema, is_injected
- elif outer_schema == inner_schema["name"]:
+
+ if "." not in outer_schema and namespace:
+ outer_schema = namespace + "." + outer_schema
+
+ if outer_schema == inner_schema["name"]:
return inner_schema, True
else:
raise Exception(
@@ -338,27 +344,28 @@ cdef _inject_schema(outer_schema, inner_schema, is_injected=False):
if schema_type == "array":
return_schema, injected = _inject_schema(
- outer_schema["items"], inner_schema, is_injected
+ outer_schema["items"], inner_schema, namespace, is_injected
)
outer_schema["items"] = return_schema
return outer_schema, injected
elif schema_type == "map":
return_schema, injected = _inject_schema(
- outer_schema["values"], inner_schema, is_injected
+ outer_schema["values"], inner_schema, namespace, is_injected
)
outer_schema["values"] = return_schema
return outer_schema, injected
elif schema_type == "record" or schema_type == "error":
# records
+ namespace, _ = schema_name(outer_schema, namespace)
fields = []
for field in outer_schema.get("fields", []):
if is_injected:
fields.append(field)
else:
return_schema, injected = _inject_schema(
- field["type"], inner_schema, is_injected
+ field["type"], inner_schema, namespace, is_injected
)
field["type"] = return_schema
fields.append(field)

MODIFY
fastavro/_schema_py.py
fastavro/_schema_py.py
@@ -78,7 +78,9 @@ def schema_name(schema, parent_ns):
namespace = schema.get("namespace", parent_ns)
if not namespace:
return namespace, name
-
+ elif "." in name:
+ return "", name
+ else:
return namespace, f"{namespace}.{name}"
@@ -422,6 +424,7 @@ def load_schema(schema_path, *, _named_schemas=None, _write_hint=True):
{
"type": "record",
"name": "Parent",
+ "namespace": "namespace",
"fields": [
{
"name": "child",
@@ -475,7 +478,7 @@ def _load_schema(schema, schema_dir, named_schemas, write_hint):
return _load_schema(schema, schema_dir, schema_copy, write_hint)
-def _inject_schema(outer_schema, inner_schema, is_injected=False):
+def _inject_schema(outer_schema, inner_schema, namespace="", is_injected=False):
# Once injected, we can stop checking to see if we need to inject since it
# should only be done once at most
if is_injected is True:
@@ -489,7 +492,7 @@ def _inject_schema(outer_schema, inner_schema, is_injected=False):
union.append(each_schema)
else:
return_schema, injected = _inject_schema(
- each_schema, inner_schema, is_injected
+ each_schema, inner_schema, namespace, is_injected
)
union.append(return_schema)
if injected is True:
@@ -500,7 +503,11 @@ def _inject_schema(outer_schema, inner_schema, is_injected=False):
elif not isinstance(outer_schema, dict):
if outer_schema in PRIMITIVES:
return outer_schema, is_injected
- elif outer_schema == inner_schema["name"]:
+
+ if "." not in outer_schema and namespace:
+ outer_schema = namespace + "." + outer_schema
+
+ if outer_schema == inner_schema["name"]:
return inner_schema, True
else:
raise Exception(
@@ -513,27 +520,28 @@ def _inject_schema(outer_schema, inner_schema, is_injected=False):
if schema_type == "array":
return_schema, injected = _inject_schema(
- outer_schema["items"], inner_schema, is_injected
+ outer_schema["items"], inner_schema, namespace, is_injected
)
outer_schema["items"] = return_schema
return outer_schema, injected
elif schema_type == "map":
return_schema, injected = _inject_schema(
- outer_schema["values"], inner_schema, is_injected
+ outer_schema["values"], inner_schema, namespace, is_injected
)
outer_schema["values"] = return_schema
return outer_schema, injected
elif schema_type == "record" or schema_type == "error":
# records
+ namespace, _ = schema_name(outer_schema, namespace)
fields = []
for field in outer_schema.get("fields", []):
if is_injected:
fields.append(field)
else:
return_schema, injected = _inject_schema(
- field["type"], inner_schema, is_injected
+ field["type"], inner_schema, namespace, is_injected
)
field["type"] = return_schema
fields.append(field)

ADD
tests/load_schema_test_7/A.avsc
+{
+ "name": "A",
+ "namespace": "namespace",
+ "type": "record",
+ "fields": [
+ {"name": "b", "type": "B"}
+ ]
+}
\ No newline at end of file

ADD
tests/load_schema_test_7/namespace.B.avsc
+{
+ "name": "namespace.B",
+ "type": "record",
+ "fields": [
+ {"name": "foo", "type": "string"}
+ ]
+}
\ No newline at end of file

MODIFY
tests/test_schema.py
tests/test_schema.py
@@ -749,3 +749,26 @@ def test_load_schema_output_is_correct_6():
],
}
assert loaded_schema == expected_schema
+
+
+def test_load_schema_resolve_namespace():
+ """https://github.com/fastavro/fastavro/issues/490"""
+ load_schema_dir = join(abspath(dirname(__file__)), "load_schema_test_7")
+ schema_path = join(load_schema_dir, "A.avsc")
+ loaded_schema = fastavro.schema.load_schema(schema_path, _write_hint=False)
+
+ expected_schema = {
+ "name": "namespace.A",
+ "type": "record",
+ "fields": [
+ {
+ "name": "b",
+ "type": {
+ "name": "namespace.B",
+ "type": "record",
+ "fields": [{"name": "foo", "type": "string"}],
+ },
+ },
+ ],
+ }
+ assert loaded_schema == expected_schema

resolve namespaces when fixing schemas