mmif-python 0.5.0__tar.gz → 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {mmif-python-0.5.0/mmif_python.egg-info → mmif-python-0.5.2}/PKG-INFO +2 -6
  2. mmif-python-0.5.2/README.md +3 -0
  3. mmif-python-0.5.2/VERSION +1 -0
  4. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/serialize/annotation.py +3 -0
  5. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/serialize/mmif.py +28 -3
  6. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/serialize/model.py +14 -8
  7. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/serialize/view.py +18 -2
  8. mmif-python-0.5.2/mmif/ver/__init__.py +2 -0
  9. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/vocabulary/__init__.py +2 -0
  10. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/vocabulary/base_types.py +36 -17
  11. {mmif-python-0.5.0 → mmif-python-0.5.2/mmif_python.egg-info}/PKG-INFO +2 -6
  12. {mmif-python-0.5.0 → mmif-python-0.5.2}/setup.py +3 -3
  13. mmif-python-0.5.0/README.md +0 -7
  14. mmif-python-0.5.0/VERSION +0 -1
  15. mmif-python-0.5.0/mmif/ver/__init__.py +0 -2
  16. {mmif-python-0.5.0 → mmif-python-0.5.2}/LICENSE +0 -0
  17. {mmif-python-0.5.0 → mmif-python-0.5.2}/MANIFEST.in +0 -0
  18. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/__init__.py +0 -0
  19. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/res/__init__.py +0 -0
  20. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/res/clams.vocabulary.yaml +0 -0
  21. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/res/do-not-edit.txt +0 -0
  22. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/res/mmif.json +0 -0
  23. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/serialize/__init__.py +0 -0
  24. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/ver/do-not-edit.txt +0 -0
  25. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/vocabulary/annotation_types.py +0 -0
  26. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/vocabulary/do-not-edit.txt +0 -0
  27. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif/vocabulary/document_types.py +0 -0
  28. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif_python.egg-info/SOURCES.txt +0 -0
  29. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif_python.egg-info/dependency_links.txt +0 -0
  30. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif_python.egg-info/requires.txt +0 -0
  31. {mmif-python-0.5.0 → mmif-python-0.5.2}/mmif_python.egg-info/top_level.txt +0 -0
  32. {mmif-python-0.5.0 → mmif-python-0.5.2}/requirements.txt +0 -0
  33. {mmif-python-0.5.0 → mmif-python-0.5.2}/setup.cfg +0 -0
