rics.collections.dicts#

Dict utility functions.

Module Attributes

KT

Key type.

VT

Value type.

VT_co

Covariant type.

HVT

Hashable value type.

OKT

Outer key type.

MakeType

See InheritedKeysDict.make().

Functions

compute_if_absent(d, key[, func])

Compute and store key using func if key is not in the dict.

flatten_dict(d, ~typing.Any], join_string, ...)

Flatten a nested dictionary.

format_changed_keys(mapping, *, baseline[, ...])

Format difference from a baseline mapping.

reverse_dict(d[, duplicate_key_action])

Swap keys and values.

unflatten_dict(d[, join_string])

Unflatten a flat dictionary.

Classes

DictComparer(mapping, *, baseline[, preference])

Utility class for comparing mapping types.

InheritedKeysDict([specific, default])

A nested dictionary that returns default-backed child dictionaries.

MakeDict

See InheritedKeysDict.make().

class KT#

Key type.

alias of TypeVar(‘KT’, bound=Hashable)

class VT#

Value type.

alias of TypeVar(‘VT’)

class VT_co#

Covariant type.

alias of TypeVar(‘VT_co’, covariant=True)

class HVT#

Hashable value type.

alias of TypeVar(‘HVT’, bound=Hashable)

class OKT#

Outer key type.

alias of TypeVar(‘OKT’, bound=Hashable)

compute_if_absent(d: dict[KT, VT], key: KT, func: Callable[[KT], VT] | None = None) VT[source]#

Compute and store key using func if key is not in the dict.

Parameters:
  • d – A dict.

  • key – The key to get.

  • func – A function to call for missing keys. Perform regular __getitem__ call if None.

Returns:

The value of k in d.

reverse_dict(d: Mapping[KT, HVT], duplicate_key_action: Literal['ignore', 'warn', 'raise', 'IGNORE', 'WARN', 'RAISE'] | ActionLevel = 'raise') dict[HVT, KT][source]#

Swap keys and values.

Parameters:
  • d – A dict to reverse.

  • duplicate_key_action – Action to take if the return dict has key collisions in the reversed dict, i.e. there are duplicate values in d. Set to ignore to allow.

Returns:

A reversed copy of d.

Examples

Reversing a dict with two elements.

>>> reverse_dict({"A": 0, "B": 1})
{0: 'A', 1: 'B'}
Raises:

ValueError – If there are duplicate values in d and duplicate_key_action='raise'.

flatten_dict(d: dict[~rics.collections.dicts.KT, ~typing.Any], join_string: str = '.', filter_predicate: ~typing.Callable[[~rics.collections.dicts.KT, ~typing.Any], bool] = <function <lambda>>, string_fn: ~typing.Callable[[~rics.collections.dicts.KT], str] | str | None = <class 'str'>) dict[str, Any][source]#

Flatten a nested dictionary.

This process is partially (or fully; depends on d and arguments) reversible, see unflatten_dict().

Parameters:
  • d – A dict to flatten. Keys must be strings.

  • join_string – Joiner for nested keys.

  • filter_predicate – A callable (key, value) -> should_keep. Default always returns True.

  • string_fn – A callable which takes a non-string key value and converts into to a string. If None, a type error will be raised for non-string keys. Default is str(key). Pass a string to resolve using get_by_full_name().

Returns:

A flattened version of d.

Examples

Flattening a shallow nested dict.

>>> flatten_dict({"foo": 0, "bar": {"foo": 1, "bar": 2}})
{'foo': 0, 'bar.foo': 1, 'bar.bar': 2}
unflatten_dict(d: dict[str | tuple[str, ...], Any], join_string: str = '.') dict[str, dict[str, Any] | Any][source]#

Unflatten a flat dictionary.

This process is reversible, see flatten_dict().

Parameters:
  • d – A flat dict to unflatten. Keys must be str or tuple.

  • join_string – Joiner for flattened keys. Ignored if d has tuple-keys.

Returns:

A nested version of d.

Examples

Unflatten a flat dict.

>>> unflatten_dict({"foo": 0, "bar.foo": 1, "bar.bar": 2})
{'foo': 0, 'bar': {'foo': 1, 'bar': 2}}

Tuple keys are also supported, including mixed key types.

>>> unflatten_dict({"foo": 0, ("bar", "foo"): 1, "bar.bar": 2})
{'foo': 0, 'bar': {'foo': 1, 'bar': 2}}
class InheritedKeysDict(specific: dict[OKT, dict[KT, VT]] | None = None, default: dict[KT, VT] | None = None)[source]#

Bases: Mapping[OKT, dict[KT, VT]]

A nested dictionary that returns default-backed child dictionaries.

The length of an InheritedKeysDict is equal to the number of specific outer keys, and is considered True when cast to bool if there are shared and/or specific keys present.

Parameters:
  • default – Shared (fallback) mappings for all contexts.

  • specific – Context-specific mappings, backed by the default fallback mappings.

Examples

A short demonstration.

>>> shared = {0: "fallback-for-0", 1: "fallback-for-1"}
>>> specific = {
...     "ctx0": {0: "c0-v0"},
...     "ctx1": {0: "c1-v0", 1: "c1-v1", 2: "c1-v2"},
... }
>>> ikd = InheritedKeysDict(default=shared, specific=specific)
>>> ikd
InheritedKeysDict(default={0: 'fallback-for-0', 1: 'fallback-for-1'}, specific={'ctx0': {0: 'c0-v0'},
'ctx1': {0: 'c1-v0', 1: 'c1-v1', 2: 'c1-v2'}})

