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
def test_path_keys(solar_system): """### Keys""" # The dictionary keys are referenced as dynamic attributes on a path. inner_from_attribute = get(path.star.planets.inner, solar_system) inner_from_string_keys = get(path["star"]["planets"]["inner"], solar_system) assert inner_from_attribute == inner_from_string_keys == solar_system["star"]["planets"]["inner"]
def test_keys_get_root_has_x_or_y_or_z(keys): expected = keys actual = get(path[has_any(path.x, path.b, path.c)], keys) assert actual == expected actual = get(path[has_any(path.a, path.y, path.c)], keys) assert actual == expected actual = get(path[has_any(path.a, path.b, path.z)], keys) assert actual == expected
def test_InfiniteLoopDetected_validate_message(): expected = 'InfiniteLoopDetected(Traversing seems to go on for ever on path: $..)' one = {} two = {} one["x"] = two two["x"] = one with pytest.raises(InfiniteLoopDetected) as exc_info: get(path.rec.a, one) assert repr(exc_info.value) == expected
def test_path_keys_special_characters(solar_system): """### Keys With Special Characters""" # Dictionary keys that are not valid python syntax can be referenced as double quoted strings. sun_equatorial_diameter = get(path.star.planets.inner[0]["Number of Moons"], solar_system) assert sun_equatorial_diameter == solar_system["star"]["planets"]["inner"][0]["Number of Moons"] # Dictionaries that have alot of keys with a dash in the name can can use **pathd** instead. It will interpret # path attributes with underscore as dashes. mercury_has_moons = get(pathd.star.planets.inner[0].has_moons, solar_system) assert mercury_has_moons == solar_system["star"]["planets"]["inner"][0]["has-moons"]
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]
def test_get_0_0_0_to_1_empty_data_cascade(): actual = list() expected_return = 1 expected = [[[1]]] actual_return = get(path[0][0][0], actual, default=1, store_default=True) assert actual == expected assert actual_return == expected_return
def test_keys_custom_filter(keys): def custom_filter(match: Match): return match.data == "2" expected = "2" actual = get(path.x.x.wc[custom_filter], keys) assert actual == expected
def test_get_a_b_c_to_1_store_default(): actual = {"x": 2} expected_return = 1 expected = {"a": {"b": {"c": 1}}, "x": 2} actual_return = get(path.a.b.c, actual, default=1, store_default=True) assert actual == expected assert actual_return == expected_return
def test_keys_get_root_has_x_and_y_and_z_trace(keys): expected_trace_messages = [ " has .x got {'x': {'x': '1', 'y'...", " has .y got {'x': {'x': '10', 'y...", " has .z got {'x': {'x': '19', 'y...", " at $[has($.x) and has($.y) and has($.z)] got {'x': {'x': {'x': '1..." ] actual_trace_messages = [] def mock_print(message): actual_trace_messages.append(message) expected = keys get(path[has_all(path.x, path.y, path.z)], keys, trace=log_to(mock_print)) assert actual_trace_messages == expected_trace_messages
def test_a_k_k_a_k_k_k_a_multiple_has(a_k_k_a_k_k_k_a): expected_trace_messages = [ " at $[0] got {'x': {'x': [{'x': {...", " has .x got {'x': [{'x': {'x': {...", " has .x.x got [{'x': {'x': {'x': [...", " has .x.x[1] got {'x': {'x': {'x': ['...", " at $[0][has($.x.x[1])] got {'x': {'x': [{'x': {...", " at $[0].y got {'x': [{'x': {'x': {...", " at $[0].y.y got [{'x': {'x': {'x': [...", " at $[0].y.y[0] got {'x': {'x': {'x': ['...", " has .x got {'x': {'x': ['973', ...", " has .x.x got {'x': ['973', '974',...", " has .z got ['979', '980', '981'...", " has .x.x[has($.z)] got {'x': ['973', '974',...", " has .x.x.x got ['973', '974', '975'...", " has .x.x.x[1] got '974'", " at $[0].y.y[0][has($.x.x[has($.z)].x[1])] got {'x': {'x': {'x': ['...", " at $[0].y.y[0].y got {'x': {'x': ['1000',...", " at $[0].y.y[0].y.y got {'x': ['1009', '1010...", " at $[0].y.y[0].y.y.y got ['1012', '1013', '10...", " at $[0].y.y[0].y.y.y[0] got '1012'" ] actual_trace_messages = [] def mock_print(message): actual_trace_messages.append(message) a = get(path[0][has(path.x.x[1])].y.y[0][has(path.x.x[has( path.z)].x[1])].y.y.y[0], a_k_k_a_k_k_k_a, trace=log_to(mock_print)) assert a == '1012' assert actual_trace_messages == expected_trace_messages
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']
def test_traversal_function_get(solar_system): """## get""" # The **get** function returns the first value the path leads to. # Get the star name from the solar_system sun = get(path.star.name, solar_system) assert sun == 'Sun' # When there is no match, MatchNotFoundError is thrown. try: get(path.star.human_population, solar_system) assert False, "Not expecting humans on the sun" except MatchNotFoundError: pass # Or if preferred, a default value can be given. human_population = get(path.star.human_population, solar_system, default=0) assert human_population == 0 # The default value can be automatically injected in to json document human_population = get(path.star.human_population, solar_system, default=1, store_default=True) assert human_population == solar_system["star"]["human_population"] # The data source can be a json data structure or a Match object. parent_match = get_match(path.star.planets.inner, solar_system) name = get(path[2].name, parent_match) assert name == "Earth"
def test_keys_find_x_has_x_eq_1_and_has_y_trace(keys): expected_trace_messages = [ " at $.x got {'x': {'x': '1', 'y'...", " has .z got {'x': '7', 'y': '8',...", " at $.x[has($.z)] got {'x': {'x': '1', 'y'...", " at $.x.x got {'x': '1', 'y': '2',...", " has .x got '1'", " has .y got '2'", " at $.x.x[has($.x == 1, <class 'int'>), has($.y)] got {'x': '1', 'y': " "'2',...", " at $.x.x.x got '1'" ] actual_trace_messages = [] def mock_print(message): actual_trace_messages.append(message) @has.these((path.x == 1, int), path.y) def predicate(parent_match: Match, x, y) -> Any: return x(parent_match) and y(parent_match) compiled_one = path.x[has(path.z)].x[predicate].x get(compiled_one, keys, trace=log_to(mock_print)) assert actual_trace_messages == expected_trace_messages
def test_keys_get_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"] actual = get(path.x.y.z, keys, trace=log_to(mock_print)) assert actual == expected assert actual_trace_messages == expected_trace_messages
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']
def test_empty_dict_index_MatchNotFoundError(): empty_dict = {} with pytest.raises(MatchNotFoundError): get(path.parent, empty_dict)
def test_vertex_tuple_invalid_for_dict(keys): expected = None actual = get(path.x.x.x[1, 2], keys, default=None) assert actual == expected
def test_vertex_slice_does_not_iterate_dict(keys): expected = None actual = get(path.x[:1], keys, default=None) assert actual == expected
def test_empty_list_index_MatchNotFoundError(): empty_list = [] with pytest.raises(MatchNotFoundError): get(path[0], empty_list)
def test_list_wildcard_on_key_MatchNotFoundError(k_a_a_k_a_a_a_k): with pytest.raises(MatchNotFoundError): get(path[wildcard], k_a_a_k_a_a_a_k)
def test_empty_list_wildcard_MatchNotFoundError(): empty_list = [] with pytest.raises(MatchNotFoundError): get(path[wildcard], empty_list)
def test_keys_x_y_z(keys): expected = keys["x"]["y"]["z"] actual = get(path.x.y.z, keys) assert actual == expected
def test_keys_x(keys): expected = keys["x"] actual = get(path.x, keys) assert actual == expected
def test_3d_0_10_0_MatchNotFoundError(three_dimensional_list): with pytest.raises(MatchNotFoundError): get(path[0][10][0], three_dimensional_list)
def test_nested_get(keys): expected = "16" actual = get_match(path.y.z, keys) actual = get(path.x, actual) assert actual == expected
def test_3d_root(three_dimensional_list): expected = three_dimensional_list actual = get(path, three_dimensional_list) assert actual == expected
def test_MatchNotFoundError_validate_message(keys): expected = "MatchNotFoundError(No get_match occurred on path: $.a)" with pytest.raises(MatchNotFoundError) as exc_info: get(path.a, keys) assert repr(exc_info.value) == expected
def test_3d_0_0_0(three_dimensional_list): expected = three_dimensional_list[0][0][0] actual = get(path[0][0][0], three_dimensional_list) assert actual == expected
def test_NestedMatchNotFoundError_validate_message(keys): expected = "NestedMatchNotFoundError(No get_match occurred on path $.a from match $.x)" with pytest.raises(NestedMatchNotFoundError) as exc_info: get(path.a, get_match(path.x, keys)) assert repr(exc_info.value) == expected