clams-python 1.1.5__tar.gz → 1.2.0__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 (41) hide show
  1. {clams-python-1.1.5/clams_python.egg-info → clams-python-1.2.0}/PKG-INFO +2 -2
  2. clams-python-1.2.0/VERSION +1 -0
  3. {clams-python-1.1.5 → clams-python-1.2.0}/clams/app/__init__.py +3 -9
  4. {clams-python-1.1.5 → clams-python-1.2.0}/clams/mmif_utils/source.py +32 -75
  5. clams-python-1.2.0/clams/ver/__init__.py +1 -0
  6. {clams-python-1.1.5 → clams-python-1.2.0/clams_python.egg-info}/PKG-INFO +2 -2
  7. {clams-python-1.1.5 → clams-python-1.2.0}/clams_python.egg-info/requires.txt +1 -1
  8. {clams-python-1.1.5 → clams-python-1.2.0}/requirements.txt +1 -1
  9. {clams-python-1.1.5 → clams-python-1.2.0}/tests/test_clamsapp.py +20 -1
  10. {clams-python-1.1.5 → clams-python-1.2.0}/tests/test_clamscli.py +5 -0
  11. clams-python-1.1.5/VERSION +0 -1
  12. clams-python-1.1.5/clams/ver/__init__.py +0 -1
  13. {clams-python-1.1.5 → clams-python-1.2.0}/LICENSE +0 -0
  14. {clams-python-1.1.5 → clams-python-1.2.0}/MANIFEST.in +0 -0
  15. {clams-python-1.1.5 → clams-python-1.2.0}/README.md +0 -0
  16. {clams-python-1.1.5 → clams-python-1.2.0}/clams/__init__.py +0 -0
  17. {clams-python-1.1.5 → clams-python-1.2.0}/clams/appmetadata/__init__.py +0 -0
  18. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/__init__.py +0 -0
  19. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/.dockerignore.template +0 -0
  20. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/.gitignore.template +0 -0
  21. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/Containerfile.template +0 -0
  22. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/LICENSE.template +0 -0
  23. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/README.md.template +0 -0
  24. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/app.py.template +0 -0
  25. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/metadata.py.template +0 -0
  26. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/app/requirements.txt.template +0 -0
  27. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/gha/for-clams-team.md.template +0 -0
  28. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/gha/workflows/issue-apps-project.yml.template +0 -0
  29. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/gha/workflows/issue-assign.yml.template +0 -0
  30. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/gha/workflows/issue-close.yml.template +0 -0
  31. {clams-python-1.1.5 → clams-python-1.2.0}/clams/develop/templates/gha/workflows/publish.yml.template +0 -0
  32. {clams-python-1.1.5 → clams-python-1.2.0}/clams/mmif_utils/__init__.py +0 -0
  33. {clams-python-1.1.5 → clams-python-1.2.0}/clams/mmif_utils/rewind.py +0 -0
  34. {clams-python-1.1.5 → clams-python-1.2.0}/clams/restify/__init__.py +0 -0
  35. {clams-python-1.1.5 → clams-python-1.2.0}/clams/serve/__init__.py +0 -0
  36. {clams-python-1.1.5 → clams-python-1.2.0}/clams_python.egg-info/SOURCES.txt +0 -0
  37. {clams-python-1.1.5 → clams-python-1.2.0}/clams_python.egg-info/dependency_links.txt +0 -0
  38. {clams-python-1.1.5 → clams-python-1.2.0}/clams_python.egg-info/entry_points.txt +0 -0
  39. {clams-python-1.1.5 → clams-python-1.2.0}/clams_python.egg-info/top_level.txt +0 -0
  40. {clams-python-1.1.5 → clams-python-1.2.0}/setup.cfg +0 -0
  41. {clams-python-1.1.5 → clams-python-1.2.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: clams-python
3
- Version: 1.1.5
3
+ Version: 1.2.0
4
4
  Summary: A collection of APIs to develop CLAMS app for python
5
5
  Home-page: https://clams.ai
6
6
  Author: Brandeis Lab for Linguistics and Computation
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: mmif-python==1.0.11
17
+ Requires-Dist: mmif-python==1.0.13
18
18
  Requires-Dist: Flask>=2
19
19
  Requires-Dist: Flask-RESTful>=0.3.9
20
20
  Requires-Dist: gunicorn>=20
@@ -0,0 +1 @@
1
+ 1.2.0
@@ -190,7 +190,7 @@ class ClamsApp(ABC):
190
190
  DeprecationWarning, stacklevel=2)
191
191
  return self._refine_params(**runtime_params)
192
192
 
193
- def sign_view(self, view: View, runtime_conf: Optional[dict] = None) -> None:
193
+ def sign_view(self, view: View, runtime_conf: dict) -> None:
194
194
  """
195
195
  A method to "sign" a new view that this app creates at the beginning of annotation.
196
196
  Signing will populate the view metadata with information and configuration of this app.
@@ -201,13 +201,6 @@ class ClamsApp(ABC):
201
201
  :param view: a view to sign
202
202
  :param runtime_conf: runtime configuration of the app as k-v pairs
203
203
  """
204
- # TODO (krim @ 8/2/23): once all devs understood this change, make runtime_conf a required argument
205
- if runtime_conf is None:
206
- warnings.warn("`runtime_conf` argument for ClamsApp.sign_view() will "
207
- "no longer be optional in the future. Please pass `runtime_params` "
208
- "from _annotate() method.",
209
- FutureWarning, stacklevel=2)
210
- return
211
204
  view.metadata.app = self.metadata.identifier
212
205
  if self._RAW_PARAMS_KEY in runtime_conf:
213
206
  for k, v in runtime_conf.items():
@@ -217,7 +210,8 @@ class ClamsApp(ABC):
217
210
  view.metadata.add_parameter(orik, str(oriv))
218
211
  else:
219
212
  view.metadata.add_parameter(orik, oriv[0])
220
- view.metadata.add_app_configuration(k, v)
213
+ else:
214
+ view.metadata.add_app_configuration(k, v)
221
215
  else:
222
216
  # meaning the parameters directly from flask or argparser and values are in lists
223
217
  for k, v in runtime_conf.items():
@@ -1,9 +1,9 @@
1
1
  import argparse
2
2
  import itertools
3
3
  import json
4
+ import pathlib
4
5
  import sys
5
6
  import textwrap
6
- from os import path
7
7
  from typing import Union, Generator, List, Optional, Iterable
8
8
  from urllib.parse import urlparse
9
9
 
@@ -171,24 +171,18 @@ class WorkflowSource:
171
171
  yield self.produce()
172
172
 
173
173
 
174
- def generate_source_mmif_from_file(documents, prefix=None, **ignored):
175
- from string import Template
174
+ def generate_source_mmif_from_file(documents, prefix=None, scheme='file', **ignored):
176
175
  at_types = {
177
176
  'video': DocumentTypes.VideoDocument,
178
177
  'audio': DocumentTypes.AudioDocument,
179
178
  'text': DocumentTypes.TextDocument,
180
179
  'image': DocumentTypes.ImageDocument
181
180
  }
182
- template = Template('''{
183
- "@type": "${at_type}",
184
- "properties": {
185
- "id": "${aid}",
186
- "mime": "${mime}",
187
- "location": "${location}" }
188
- }''')
189
181
  pl = WorkflowSource()
190
- if prefix and not path.isabs(prefix):
191
- raise ValueError(f"prefix must be an absolute path; given \"{prefix}\".")
182
+ if prefix:
183
+ prefix = pathlib.PurePosixPath(prefix)
184
+ if not prefix.is_absolute():
185
+ raise ValueError(f"prefix must be an absolute path; given \"{prefix}\".")
192
186
  for doc_id, arg in enumerate(documents, start=1):
193
187
  arg = arg.strip()
194
188
  if len(arg) < 1:
@@ -200,57 +194,20 @@ def generate_source_mmif_from_file(documents, prefix=None, **ignored):
200
194
  raise ValueError(
201
195
  f'Invalid MIME types, or no MIME type and/or path provided, in argument {doc_id-1} to source'
202
196
  )
