Skip to content

Namespace

datamate.namespaces

Module for handling nested dictionary-like objects with attribute access support.

This module provides the Namespace class and related utilities for working with nested dictionary-like objects that support both attribute and key-based access.

Namespace

Bases: Dict[str, Any]

A dictionary subclass supporting both attribute and item access.

Attributes:

Name Type Description
__dict__ Dict[str, Any]

The underlying dictionary storage.

Examples:

ns = Namespace({"a": 1, "b": {"c": 2}})
assert ns.a == 1
assert ns.b.c == 2
Source code in datamate/namespaces.py
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
class Namespace(Dict[str, Any]):
    """
    A dictionary subclass supporting both attribute and item access.

    Attributes:
        __dict__ (Dict[str, Any]): The underlying dictionary storage.

    Examples:
        ```python
        ns = Namespace({"a": 1, "b": {"c": 2}})
        assert ns.a == 1
        assert ns.b.c == 2
        ```
    """

    def __dir__(self) -> List[str]:
        """Return list of valid attributes including dictionary keys."""
        return list(set([*dict.__dir__(self), *dict.__iter__(self)]))

    def __getattr__(self, key: str) -> Any:
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            raise AttributeError(key)

    def __setattr__(self, key: str, val: object) -> None:
        dict.__setitem__(self, key, val)

    def __delattr__(self, key: str) -> None:
        dict.__delitem__(self, key)

    @property
    def __dict__(self) -> dict:  # type: ignore
        return self

    def __repr__(self) -> str:
        def single_line_repr(elem: object) -> str:
            if isinstance(elem, list):
                return "[" + ", ".join(map(single_line_repr, elem)) + "]"
            elif isinstance(elem, Namespace):
                return (
                    f"{elem.__class__.__name__}("
                    + ", ".join(f"{k}={single_line_repr(v)}" for k, v in elem.items())
                    + ")"
                )
            else:
                return repr(elem).replace("\n", " ")

        def repr_in_context(elem: object, curr_col: int, indent: int) -> str:
            sl_repr = single_line_repr(elem)
            if len(sl_repr) <= 80 - curr_col:
                return sl_repr
            elif isinstance(elem, list):
                return (
                    "[\n"
                    + " " * (indent + 2)
                    + (",\n" + " " * (indent + 2)).join(
                        repr_in_context(e, indent + 2, indent + 2) for e in elem
                    )
                    + "\n"
                    + " " * indent
                    + "]"
                )
            elif isinstance(elem, Namespace):
                return (
                    f"{elem.__class__.__name__}(\n"
                    + " " * (indent + 2)
                    + (",\n" + " " * (indent + 2)).join(
                        f"{k} = " + repr_in_context(v, indent + 5 + len(k), indent + 2)
                        for k, v in elem.items()
                    )
                    + "\n"
                    + " " * indent
                    + ")"
                )
            else:
                return repr(elem)

        return repr_in_context(self, 0, 0)

    def __eq__(self, other):
        return all_true(compare(namespacify(self), namespacify(other)))

    def __ne__(self, other):
        return not self.__eq__(other)

    def without(self, key: str) -> "Namespace":
        """
        Return a copy of the namespace without the specified key.

        Args:
            key: Key to remove from the namespace.

        Returns:
            New namespace without the specified key.
        """
        _copy = self.deepcopy()
        _copy.pop(key)
        return _copy

    def is_superset(self, other):
        return is_subset(self, other)

    def is_subset(self, other: Union[Dict, "Namespace"]) -> bool:
        """
        Check if this namespace is a subset of another.

        Args:
            other: The potential superset to compare against.

        Returns:
            True if this namespace is a subset of other.
        """
        return is_superset(other, self)

    def is_disjoint(self, other_dict):
        """
        Check whether another dictionary is disjoint with respect to this one.

        Two dictionaries are considered disjoint if they have no common keys.

        Parameters:
        other_dict (dict): The other dictionary to check for disjointness.

        Returns:
        bool: True if the other dictionary is disjoint with respect to this one,
              False otherwise.
        """
        return is_disjoint(self, other_dict)

    def to_df(self, name: str = "", seperator: str = ".") -> "pd.DataFrame":  # type: ignore
        """
        Convert namespace to flattened DataFrame.

        Args:
            name: Column name for the resulting DataFrame.
            seperator: Character to use for separating nested keys.

        Returns:
            Flattened DataFrame representation.
        """
        as_dict = self.to_dict()  # namespace need deepcopy method
        df = pd.json_normalize(as_dict, sep=seperator).T
        if name:
            df = df.rename({0: name}, axis=1)
        return df

    def diff(
        self, other: "Namespace", name1: str = "self", name2: str = "other"
    ) -> "Namespace":
        """
        Compare two namespaces and return their differences.

        Args:
            other: The namespace to compare against.
            name1: Label for the current namespace in the diff output.
            name2: Label for the other namespace in the diff output.

        Returns:
            A namespace containing the differences, with + indicating additions,
            - indicating deletions, and ≠ indicating changes.

        Examples:
            ```python
            ns1 = Namespace({"a": 1, "b": 2})
            ns2 = Namespace({"b": 3, "c": 4})
            diff = ns1.diff(ns2)
            # Returns: {
            #   "self": ["+a: 1", "≠b: 2", "-c"],
            #   "other": ["-a", "≠b: 3", "+c: 4"]
            # }
            ```
        """
        if self is None or other is None:
            return Namespace({name1: self, name2: other})

        _self = namespacify(self)
        other = namespacify(other)
        diff1: List[str] = []
        diff2: List[str] = []
        diff = {name1: diff1, name2: diff2}

        def _diff(self: Namespace, other: Namespace, parent: str = "") -> None:
            for k, v in self.items():
                if k not in other:
                    _diff1 = f"+{parent}.{k}: {v}" if parent else f"+{k}: {v}"
                    _diff2 = f"-{parent}.{k}" if parent else f"-{k}"
                    diff1.append(_diff1)
                    diff2.append(_diff2)
                elif v == other[k]:
                    pass
                elif isinstance(v, Namespace):
                    _parent = f"{parent}.{k}" if parent else f"{k}"
                    _diff(v, other[k], parent=_parent)
                else:
                    _diff1 = f"≠{parent}.{k}: {v}" if parent else f"≠{k}: {v}"
                    _diff2 = (
                        f"≠{parent}.{k}: {other[k]}" if parent else f"≠{k}: {other[k]}"
                    )
                    diff1.append(_diff1)
                    diff2.append(_diff2)

            for k, v in other.items():
                if k not in self:
                    _diff1 = f"-{parent}.{k}" if parent else f"-{k}"
                    _diff2 = f"+{parent}.{k}: {v}" if parent else f"+{k}: {v}"
                    diff1.append(_diff1)
                    diff2.append(_diff2)

        _diff(_self, other)
        return namespacify(diff)

    def walk(self) -> Iterator[Tuple[str, Any]]:
        """
        Recursively walk through the namespace and yield key-value pairs.

        Yields:
            Tuples of (key, value) for each item in the namespace, including nested items.

        Examples:
            ```python
            ns = Namespace({"a": 1, "b": {"c": 2}})
            for key, value in ns.walk():
                print(f"{key}: {value}")
            # Prints:
            # a: 1
            # b: {'c': 2}
            # c: 2
            ```
        """
        yield from dict_walk(self)

    def equal_values(self, other: "Namespace") -> bool:
        """
        Compare values recursively with another namespace.

        Args:
            other: The namespace to compare against.

        Returns:
            True if all values match recursively, False otherwise.
        """
        return compare(self, other)

    def copy(self) -> "Namespace":
        """Create a shallow copy of the namespace."""
        return copy(self)

    def deepcopy(self) -> "Namespace":
        """Create a deep copy of the namespace."""
        return deepcopy(self)

    def to_dict(self) -> Dict[str, Any]:
        """Convert the namespace to a regular dictionary recursively."""
        return to_dict(self)

    def depth(self) -> int:
        """
        Calculate the maximum depth of nested dictionaries.

        Returns:
            The maximum nesting level, where 0 means no nesting.
        """
        return depth(self)

    def pformat(self):
        return pformat(self)

    def all(self):
        return all_true(self)

