Repository navigation
canonicalize aliased property names instead of mirroring them #401
Description
Bug Description
After #398 (released in 1.5.3), a label value shared by all TimeFrame annotations in a view is lifted into the view's contains metadata at serialization; that part is intended. However, when such a file is read back, the lifted value is no longer reachable via the deprecated alias; tf.get_property('label') returns the value, but tf.get_property('frameType') returns None.
The root cause is how the SDK has handled property aliases since ed83689 (MMIF 1.0.2); Annotation._add_prop_aliases keeps the key as spelled in the input and mirrors the value into _props_ephemeral under every other spelling. Namely, one logical property lives in N slots, and every code path that writes a property has to keep all N in sync. The contains distribution step in Mmif._deserialize (and View.new_annotation) never did. The fix in #398 for the original #396 report (guarding _props_ephemeral for the primary key) was a patch on top of the same design, and when I tried the naive fix for the above (mirroring the distributed default too), the symmetric case broke instead; an annotation's own label lost to a view-level label default when queried as frameType.
Other symptoms of the same design (I think these are all one bug):
factor_out_shared_propertiescompares literal keys; two TFs withlabel=xplus one withframeType=xare not factored at all (nor is a 3-vs-3 split).containsmetadata itself can carry a deprecated key (new_contain(TimeFrame, frameType=...)is in the test suite), since nothing normalizes it.View.get_annotations(frameType=...)doesn't match an annotation that spells itlabel, and vice versa._get_labelhardcodesframeType/boxTypeinside the SDK, while the alias table has lived inclams-vocabulary(_property_aliaseson each type) for a while now.
Reproduction steps
m = Mmif(validate=False); v = m.new_view(); v.metadata.app = 'http://x/1'
for s in (0, 5):
v.new_annotation(AnnotationTypes.TimeFrame, start=s, end=s + 5, label='bars')
tf = next(Mmif(m.serialize()).views[0].get_annotations())
tf.get('label') # 'bars'
tf.get('frameType') # NoneExpected behavior
Both spellings resolve to the same value in all of the situations above; serialized properties and contains carry canonical names only.
So, here's a plan for the fix by canonicalizing on serialize resolving on de-serialize
- One resolver,
canonical_prop_name(at_type, name), reading the alias table from the latest version of the type inclams-vocabulary(AnnotationTypes.<shortname>._property_aliases); no alias data in mmif-python. properties(and ephemeral props) hold canonical keys only;AnnotationPropertiesrewrites deprecated spellings on deserialization (canonical wins on a collision, with a warning) and on assignment (with a deprecation warning), and resolves them on lookup, so'frameType' in ann.propertiesandann.properties['frameType']keep working against the singlelabelslot;Containcanonicalizes its keys the same way. NOTE THAT this changes what comes out of the SDK; serialized output rewrites deprecated spellings to canonical ones (frameType/boxType-->label,word-->text,classifications-->classification), also insidecontains, and key enumeration (ann.properties.keys(),items(), iteration) yields canonical names only, so code that scans keys forframeTypehas to look forlabel(membership tests and item access by the old spelling are unaffected). Annotations added viaView.add_annotationnow also seecontainsdefaults immediately (previously only after a round trip).Annotation.__getitem__/get_propertyandget_annotationsfilters resolve the requested name first._add_prop_aliasesand_get_labelgo away.
Log output
Screenshots
No response
Additional context
Also see #398
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity