Skip to content

Commit

Permalink
apply hotfix for schema info collection combiner handling
Browse files Browse the repository at this point in the history
  • Loading branch information
braingram committed Nov 27, 2024
1 parent d676e11 commit ee28670
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 27 deletions.
97 changes: 82 additions & 15 deletions asdf/_node_info.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import re
from collections import namedtuple

from .exceptions import AsdfSchemaResolutionError
from .schema import load_schema
from .treeutil import get_children

Expand All @@ -20,6 +21,82 @@ def _filter_tree(info, filters):
return len(info.children) > 0 or all(f(info.node, info.identifier) for f in filters)


def _get_matching_schema_property(schema, key):
if "properties" in schema:
props = schema["properties"]
if key in props:
return props[key]
if "patternProperties" in props:
patterns = props["patternProperties"]
for regex in patterns:
if re.search(regex, key):
return patterns[regex]
return None


def _get_subschema_for_property(schema, key):
# This does NOT handle $ref the expectation is that the schema
# is loaded with resolve_references=True
applicable = []

# first check properties and patternProperties
subschema = _get_matching_schema_property(schema, key)
if subschema is not None:
applicable.append(subschema)

# next handle schema combiners
if "not" in schema:
# since we're only concerned here with if the schema applies
# it doesn't matter if the schema is nested in a not
subschema = _get_subschema_for_property(schema["not"], key)
if subschema is not None:
applicable.append(subschema)

for combiner in ("allOf", "oneOf", "anyOf"):
for combined_schema in schema.get(combiner, []):
subschema = _get_subschema_for_property(combined_schema, key)
if subschema is not None:
applicable.append(subschema)

if not applicable:
return None

if len(applicable) > 1:
msg = (
f"schema info could not be determined for {key} since "
f"{len(applicable)} possibly applicable schemas were found."
)
raise AsdfSchemaResolutionError(msg)

return applicable[0]


def _get_schema_key(schema, key):
applicable = []
if key in schema:
applicable.append(schema[key])
if "not" in schema:
possible = _get_schema_key(schema["not"], key)
if possible is not None:
applicable.append(possible)
for combiner in ("allOf", "oneOf", "anyOf"):
for combined_schema in schema.get(combiner, []):
possible = _get_schema_key(combined_schema, key)
if possible is not None:
applicable.append(possible)
if not applicable:
return None

if len(applicable) > 1:
msg = (
f"schema info could not be determined for {key} since "
f"{len(applicable)} possibly applicable schemas were found."
)
raise AsdfSchemaResolutionError(msg)

return applicable[0]


def create_tree(key, node, identifier="root", filters=None, refresh_extension_manager=False):
"""
Create a `NodeSchemaInfo` tree which can be filtered from a base node.
Expand Down Expand Up @@ -214,22 +291,12 @@ def parent_node(self):

@property
def info(self):
if self.schema is not None:
return self.schema.get(self.key, None)

return None
if self.schema is None:
return None
return _get_schema_key(self.schema, self.key)

def get_schema_for_property(self, identifier):
subschema = self.schema.get("properties", {}).get(identifier, None)
if subschema is not None:
return subschema

subschema = self.schema.get("properties", {}).get("patternProperties", None)
if subschema:
for key in subschema:
if re.search(key, identifier):
return subschema[key]
return {}
return _get_subschema_for_property(self.schema, identifier) or {}

def set_schema_for_property(self, parent, identifier):
"""Extract a subschema from the parent for the identified property"""
Expand All @@ -241,7 +308,7 @@ def set_schema_from_node(self, node, extension_manager):

tag_def = extension_manager.get_tag_definition(node._tag)
schema_uri = tag_def.schema_uris[0]
schema = load_schema(schema_uri)
schema = load_schema(schema_uri, resolve_references=True)

self.schema = schema

Expand Down
37 changes: 25 additions & 12 deletions asdf/_tests/test_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ def manifest_extension(tmp_path):
description: Some silly description
type: integer
archive_catalog:
datatype: int
destination: [ScienceCommon.silly]
datatype: int
destination: [ScienceCommon.silly]
clown:
title: clown name
description: clown description
Expand Down Expand Up @@ -231,14 +231,14 @@ def manifest_extension(tmp_path):
title: Attribute1 Title
type: string
archive_catalog:
datatype: str
destination: [ScienceCommon.attribute1]
datatype: str
destination: [ScienceCommon.attribute1]
attribute2:
title: Attribute2 Title
type: string
archive_catalog:
datatype: str
destination: [ScienceCommon.attribute2]
datatype: str
destination: [ScienceCommon.attribute2]
...
"""

Expand All @@ -251,19 +251,29 @@ def manifest_extension(tmp_path):
type: object
title: object with info support 3 title
description: object description
allOf:
- $ref: drink_ref-1.0.0
...
"""
drink_ref_schema = """
%YAML 1.1
---
$schema: "asdf://stsci.edu/schemas/asdf/asdf-schema-1.1.0"
id: "asdf://somewhere.org/asdf/schemas/drink_ref-1.0.0"
properties:
attributeOne:
title: AttributeOne Title
description: AttributeOne description
type: string
archive_catalog:
datatype: str
destination: [ScienceCommon.attributeOne]
datatype: str
destination: [ScienceCommon.attributeOne]
attributeTwo:
title: AttributeTwo Title
description: AttributeTwo description
type: string
archive_catalog:
allOf:
- title: AttributeTwo Title
description: AttributeTwo description
type: string
archive_catalog:
datatype: str
destination: [ScienceCommon.attributeTwo]
...
Expand All @@ -278,6 +288,9 @@ def manifest_extension(tmp_path):
spath = tmp_path / "schemas" / "drink-1.0.0.yaml"
with open(spath, "w") as fschema:
fschema.write(drink_schema)
spath = tmp_path / "schemas" / "drink_ref-1.0.0.yaml"
with open(spath, "w") as fschema:
fschema.write(drink_ref_schema)
os.mkdir(tmp_path / "manifests")
mpath = str(tmp_path / "manifests" / "foo_manifest-1.0.yaml")
with open(mpath, "w") as fmanifest:
Expand Down
6 changes: 6 additions & 0 deletions asdf/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,9 @@ class AsdfSerializationError(RepresenterError):
that the object does not have a supporting asdf Converter and needs to
be manually converted to a supported type.
"""


class AsdfSchemaResolutionError(ValueError):
"""
An attempt to lookup schema for an attribute failed.
"""

0 comments on commit ee28670

Please sign in to comment.