Repository navigation
handling of annotation props in contains metadata #396
Copy link
Copy link
Closed
Labels
🐛BSomething isn't workingSomething isn't working
Description
Bug Description
The MMIF spec is explicit that "the annotation-level value takes precedence over the view-level default", and the serialization package honors this when the canonical property name is used — the annotation's value lives in .properties, which Annotation.getitem checks before any distributed contains default.
However, when reading an older MMIF that carries the value under a deprecated property name (which the implementation aliases to the canonical name via _prop_aliases and _props_ephemeral for temporary storage for canonicalization),
mmif-python/mmif/serialize/mmif.py
Lines 254 to 276 in 12d5c6a
| view._parent_mmif = self | |
| # this dict will be populated with properties | |
| # that are not encoded in individual annotations objects themselves | |
| extrinsic_props = defaultdict(dict) | |
| for at_type, type_lv_props in view.metadata.contains.items(): | |
| for prop_key, prop_value in type_lv_props.items(): | |
| extrinsic_props[at_type][prop_key] = prop_value | |
| for ann in view.get_annotations(): | |
| ## for "capital" Annotation properties | |
| # first add all extrinsic properties to the Annotation objects | |
| # as "ephemeral" properties | |
| for prop_key, prop_value in extrinsic_props[ann.at_type].items(): | |
| ann._props_ephemeral[prop_key] = prop_value | |
| # then, do the same to associated Document objects. Note that, | |
| # in a view, it is guaranteed that all Annotation objects are not duplicates | |
| if ann.at_type == AnnotationTypes.Annotation: | |
| doc_id = ann.get_property('document') | |
| try: | |
| for prop_key, prop_value in ann.properties.items(): | |
| doc = cast(Document, self.__getitem__(doc_id)) | |
| if not isinstance(doc, Document): | |
| raise KeyError | |
| doc._props_ephemeral[prop_key] = prop_value |
the common-prop distribution step writes the "view-level default" value into that same temporary dict and hinders the canonicalization step, resulting in loss of the aliased (annotation-level) prop value. Namely the overwrite priority works in reverse.
Reproduction steps
For example,
{
"metadata": { "mmif": "http://mmif.clams.ai/1.2.0" },
"documents": [
{ "@type": "http://mmif.clams.ai/vocabulary/VideoDocument/v1",
"properties": { "id": "m1", "mime": "video/mp4", "location": "file:///v.mp4" } }
],
"views": [
{ "id": "v1",
"metadata": {
"app": "http://example.com/app/1.0",
"contains": {
"http://mmif.clams.ai/vocabulary/TimeFrame/v6": { "document": "m1", "label": "credits" }
}
},
"annotations": [
{ "@type": "http://mmif.clams.ai/vocabulary/TimeFrame/v6",
"properties": { "id": "v1:tf1", "start": 0, "end": 100, "frameType": "bars" } }
]
}
]
}After reading this MMIF,
m = Mmif(in_str)
print(m.get('v1:tf1').get('label'))
# "credits", not "bars"
### Expected behavior
_No response_
### Log output
```shellScreenshots
No response
Additional context
this was found while working on #393
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
🐛BSomething isn't workingSomething isn't working
Type
Projects
- StatusShow more project fieldsDone
Activity
keighrim commentedon Jul 7, 2026
I found another related latent issue while working on this.
A while back I implemented automatic "factor-out" machinery following that recommendation in the spec. The logic is simple: if all annotations of a type in a view share the same (k, v) properties, the SDK grabs it and lifts it up to the
containsmetadata at serialization time. However, I found my old implementation is flawed in that it only kicks in for the auto-generated "capital" Annotation type, it never generalized to arbitrary types or arbitrary shared properties, so any type else thanAnnotationwould slips past the factor-out entirely and gets serialized with redundant properties in each individual object.keighrim commentedon Jul 8, 2026
closed via #398