From 906c486d66ce4b6ef03af9e85a5cccd575efe9c2 Mon Sep 17 00:00:00 2001 From: Keigh Rim Date: Thu, 28 Mar 2024 16:37:35 -0400 Subject: [PATCH] updated how default values work with `type=map` runtime parameter --- clams/appmetadata/__init__.py | 16 +++++++++++----- tests/test_clamsapp.py | 27 ++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/clams/appmetadata/__init__.py b/clams/appmetadata/__init__.py index dde012c..9a426ac 100644 --- a/clams/appmetadata/__init__.py +++ b/clams/appmetadata/__init__.py @@ -158,10 +158,13 @@ class RuntimeParameter(_BaseModel): None, description="(optional) List of string values that can be accepted." ) - default: Union[real_valued_primitives, List[real_valued_primitives], Dict[str, str]] = pydantic.Field( + default: Union[real_valued_primitives, List[real_valued_primitives]] = pydantic.Field( None, - description="(optional) Default value for the parameter. Only valid for optional parameters. Namely, setting " - "a default value makes a parameter `optional`." + description="(optional) Default value for the parameter.\n\n" + "Notes for developers: \n\n" + "Setting a default value makes a parameter `optional`. \n\n" + "When ``multivalued=True``, the default value should be a list of values. \n\n" + "When ``type=map``, the default value should be a list of colon-separated strings. \n\n" ) multivalued: bool = pydantic.Field( ..., @@ -178,6 +181,9 @@ def __init__(self, **kwargs): self.multivalued = True if self.multivalued is None: self.multivalued = False + if self.multivalued: + if not isinstance(self.default, list): + self.default = [self.default] class Config: title = 'CLAMS App Runtime Parameter' @@ -383,8 +389,8 @@ def add_parameter(self, name: str, description: str, type: param_value_types, """ Helper method to add an element to the ``parameters`` list. """ - # casting default values (when the value is not nothing) makes sure - # the values are correctly casted by the pydantic + # casting default values (when the value is not nothing) to str + # will make sure the values are correctly handled by the pydantic # see https://docs.pydantic.dev/1.10/usage/types/#unions # e.g. casting 0.1 using the `primitives` dict will result in 0 (int) # while casting "0.1" using the `primitives` dict will result in 0.1 (float) diff --git a/tests/test_clamsapp.py b/tests/test_clamsapp.py index dda8758..ea29317 100644 --- a/tests/test_clamsapp.py +++ b/tests/test_clamsapp.py @@ -152,18 +152,39 @@ def test_appmetadata(self): with self.assertRaises(ValueError): self.app.metadata.add_input_oneof(i, j) # now parameters + num_params = 1 # starts with `raise_error` in metadata.py # using a custom class # this should conflict with existing parameter with self.assertRaises(ValueError): self.app.metadata.add_parameter( name='raise_error', description='force raise a ValueError', type='boolean', default='false') - # using python dict + num_params += 0 + + # some random multiple choice parameters + self.app.metadata.add_parameter( + name='single_choice', description='meaningless choice for a single value', + type='integer', choices=[1, 2, 3, 4, 5], default='3', multivalued=False) + num_params += 1 + self.app.metadata.add_parameter( - name='multiple_choice', description='meaningless multiple choice option', + name='multiple_choice', description='meaningless choice for any number of values', type='integer', choices=[1, 2, 3, 4, 5], default=3, multivalued=True) + num_params += 1 + metadata = json.loads(self.app.appmetadata()) - self.assertEqual(len(metadata['parameters']), 2 + len(self.app.universal_parameters)) + self.assertEqual(len(metadata['parameters']), num_params + len(self.app.universal_parameters)) + + for p in metadata['parameters']: + # default value must match the data type + if p['name'] == 'single_choice': + self.assertFalse(p['multivalued']) + self.assertTrue(isinstance(p['default'], int)) + # default for multivalued para must be a list + if p['name'] == 'multiple_choice': + self.assertTrue(p['multivalued']) + self.assertTrue(isinstance(p['default'], list)) + self.assertEqual(len(p['default']), 1) # now more additional metadata self.app.metadata.add_more('one', 'more') self.assertEqual(self.app.metadata.more['one'], 'more')