Coverage for harbor_cli/utils/utils.py: 94%
41 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-09 12:09 +0100
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-09 12:09 +0100
1"""Utility functions that can't be neatly categorized, or are so niche
2that they don't need their own module."""
3from __future__ import annotations
5from typing import Any
6from typing import Iterable
7from typing import MutableMapping
8from typing import TypeVar
10MappingType = TypeVar("MappingType", bound=MutableMapping[str, Any])
13def replace_none(d: MappingType, replacement: Any = "") -> MappingType:
14 """Replaces None values in a dict with a given replacement value.
15 Iterates recursively through nested dicts and iterables.
17 Untested with iterables other than list, tuple, and set.
18 """
20 if d is None:
21 return replacement
23 def _try_convert_to_original_type(
24 value: Iterable[Any], original_type: type
25 ) -> Iterable[Any]:
26 """Try to convert an iterable to the original type.
27 If the original type cannot be constructed with an iterable as
28 the only argument, return a list instead.
29 """
30 try:
31 return original_type(value)
32 except TypeError:
33 return list(value)
35 def _iter_iterable(value: Iterable[Any]) -> Iterable[Any]:
36 """Iterates through an iterable recursively, replacing None values."""
37 t = type(value)
38 v_generator = (item if item is not None else replacement for item in value)
39 values = []
41 for item in v_generator:
42 v = None
43 if isinstance(item, MutableMapping):
44 v = replace_none(item)
45 elif isinstance(item, str): # don't need to recurse into each char
46 v = item # type: ignore
47 elif isinstance(item, Iterable):
48 v = _iter_iterable(item) # type: ignore
49 else:
50 v = item
51 values.append(v)
52 if values: 52 ↛ 55line 52 didn't jump to line 55, because the condition on line 52 was never false
53 return _try_convert_to_original_type(values, t)
54 else:
55 return value
57 for key, value in d.items():
58 if isinstance(value, MutableMapping):
59 d[key] = replace_none(value)
60 elif isinstance(value, str):
61 d[key] = value
62 elif isinstance(value, Iterable):
63 d[key] = _iter_iterable(value)
64 elif value is None:
65 d[key] = replacement
66 return d