Common Utilities

deep_get(dictionary, deep_key, default=None, do_flatten=False, *, enable_undefined=False)[source]

Get a nested value from within a dictionary. Supports also nested lists, in which case the result is a a list of values

Arguments:
dictionary(dict):

the input

deep_key(str):

nested key of the form aaa.bbb.ccc.ddd

default:

the default value, in case the path does not exist

do_flatten(bool): optional

flatten the outputs, in case the result is multiple outputs

enable_undefined(bool): optional

if set, then keys that are not in the dictionary return Undefined. otherwise, returns None for undefined keys. Default is False.

Returns:

the nested attribute(s) or the default value. For example:

For example:

example = {"a": {"b": [{"c": [None, {"d": [1]}]}, {"c": [None, {"d": [2]}, {"d": 3}]}, {"c": []}]}}
assert deep_get(example, "a.b.c.d") == [[[1]], [[2], 3], []]
assert deep_get(example, "a.b.c.d", do_flatten=True) == [1, 2, 3]
nested(func, default=None)[source]

get a nested value if it exists, or return the given default if it doesn’t

Arguments:
func(function):

A function with no arguments that returns the nested value

default:

the default value, in case the nested value does not exist

Returns:

the nested attribute(s) or the default value. For example:

For example:

d = D(c=C(b=B(a=A(i=5))))
assert nested(lambda: d.c.b.a.i) == 5

d.c.foo = [1, 2, 3]
assert nested(lambda: d.c.foo[100]) is None
assert nested(lambda: d.c.foo[2]) == 3
flatten(iterable, ignore_none=False) → list[source]

Flatten an iterable completely, getting rid of None values

Arguments:
iterable(Iterable):

the input

ignore_none(bool): optional

whether to skip None values or not. Default is False.

Returns:

A flattened list

flatten(
[
  [[1]],
  [[2], 3, None, (5,)],
  []
], ignore_none=True) == [1, 2, 3, 5]
default_factories(func)[source]

A function decorator that allows to have default values that are generators of the actual default values to be used. This is useful when the default values are mutable, like dicts or lists

Arguments:
iterable(Iterable):

the input

ignore_none(bool): optional

whether to skip None values or not. Default is False.

Returns:

A flattened list

For example:

@default_factories
def func(a, b = 0, c = list, d = dict):
     return a, b, c, d

assert func(1) == 1, 0, [] , {}

Testing Utilities

find_diff(first, second) → dict | str[source]

Utility for testing to find the differences between two values that are “supposed” to be equal. This is useful to have more useful assertion error messages, especially with pytest, using pytest_assertrepr_compare.

Arguments:
first:

first value. Can be a Structure, list, dict, set

second:

second value. Can be a Structure, list, dict, set

Returns:

a dictionary with the differences, or the difference is trivial - a string. Note that this function does not employ any sophisticated algorithm and is meant just as a best-effort utility for testing.

Example of the output (taken from the unit tests):

actual = find_diff(bar2, bar1)
assert actual == {
    "f": {
        "m['aaa']": {"age": "123 vs 12"},
        "m": {
            "missing values": ["bbb"],
            "additional values": ["ccc"],
        },
    },
    "missing values": ["x"],
}

Showing the Differences in Failed pytest Assertions

Typedpy includes a pytest hook that uses find_diff() to explain a failed assertion that two Structures are equal. To enable it, import it in your conftest.py:

# conftest.py
from typedpy.testing import pytest_assertrepr_compare  # noqa: F401

Then, a failing assert actual == expected between two Structures shows exactly what is different, including in nested Structures and collections:

class Address(Structure):
    city: str
    zip_code: str

class Person(Structure):
    name: str
    age: int
    address: Address
    tags: list[str]
    scores: Map[str, int]

def test_people_equal():
    expected = Person(name="Joe", age=30, address=Address(city="NY", zip_code="10001"),
                      tags=["a", "b"], scores={"math": 90, "art": 80})
    actual = Person(name="Joe", age=31, address=Address(city="NY", zip_code="10002"),
                    tags=["b", "a"], scores={"math": 90, "music": 70})
    assert actual == expected

The failure message is:

found the following differences between the structures:
{"address": {"zip_code": "10002 vs 10001"}, "age": "31 vs 30", "scores": {"missing values": ["music"], "additional values": ["art"]}, "tags": {"different location": ["index 0 vs 1", "index 1 vs 0"]}}

Values that appear on the left side of the comparison but not on the right side are listed under “missing values”, and values that appear on the right side but not on the left side are listed under “additional values”. In the example above, “music” appears in actual but not in expected, so it is listed under “missing values”.

To print the differences as indented JSON, one entry per line, set:

# conftest.py
from typedpy.structures import TypedPyDefaults

TypedPyDefaults.pretty_print_pytest_diff = True

Then the failure message above becomes:

found the following differences between the structures:
{
  "address": {
    "zip_code": "10002 vs 10001"
  },
  "age": "31 vs 30",
  "scores": {
    "missing values": [
      "music"
    ],
    "additional values": [
      "art"
    ]
  },
  "tags": {
    "different location": [
      "index 0 vs 1",
      "index 1 vs 0"
    ]
  }
}

Note that pytest truncates long assertion messages to about 8 lines, unless running with -vv, so the indented format is most useful together with -vv.

Verifying Trusted Deserialization Compatibility

See Deserialization From Trusted Data.

assert_trusted_deserialization_mapper_is_safe(cls, wrapping_mapper=None)[source]