Skip to content

Commit 5891621

Browse files
authored
Merge pull request #340 from clamsproject/develop
releasing 1.2.0
2 parents 56162a8 + 0415fff commit 5891621

File tree

17 files changed

+1195
-216
lines changed

17 files changed

+1195
-216
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,5 @@ mmif/vocabulary
8282
documentation/_build/
8383

8484
/VERSION
85+
_issues
86+

mmif/serialize/annotation.py

Lines changed: 100 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ def _deserialize(self, input_dict: dict) -> None:
7474

7575
def _cache_alignment(self, alignment_ann: 'Annotation', alignedto_ann: 'Annotation') -> None:
7676
"""
77-
Cache alignment information. This cache will not be serialized.
78-
77+
Cache alignment information. This cache will not be serialized.
78+
7979
:param alignment_ann: the Alignment annotation that has this annotation on one side
8080
:param alignedto_ann: the annotation that this annotation is aligned to (other side of Alignment)
81+
:return: None
8182
"""
8283
self._alignments[alignment_ann] = alignedto_ann
8384

@@ -228,7 +229,7 @@ def add_property(self, name: str,
228229
value: Union[PRMTV_TYPES, LIST_PRMTV, LIST_LIST_PRMTV, DICT_PRMTV, DICT_LIST_PRMTV]) -> None:
229230
"""
230231
Adds a property to the annotation's properties.
231-
232+
232233
:param name: the name of the property
233234
:param value: the property's desired value
234235
:return: None
@@ -256,18 +257,41 @@ def __getitem__(self, prop_name: str):
256257

257258
def get(self, prop_name: str, default=None):
258259
"""
259-
A getter for Annotation, will search for a property by its name,
260-
and return the value if found, or the default value if not found.
261-
This is designed to allow for directly accessing properties without
262-
having to go through the properties object, or view-level
263-
annotation metadata (common properties) encoded in the
264-
``view.metadata.contains`` dict. Note that the regular properties
265-
will take the priority over the view-level common properties when
266-
there are name conflicts.
267-
268-
:param prop_name: the name of the property to get
269-
:param default: the value to return if the property is not found
270-
:return: the value of the property
260+
Safe property access with optional default value.
261+
262+
Searches for an annotation property by name and returns its value,
263+
or a default value if not found. This method searches in multiple
264+
locations with the following priority:
265+
266+
1. Direct properties (in ``annotation.properties``)
267+
2. Ephemeral properties (view-level metadata from ``contains``)
268+
3. Special fields (``@type``, ``properties``)
269+
270+
This allows convenient access to properties without explicitly
271+
checking the ``properties`` object or view-level metadata.
272+
273+
:param prop_name: The name of the property to retrieve
274+
:param default: The value to return if the property is not found (default: None)
275+
:return: The property value, or the default value if not found
276+
277+
Examples
278+
--------
279+
.. code-block:: python
280+
281+
# Access annotation properties:
282+
label = annotation.get('label', default='unknown')
283+
start_time = annotation.get('start', default=0)
284+
285+
# Access @type:
286+
at_type = annotation.get('@type')
287+
288+
# Safe access with custom default:
289+
targets = annotation.get('targets', default=[])
290+
291+
See Also
292+
--------
293+
__getitem__ : Direct property access that raises KeyError when not found
294+
get_property : Alias for this method
271295
"""
272296
try:
273297
return self.__getitem__(prop_name)
@@ -336,29 +360,32 @@ def add_property(self, name: str,
336360
) -> None:
337361
"""
338362
Adds a property to the document's properties.
339-
340-
Unlike the parent :class:`Annotation` class, added properties of a
341-
``Document`` object can be lost during serialization unless it belongs
342-
to somewhere in a ``Mmif`` object. This is because we want to keep
343-
``Document`` object as "read-only" as possible. Thus, if you want to add
344-
a property to a ``Document`` object,
345-
346-
* add the document to a ``Mmif`` object (either in the documents list or
363+
364+
Unlike the parent :class:`Annotation` class, added properties of a
365+
``Document`` object can be lost during serialization unless it belongs
366+
to somewhere in a ``Mmif`` object. This is because we want to keep
367+
``Document`` object as "read-only" as possible. Thus, if you want to add
368+
a property to a ``Document`` object,
369+
370+
* add the document to a ``Mmif`` object (either in the documents list or
347371
in a view from the views list), or
348372
* directly write to ``Document.properties`` instead of using this method
349-
(which is not recommended).
350-
351-
With the former method, the SDK will record the added property as a
352-
`Annotation` annotation object, separate from the original `Document`
373+
(which is not recommended).
374+
375+
With the former method, the SDK will record the added property as a
376+
`Annotation` annotation object, separate from the original `Document`
353377
object. See :meth:`.Mmif.generate_capital_annotations()` for more.
354-
378+
355379
A few notes to keep in mind:
356-
357-
#. You can't overwrite an existing property of a ``Document`` object.
358-
#. A MMIF can have multiple ``Annotation`` objects with the same
380+
381+
#. You can't overwrite an existing property of a ``Document`` object.
382+
#. A MMIF can have multiple ``Annotation`` objects with the same
359383
property name but different values. When this happens, the SDK will
360-
only keep the latest value (in order of appearances in views list) of
384+
only keep the latest value (in order of appearances in views list) of
361385
the property, effectively overwriting the previous values.
386+
387+
:param name: the name of the property
388+
:param value: the property's desired value (note: Document accepts fewer value types than Annotation)
362389
"""
363390
# we don't checking if this k-v already exists in _original (new props) or _ephemeral (read from existing MMIF)
364391
# because it is impossible to keep the _original updated when a new annotation is added (via `new_annotation`)
@@ -378,13 +405,44 @@ def add_property(self, name: str,
378405

379406
def get(self, prop_name, default=None):
380407
"""
381-
A special getter for Document properties. The major difference from
382-
the super class's :py:meth:`Annotation.get` method is that Document
383-
class has one more set of *"pending"* properties, that are added after
384-
the Document object is created and will be serialized as a separate
385-
:py:class:`Annotation` object of which ``@type = Annotation``. The
386-
pending properties will take the priority over the regular properties
387-
when there are conflicts.
408+
Safe property access with optional default value for Document objects.
409+
410+
Searches for a document property by name and returns its value, or a
411+
default value if not found. Documents have a more complex property
412+
hierarchy than regular annotations:
413+
414+
Priority order (highest to lowest):
415+
1. Special fields ('id', 'location')
416+
2. Pending properties (added after creation, to be serialized as ``Annotation`` objects)
417+
3. Ephemeral properties (from existing ``Annotation`` annotations or view metadata)
418+
4. Original properties (in ``document.properties``)
419+
420+
This allows convenient access to all document properties regardless of
421+
where they're stored internally.
422+
423+
:param prop_name: The name of the property to retrieve
424+
:param default: The value to return if the property is not found (default: None)
425+
:return: The property value, or the default value if not found
426+
427+
Examples
428+
--------
429+
.. code-block:: python
430+
431+
# Access document properties:
432+
mime = document.get('mime', default='application/octet-stream')
433+
location = document.get('location')
434+
435+
# Access properties added after creation (pending):
436+
author = document.get('author', default='anonymous')
437+
publisher = document.get('publisher')
438+
439+
# Access ephemeral properties from Annotation objects:
440+
sentiment = document.get('sentiment', default='neutral')
441+
442+
See Also
443+
--------
444+
add_property : Add a new property to the document
445+
Mmif.generate_capital_annotations : How pending properties are serialized
388446
"""
389447
if prop_name == 'id':
390448
# because all three dicts have `id` key as required field, we need
@@ -399,7 +457,7 @@ class has one more set of *"pending"* properties, that are added after
399457
elif prop_name in self._props_ephemeral:
400458
return self._props_ephemeral[prop_name]
401459
else:
402-
return super().get(prop_name)
460+
return super().get(prop_name, default)
403461

404462
get_property = get
405463

@@ -559,8 +617,8 @@ def _deserialize(self, input_dict: dict) -> None:
559617
self.location = input_dict.pop("location")
560618
super()._deserialize(input_dict)
561619

562-
def _serialize(self, alt_container: Optional[Dict] = None) -> dict:
563-
serialized = super()._serialize()
620+
def _serialize(self, *args, **kwargs) -> dict:
621+
serialized = super()._serialize(**kwargs)
564622
if "location_" in serialized:
565623
serialized["location"] = serialized.pop("location_")
566624
return serialized

0 commit comments

Comments
 (0)