203
- if prefix and path.isabs(location):
204
- raise ValueError(f"when prefix is used, file location must not be an absolute path; given \"{location}\".")
205
- elif not prefix and not path.isabs(location):
206
- raise ValueError(f'file location must be an absolute path, or --prefix must be used; given \"{location}\".')
207
- elif prefix and not path.isabs(location):
208
- location = path.join(prefix, location)
209
- doc = template.substitute(
210
- at_type=str(at_types[mime.split('/', maxsplit=1)[0]]),
211
- aid=f'd{doc_id}',
212
- mime=mime,
213
- location=location
214
- )
215
- pl.add_document(doc)
216
- return pl.produce().serialize(pretty=True)
217
-
218
-
219
- def generate_source_mmif_from_customscheme(documents, scheme, **ignored):
220
- from string import Template
221
- at_types = {
222
- 'video': DocumentTypes.VideoDocument,
223
- 'audio': DocumentTypes.AudioDocument,
224
- 'text': DocumentTypes.TextDocument,
225
- 'image': DocumentTypes.ImageDocument
226
- }
227
- template = Template('''{
228
- "@type": "${at_type}",
229
- "properties": {
230
- "id": "${aid}",
231
- "mime": "${mime}",
232
- "location": "${location}" }
233
- }''')
234
- pl = WorkflowSource()
235
- for doc_id, arg in enumerate(documents, start=1):
236
- arg = arg.strip()
237
- if len(arg) < 1:
238
- continue
239
- result = arg.split(':', maxsplit=1)
240
- if len(result) == 2 and result[0].split('/', maxsplit=1)[0] in at_types:
241
- mime, location = result
242
- else:
243
- raise ValueError(
244
- f'Invalid MIME types, or no MIME type and/or path provided, in argument {doc_id-1} to source'
245
- )
246
- if urlparse(location).scheme == '':
247
- location = scheme + '://' + location
248
- doc = template.substitute(
249
- at_type=str(at_types[mime.split('/', maxsplit=1)[0]]),
250
- aid=f'd{doc_id}',
251
- mime=mime,
252
- location=location
253
- )
197
+ location_uri = urlparse(location, scheme=scheme)
198
+ if location_uri.scheme == 'file':
199
+ location = pathlib.PurePosixPath(location_uri.path)
200
+ if prefix and location.is_absolute():
201
+ raise ValueError(f"when prefix is used, file location must not be an absolute path; given \"{location}\".")
202
+ elif not prefix and not location.is_absolute():
203
+ raise ValueError(f'file location must be an absolute path, or --prefix must be used; given \"{location}\".')
204
+ elif prefix and not location.is_absolute():
205
+ location = prefix / location
206
+ doc = Document()
207
+ doc.at_type = at_types[mime.split('/', maxsplit=1)[0]]
208
+ doc.properties.location = f"{location_uri.scheme}://{location}"
209
+ doc.properties.id = f'd{doc_id}'
210
+ doc.properties.mime = mime
254
211
  pl.add_document(doc)
255
212
  return pl.produce().serialize(pretty=True)
256
213
 
@@ -274,19 +231,22 @@ def prep_argparser(**kwargs):
274
231
  default=None,
275
232
  action='store',
276
233
  nargs='+',
277
- help="This list of documents should be colon-joined pairs of document types and file paths. A document type "
278
- "can be one of ``audio``, ``video``, ``text``, ``image``, or a MIME type string (such as video/mp4). The "
279
- "output will be a MMIF file containing a document for each of those file paths, with the appropriate "
280
- "``@type`` and MIME type (if given), printed to the standard output. "
234
+ help='This list of documents MUST be colon-delimited pairs of document types and file locations. A document '
235
+ 'type can be one of `audio`, `video`, `text`, `image`, or a MIME type string (such as video/mp4). The '
236
+ 'file locations MUST be valid URI strings (e.g. `file:///path/to/file.mp4`, or URI scheme part can be '
237
+ 'omitted, when `--scheme` flag is used). Note that when `file://` scheme is used (default), locations '
238
+ 'MUST BE POSIX forms (Windows forms are not supported). The output will be a MMIF file containing a '
239
+ 'document for each of those file paths, with the appropriate ``@type`` and MIME type (if given).'
281
240
  )
282
241
  parser.add_argument(
283
242
  '-p', '--prefix',
284
243
  default=None,
285
244
  metavar='PATH',
286
245
  nargs='?',
287
- help='An absolute path to use as prefix for file paths (ONLY WORKS with `file` scheme, ignored otherwise). If '
288
- 'prefix is set, document file paths MUST be relative. Useful when creating source MMIF files from a '
289
- 'system that\'s different from the system that actually runs the workflow (e.g. in a container).'
246
+ help='An absolute path to use as prefix for file paths (ONLY WORKS with the default `file://` scheme, ignored '
247
+ 'otherwise. MUST BE a POSIX form, Windows form is not supported). If prefix is set, document file paths '
248
+ 'MUST be relative. Useful when creating source MMIF files from a system that\'s different from the '
249
+ 'environment that actually runs the workflow (e.g. in a container).'
290
250
  )
291
251
  parser.add_argument(
292
252
  '-o', '--output',
@@ -300,7 +260,7 @@ def prep_argparser(**kwargs):
300
260
  default='file',
301
261
  action='store',
302
262
  nargs='?',
303
- help='A scheme to associate with the document location URI. When not given, the default scheme is `file`.'
263
+ help='A scheme to associate with the document location URI. When not given, the default scheme is `file://`.'
304
264
  )
