clams-python 1.2.0__tar.gz → 1.2.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 (41) hide show
  1. {clams-python-1.2.0/clams_python.egg-info → clams_python-1.2.2}/PKG-INFO +2 -2
  2. clams_python-1.2.2/VERSION +1 -0
  3. {clams-python-1.2.0 → clams_python-1.2.2}/clams/app/__init__.py +28 -7
  4. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/app.py.template +1 -2
  5. {clams-python-1.2.0 → clams_python-1.2.2}/clams/restify/__init__.py +3 -1
  6. clams_python-1.2.2/clams/ver/__init__.py +1 -0
  7. {clams-python-1.2.0 → clams_python-1.2.2/clams_python.egg-info}/PKG-INFO +2 -2
  8. {clams-python-1.2.0 → clams_python-1.2.2}/clams_python.egg-info/requires.txt +1 -1
  9. {clams-python-1.2.0 → clams_python-1.2.2}/requirements.txt +1 -1
  10. {clams-python-1.2.0 → clams_python-1.2.2}/tests/test_clamsapp.py +22 -4
  11. clams-python-1.2.0/VERSION +0 -1
  12. clams-python-1.2.0/clams/ver/__init__.py +0 -1
  13. {clams-python-1.2.0 → clams_python-1.2.2}/LICENSE +0 -0
  14. {clams-python-1.2.0 → clams_python-1.2.2}/MANIFEST.in +0 -0
  15. {clams-python-1.2.0 → clams_python-1.2.2}/README.md +0 -0
  16. {clams-python-1.2.0 → clams_python-1.2.2}/clams/__init__.py +0 -0
  17. {clams-python-1.2.0 → clams_python-1.2.2}/clams/appmetadata/__init__.py +0 -0
  18. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/__init__.py +0 -0
  19. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/.dockerignore.template +0 -0
  20. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/.gitignore.template +0 -0
  21. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/Containerfile.template +0 -0
  22. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/LICENSE.template +0 -0
  23. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/README.md.template +0 -0
  24. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/metadata.py.template +0 -0
  25. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/app/requirements.txt.template +0 -0
  26. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/gha/for-clams-team.md.template +0 -0
  27. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/gha/workflows/issue-apps-project.yml.template +0 -0
  28. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/gha/workflows/issue-assign.yml.template +0 -0
  29. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/gha/workflows/issue-close.yml.template +0 -0
  30. {clams-python-1.2.0 → clams_python-1.2.2}/clams/develop/templates/gha/workflows/publish.yml.template +0 -0
  31. {clams-python-1.2.0 → clams_python-1.2.2}/clams/mmif_utils/__init__.py +0 -0
  32. {clams-python-1.2.0 → clams_python-1.2.2}/clams/mmif_utils/rewind.py +0 -0
  33. {clams-python-1.2.0 → clams_python-1.2.2}/clams/mmif_utils/source.py +0 -0
  34. {clams-python-1.2.0 → clams_python-1.2.2}/clams/serve/__init__.py +0 -0
  35. {clams-python-1.2.0 → clams_python-1.2.2}/clams_python.egg-info/SOURCES.txt +0 -0
  36. {clams-python-1.2.0 → clams_python-1.2.2}/clams_python.egg-info/dependency_links.txt +0 -0
  37. {clams-python-1.2.0 → clams_python-1.2.2}/clams_python.egg-info/entry_points.txt +0 -0
  38. {clams-python-1.2.0 → clams_python-1.2.2}/clams_python.egg-info/top_level.txt +0 -0
  39. {clams-python-1.2.0 → clams_python-1.2.2}/setup.cfg +0 -0
  40. {clams-python-1.2.0 → clams_python-1.2.2}/setup.py +0 -0
  41. {clams-python-1.2.0 → clams_python-1.2.2}/tests/test_clamscli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: clams-python
3
- Version: 1.2.0
3
+ Version: 1.2.2
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.13
17
+ Requires-Dist: mmif-python==1.0.14
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.2
@@ -122,7 +122,9 @@ class ClamsApp(ABC):
122
122
  if key not in self.annotate_param_spec:
123
123
  issued_warnings.append(UserWarning(f'An undefined parameter "{key}" (value: "{runtime_params[key]}") is passed'))
124
124
  # this will do casting + refinement altogether
125
+ self.logger.debug(f"User parameters: {runtime_params}")
125
126
  refined = self._refine_params(**runtime_params)
127
+ self.logger.debug(f"Refined parameters: {refined}")
126
128
  pretty = refined.get('pretty', False)
127
129
  with warnings.catch_warnings(record=True) as ws:
128
130
  annotated = self._annotate(mmif, **refined)
@@ -176,7 +178,14 @@ class ClamsApp(ABC):
176
178
  raise ValueError(f"Value for parameter \"{parameter.name}\" must be one of {parameter.choices}.")
177
179
  refined[parameter.name] = casted[parameter.name]
178
180
  elif parameter.default is not None:
