diff --git a/birdvisu/annotations/__init__.py b/birdvisu/annotations/__init__.py index 667572c..68f250f 100644 --- a/birdvisu/annotations/__init__.py +++ b/birdvisu/annotations/__init__.py @@ -24,7 +24,7 @@ approach.""" from ..topo_v3 import TopologyV3, VertexID, Edge, VertexFinder from collections import defaultdict -from collections.abc import Hashable +from collections.abc import Hashable, Sequence from dataclasses import dataclass, field from abc import ABC, abstractmethod from typing import Any @@ -58,6 +58,7 @@ class AnnotatedTopology: if ann_id in self.annotations: if ann_id.annotator.idempotent: return None # Shortcut :-) # Scrap old data before re-running + # FIXME: What to do with interfaces? old_annot = self.annotations[ann_id] for v in old_annot.for_vertex: self.vertex_annotators[v].remove(ann_id) @@ -73,9 +74,10 @@ class AnnotatedTopology: if annotation.annotated_topology is not None and annotation.annotated_topology != self: raise ValueError('Annotator claims to annotate different topology!') annotation.topology = self - if annotation.annotator_id is not None and annotation.annotator_id != ann_id: + if annotation.annotator_id is not None and annotation.annotator_id != ann_id and annotation.annotator_id not in ann_id.interfaces: raise ValueError('Annotator fakes its ID!') - annotation.annotator_id = ann_id + if annotation.annotator_id is None: + annotation.annotator_id = ann_id for v in annotation.for_vertex: self.vertex_annotators[v].add(ann_id) for e in annotation.for_edge: @@ -146,8 +148,24 @@ class Annotator(ABC): for use by other programs. Using basic types like scalars, lists and dictionaries is probably safe, but using sets is not (please, think of the ~~kittens~~ JSON and use dicts with 1 as a value.). Once the Annotation is - output, it must not be modified (and reference to it should not be kept).""" + output, it must not be modified (and reference to it should not be kept). + + The Annotation may by default only be scoped under the ID of the Annotator + (i.e. Annotation.annotator is the AnnotatorID of the Annotator which + created that Annotation). While this design prevents the clashes of + different Annotations, it does not provide any way for different Annotators + to provide Annotations in the same scope, thus requiring knowledge of the + exact Annotator that was used to create the retrieved Annotation. We + elevate this issue by allowing Annotators to specify a set of other + AnnotatorIDs in the :attr:interface attribute, which are also permitted to + be output. However, there can only exist one Annotation for each interface, + so using this requires being careful. Also, an Annotator can only return + one Annotation, so you might want to use two Annotators: one for the actual + annotation, and second for transforming that Annotation into the interfaced + one. (The specific syntax of the interface is determined by the class + mentioned in the interface's AnnotatorID.)""" idempotent: bool = False + interfaces: Sequence[AnnotatorID] = () @abstractmethod def __init__(self, param: None | Hashable): ... @abstractmethod