@@ -1,16 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mmif-python
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Python implementation of MultiMedia Interchange Format specification. (https://mmif.clams.ai)
5
5
  Home-page: https://mmif.clams.ai
6
6
  Author: Brandeis Lab for Linguistics and Computation
7
7
  Author-email: admin@clams.ai
8
8
  License: UNKNOWN
9
- Description: # MMIF for python
10
-
11
- **NOTE** that this project is in pre-alpha and being actively developed. Nothing is guaranteed to reliably work for the moment and developer need to be very careful when using APIs implemented here. Please use [the issue track](https://github.com/clamsproject/mmif/issues) to report bugs and malfunctions.
12
-
13
- ## MultiMedia Interchange Format
9
+ Description: ## MultiMedia Interchange Format
14
10
  [MMIF](https://mmif.clams.ai) is a JSON(-LD)-based data format designed for transferring annotation data between computational analysis applications in [CLAMS project](https://clams.ai).
15
11
 
16
12
 
@@ -0,0 +1,3 @@
1
+ ## MultiMedia Interchange Format
2
+ [MMIF](https://mmif.clams.ai) is a JSON(-LD)-based data format designed for transferring annotation data between computational analysis applications in [CLAMS project](https://clams.ai).
3
+
@@ -0,0 +1 @@
1
+ 0.5.2
@@ -113,7 +113,10 @@ class Document(Annotation):
113
113
  :param document_obj: the JSON data that defines the document
114
114
  """
115
115
  def __init__(self, doc_obj: Optional[Union[bytes, str, dict]] = None) -> None:
116
+ # to store the parent view ID
116
117
  self._parent_view_id = ''
118
+ self.reserved_names.add('_parent_view_id')
119
+
117
120
  self._type: Union[ThingTypesBase, DocumentTypesBase] = ThingTypesBase('')
118
121
  self.properties: DocumentProperties = DocumentProperties()
119
122
  self.disallow_additional_properties()
@@ -68,6 +68,31 @@ class Mmif(MmifObject):
68
68
  json_str = json.loads(json_str)
69
69
  jsonschema.validators.validate(json_str, schema)
70
70
 
71
+ def serialize(self, pretty: bool = False, sanitize: bool = False) -> str:
72
+ if sanitize:
73
+ self.sanitize()
74
+ return super().serialize(pretty)
75
+
76
+ def sanitize(self):
77
+ """
78
+ Sanitizes a Mmif object by running some safeguards.
79
+ Concretely, it performs the following before returning the JSON string.
80
+
81
+ #. validating output using built-in MMIF jsonschema
82
+ #. remove non-existing annotation types from ``contains`` metadata
83
+
84
+ """
85
+ for view in self.views:
86
+ existing_at_types = set(annotation.at_type for annotation in view.annotations)
87
+ to_pop = set()
88
+ for contains_at_type in view.metadata.contains.keys():
89
+ if contains_at_type not in existing_at_types:
90
+ to_pop.add(contains_at_type)
91
+ for key in to_pop:
92
+ view.metadata.contains.pop(key)
93
+ serialized = self.serialize()
94
+ self.validate(serialized)
95
+
71
96
  def new_view_id(self) -> str:
72
97
  """
73
98
  Fetches an ID for a new view.
@@ -241,14 +266,14 @@ class Mmif(MmifObject):
241
266
  :return: a dict that keyed by view IDs (str) and has lists of alignment Annotation objects as values.
242
267
  """
243
268
  v_and_a = {}
244
- at_type1 = ThingTypesBase.from_str(at_type1) if isinstance(at_type1, str) else at_type1
245
- at_type2 = ThingTypesBase.from_str(at_type2) if isinstance(at_type2, str) else at_type2
269
+ at_type1, at_type2 = [ThingTypesBase.from_str(x) if isinstance(x, str) else x for x in (at_type1, at_type2)]
246
270
  assert at_type1 != at_type2, f"Alignment must be between two different types, given only one: {at_type1}"
247
271
  for alignment_view in self.get_all_views_contain(AnnotationTypes.Alignment):
248
272
  alignments = []
249
273
  contains_meta = alignment_view.metadata.contains[AnnotationTypes.Alignment]
250
274
  if 'sourceType' in contains_meta and 'targetType' in contains_meta:
251
- aligned_types = list({contains_meta['sourceType'], contains_meta['targetType']})
275
+ aligned_types = [ThingTypesBase.from_str(x)
276
+ for x in {contains_meta['sourceType'], contains_meta['targetType']}]
252
277
  if len(aligned_types) == 2 and at_type1 in aligned_types and at_type2 in aligned_types:
253
278
  alignments.extend(alignment_view.annotations)
254
279
  else:
@@ -37,7 +37,7 @@ class MmifObject(object):
37
37
  an actual representation with a JSON formatted string or equivalent
38
38
  `dict` object argument.
39
39
 
40
- This superclass has three specially designed instance variables, and these
40
+ This superclass has four specially designed instance variables, and these
41
41
  variable names cannot be used as attribute names for MMIF objects.
42
42
 
43
43
  1. _unnamed_attributes:
@@ -54,12 +54,15 @@ class MmifObject(object):
54
54
  When serialize, an object will skip its *empty* (e.g. zero-length, or None)
55
55
  attributes unless they are in this list. Otherwise, the serialized JSON
56
56
  string would have empty representations (e.g. ``""``, ``[]``).
57
+ 4. _exclude_from_diff:
58
+ This is a simple list of names of attributes that should be excluded from
59
+ the diff calculation in ``__eq__``.
57
60
 
58
61
  # TODO (krim @ 8/17/20): this dict is however, a duplicate with the type hints in the class definition.
59
62
  Maybe there is a better way to utilize type hints (e.g. getting them as a programmatically), but for now
60
63
  developers should be careful to add types to hints as well as to this dict.
61
64
 
62
- Also note that those two special attributes MUST be set in the __init__()
65
+ Also note that those special attributes MUST be set in the __init__()
63
66
  before calling super method, otherwise deserialization will not work.
64
67
 
65
68
  And also, a subclass that has one or more *named* attributes, it must
@@ -74,17 +77,15 @@ class MmifObject(object):
74
77
  an ID value automatically generated, based on its parent object.
75
78
  """
76
79
 
77
- reserved_names: Set = {
80
+ reserved_names: Set[str] = {
78
81
  'reserved_names',
79
82
  '_unnamed_attributes',
80
83
  '_attribute_classes',
81
84
  '_required_attributes',
82
- # used in Document class to store parent view id
83
- '_parent_view_id',
84
- # used in View class to autogenerate annotation ids
85
- '_id_counts'
85
+ '_exclude_from_diff'
86
86
  }
87
87
  _unnamed_attributes: Optional[dict]
88
+ _exclude_from_diff: Set[str]
88
89
  _attribute_classes: Dict[str, Type] = {} # Mapping: str -> Type
89
90
 
90
91
  def __init__(self, mmif_obj: Optional[Union[bytes, str, dict]] = None) -> None:
@@ -92,6 +93,8 @@ class MmifObject(object):
92
93
  mmif_obj = mmif_obj.decode('utf8')
93
94
  if not hasattr(self, '_required_attributes'):
94
95
  self._required_attributes = []
96
+ if not hasattr(self, '_exclude_from_diff'):
97
+ self._exclude_from_diff = set()
95
98
  if not hasattr(self, '_unnamed_attributes'):
96
99
  self._unnamed_attributes = {}
97
100
  if mmif_obj is not None:
@@ -253,7 +256,10 @@ class MmifObject(object):
253
256
 
254
257
  def __eq__(self, other) -> bool:
255
258
  return isinstance(other, type(self)) and \
256
- len(DeepDiff(self, other, report_repetition=True, exclude_types=[datetime])) == 0
259
+ len(DeepDiff(self, other, report_repetition=True, exclude_types=[datetime],
260
+ # https://github.com/clamsproject/mmif-python/issues/214
261
+ exclude_paths=self._exclude_from_diff)
262
+ ) == 0
257
263
 
258
264
  def __len__(self) -> int:
259
265
  """
@@ -31,7 +31,11 @@ class View(MmifObject):
31
31
  """
32
32
 
33
33
  def __init__(self, view_obj: Optional[Union[bytes, str, dict]] = None) -> None:
34
+ # used to autogenerate annotation ids
34
35
  self._id_counts = {}
36
+ self.reserved_names.add("_id_counts")
37
+ self._exclude_from_diff = {"_id_counts"}
38
+
35
39
  self.id: str = ''
36
40
  self.metadata: ViewMetadata = ViewMetadata()
37
41
  self.annotations: AnnotationsList = AnnotationsList()
@@ -246,12 +250,21 @@ class ViewMetadata(MmifObject):
246
250
  'error': ErrorDict,
247
251
  'contains': ContainsDict
248
252
  }
249
- # in theory, either `contains` or `error` should appear in a `view`
253
+ # in theory, *oneOf* `contains`, `error`, or `warnings` should appear in a `view`
250
254
  # but with current implementation, there's no easy way to set a condition
251
255
  # for `oneOf` requirement
252
256
  # see MmifObject::_required_attributes in model.py
257
+ # also see this class' `_serialize()` override implementation
253
258
  super().__init__(viewmetadata_obj)
254
259
 
260
+ def _serialize(self, alt_container: Optional[Dict] = None) -> dict:
261
+ serialized = super()._serialize()
262
+ # `_serialize()` eliminates any *empty* attributes, so
263
+ # when no "contains", "errors", nor "warnings", at least add an empty contains back
264
+ if not (self.contains.items() or self.error or self.warnings):
265
+ serialized['contains'] = {}
266
+ return serialized
267
+
255
268
  def new_contain(self, at_type: Union[str, ThingTypesBase], **contains_metadata) -> Optional['Contain']:
256
269
  """
257
270
  Adds a new element to the ``contains`` dictionary.
@@ -364,4 +377,7 @@ class ContainsDict(DataDict[ThingTypesBase, Contain]):
364
377
  return self._items.get(key, default)
365
378
 
366
379
  def __contains__(self, item: Union[str, ThingTypesBase]):
367
- return item in list(self._items.keys())
380
+ return item in self._items
381
+
382
+ def pop(self, key):
383
+ self._items.pop(key)
@@ -0,0 +1,2 @@
1
+ __version__ = "0.5.2"
2
+ __specver__ = "0.5.0"
@@ -5,3 +5,5 @@ from .base_types import AnnotationTypesBase
5
5
  from .base_types import DocumentTypesBase
6
6
  from .annotation_types import AnnotationTypes
7
7
  from .document_types import DocumentTypes
8
+
9
+ typevers = {**ThingType.typevers, **AnnotationTypes.typevers, **DocumentTypes.typevers}
@@ -1,6 +1,8 @@
1
1
  # This file is auto-generated by setup.py
2
+ import warnings
2
3
  from typing import ClassVar
3
4
 
5
+
4
6
  class TypesBase(object):
5
7
  """
6
8
  Base class for arbitrary vocabulary type.
@@ -10,8 +12,10 @@ class TypesBase(object):
10
12
  _prefixes = {}
11
13
  base_uri: str
12
14
  shortname: str
13
-
15
+ fuzzy_eq: bool
16
+
14
17
  def __init__(self, type_uri: str):
18
+ self.fuzzy_eq = False
15
19
  self.parse_names(type_uri)
16
20
  if self.__repr__() not in self.__class__._prefixes:
17
21
  # prepare auto-inferred prefix for Annotations.[].id field
@@ -109,6 +113,10 @@ class ClamsTypesBase(TypesBase):
109
113
  version: str
110
114
  dev_version: ClassVar[str] = 'develop'
111
115
 
116
+ def __init__(self, type_uri, fuzzymode=True):
117
+ super().__init__(type_uri)
118
+ self.fuzzy_eq = fuzzymode
119
+
112
120
  def parse_names(self, type_uri:str):
113
121
  if not type_uri:
114
122
  self.base_uri, self.shortname, self.version = "", "", ""
@@ -130,27 +138,38 @@ class ClamsTypesBase(TypesBase):
130
138
 
131
139
  def __hash__(self):
132
140
  return hash(str(self))
141
+
142
+ def normalize_attype_vers(self):
143
+ if '.' in self.version and self._check_legacy_version(self.version):
144
+ # legacy mapping: see https://github.com/clamsproject/mmif/issues/14#issuecomment-1504439497
145
+ if self.version == '0.4.2' and self.shortname == 'Annotation':
146
+ return 'v2'
147
+ elif self.version.startswith('0.4.'):
148
+ return 'v1'
149
+ else:
150
+ return self.version
133
151
 
134
- def __eq__(self, other):
135
- if isinstance(other, str):
136
- other = ThingTypesBase.from_str(other)
152
+ def _eq_internal(self, other, fuzzy):
137
153
  if isinstance(other, ClamsTypesBase):
138
- vers = []
139
- for o in (self, other):
140
- if '.' in o.version and self._check_legacy_version(o.version):
141
- # legacy mapping: see https://github.com/clamsproject/mmif/issues/14#issuecomment-1504439497
142
- if o.version == '0.4.2' and o.shortname == 'Annotation':
143
- vers.append('v2')
144
- elif o.version.startswith('0.4.'):
145
- vers.append('v1')
146
- else:
147
- vers.append(o.version)
148
- return self.base_uri == other.base_uri \
149
- and self.shortname == other.shortname \
150
- and vers[0] == vers[1]
154
+ if fuzzy:
155
+ vers = [x.normalize_attype_vers() for x in (self, other)]
156
+ if self.base_uri == other.base_uri and self.shortname == other.shortname:
157
+ if vers[0] != vers[1]:
158
+ warnings.warn(f'version difference detected: {self.version}&{other.version}@{self.shortname}')
159
+ return True
160
+ return False
161
+ else:
162
+ return self.base_uri == other.base_uri \
163
+ and self.shortname == other.shortname \
164
+ and self.version == other.version
151
165
  else:
152
166
  return False
153
167
 
168
+ def __eq__(self, other):
169
+ if isinstance(other, str):
170
+ other = ThingTypesBase.from_str(other)
171
+ return self._eq_internal(other, self.fuzzy_eq and other.fuzzy_eq)
172
+
154
173
  def __repr__(self):
155
174
  if self.version[0] == 'v':
156
175
  return f'{self.base_uri}/vocabulary/{self.shortname}/{self.version}'
@@ -1,16 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mmif-python
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Python implementation of MultiMedia Interchange Format specification. (https://mmif.clams.ai)
5
5
  Home-page: https://mmif.clams.ai
6
6
  Author: Brandeis Lab for Linguistics and Computation
7
7
  Author-email: admin@clams.ai
8
8
  License: UNKNOWN
9
- Description: # MMIF for python
10
-
11
- **NOTE** that this project is in pre-alpha and being actively developed. Nothing is guaranteed to reliably work for the moment and developer need to be very careful when using APIs implemented here. Please use [the issue track](https://github.com/clamsproject/mmif/issues) to report bugs and malfunctions.
12
-
13
- ## MultiMedia Interchange Format
9
+ Description: ## MultiMedia Interchange Format
14
10
  [MMIF](https://mmif.clams.ai) is a JSON(-LD)-based data format designed for transferring annotation data between computational analysis applications in [CLAMS project](https://clams.ai).
15
11
 
16
12
 
@@ -134,7 +134,7 @@ def generate_vocabulary(spec_version, clams_types_vers):
134
134
  f"from .{mod_name} import {class_name}"
135
135
  for mod_name, classes in types.items()
136
136
  for class_name in classes
137
- )+'\n'
137
+ ) + '\n\n' + "typevers = {**ThingType.typevers, **AnnotationTypes.typevers, **DocumentTypes.typevers}" + '\n'
138
138
  )
139
139
  document_types = []
140
140
  annotation_types = []
@@ -161,7 +161,7 @@ def generate_vocabulary(spec_version, clams_types_vers):
161
161
  return vocabulary_dir
162
162
 
163
163
 
164
- def get_latest_mmif_gittag(version: str):
164
+ def get_latest_mmif_gittag():
165
165
  # vmaj, vmin, vpat = version.split('.')[0:3]
166
166
  res = request.urlopen('https://api.github.com/repos/clamsproject/mmif/git/refs/tags')
167
167
  body = json.loads(res.read())
@@ -200,7 +200,7 @@ def prep_ext_files(setuptools_cmd):
200
200
  def mod_run(self):
201
201
  # will infer the `spec_ver` from the latest git tag available either on GH or local `mmif` repository.
202
202
  # NOTE that when `make develop`, it will use specification files from upstream "develop" branch of `mmif` repo
203
- latest_mmif_gittag = get_latest_mmif_gittag(version)
203
+ latest_mmif_gittag = get_latest_mmif_gittag()
204
204
  spec_file_gitref = latest_mmif_gittag if '.dev' not in version else 'develop'
205
205
  # legacy version tags were formatted as xx-a.b.c (e.g., vocab-0.0.1)
206
206
  spec_version = latest_mmif_gittag.split('-')[-1]
@@ -1,7 +0,0 @@
1
- # MMIF for python
2
-
3
- **NOTE** that this project is in pre-alpha and being actively developed. Nothing is guaranteed to reliably work for the moment and developer need to be very careful when using APIs implemented here. Please use [the issue track](https://github.com/clamsproject/mmif/issues) to report bugs and malfunctions.
4
-
5
- ## MultiMedia Interchange Format
6
- [MMIF](https://mmif.clams.ai) is a JSON(-LD)-based data format designed for transferring annotation data between computational analysis applications in [CLAMS project](https://clams.ai).
7
-
mmif-python-0.5.0/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.5.0
@@ -1,2 +0,0 @@
1
- __version__ = "0.5.0"
2
- __specver__ = "0.5.0"
File without changes
File without changes
File without changes