305
265
  return parser
306
266
 
@@ -310,10 +270,7 @@ def main(args):
310
270
  out_f = open(args.output, 'w')
311
271
  else:
312
272
  out_f = sys.stdout
313
- if args.scheme == 'file':
314
- mmif = generate_source_mmif_from_file(**vars(args))
315
- else:
316
- mmif = generate_source_mmif_from_customscheme(**vars(args))
273
+ mmif = generate_source_mmif_from_file(windows_path=False, **vars(args))
317
274
  out_f.write(mmif)
318
275
  return mmif
319
276
 
@@ -0,0 +1 @@
1
+ __version__ = "1.2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: clams-python
3
- Version: 1.1.5
3
+ Version: 1.2.0
4
4
  Summary: A collection of APIs to develop CLAMS app for python
5
5
  Home-page: https://clams.ai
6
6
  Author: Brandeis Lab for Linguistics and Computation
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: mmif-python==1.0.11
17
+ Requires-Dist: mmif-python==1.0.13
18
18
  Requires-Dist: Flask>=2
19
19
  Requires-Dist: Flask-RESTful>=0.3.9
20
20
  Requires-Dist: gunicorn>=20
@@ -1,4 +1,4 @@
1
- mmif-python==1.0.11
1
+ mmif-python==1.0.13
2
2
  Flask>=2
3
3
  Flask-RESTful>=0.3.9
4
4
  gunicorn>=20
@@ -1,4 +1,4 @@
1
- mmif-python==1.0.11
1
+ mmif-python==1.0.13
2
2
 
3
3
  Flask>=2
4
4
  Flask-RESTful>=0.3.9
@@ -228,7 +228,26 @@ class TestClamsApp(unittest.TestCase):
228
228
  in_mmif.metadata.mmif = f"http://mmif.clams.ai/{incompat_ver}"
229
229
  with pytest.raises(ValueError):
230
230
  self.app.annotate(in_mmif)
231
-
231
+
232
+ def test_sign_view(self):
233
+ m = Mmif(self.in_mmif)
234
+ v1 = m.new_view()
235
+ self.app.sign_view(v1, {})
236
+ self.assertEqual(v1.metadata.app, self.app.metadata.identifier)
237
+ self.assertEqual(len(v1.metadata.parameters), 0)
238
+ v2 = m.new_view()
239
+ raw_query_string_arguments = {'undefined_param1': ['value1']} # values are lists as our restifier uses `to_dict(flat=False)`
240
+ self.app.sign_view(v2, self.app._refine_params(**raw_query_string_arguments))
241
+ self.assertEqual(v2.metadata.app, self.app.metadata.identifier)
242
+ self.assertEqual(len(v2.metadata.parameters), 1)
243
+ self.assertFalse(clams.ClamsApp._RAW_PARAMS_KEY in v2.metadata.appConfiguration)
244
+ for param in self.app.metadata.parameters:
245
+ # any parameter with defaults should be recorded
246
+ if param.default is not None:
247
+ self.assertTrue(param.name in v2.metadata.appConfiguration)
248
+ elif param.name not in raw_query_string_arguments:
249
+ self.assertFalse(param.name in v2.metadata.appConfiguration)
250
+
232
251
  def test_annotate(self):
233
252
  # The example app is hard-coded to **always** emit version mismatch warning
234
253
  out_mmif = self.app.annotate(self.in_mmif)
@@ -3,6 +3,7 @@ import copy
3
3
  import io
4
4
  import os
5
5
  import unittest
6
+ import unittest.mock
6
7
 
7
8
  from mmif.serialize import Mmif
8
9
  from mmif.vocabulary import DocumentTypes, AnnotationTypes
@@ -63,6 +64,10 @@ class TestSource(unittest.TestCase):
63
64
  with self.assertRaises(ValueError):
64
65
  self.generate_source_mmif()
65
66
 
67
+ @unittest.mock.patch('os.name', 'nt')
68
+ def test_on_windows(self):
69
+ self.test_accept_file_paths()
70
+
66
71
  def test_accept_prefixed_file_paths(self):
67
72
  self.prefix = '/a/b'
68
73
  self.docs.append("video:c.mp4")
@@ -1 +0,0 @@
1
- 1.1.5
@@ -1 +0,0 @@
1
- __version__ = "1.1.5"
File without changes
File without changes
File without changes
File without changes
File without changes