__dir__

__dir__()

Return list of valid attributes including dictionary keys.

Source code in datamate/namespaces.py
44
45
46
def __dir__(self) -> List[str]:
    """Return list of valid attributes including dictionary keys."""
    return list(set([*dict.__dir__(self), *dict.__iter__(self)]))

without

without(key)

Return a copy of the namespace without the specified key.

Parameters:

Name Type Description Default
key str

Key to remove from the namespace.

required

Returns:

Type Description
Namespace

New namespace without the specified key.

Source code in datamate/namespaces.py
115
116
117
118
119
120
121
122
123
124
125
126
127
def without(self, key: str) -> "Namespace":
    """
    Return a copy of the namespace without the specified key.

    Args:
        key: Key to remove from the namespace.

    Returns:
        New namespace without the specified key.
    """
    _copy = self.deepcopy()
    _copy.pop(key)
    return _copy

is_subset

is_subset(other)

Check if this namespace is a subset of another.

Parameters:

Name Type Description Default
other Union[Dict, Namespace]

The potential superset to compare against.

required

Returns:

Type Description
bool

True if this namespace is a subset of other.

Source code in datamate/namespaces.py
132
133
134
135
136
137
138
139
140
141
142
def is_subset(self, other: Union[Dict, "Namespace"]) -> bool:
    """
    Check if this namespace is a subset of another.

    Args:
        other: The potential superset to compare against.

    Returns:
        True if this namespace is a subset of other.
    """
    return is_superset(other, self)

is_disjoint

is_disjoint(other_dict)

Check whether another dictionary is disjoint with respect to this one.

Two dictionaries are considered disjoint if they have no common keys.

Parameters: other_dict (dict): The other dictionary to check for disjointness.

bool: True if the other dictionary is disjoint with respect to this one, False otherwise.

Source code in datamate/namespaces.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def is_disjoint(self, other_dict):
    """
    Check whether another dictionary is disjoint with respect to this one.

    Two dictionaries are considered disjoint if they have no common keys.

    Parameters:
    other_dict (dict): The other dictionary to check for disjointness.

    Returns:
    bool: True if the other dictionary is disjoint with respect to this one,
          False otherwise.
    """
    return is_disjoint(self, other_dict)

to_df

to_df(name='', seperator='.')

Convert namespace to flattened DataFrame.

Parameters:

Name Type Description Default
name str

Column name for the resulting DataFrame.

''
seperator str

Character to use for separating nested keys.

'.'

Returns:

Type Description
DataFrame

Flattened DataFrame representation.

Source code in datamate/namespaces.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
def to_df(self, name: str = "", seperator: str = ".") -> "pd.DataFrame":  # type: ignore
    """
    Convert namespace to flattened DataFrame.

    Args:
        name: Column name for the resulting DataFrame.
        seperator: Character to use for separating nested keys.

    Returns:
        Flattened DataFrame representation.
    """
    as_dict = self.to_dict()  # namespace need deepcopy method
    df = pd.json_normalize(as_dict, sep=seperator).T
    if name:
        df = df.rename({0: name}, axis=1)
    return df

diff

diff(other, name1='self', name2='other')

Compare two namespaces and return their differences.

Parameters:

Name Type Description Default
other Namespace

The namespace to compare against.

required
name1 str

Label for the current namespace in the diff output.

'self'
name2 str

Label for the other namespace in the diff output.

'other'

Returns:

Type Description
Namespace

A namespace containing the differences, with + indicating additions,

Namespace
  • indicating deletions, and ≠ indicating changes.

Examples:

ns1 = Namespace({"a": 1, "b": 2})
ns2 = Namespace({"b": 3, "c": 4})
diff = ns1.diff(ns2)
# Returns: {
#   "self": ["+a: 1", "≠b: 2", "-c"],
#   "other": ["-a", "≠b: 3", "+c: 4"]
# }
Source code in datamate/namespaces.py
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def diff(
    self, other: "Namespace", name1: str = "self", name2: str = "other"
) -> "Namespace":
    """
    Compare two namespaces and return their differences.

    Args:
        other: The namespace to compare against.
        name1: Label for the current namespace in the diff output.
        name2: Label for the other namespace in the diff output.

    Returns:
        A namespace containing the differences, with + indicating additions,
        - indicating deletions, and ≠ indicating changes.

    Examples:
        ```python
        ns1 = Namespace({"a": 1, "b": 2})
        ns2 = Namespace({"b": 3, "c": 4})
        diff = ns1.diff(ns2)
        # Returns: {
        #   "self": ["+a: 1", "≠b: 2", "-c"],
        #   "other": ["-a", "≠b: 3", "+c: 4"]
        # }
        ```
    """
    if self is None or other is None:
        return Namespace({name1: self, name2: other})

    _self = namespacify(self)
    other = namespacify(other)
    diff1: List[str] = []
    diff2: List[str] = []
    diff = {name1: diff1, name2: diff2}

    def _diff(self: Namespace, other: Namespace, parent: str = "") -> None:
        for k, v in self.items():
            if k not in other:
                _diff1 = f"+{parent}.{k}: {v}" if parent else f"+{k}: {v}"
                _diff2 = f"-{parent}.{k}" if parent else f"-{k}"
                diff1.append(_diff1)
                diff2.append(_diff2)
            elif v == other[k]:
                pass
            elif isinstance(v, Namespace):
                _parent = f"{parent}.{k}" if parent else f"{k}"
                _diff(v, other[k], parent=_parent)
            else:
                _diff1 = f"≠{parent}.{k}: {v}" if parent else f"≠{k}: {v}"
                _diff2 = (
                    f"≠{parent}.{k}: {other[k]}" if parent else f"≠{k}: {other[k]}"
                )
                diff1.append(_diff1)
                diff2.append(_diff2)

        for k, v in other.items():
            if k not in self:
                _diff1 = f"-{parent}.{k}" if parent else f"-{k}"
                _diff2 = f"+{parent}.{k}: {v}" if parent else f"+{k}: {v}"
                diff1.append(_diff1)
                diff2.append(_diff2)

    _diff(_self, other)
    return namespacify(diff)

walk

walk()

Recursively walk through the namespace and yield key-value pairs.

Yields:

Type Description
Tuple[str, Any]

Tuples of (key, value) for each item in the namespace, including nested items.

Examples:

ns = Namespace({"a": 1, "b": {"c": 2}})
for key, value in ns.walk():
    print(f"{key}: {value}")
# Prints:
# a: 1
# b: {'c': 2}
# c: 2
Source code in datamate/namespaces.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
def walk(self) -> Iterator[Tuple[str, Any]]:
    """
    Recursively walk through the namespace and yield key-value pairs.

    Yields:
        Tuples of (key, value) for each item in the namespace, including nested items.

    Examples:
        ```python
        ns = Namespace({"a": 1, "b": {"c": 2}})
        for key, value in ns.walk():
            print(f"{key}: {value}")
        # Prints:
        # a: 1
        # b: {'c': 2}
        # c: 2
        ```
    """
    yield from dict_walk(self)

equal_values

equal_values(other)

Compare values recursively with another namespace.

Parameters:

Name Type Description Default
other Namespace

The namespace to compare against.

required

Returns:

Type Description
bool

True if all values match recursively, False otherwise.

Source code in datamate/namespaces.py
261
262
263
264
265
266
267
268
269
270
271
def equal_values(self, other: "Namespace") -> bool:
    """
    Compare values recursively with another namespace.

    Args:
        other: The namespace to compare against.

    Returns:
        True if all values match recursively, False otherwise.
    """
    return compare(self, other)

copy

copy()

Create a shallow copy of the namespace.

Source code in datamate/namespaces.py
273
274
275
def copy(self) -> "Namespace":
    """Create a shallow copy of the namespace."""
    return copy(self)

deepcopy

deepcopy()

Create a deep copy of the namespace.

Source code in datamate/namespaces.py
277
278
279
def deepcopy(self) -> "Namespace":
    """Create a deep copy of the namespace."""
    return deepcopy(self)

to_dict

to_dict()

Convert the namespace to a regular dictionary recursively.

Source code in datamate/namespaces.py
281
282
283
def to_dict(self) -> Dict[str, Any]:
    """Convert the namespace to a regular dictionary recursively."""
    return to_dict(self)

depth

depth()

Calculate the maximum depth of nested dictionaries.

Returns:

Type Description
int

The maximum nesting level, where 0 means no nesting.

Source code in datamate/namespaces.py
285
286
287
288
289
290
291
292
def depth(self) -> int:
    """
    Calculate the maximum depth of nested dictionaries.

    Returns:
        The maximum nesting level, where 0 means no nesting.
    """
    return depth(self)

namespacify

namespacify(obj)

Recursively convert mappings and ad-hoc Namespaces to Namespace objects.

Parameters:

Name Type Description Default
obj object

The object to convert into a Namespace.

required

Returns:

Type Description
Namespace

A new Namespace object with both item and attribute access.

Raises:

Type Description
TypeError

If the object cannot be converted to a Namespace.

Examples:

class MyClass:
    def __init__(self):
        self.a = 1
        self.b = {"c": 2}

obj = MyClass()
ns = namespacify(obj)
assert ns.a == 1
assert ns.b.c == 2
Source code in datamate/namespaces.py
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
def namespacify(obj: object) -> Namespace:
    """
    Recursively convert mappings and ad-hoc Namespaces to `Namespace` objects.

    Args:
        obj: The object to convert into a Namespace.

    Returns:
        A new Namespace object with both item and attribute access.

    Raises:
        TypeError: If the object cannot be converted to a Namespace.

    Examples:
        ```python
        class MyClass:
            def __init__(self):
                self.a = 1
                self.b = {"c": 2}

        obj = MyClass()
        ns = namespacify(obj)
        assert ns.a == 1
        assert ns.b.c == 2
        ```
    """
    if isinstance(obj, (type(None), bool, int, float, str, type, bytes)):
        return obj
    elif isinstance(obj, Path):
        return str(obj)
    elif isinstance(obj, (list, tuple)):
        return [namespacify(v) for v in obj]
    elif isinstance(obj, (ndarray)):
        return [namespacify(v.item()) for v in obj]
    elif isinstance(obj, Mapping):
        return Namespace({k: namespacify(obj[k]) for k in obj})
    elif get_origin(obj) is not None:
        return obj
    else:
        try:
            return namespacify(vars(obj))
        except TypeError as e:
            raise TypeError(f"namespacifying {obj} of type {type(obj)}: {e}.") from e

is_subset

is_subset(dict1, dict2)

Check whether dict2 is a subset of dict1.

Parameters:

Name Type Description Default
dict1 Union[Dict, Namespace]

The superset dictionary.

required
dict2 Union[Dict, Namespace]

The subset dictionary.

required

Returns:

Type Description
bool

True if dict2 is a subset of dict1, False otherwise.

Examples:

d1 = {"a": 1, "b": {"c": 2, "d": 3}}
d2 = {"b": {"c": 2}}
assert is_subset(d1, d2) == True
Source code in datamate/namespaces.py
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
def is_subset(dict1: Union[Dict, Namespace], dict2: Union[Dict, Namespace]) -> bool:
    """
    Check whether dict2 is a subset of dict1.

    Args:
        dict1: The superset dictionary.
        dict2: The subset dictionary.

    Returns:
        True if dict2 is a subset of dict1, False otherwise.

    Examples:
        ```python
        d1 = {"a": 1, "b": {"c": 2, "d": 3}}
        d2 = {"b": {"c": 2}}
        assert is_subset(d1, d2) == True
        ```
    """
    for key, value in dict2.items():
        if key not in dict1:
            return False
        if isinstance(value, dict):
            if not is_subset(dict1[key], value):
                return False
        else:
            if dict1[key] != value:
                return False
    return True

is_superset

is_superset(dict1, dict2)

Check whether dict2 is a superset of dict1.

Parameters:

Name Type Description Default
dict1 Union[Dict, Namespace]

The subset dictionary.

required
dict2 Union[Dict, Namespace]

The superset dictionary.

required

Returns:

Type Description
bool

True if dict2 is a superset of dict1, False otherwise.

Source code in datamate/namespaces.py
376
377
378
379
380
381
382
383
384
385
386
387
def is_superset(dict1: Union[Dict, Namespace], dict2: Union[Dict, Namespace]) -> bool:
    """
    Check whether dict2 is a superset of dict1.

    Args:
        dict1: The subset dictionary.
        dict2: The superset dictionary.

    Returns:
        True if dict2 is a superset of dict1, False otherwise.
    """
    return is_subset(dict2, dict1)

is_disjoint

is_disjoint(dict1, dict2)

Check whether two dictionaries are disjoint.

Parameters:

Name Type Description Default
dict1 Union[Dict, Namespace]

First dictionary to compare.

required
dict2 Union[Dict, Namespace]

Second dictionary to compare.

required

Returns:

Type Description
bool

True if the dictionaries have no common keys at any nesting level.

Examples:

d1 = {"a": 1, "b": {"c": 2}}
d2 = {"d": 3, "e": {"f": 4}}
assert is_disjoint(d1, d2) == True
Source code in datamate/namespaces.py
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
def is_disjoint(dict1: Union[Dict, Namespace], dict2: Union[Dict, Namespace]) -> bool:
    """
    Check whether two dictionaries are disjoint.

    Args:
        dict1: First dictionary to compare.
        dict2: Second dictionary to compare.

    Returns:
        True if the dictionaries have no common keys at any nesting level.

    Examples:
        ```python
        d1 = {"a": 1, "b": {"c": 2}}
        d2 = {"d": 3, "e": {"f": 4}}
        assert is_disjoint(d1, d2) == True
        ```
    """
    dict1_keys = set(key for key, _ in dict_walk(dict1))
    dict2_keys = set(key for key, _ in dict_walk(dict2))
    return dict1_keys.isdisjoint(dict2_keys)

to_dict

to_dict(obj)

Convert a Namespace or nested structure to a regular dictionary.

Parameters:

Name Type Description Default
obj Any

Object to convert to a dictionary.

required

Returns:

Type Description
Dict[str, Any]

A dictionary representation of the input object.

Source code in datamate/namespaces.py
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
def to_dict(obj: Any) -> Dict[str, Any]:
    """
    Convert a Namespace or nested structure to a regular dictionary.

    Args:
        obj: Object to convert to a dictionary.

    Returns:
        A dictionary representation of the input object.
    """
    if isinstance(obj, dict):
        return dict((k, to_dict(v)) for k, v in obj.items())
    if isinstance(obj, list):
        return [to_dict(v) for v in obj]
    elif isinstance(obj, Namespace):
        return dict((k, to_dict(v)) for k, v in obj.items())
    else:
        return obj

depth

depth(obj)

Calculate the maximum depth of nested dictionaries.

Parameters:

Name Type Description Default
obj Union[Dict, Namespace]

Dictionary or Namespace to measure depth of.

required

Returns:

Type Description
int

Maximum nesting level, where 0 means no nesting.

Examples:

d = {"a": 1, "b": {"c": {"d": 2}}}
assert depth(d) == 3
Source code in datamate/namespaces.py
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
def depth(obj: Union[Dict, Namespace]) -> int:
    """
    Calculate the maximum depth of nested dictionaries.

    Args:
        obj: Dictionary or Namespace to measure depth of.

    Returns:
        Maximum nesting level, where 0 means no nesting.

    Examples:
        ```python
        d = {"a": 1, "b": {"c": {"d": 2}}}
        assert depth(d) == 3
        ```
    """
    if isinstance(obj, (dict, Namespace)):
        return 1 + (max(map(depth, obj.values())) if obj else 0)
    return 0

pformat

pformat(obj)

Pretty format a Namespace or dictionary for display.

Parameters:

Name Type Description Default
obj Any

Object to format.

required

Returns:

Type Description
str

String representation of the object with proper indentation.

Source code in datamate/namespaces.py
454
455
456
457
458
459
460
461
462
463
464
465
466
467
def pformat(obj: Any) -> str:
    """
    Pretty format a Namespace or dictionary for display.

    Args:
        obj: Object to format.

    Returns:
        String representation of the object with proper indentation.
    """
    import pprint

    pretty_printer = pprint.PrettyPrinter(depth=100)
    return pretty_printer.pformat(obj)

compare

compare(obj1, obj2)

Type agnostic comparison for basic types and nested dictionaries.

Parameters:

Name Type Description Default
obj1 Any

First object to compare.

required
obj2 Any

Second object to compare.

required

Returns:

Type Description
Union[bool, Namespace]

Boolean for simple types, Namespace of comparison results for complex types.

Examples:

ns1 = Namespace({"a": 1, "b": {"c": 2}})
ns2 = Namespace({"a": 1, "b": {"c": 3}})
result = compare(ns1, ns2)
assert result.a == True
assert result.b.c == False
Source code in datamate/namespaces.py
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
def compare(obj1: Any, obj2: Any) -> Union[bool, "Namespace"]:
    """
    Type agnostic comparison for basic types and nested dictionaries.

    Args:
        obj1: First object to compare.
        obj2: Second object to compare.

    Returns:
        Boolean for simple types, Namespace of comparison results for complex types.

    Examples:
        ```python
        ns1 = Namespace({"a": 1, "b": {"c": 2}})
        ns2 = Namespace({"a": 1, "b": {"c": 3}})
        result = compare(ns1, ns2)
        assert result.a == True
        assert result.b.c == False
        ```
    """
    if isinstance(obj1, (type(None), bool, int, float, str, type)) and isinstance(
        obj2, (type(None), bool, int, float, str, type)
    ):
        return obj1 == obj2
    elif isinstance(obj1, (list, tuple)) and isinstance(obj2, (list, tuple)):
        return False if len(obj1) != len(obj2) else obj1 == obj2
    elif isinstance(obj1, (ndarray)) and isinstance(obj2, (ndarray)):
        return compare(obj1.tolist(), obj2.tolist())
    elif isinstance(obj1, Mapping) and isinstance(obj2, Mapping):
        _obj1, _obj2 = obj1.deepcopy(), obj2.deepcopy()
        out = {}
        for key in (
            set(_obj1.keys())
            .difference(set(_obj2.keys()))
            .union(set(_obj2.keys()).difference(set(_obj1.keys())))
        ):
            out[key] = False
            _obj1.pop(key, None)
            _obj2.pop(key, None)
        for k in _obj1:
            out[k] = compare(_obj1[k], obj2[k])
        return Namespace(out)
    elif not isinstance(obj1, type(obj2)):
        return False

all_true

all_true(obj)

Check if all elements in a nested structure evaluate to True.

Parameters:

Name Type Description Default
obj Any

Object to evaluate, can be nested structure.

required

Returns:

Type Description
bool

True if bool(element) is True for all elements in nested obj.

Raises:

Type Description
TypeError

If object cannot be evaluated.

Examples:

ns = Namespace({"a": True, "b": {"c": 1, "d": "text"}})
assert all_true(ns) == True
Source code in datamate/namespaces.py
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
def all_true(obj: Any) -> bool:
    """
    Check if all elements in a nested structure evaluate to True.

    Args:
        obj: Object to evaluate, can be nested structure.

    Returns:
        True if bool(element) is True for all elements in nested obj.

    Raises:
        TypeError: If object cannot be evaluated.

    Examples:
        ```python
        ns = Namespace({"a": True, "b": {"c": 1, "d": "text"}})
        assert all_true(ns) == True
        ```
    """
    if isinstance(obj, (type(None), bool, int, float, str, type, bytes)):
        return bool(obj)
    elif isinstance(obj, Path):
        return bool(obj)
    elif isinstance(obj, (list, tuple)):
        return all([all_true(v) for v in obj])
    elif isinstance(obj, (ndarray)):
        return all([all_true(v.item()) for v in obj])
    elif isinstance(obj, Mapping):
        return all([all_true(obj[k]) for k in obj])
    else:
        try:
            return all_true(vars(obj))
        except TypeError as e:
            raise TypeError(f"all {obj} of type {type(obj)}: {e}.") from e

dict_walk

dict_walk(dictionary)

Recursively walk through a nested dictionary and yield key-value pairs.

Parameters:

Name Type Description Default
dictionary Union[Dict, Namespace]

Dictionary or Namespace to traverse.

required

Yields:

Type Description
Tuple[str, Any]

Tuple of (key, value) for each item, including nested items.

Examples:

d = {"a": 1, "b": {"c": 2}}
for key, value in dict_walk(d):
    print(f"{key}: {value}")
# Prints:
# a: 1
# b: {'c': 2}
# c: 2
Source code in datamate/namespaces.py
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
def dict_walk(dictionary: Union[Dict, Namespace]) -> Iterator[Tuple[str, Any]]:
    """
    Recursively walk through a nested dictionary and yield key-value pairs.

    Args:
        dictionary: Dictionary or Namespace to traverse.

    Yields:
        Tuple of (key, value) for each item, including nested items.

    Examples:
        ```python
        d = {"a": 1, "b": {"c": 2}}
        for key, value in dict_walk(d):
            print(f"{key}: {value}")
        # Prints:
        # a: 1
        # b: {'c': 2}
        # c: 2
        ```
    """
    for key, value in dictionary.items():
        yield (key, value)
        if isinstance(value, dict):
            yield from dict_walk(value)