179
- refined[parameter.name] = parameter.default
181
+ # have to cast the default values as well, since
182
+ # 1. `map` type default values are not actually expected as a map (dict)
183
+ # 2. developers can use data type not matching the spec
184
+ if isinstance(parameter.default, list):
185
+ casted_default = self.annotate_param_caster.cast({parameter.name: list(map(str, parameter.default))})
186
+ else:
187
+ casted_default = self.annotate_param_caster.cast({parameter.name: [str(parameter.default)]})
188
+ refined.update(casted_default)
180
189
  else:
181
190
  raise ValueError(f"Cannot find configuration for a required parameter \"{parameter.name}\".")
182
191
  # raw input params are hidden under a special key
@@ -202,11 +211,12 @@ class ClamsApp(ABC):
202
211
  :param runtime_conf: runtime configuration of the app as k-v pairs
203
212
  """
204
213
  view.metadata.app = self.metadata.identifier
214
+ params_map = {p.name: p for p in self.metadata.parameters}
205
215
  if self._RAW_PARAMS_KEY in runtime_conf:
206
216
  for k, v in runtime_conf.items():
207
217
  if k == self._RAW_PARAMS_KEY:
208
218
  for orik, oriv in v.items():
209
- if orik in self.metadata.parameters and self.metadata.parameters[orik].multivalued:
219
+ if orik in params_map and params_map[orik].multivalued:
210
220
  view.metadata.add_parameter(orik, str(oriv))
211
221
  else:
212
222
  view.metadata.add_parameter(orik, oriv[0])
@@ -215,7 +225,7 @@ class ClamsApp(ABC):
215
225
  else:
216
226
  # meaning the parameters directly from flask or argparser and values are in lists
217
227
  for k, v in runtime_conf.items():
218
- if k in self.metadata.parameters and self.metadata.parameters[k].multivalued:
228
+ if k in params_map and params_map[k].multivalued:
219
229
  view.metadata.add_parameter(k, str(v))
220
230
  else:
221
231
  view.metadata.add_parameter(k, v[0])
@@ -335,8 +345,8 @@ class ParameterCaster(object):
335
345
  assert isinstance(vs, list), f"Expected a list of values for key {k}, but got {vs} of type {type(vs)}"
336
346
  assert all(isinstance(v, str) for v in vs), f"Expected a list of strings for key {k}, but got {vs} of types {[type(v) for v in vs]}"
337
347
  if k in self.param_spec:
348
+ valuetype, multivalued = self.param_spec[k]
338
349
  for v in vs:
339
- valuetype, multivalued = self.param_spec[k]
340
350
  if multivalued or k not in casted: # effectively only keeps the first value for non-multi params
341
351
  if valuetype == bool:
342
352
  v = self.bool_param(v)
@@ -355,11 +365,22 @@ class ParameterCaster(object):
355
365
  casted.setdefault(k, []).append(v)
356
366
  else:
357
367
  casted[k] = v
368
+ # when an empty value is passed (usually as a default value)
369
+ # just add an empty list or dict as a placeholder
370
+ # explicit check for `len == 0` is required to prevent
371
+ # empty values are set for params that don't have default values
372
+ if multivalued and len(vs) == 0:
373
+ if valuetype == dict:
374
+ casted.setdefault(k, {})
375
+ else:
376
+ casted.setdefault(k, [])
358
377
  else:
359
- if len(vs) > 1:
360
- casted[k] = vs
361
- else:
378
+ # if the parameter is not defined in the spec, there's no easy way to figure out
379
+ # the user's intention whether it should be a list (multivalued) or a single value.
380
+ if len(vs) == 1:
362
381
  casted[k] = vs[0]
382
+ else:
383
+ casted[k] = vs
363
384
  return casted # pytype: disable=bad-return-type
364
385
 
365
386
  @staticmethod
@@ -13,7 +13,6 @@ The app.py script does several things:
13
13
 
14
14
  import argparse
15
15
  import logging
16
- from typing import Union
17
16
 
18
17
  # Imports needed for Clams and MMIF.
19
18
  # Non-NLP Clams applications will require AnnotationTypes
@@ -36,7 +35,7 @@ class $APP_CLASS_NAME(ClamsApp):
36
35
  # When using the ``metadata.py`` leave this do-nothing "pass" method here.
37
36
  pass
38
37
 
39
- def _annotate(self, mmif: Union[str, dict, Mmif], **parameters) -> Mmif:
38
+ def _annotate(self, mmif: Mmif, **parameters) -> Mmif:
40
39
  # see https://sdk.clams.ai/autodoc/clams.app.html#clams.app.ClamsApp._annotate
41
40
  raise NotImplementedError
42
41
 
@@ -124,7 +124,9 @@ class ClamsHTTPApi(Resource):
124
124
 
125
125
  :return: Returns app metadata in a HTTP response.
126
126
  """
