Skip to content

Commit 166d8b6

Browse files
committed
add warning for colons in map parameter keys
1 parent a8d97fc commit 166d8b6

3 files changed

Lines changed: 43 additions & 9 deletions

File tree

clams/app/__init__.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,22 @@ def str_param(value) -> str:
739739
@staticmethod
740740
def kv_param(value) -> Dict[str, str]:
741741
"""
742-
Helper function to convert string values to key-value pair type.
743-
"""
744-
return dict([value.split(map_param_kv_delimiter, 1)])
742+
Helper function to convert a colon-separated string into a
743+
single-entry dictionary. The first colon is used as the
744+
key-value delimiter; colons are not allowed in keys.
745+
746+
:param value: a colon-separated key-value string (e.g. ``key:value``)
747+
:type value: str
748+
:returns: a single-entry dict parsed from the input
749+
:rtype: Dict[str, str]
750+
"""
751+
k, v = value.split(map_param_kv_delimiter, 1)
752+
if map_param_kv_delimiter in v:
753+
warnings.warn(
754+
f"The map parameter value {value!r} contains "
755+
f"multiple '{map_param_kv_delimiter}' characters. "
756+
f"Only the first one is used as the delimiter "
757+
f"(key={k!r}, value={v!r}). "
758+
f"Colons are not allowed in map parameter keys."
759+
)
760+
return {k: v}

clams/appmetadata/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,13 @@ class RuntimeParameter(_BaseModel):
203203
"forced. \n\n"
204204
"Notes for developers: \n\n"
205205
"When the type is ``map``, the parameter value (still a single string from the users' perspective) "
206-
"must be formatted as a ``KEY:VALUE`` pair, namely a colon-separated string. To pass multiple "
207-
"key-value pairs, users need to pass the parameter multiple times (remember ``type=map`` implies "
208-
"``multivalued=true``) with pairs in the colon-separated format. \n\n"
209-
"Also, the `VALUE` part of the user input is always expected and handled as a string. If a "
210-
"developers wants to do more text processing on the passed value to accept more complex data types "
206+
"must be formatted as a ``KEY:VALUE`` pair, namely a colon-separated string. The first colon is "
207+
"always used as the delimiter, so **colons are not allowed in keys**. Colons may appear in "
208+
"values (everything after the first colon becomes the value), but a warning will be emitted. "
209+
"To pass multiple key-value pairs, users need to pass the parameter multiple times (remember "
210+
"``type=map`` implies ``multivalued=true``) with pairs in the colon-separated format. \n\n"
211+
"Also, the ``VALUE`` part of the user input is always expected and handled as a string. If a "
212+
"developer wants to do more text processing on the passed value to accept more complex data types "
211213
"or structures (e.g., map from a string to a list of strings), it is up to the developer. However, "
212214
"any additional form requirements should be precisely described in the ``description`` field for "
213215
"users. \n\n"

tests/test_clamsapp.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,23 @@ def test_cast(self):
596596
with warnings.catch_warnings():
597597
warnings.simplefilter("error")
598598
caster.cast(params)
599-
599+
600+
def test_kv_param_simple(self):
601+
result = ParameterCaster.kv_param('key:value')
602+
self.assertEqual(result, {'key': 'value'})
603+
# no warning for a single colon
604+
with warnings.catch_warnings():
605+
warnings.simplefilter("error")
606+
ParameterCaster.kv_param('key:value')
607+
608+
def test_kv_param_colon_in_value_warns(self):
609+
with warnings.catch_warnings(record=True) as w:
610+
warnings.simplefilter("always")
611+
result = ParameterCaster.kv_param('a:b:c')
612+
self.assertEqual(result, {'a': 'b:c'})
613+
self.assertEqual(len(w), 1)
614+
self.assertIn('multiple', str(w[0].message).lower())
615+
600616

601617
if __name__ == '__main__':
602618
unittest.main()

0 commit comments

Comments
 (0)