Skip to content

canonicalize aliased property names instead of mirroring them #401

Closed

Description

@keighrim

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):

  1. factor_out_shared_properties compares literal keys; two TFs with label=x plus one with frameType=x are not factored at all (nor is a 3-vs-3 split).
  2. contains metadata itself can carry a deprecated key (new_contain(TimeFrame, frameType=...) is in the test suite), since nothing normalizes it.
  3. View.get_annotations(frameType=...) doesn't match an annotation that spells it label, and vice versa.
  4. _get_label hardcodes frameType/boxType inside the SDK, while the alias table has lived in clams-vocabulary (_property_aliases on 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')  # None

Expected 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

  1. One resolver, canonical_prop_name(at_type, name), reading the alias table from the latest version of the type in clams-vocabulary (AnnotationTypes.<shortname>._property_aliases); no alias data in mmif-python.
  2. properties (and ephemeral props) hold canonical keys only; AnnotationProperties rewrites 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.properties and ann.properties['frameType'] keep working against the single label slot; Contain canonicalizes 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 inside contains, and key enumeration (ann.properties.keys(), items(), iteration) yields canonical names only, so code that scans keys for frameType has to look for label (membership tests and item access by the old spelling are unaffected). Annotations added via View.add_annotation now also see contains defaults immediately (previously only after a round trip).
  3. Annotation.__getitem__/get_property and get_annotations filters resolve the requested name first.
  4. _add_prop_aliases and _get_label go away.

Log output

Screenshots

No response

Additional context

Also see #398

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛BSomething isn't working

    Type

    No type

    Projects

    • Status
      Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions