Beispiel #1
0
def test_traversal_function_set(solar_system):
    """## set_"""

    # The **set_** function modifies the json document.

    # Use the set_ function to modify the star name.
    sun = get(path.star.name, solar_system)
    assert sun == 'Sun'
    set_(path.star.name, "RedSun", solar_system)
    sun = get(path.star.name, solar_system)
    assert sun == 'RedSun'
    assert solar_system["star"]["name"] == 'RedSun'

    # Use the set_ to add planet9.   This example creates multiple objects in one step.
    name = get(path.star.planets.outer[4].name, solar_system, default=None)
    assert name is None
    planets_count = len(list(find(path.star.planets.wc[wc].name, solar_system)))
    assert planets_count == 8

    set_(path.star.planets.outer[4].name, 'planet9', solar_system, cascade=True)

    name = get(path.star.planets.outer[4].name, solar_system, default=None)
    assert name == 'planet9'
    planets_count = len(list(find(path.star.planets.wc[wc].name, solar_system)))
    assert planets_count == 9
Beispiel #2
0
def test_query_examples_list_first_two_inner_planets(solar_system):
    expected = [solar_system["star"]["planets"]["inner"][0],
                solar_system["star"]["planets"]["inner"][1]]
    first_two_planets = [p for p in find(path.star.planets.inner[0:2], solar_system)]
    assert first_two_planets == expected

    first_two_planets = [p for p in find(path.star.planets.inner[0, 1], solar_system)]
    assert first_two_planets == expected
Beispiel #3
0
def test_path_has_filter_type_conversion(solar_system):
    """### has filter type conversion"""

    # Sometimes the value is the wrong type for the comparison operator. In this example the attribute
    # "Number of Moons" is str type.
    planets = [planet for planet in find(path.rec[has(path["Number of Moons"] > "5")].name, solar_system)]
    assert planets == ['Jupiter', 'Saturn']

    # This is how to convert the type to an int before applying the comparison operator.
    planets = [planet for planet in find(path.rec[has(path["Number of Moons"] > 5, int)].name, solar_system)]
    assert planets == ['Jupiter', 'Saturn', 'Uranus', 'Neptune']
Beispiel #4
0
def test_path_filter_has_all(solar_system):
    """### logical and, or and not filters"""

    # #### has_all
    # A regular express to test if second letter in the value is an a.
    second_letter_is_a = re.compile(r".a.*").fullmatch

    # The **has_all** function evaluates as the logical **and** operator.   It is equivalent to: (arg1 and arg2 and ...)
    found = [planet for planet in find(
        path.rec[has_all(path.diameter < 10000, (path.name, second_letter_is_a))].name,
        solar_system)
             ]
    assert found == ['Mars']

    # #### has_any
    # The **has_any** function evaluates as the logical **or** operator.   It is equivalent to: (arg1 and arg2 and ...)
    found = [planet for planet in find(
        path.rec[has_any(path.diameter < 10000, (path.name, second_letter_is_a))].name,
        solar_system)
             ]
    assert found == ['Mercury', 'Earth', 'Mars', 'Saturn']

    # #### has_not
    # The **has_not** function evaluates as the logical **not** operator.   It is equivalent to: (not arg)
    # This example find all the planets names not not equal to Earth.  Note the double nots.
    found = [planet for planet in find(
        path.rec[has_not(path.name != 'Earth')].name,
        solar_system)
             ]
    assert found == ['Earth']

    # #### Combining has, has_all, has_any, and has_not filters.
    # Each of the **has** function can be passed as arguments to any of the other **has** function to construct complex
    # boolean equation.  This example is equivalent to:
    # (10000 > diameter  or diameter > 20000) and second_letter_is_a(name))
    found = [planet for planet in find(
        path.rec[has_all(has_any(path.diameter < 10000, path.diameter > 20000), (path.name, second_letter_is_a))].name,
        solar_system)
             ]
    assert found == ['Mars', 'Saturn']

    # #### has.these
    # The decorator **has.these** can be used to construct the boolean equations more explicitly.  This example shows
    # to use python built in and, or and not operators.
    @has.these(path.diameter < 10000, path.diameter > 20000, (path.name, second_letter_is_a))
    def predicate(parent_match: Match, small_diameter, large_diameter, name_second_letter_is_a):
        return (small_diameter(parent_match) or large_diameter(parent_match)) and name_second_letter_is_a(parent_match)

    found = [planet for planet in find(path.rec[predicate].name, solar_system)]
    assert found == ['Mars', 'Saturn']
Beispiel #5
0
def test_traversal_function_find(solar_system):
    """## find"""

    # The **find** function returns an Iterator that iterates to each value the path leads to.  Each value is
    # determine on its iteration.

    # Find all of the planet names.
    inner_planets = [planet for planet in find(path.star.planets.inner[wc].name, solar_system)]
    assert inner_planets == ['Mercury', 'Venus', 'Earth', 'Mars']

    # The data source can be a json data structure or a Match object.
    parent_match = get_match(path.star.planets.inner, solar_system)
    inner_planets = [planet for planet in find(path[wc].name, parent_match)]
    assert inner_planets == ['Mercury', 'Venus', 'Earth', 'Mars']
Beispiel #6
0
def test_path_list_wildcard(solar_system):
    """### Wildcard as an Index."""

    # The **wildcard** word can be used as a list index.   It is useful for iterating over attributes.
    all_outer = [planet for planet in find(path.star.planets.outer[wildcard].name, solar_system)]
    assert all_outer == ["Jupiter", "Saturn", "Uranus", "Neptune"]

    # The **wc** is the short version of wildcard.
    all_outer = [planet for planet in find(path.star.planets.outer[wc].name, solar_system)]
    assert all_outer == ["Jupiter", "Saturn", "Uranus", "Neptune"]

    # The dictionary wildcard is given as dot notation and cannot be used to iterator over a list.  The list wildcard
    # is given as an index and cannot be used to iterate over dictionary keys.
    all_planets = [p for p in find(path.star.planets.wc[wc].name, solar_system)]
    assert all_planets == ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
Beispiel #7
0
def test_path_keys_wildcard(solar_system):
    """### Wildcard as a Key."""

    # The **wildcard** attribute specifies all sibling keys.   It is useful for iterating over attributes.
    star_children = [child for child in find(path.star.wildcard, solar_system)]
    assert star_children == [solar_system["star"]["name"],
                             solar_system["star"]["diameter"],
                             solar_system["star"]["age"],
                             solar_system["star"]["planets"], ]

    # The **wc** is the short version of wildcard.
    star_children = [child for child in find(path.star.wc, solar_system)]
    assert star_children == [solar_system["star"]["name"],
                             solar_system["star"]["diameter"],
                             solar_system["star"]["age"],
                             solar_system["star"]["planets"], ]
Beispiel #8
0
def test_quick_start(solar_system):
    """# Quick start"""

    # All of the treepath components should be imported as follows:
    # ```python
    # from treepath import path, find, wc, set_, get, has, get_match, find_matches, pathd, wildcard, \
    #     MatchNotFoundError, Match, log_to, has_all, has_any, has_not, pprop, mprop
    # ```

    # A treepath example that fetches the value 1 from data.
    data = {
        "a": {
            "b": [
                {
                    "c": 1
                },
                {
                    "c": 2
                }]
        }
    }
    value = get(path.a.b[0].c, data)
    assert value == 1

    # A treepath example that fetches the values 1 and 2 from data.
    value = [value for value in find(path.a.b[wc].c, data)]
    assert value == [1, 2]
Beispiel #9
0
def test_k_a_a_k_a_a_a_k_find_all_tuple(k_a_a_k_a_a_a_k):
    result = find(path.y[0, 1, 2][0, 1, 2].y[0, 1, 2][0, 1, 2][0, 1, 2].y, k_a_a_k_a_a_a_k)
    for expected_path, expected_value in gen_test_data(k_a_a_k_a_a_a_k, nyiy, naia, naia, nyiy, naia, naia, naia, yyia):
        actual = next(result)
        assert actual == expected_value

    assert_done_iterating(result)
Beispiel #10
0
def test_keys_find_rec_x_trace(keys):
    expected_trace_messages = [
        " at $.x got {'x': {'x': '1', 'y'...",
        " at $.x. got {'x': {'x': '1', 'y'...",
        " at $.x.x got {'x': '1', 'y': '2',...",
        " at $.x. got {'x': '1', 'y': '2',...", " at $.x.x.x got '1'",
        " at $.x.x. got '1'", ' at $.x.x.x. got no match',
        " at $.x.x. got '2'", ' at $.x.x.y. got no match',
        " at $.x.x. got '3'", ' at $.x.x.z. got no match',
        ' at $.x.x. got no match', " at $.x. got {'x': '4', 'y': '5',...",
        " at $.x.y.x got '4'", " at $.x.y. got '4'",
        ' at $.x.y.x. got no match', " at $.x.y. got '5'",
        ' at $.x.y.y. got no match', " at $.x.y. got '6'",
        ' at $.x.y.z. got no match', ' at $.x.y. got no match',
        " at $.x. got {'x': '7', 'y': '8',...", " at $.x.z.x got '7'",
        " at $.x.z. got '7'", ' at $.x.z.x. got no match',
        " at $.x.z. got '8'", ' at $.x.z.y. got no match',
        " at $.x.z. got '9'", ' at $.x.z.z. got no match',
        ' at $.x.z. got no match', ' at $.x. got no match'
    ]

    actual_trace_messages = []

    def mock_print(message):
        actual_trace_messages.append(message)

    for _ in find(path.x.rec.x, keys, trace=log_to(mock_print)):
        pass

    assert actual_trace_messages == expected_trace_messages
Beispiel #11
0
def test_path_filter_customer_predicate(solar_system):
    """### A custom filter."""

    # A predicate is a single argument function that returns anything. The argument is the current match.   The has
    # function is a fancy predicate.

    # This example writes a custom predicate that find all of Earth's neighbours.
    def my_neighbor_is_earth(match: Match):
        i_am_planet = get_match(path.parent.parent.parent.planets, match, must_match=False)
        if not i_am_planet:
            return False

        index_before_planet = match.data_name - 1
        before_planet = get_match(path[index_before_planet][has(path.name == "Earth")], match.parent,
                                  must_match=False)
        if before_planet:
            return True

        index_after_planet = match.data_name + 1
        before_planet = get_match(path[index_after_planet][has(path.name == "Earth")], match.parent,
                                  must_match=False)
        if before_planet:
            return True

        return False

    earth = [planet for planet in find(path.rec[my_neighbor_is_earth].name, solar_system)]
    assert earth == ['Venus', 'Mars']
Beispiel #12
0
def test_a_k_k_a_k_k_k_a_find_all_tuple(a_k_k_a_k_k_k_a):
    result = find(path[0, 1, 2].y.y[0, 1, 2].y.y.y[0, 1, 2], a_k_k_a_k_k_k_a)
    for expected_path, expected_value in gen_test_data(a_k_k_a_k_k_k_a, naia, nyiy, nyiy, naia, nyiy, nyiy, nyiy, yaia):
        actual = next(result)
        assert actual == expected_value

    assert_done_iterating(result)
Beispiel #13
0
def test_a_k_k_a_k_k_k_a_find_all_wildcard(a_k_k_a_k_k_k_a):
    result = find(path[0].wc.wc[0].wc.wc.wc[0], a_k_k_a_k_k_k_a)
    for expected_path, expected_value in gen_test_data(a_k_k_a_k_k_k_a, n0i0,
                                                       naia, naia, n0i0, naia,
                                                       naia, naia, y0i0):
        actual = next(result)
        assert actual == expected_value
    assert_done_iterating(result)
Beispiel #14
0
def test_path_recursion(solar_system):
    """## Recursion"""

    # The **recursive** word implies recursive search.  It executes a preorder tree traversal.  The search algorithm
    # descends the tree hierarchy evaluating the path on each vertex until a match occurs.  On each iteration it
    # continues where it left off. This is an example that finds all the planets names.
    all_planets = [p for p in find(path.star.planets.recursive.name, solar_system)]
    assert all_planets == ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']

    # The **rec** is the short version of recursive.
    all_planets = [p for p in find(path.star.planets.rec.name, solar_system)]
    assert all_planets == ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']

    # Here is another example that finds all the celestial bodies names.
    all_celestial_bodies = [p for p in find(path.rec.name, solar_system)]
    assert all_celestial_bodies == ['Sun', 'Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus',
                                    'Neptune']
Beispiel #15
0
def test_3d_find_all_slice_variety(three_dimensional_list):
    result = find(path[::-1][:1:][0::2], three_dimensional_list)
    for x in range(*slice(None, None, -1).indices(3)):
        for y in range(*slice(None, 1, None).indices(3)):
            for z in range(*slice(0, None, 2).indices(3)):
                actual = next(result)
                assert actual == three_dimensional_list[x][y][z]
    assert_done_iterating(result)
