Dict Path

DictPath is a library for navigating json data.

The DictPath library offers a convenient way to get a specific value out of a structure of nested dicts and lists.

Writing DictPath expressions

A DictPath expression is a .-separated path. The following elements are supported:

  1. .dkey: Return the value under dkey in the dict. dkey cannot be an empty string. Use the * character to get all values of the dictionary.

  2. lst[lkey=lvalue]: Find a dictionary in a list of dictionaries. Find the dict with lkey=lvalue. lvalue can be an empty string. lst and lkey cannot be an empty string. If no or more than one dict matches the filter, a LookupError is raised. The * character can be used for lkey and lvalue to match respectively any key or value. \0 can be used for lvalue to match against the value``None``.

    If no single key uniquely identifies an object, multiple keys can be used: lst[lkey1=lvalue1][lkey2=lvalue2].

Each element of the path (keys or values) must escape the following special characters with a single backslash: \, [, ], ., * and =. Other characters must not be escaped.

A leading . character represent the entire data structure provided to the dict path library. As such, the following dict paths are logically equivalent to each other: a.b.c and .a.b.c. A dict path can also consist of a single dot (.). This expression represents the identity function.

Using DictPath in code

Warning

The dict path library only works correctly when the keys and values, referenced in a dict path expression, are of a primitive type and the type is the same for all keys and values at the same level. For example, {"True": 1, True: 2} is not a valid dictionary.

  • To convert a dictpath expression to a DictPath instance, use dict_path.to_path. Use dict_path.to_wild_path in order to allow wildcards (*) to be used in the dict path expression.

  • To get the element from a collection use DictPath.get_element(collection)

  • To set an element in a collection use DictPath.set_element(collection, value)

class inmanta.util.dict_path.DictPath[source]
A base class for all non-wild dict paths segments. The key difference between WildDictPath and DictPath subclasses are:
  1. WildDictPath can only get a list of elements, with get_elements. If no element is found, an empty list is returned, no error is raised.

  2. DictPath can not use get_elements as it is always expected to have exactly one match.

  3. DictPath can use get_element, which will return the matching element, or raise an exception if more or less than one is found.

  4. DictPath can set values, using set_element, and can build the dict structure expected by the path by using the construct flag in the get_element method.

abstract get_element(container: object, construct: bool = False) object[source]

Get the element identified by this Path from the given collection

Parameters:
  • container – the container to search in

  • construct – construct a dict on the location identified by this path in the container if the element doesn’t exist. Return this new dict.

Raises:

KeyError – if the element is not found or if more than one occurrence was found.

get_elements(container: object) list[object][source]

Get the elements identified by this Path from the given collection. If no element is matched, an empty list is returned.

Parameters:

container – the container to search in

abstract get_key() str[source]

Return the dictionary key referenced by this element in the dict path.

get_path_sections() Sequence[DictPath][source]

Get the individual parts of this path

abstract remove(container: object) None[source]
Remove an element if it exists:
  • On an InDict or a WildInDict: Remove the referenced key from the dictionary.

  • On a KeyedList or a WildKeyedList: Remove the referenced element from the list.

  • On a NullPath: This operation is not supported on a NullPath.

abstract set_element(container: object, value: object, construct: bool = True) None[source]

Set the element identified by this Path from the given collection.

If construct is True, all containers on the path towards the value are constructed if absent.

Raises:

LookupError – if the path leading to the element is not found or if more than one occurrence was found.

inmanta.util.dict_path.to_path(inp: str) DictPath[source]

Convert a string to a DictPath

Raises:

InvalidPathException – the path is not valid

inmanta.util.dict_path.to_wild_path(inp: str) WildDictPath[source]

Convert a string to a WildDictPath

Raises:

InvalidPathException – the path is not valid

Example

from inmanta.util import dict_path

container = {
    "a": "b",
    "c": {
        "e": "f"
    },
    "g": [
        {"h": "i", "j": "k"},
        {"h": "a", "j": "b"}
    ]
}

assert dict_path.to_path("a").get_element(container) == "b"
assert dict_path.to_path("c.e").get_element(container) == "f"
assert dict_path.to_path("g[h=i]").get_element(container) == {"h": "i", "j": "k"}

assert dict_path.to_wild_path("c.*").get_elements(container) == ["f"]
assert sorted(dict_path.to_wild_path("g[h=i].*").get_elements(container)) == ["i", "k"]
assert dict_path.to_wild_path("g[*=k]").get_elements(container) == [{"h": "i", "j": "k"}]

dict_path.to_path("g[h=b].i").set_element(container, "z")
assert dict_path.to_path("g[h=b]").get_element(container) == {"h": "b", "i": "z"}
assert dict_path.to_path("g[h=b].i").get_element(container) == "z"