Add initial idea of Annotator interfaces

This would allow Annotators to depend on Annotations without knowing
how they were created.

However, keeping track of these generic Annotations (GAnns) is hard:
	- When another Annotator runs and returns a GAnn, do we replace it?
	  Alternatively, should multiple Annotators be allowed to return the
	  same GAnn?
	- If an Annotator previously returned a GAnn but not when being
	  re-run, do we drop the GAnn?
	- Should we keep track of which Annotator created which GAnn? If so,
	  how and what granularity?
ann_interfaces
LEdoian 1 year ago
parent 923b523e84
commit ab132d4d86

@ -24,7 +24,7 @@ approach."""
from ..topo_v3 import TopologyV3, VertexID, Edge, VertexFinder from ..topo_v3 import TopologyV3, VertexID, Edge, VertexFinder
from collections import defaultdict from collections import defaultdict
from collections.abc import Hashable from collections.abc import Hashable, Sequence
from dataclasses import dataclass, field from dataclasses import dataclass, field
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Any from typing import Any
@ -58,6 +58,7 @@ class AnnotatedTopology:
if ann_id in self.annotations: if ann_id in self.annotations:
if ann_id.annotator.idempotent: return None # Shortcut :-) if ann_id.annotator.idempotent: return None # Shortcut :-)
# Scrap old data before re-running # Scrap old data before re-running
# FIXME: What to do with interfaces?
old_annot = self.annotations[ann_id] old_annot = self.annotations[ann_id]
for v in old_annot.for_vertex: for v in old_annot.for_vertex:
self.vertex_annotators[v].remove(ann_id) 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: if annotation.annotated_topology is not None and annotation.annotated_topology != self:
raise ValueError('Annotator claims to annotate different topology!') raise ValueError('Annotator claims to annotate different topology!')
annotation.topology = self 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!') 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: for v in annotation.for_vertex:
self.vertex_annotators[v].add(ann_id) self.vertex_annotators[v].add(ann_id)
for e in annotation.for_edge: 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 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 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 ~~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 idempotent: bool = False
interfaces: Sequence[AnnotatorID] = ()
@abstractmethod @abstractmethod
def __init__(self, param: None | Hashable): ... def __init__(self, param: None | Hashable): ...
@abstractmethod @abstractmethod

Loading…
Cancel
Save