127
- return self.json_to_response(self.cla.appmetadata(**request.args))
127
+ # this will catch duplicate arguments with different values into a list under the key
128
+ raw_params = request.args.to_dict(flat=False)
129
+ return self.json_to_response(self.cla.appmetadata(**raw_params))
128
130
 
129
131
  def post(self) -> Response:
130
132
  """
@@ -0,0 +1 @@
1
+ __version__ = "1.2.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: clams-python
3
- Version: 1.2.0
3
+ Version: 1.2.2
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.13
17
+ Requires-Dist: mmif-python==1.0.14
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.13
1
+ mmif-python==1.0.14
2
2
  Flask>=2
3
3
  Flask-RESTful>=0.3.9
4
4
  gunicorn>=20
@@ -1,4 +1,4 @@
1
- mmif-python==1.0.13
1
+ mmif-python==1.0.14
2
2
 
3
3
  Flask>=2
4
4
  Flask-RESTful>=0.3.9
@@ -236,8 +236,8 @@ class TestClamsApp(unittest.TestCase):
236
236
  self.assertEqual(v1.metadata.app, self.app.metadata.identifier)
237
237
  self.assertEqual(len(v1.metadata.parameters), 0)
238
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))
239
+ args2 = {'undefined_param1': ['value1']} # values are lists as our restifier uses `to_dict(flat=False)`
240
+ self.app.sign_view(v2, self.app._refine_params(**args2))
241
241
  self.assertEqual(v2.metadata.app, self.app.metadata.identifier)
242
242
  self.assertEqual(len(v2.metadata.parameters), 1)
243
243
  self.assertFalse(clams.ClamsApp._RAW_PARAMS_KEY in v2.metadata.appConfiguration)
@@ -245,8 +245,22 @@ class TestClamsApp(unittest.TestCase):
245
245
  # any parameter with defaults should be recorded
246
246
  if param.default is not None:
247
247
  self.assertTrue(param.name in v2.metadata.appConfiguration)
248
- elif param.name not in raw_query_string_arguments:
248
+ elif param.name not in args2:
249
249
  self.assertFalse(param.name in v2.metadata.appConfiguration)
250
+ v3 = m.new_view()
251
+ self.app.metadata.add_parameter('multivalued_param', 'dummy parameter with multivalues', type='string', multivalued=True, default=[])
252
+ args3 = {'undefined_param1': ['value1']}
253
+ self.app.sign_view(v3, self.app._refine_params(**args3))
254
+ self.assertEqual(len(v3.metadata.parameters), 1)
255
+ self.assertEqual(len(v3.metadata.appConfiguration), 3) # universal (pretty) + `raise_error` in metadata.py + `multivalued_param`
256
+ self.assertEqual(v3.metadata.appConfiguration['multivalued_param'], [])
257
+ v4 = m.new_view()
258
+ multiple_values = ['value1', 'value2']
259
+ args4 = {'multivalued_param': multiple_values}
260
+ self.app.sign_view(v4, self.app._refine_params(**args4))
261
+ self.assertEqual(len(v4.metadata.parameters), 1)
262
+ self.assertEqual(len(v4.metadata.appConfiguration), 3)
263
+ self.assertEqual(len(v4.metadata.parameters['multivalued_param']), len(str(multiple_values)))
250
264
 
251
265
  def test_annotate(self):
252
266
  # The example app is hard-coded to **always** emit version mismatch warning
@@ -307,6 +321,10 @@ class TestClamsApp(unittest.TestCase):
307
321
  description='float +def +mv')
308
322
  self.app.metadata.add_parameter('param7', type='number', default=[], multivalued=True,
309
323
  description='float +def +empty +mv')
324
+ # need to manually rebuild caster object, because the metadata has updated after the app is initialized
325
+ for param_spec in self.app.metadata.parameters:
326
+ self.app.annotate_param_spec[param_spec.name] = (param_spec.type, param_spec.multivalued)
327
+ self.app.annotate_param_caster = ParameterCaster(self.app.annotate_param_spec)
310
328
  with self.assertRaises(ValueError):
311
329
  self.app._refine_params(**{}) # param1 is required, but not passed
312
330
  dummy_params = {'param1': ['okay'], 'non_parameter': ['should be ignored']}
@@ -375,7 +393,7 @@ class TestRestifier(unittest.TestCase):
375
393
  def test_can_put_as_json(self):
376
394
  put = self.app.put('/', data=ExampleInputMMIF.get_mmif(), headers={"Content-Type": "Application/json"})
377
395
  self.assertIsNotNone(put)
378
- self.assertEqual(put.status_code, 200)
396
+ self.assertEqual(200, put.status_code)
379
397
  self.assertIsNotNone(Mmif(put.get_data(as_text=True)))
380
398
 
381
399
  def test_can_pass_params(self):
@@ -1 +0,0 @@
1
- 1.2.0
@@ -1 +0,0 @@
1
- __version__ = "1.2.0"
File without changes
File without changes
File without changes
File without changes
File without changes