88SAFE_COMMA_SEPARATED_STRING_PATTERN = r"[^a-zA-Z0-9_,.\s-]"
99safe_comma_separated_string_regex = re .compile (SAFE_COMMA_SEPARATED_STRING_PATTERN )
1010
11+ NULL = "__NULL__"
12+ FALSE = "FALSE"
13+ TRUE = "TRUE"
14+
1115
1216@overload
1317def string (value : str | None ) -> str | None :
@@ -347,17 +351,28 @@ def boolean(value: str | bool | None) -> str:
347351 'FALSE'
348352 """
349353 return (
350- "TRUE"
351- if (value is not None and str (value ).lower () in ["true" , "t" ])
352- else "FALSE"
354+ TRUE if (value is not None and str (value ).lower () in ["true" , "t" ]) else FALSE
353355 )
354356
355357
358+ def _serialize (value : Any ):
359+ if value is None :
360+ value = f"'{ NULL } '"
361+
362+ elif isinstance (value , str ):
363+ value = f"'{ string (value )} '"
364+
365+ elif isinstance (value , bool ):
366+ value = f"'{ boolean (value )} '"
367+
368+ return str (value )
369+
370+
356371def vector_from_parts (
357372 parts : dict [str , Any ],
358373 new_part_key : str ,
359374 existing_part_keys : list [str ],
360- default : Any = "__NULL__" ,
375+ default : Any = NULL ,
361376) -> None :
362377 """
363378 Add a new key to the `parts` dictionary named `new_part_key`.
@@ -402,7 +417,24 @@ def vector_from_parts(
402417 ... ["scale.bar.coords.x", "scale.bar.coords.y"]
403418 ... )
404419 >>> query_params
405- {'scale.bar.coords': "c('0.5','1.0')"}
420+ {'scale.bar.coords': 'c(0.5,1.0)'}
421+
422+ **Convert two keys with numerical and string values into a single two-item vector.**
423+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
424+
425+ .. doctest::
426+
427+ >>> query_params = {
428+ ... "scale.bar.coords.x": 0.5,
429+ ... "scale.bar.coords.y": '1.0'
430+ ... }
431+ >>> vector_from_parts(
432+ ... query_params,
433+ ... "scale.bar.coords",
434+ ... ["scale.bar.coords.x", "scale.bar.coords.y"]
435+ ... )
436+ >>> query_params
437+ {'scale.bar.coords': "c(0.5,'1.0')"}
406438
407439 **Convert two keys into a single two-item vector where one value is `None`**
408440 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -419,7 +451,7 @@ def vector_from_parts(
419451 ... ["scale.bar.coords.x", "scale.bar.coords.y"]
420452 ... )
421453 >>> query_params
422- {'scale.bar.coords': "c(' 0.5' ,'__NULL__')"}
454+ {'scale.bar.coords': "c(0.5,'__NULL__')"}
423455
424456 **Convert two keys into an empty value where all values are `None`**
425457 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -437,17 +469,49 @@ def vector_from_parts(
437469 ... )
438470 >>> query_params
439471 {'scale.bar.coords': '__NULL__'}
472+
473+ **Convert many keys of varying types into a single vector.**
474+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
475+
476+ .. doctest::
477+
478+ >>> query_params = {
479+ ... "scale.bar.coords.x": 0.5,
480+ ... "scale.bar.coords.y": '1.0',
481+ ... "scale.bar.coords.z": None,
482+ ... "scale.bar.coords.a": 123,
483+ ... "scale.bar.coords.b": True,
484+ ... "scale.bar.coords.c": False
485+ ... }
486+ >>> vector_from_parts(
487+ ... query_params,
488+ ... "scale.bar.coords",
489+ ... [
490+ ... "scale.bar.coords.x",
491+ ... "scale.bar.coords.y",
492+ ... "scale.bar.coords.z",
493+ ... "scale.bar.coords.a",
494+ ... "scale.bar.coords.b",
495+ ... "scale.bar.coords.c",
496+ ... ]
497+ ... )
498+ >>> query_params
499+ {'scale.bar.coords': "c(0.5,'1.0','__NULL__',123,'TRUE','FALSE')"}
440500 """
441- part_values : list [str | None ] = []
501+ if not isinstance (parts , dict ): # pyright: ignore[reportUnnecessaryIsInstance]
502+ raise TypeError (
503+ f"`parts` must be a dictionary. The value given is a `{ type (parts )} `."
504+ )
505+
506+ part_values : list [Any ] = []
442507 for part in existing_part_keys :
443- part_values .append (str ( parts [ part ]) if parts [ part ] is not None else None )
508+ part_values .append (parts . get ( part ) )
444509 del parts [part ]
445510
446511 if all ([value is None or value == "" for value in part_values ]):
447512 parts [new_part_key ] = default
448513 else :
449- serialized_part_values = "','" .join ([
450- "__NULL__" if part_value is None else part_value
451- for part_value in part_values
514+ serialized_part_values = "," .join ([
515+ _serialize (part_value ) for part_value in part_values
452516 ])
453- parts [new_part_key ] = f"c(' { serialized_part_values } ' )"
517+ parts [new_part_key ] = f"c({ serialized_part_values } )"
0 commit comments