Beispiel #16
0
def test_3d_find_all_comma_delimited(three_dimensional_list):
    result = find(path[2, 1, 0][0, 1][0, 2, 1], three_dimensional_list)
    for x in [2, 1, 0]:
        for y in [0, 1]:
            for z in [0, 2, 1]:
                actual = next(result)
                assert actual == three_dimensional_list[x][y][z]
    assert_done_iterating(result)
Beispiel #17
0
def test_k_a_a_k_a_a_a_k_find_all_wildcard(k_a_a_k_a_a_a_k):
    result = find(path.wc[0][0].wc[0][0][0].wc, k_a_a_k_a_a_a_k)
    for expected_path, expected_value in gen_test_data(k_a_a_k_a_a_a_k, naia,
                                                       n0i0, n0i0, naia, n0i0,
                                                       n0i0, n0i0, yaia):
        actual = next(result)
        assert actual == expected_value
    assert_done_iterating(result)
Beispiel #18
0
def test_path_has_filter(solar_system):
    """### has filter"""

    # The **has** function is a filter that evaluates a branched off path relative to its parent path.  This example
    # finds all celestial bodies that have planets.
    sun = get(path.rec[has(path.planets)].name, solar_system)
    assert sun == "Sun"

    # This search finds all celestial bodies that have a has-moons attribute.
    all_celestial_bodies_moon_attribute = [planet for planet in find(path.rec[has(pathd.has_moons)].name, solar_system)]
    assert all_celestial_bodies_moon_attribute == ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus',
                                                   'Neptune']

    # This search finds all celestial bodies that have moons. Note the **operator.truth** is used to exclude planets
    # that don't have moons.
    all_celestial_bodies_moon_attribute = [planet for planet in
                                           find(path.rec[has(pathd.has_moons, operator.truth)].name, solar_system)]
    assert all_celestial_bodies_moon_attribute == ['Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
Beispiel #19
0
def test_k_a_a_k_a_a_a_k_find_all_tuple(k_a_a_k_a_a_a_k):
    result = find(
        path["x", "y", "z"][0][0]["x", "y", "z"][0][0][0]["x", "y", "z"],
        k_a_a_k_a_a_a_k)
    for expected_path, expected_value in gen_test_data(k_a_a_k_a_a_a_k, naia,
                                                       n0i0, n0i0, naia, n0i0,
                                                       n0i0, n0i0, yaia):
        actual = next(result)
        assert actual == expected_value
    assert_done_iterating(result)
Beispiel #20
0
def test_path_list(solar_system):
    """### Indexes"""

    # List can be access using index.
    earth = get(path.star.planets.inner[2], solar_system)
    assert earth == solar_system["star"]["planets"]["inner"][2]

    # List the third inner and outer planet.
    last_two = [planet for planet in find(path.star.wc.wc[2].name, solar_system)]
    assert last_two == ['Earth', 'Uranus']
Beispiel #21
0
def test_path_list_slice(solar_system):
    """### Slices"""

    # List can be access using slices.

    # List the first two planets.
    first_two = [planet for planet in find(path.star.planets.outer[:2].name, solar_system)]
    assert first_two == ["Jupiter", "Saturn"]

    # List the last two planets.
    last_two = [planet for planet in find(path.star.planets.outer[-2:].name, solar_system)]
    assert last_two == ["Uranus", "Neptune"]

    # List all outer planets in reverse.
    last_two = [planet for planet in find(path.star.planets.outer[::-1].name, solar_system)]
    assert last_two == ["Neptune", "Uranus", "Saturn", "Jupiter"]

    # List the last inner and outer planets.
    last_two = [planet for planet in find(path.star.wc.wc[-1:].name, solar_system)]
    assert last_two == ["Mars", "Neptune"]
Beispiel #22
0
def test_3d_find_specific_tuple(three_dimensional_list):
    result = find(path[2, 3, "a", 1], three_dimensional_list)
    actual = next(result)
    expected = three_dimensional_list[2]
    assert actual == expected

    actual = next(result)
    expected = three_dimensional_list[1]
    assert actual == expected

    assert_done_iterating(result)
Beispiel #23
0
def test_path_has_filter_operators_as_single_argument_functions(solar_system):
    """### has filter comparison operators as single argument functions"""

    # A filter operator can be specified as a single argument function.  Here an example that searches for planets that
    # have the same diameter as earth.
    earths_diameter = partial(operator.eq, 12756)
    earth = [planet for planet in find(path.rec[has(path.diameter, earths_diameter)].name, solar_system)]
    assert earth == ['Earth']

    # Any single argument function can be used as an operator.  This example uses a Regular Expression to finds
    # planets that end with s.
    name_ends_with_s = re.compile(r"\w+s").match
    earth = [planet for planet in find(path.rec[has(path.name, name_ends_with_s)].name, solar_system)]
    assert earth == ['Venus', 'Mars', 'Uranus']

    # This example uses a closure to find planets that have the same diameter as earth.
    def smaller_than_earth(value):
        return value < 12756

    earth = [planet for planet in find(path.rec[has(path.diameter, smaller_than_earth)].name, solar_system)]
    assert earth == ['Mercury', 'Venus', 'Mars']
Beispiel #24
0
def test_TraversingError_validate_message(keys):
    expected = f"TraversingError(Evaluation of predicate failed because of error: NotImplementedError(){os.linesep}" \
               f"  path: $.x.x.x{os.linesep}" \
               f"  last_match: $.x.x.x=1)"
    with pytest.raises(TraversingError) as exc_info:

        def crap(*args):
            raise NotImplementedError

        next(find(path.x.x.x[crap], keys))

    assert repr(exc_info.value) == expected
Beispiel #25
0
def test_a_k_k_a_k_k_k_a_find_all_tuple(a_k_k_a_k_k_k_a):
    result = find(
        path[0]["x", "y", "z"]["x", "y", "z"][0]["x", "y", "z"]["x", "y",
                                                                "z"]["x", "y",
                                                                     "z"][0],
        a_k_k_a_k_k_k_a)
    for expected_path, expected_value in gen_test_data(a_k_k_a_k_k_k_a, n0i0,
                                                       naia, naia, n0i0, naia,
                                                       naia, naia, y0i0):
        actual = next(result)
        assert actual == expected_value
    assert_done_iterating(result)
Beispiel #26
0
def test_keys_find_specific_tuple(keys):
    result = find(path["z", "a", 1, "x"], keys)

    actual = next(result)
    expected = keys["z"]
    assert actual == expected

    actual = next(result)
    expected = keys["x"]
    assert actual == expected

    assert_done_iterating(result)
Beispiel #27
0
def test_keys_find_x_a_z_trace(keys):
    expected_trace_messages = [
        " at $.x got {'x': {'x': '1', 'y'...", ' at $.x.a got no match'
    ]
    actual_trace_messages = []

    def mock_print(message):
        actual_trace_messages.append(message)

    for _ in find(path.x.a.z, keys, trace=log_to(mock_print)):
        pass

    assert actual_trace_messages == expected_trace_messages
Beispiel #28
0
def test_tracing(solar_system):
    """## Tracing Debugging"""

    # All of the functions: get, find, get_match and find_matchesm, support tracing.   An option, when enabled,
    # records the route the algorithm takes to determine a match.

    # This example logs the route the algorithm takes to find the inner planets.  The **print**
    # function is give to capture the logs, but any single argument function can be used.
    inner_planets = [planet for planet in find(path.star.planets.inner[wc].name, solar_system, trace=log_to(print))]
    assert inner_planets == ['Mercury', 'Venus', 'Earth', 'Mars']

    # The results
    """
Beispiel #29
0
def test_keys_find_x_y_z_trace(keys):
    expected_trace_messages = [
        " at $.x got {'x': {'x': '1', 'y'...",
        " at $.x.y got {'x': '4', 'y': '5',...", " at $.x.y.z got '6'"
    ]
    actual_trace_messages = []

    def mock_print(message):
        actual_trace_messages.append(message)

    expected = keys["x"]["y"]["z"]
    for actual in find(path.x.y.z, keys, trace=log_to(mock_print)):
        assert actual == expected
        assert actual_trace_messages == expected_trace_messages
Beispiel #30
0
def test_keys_find_z_y_has_a_then_no_match_trace(keys):
    expected_trace_messages = [
        " at $.z got {'x': {'x': '19', 'y...",
        " at $.z.y got {'x': '22', 'y': '23...", '     has .a got no match',
        ' at $.z.y[has($.a)] got no match'
    ]
    actual_trace_messages = []

    def mock_print(message):
        actual_trace_messages.append(message)

    for _ in find(path.z.y[has(path.a)].x, keys, trace=log_to(mock_print)):
        pass

    assert actual_trace_messages == expected_trace_messages