Coverage for nexusLIMS/utils/dicts.py: 100%
27 statements
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
1"""Dictionary manipulation utilities for NexusLIMS."""
3from typing import Any, Dict
5from benedict import benedict
8def get_nested_dict_value_by_path(nest_dict, path):
9 """
10 Get a nested dictionary value by path.
12 Get the value from within a nested dictionary structure by traversing into
13 the dictionary as deep as that path found and returning that value.
15 Uses python-benedict for robust nested dictionary operations.
17 Parameters
18 ----------
19 nest_dict : dict
20 A dictionary of dictionaries that is to be queried
21 path : tuple
22 A tuple (or other iterable type) that specifies the subsequent keys
23 needed to get to a a value within `nest_dict`
25 Returns
26 -------
27 value : object or None
28 The value at the path within the nested dictionary; if there's no
29 value there, return None
30 """
31 # Disable keypath_separator to avoid conflicts with keys containing special chars
32 return benedict(nest_dict, keypath_separator=None).get(list(path))
35def set_nested_dict_value(nest_dict, path, value):
36 """
37 Set a nested dictionary value by path.
39 Set a value within a nested dictionary structure by traversing into
40 the dictionary as deep as that path found and changing it to `value`.
42 Uses python-benedict for robust nested dictionary operations.
44 Parameters
45 ----------
46 nest_dict : dict
47 A dictionary of dictionaries that is to be queried
48 path : tuple
49 A tuple (or other iterable type) that specifies the subsequent keys
50 needed to get to a a value within `nest_dict`
51 value : object
52 The value which will be given to the path in the nested dictionary
54 Returns
55 -------
56 value : object
57 The value at the path within the nested dictionary
58 """
59 # Disable keypath_separator to avoid conflicts with keys containing special chars
60 b = benedict(nest_dict, keypath_separator=None)
61 b[list(path)] = value # Updates in-place (benedict is dict subclass)
64def try_getting_dict_value(dict_, key):
65 """
66 Try to get a nested dictionary value.
68 This method will try to get a value from a dictionary (potentially
69 nested) and fail silently if the value is not found, returning None.
71 Parameters
72 ----------
73 dict_ : dict
74 The dictionary from which to get a value
75 key : str or tuple
76 The key to query, or if an iterable container type (tuple, list,
77 etc.) is given, the path into a nested dictionary to follow
79 Returns
80 -------
81 val : object or None
82 The value of the dictionary specified by `key`. If the dictionary
83 does not have a key, returns None without raising an error
84 """
85 try:
86 if isinstance(key, str):
87 return dict_[key]
88 if hasattr(key, "__iter__"):
89 return get_nested_dict_value_by_path(dict_, key)
90 except (KeyError, TypeError):
91 return None
92 else:
93 # we shouldn't reach this line, but always good to return a consistent
94 # value just in case
95 return None # pragma: no cover
98def sort_dict(item):
99 """Recursively sort a dictionary by keys."""
100 return {
101 k: sort_dict(v) if isinstance(v, dict) else v
102 for k, v in sorted(item.items(), key=lambda i: i[0].lower())
103 }
106def remove_dtb_element(tree, path):
107 """
108 Remove an element from a DictionaryTreeBrowser by setting it to None.
110 Helper method that sets a specific leaf of a DictionaryTreeBrowser to None.
111 Use with :py:meth:`remove_dict_nones` to fully remove the desired DTB element after
112 setting it to None (after converting DTB to dictionary).
114 Parameters
115 ----------
116 tree : :py:class:`~hyperspy.misc.utils.DictionaryTreeBrowser`
117 the ``DictionaryTreeBrowser`` object to remove the object from
118 path : str
119 period-delimited path to a DTB element
121 Returns
122 -------
123 tree : :py:class:`~hyperspy.misc.utils.DictionaryTreeBrowser`
124 """
125 tree.set_item(path, None)
127 return tree
130def remove_dict_nones(dictionary: Dict[Any, Any]) -> Dict[Any, Any]:
131 """
132 Delete keys with a value of ``None`` in a dictionary, recursively.
134 Taken from https://stackoverflow.com/a/4256027.
136 Parameters
137 ----------
138 dictionary
139 The dictionary, with keys that have None values removed
141 Returns
142 -------
143 dict
144 The same dictionary, but with "Nones" removed
145 """
146 for key, value in list(dictionary.items()):
147 if value is None:
148 del dictionary[key]
149 elif isinstance(value, dict):
150 remove_dict_nones(value)
151 return dictionary