The value of key 0 is inherited for ‘ctx0’. The ‘ctx1’-context defines all shared keys, as well as a unique key.

>>> ikd["ctx0"]
{0: 'c0-v0', 1: 'fallback-for-1'}
>>> ikd["ctx1"]
{0: 'c1-v0', 1: 'c1-v1', 2: 'c1-v2'}

The InheritedKeysDict.__contains__-method is True for all keys. Unknown keys simply return the default values. This will be an empty if no specific keys are specified.

>>> "unseen-key" in ikd
True
>>> ikd["unseen-key"]
{0: 'fallback-for-0', 1: 'fallback-for-1'}

The length of ikd is equal to the number of specific contexts (two in this case).

copy() InheritedKeysDict[OKT, KT, VT][source]#

Make a copy of this InheritedKeysDict.

classmethod make(arg: InheritedKeysDict[OKT, KT, VT] | MakeDict[OKT, KT, VT]) InheritedKeysDict[OKT, KT, VT][source]#

Create instance from a mapping.

The given argument must be on the format:

{
    "default": {key: value},
    "specific": {
        ctx0: {key: value},
        ctx1: {key: value},
        ...
        ctxN: {key: value},
    }
}

No other top-level keys are accepted, but neither default nor context-specific are required.

Parameters:

arg – Input to make an instance from.

Returns:

A new instance.

Raises:

ValueError – If there are any keys other than ‘default’ and ‘context-specific’ present in mapping.

class MakeDict[source]#

Bases: TypedDict, Generic[OKT, KT, VT]

See InheritedKeysDict.make().

default: dict[KT, VT]#
specific: dict[OKT, dict[KT, VT]]#
MakeType = rics.collections.dicts.InheritedKeysDict[~OKT, ~KT, ~VT] | rics.collections.dicts.MakeDict[~OKT, ~KT, ~VT]#

See InheritedKeysDict.make().

format_changed_keys(mapping: Mapping[KT, VT_co], *, baseline: Mapping[KT, VT_co], added: bool | Literal['keys'] = True, updated: bool | Literal['keys'] = True, removed: bool | Literal['keys'] = 'keys') str[source]#

Format difference from a baseline mapping.

Pass 'keys' instead of True / False to show affected keys without values.

Parameters:
  • mapping – The current mapping.

  • baseline – A baseline mapping.

  • added – Determines how and if to print added keys.

  • updated – Determines how and if to print updated keys.

  • removed – Determines how and if to print removed keys.

Returns:

Stylized difference between a mapping and the baseline.

Examples

>>> mapping = {0: None, 1: 1, -1: None}
>>> b = {0: 0, 1: 1, 2: 2}  # baseline

Default behavior.

>>> format_changed_keys(mapping, baseline=b)
'added(1): {-1: None}, updated(1): {0: None}, removed(1): {2}'

Showing added keys without values.

>>> format_changed_keys(mapping, baseline=b, added="keys")
'added(1): {-1}, updated(1): {0: None}, removed(1): {2}'

Removed keys are printed without values by default. Passing True prints the values.

>>> format_changed_keys(mapping, baseline=b, removed=True)
'added(1): {-1: None}, updated(1): {0: None}, removed(1): {2: 2}'

To hide a change type entirely, pass False instead.

>>> format_changed_keys(mapping, baseline=b, updated=False)
'added(1): {-1: None}, removed(1): {2}'

Notes

This is a convenience function that warps DictComparer.to_string().

class DictComparer(mapping: Mapping[KT, VT_co], *, baseline: Mapping[KT, VT_co], preference: Literal['old', 'new'] = 'new')[source]#

Bases: Generic[KT, VT_co]

Utility class for comparing mapping types.

Parameters:
  • mapping – The current mapping.

  • baseline – A baseline mapping.

  • preference – Determines values in updated. Set to ‘old’ to prefer baseline values.

Notes

Preference#

Value preference for updated keys.

alias of Literal[‘old’, ‘new’]

to_string(*, added: bool | Literal['keys'] = True, updated: bool | Literal['keys'] = True, removed: bool | Literal['keys'] = 'keys', formatter: Callable[[dict[str, set[KT] | dict[KT, VT_co]]], str] | None = None) str[source]#

Format changed keys.

Pass 'keys' instead of True / False to show affected keys without values.

Parameters:
  • added – Determines how and if to print added keys.

  • updated – Determines how and if to print updated keys.

  • removed – Determines how and if to print removed keys.

  • formatter – A callable ({change_type: {key: value} | {key, ...}) -> str, where change_type = 'added'|'updated'|'removed'. Use default_formatter() if None.

Returns:

Pretty-printed changes.

property baseline: Mapping[KT, VT_co]#

Get the original baseline mapping.

property mapping: Mapping[KT, VT_co]#

Get the original mapping.

property preference: Literal['old', 'new']#

Value preference for updated key.

property total: int#

Total number of changed keys.

property changed: dict[KT, VT_co][source]#

Get a dict containing all added, changed or removed keys.

property added: dict[KT, VT_co][source]#

Get a dict containing new keys.

property removed: dict[KT, VT_co][source]#

Get a dict containing removed keys.

property updated: dict[KT, VT_co][source]#

Get a dict containing updated values (see preference) for baseline keys.

classmethod default_formatter(changes_by_type: dict[str, set[Any] | dict[Any, Any]]) str[source]#

Default formatting implementation.