1 # SPDX-License-Identifier: GPL-2.0-only
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2025 by the Nominatim developer community.
6 # For a full list of authors see the git log.
8 Helper functions to compare expected values.
14 'exactly': lambda exp, act: exp == act,
15 'more than': lambda exp, act: act > exp,
16 'less than': lambda exp, act: act < exp,
21 return json.dumps(obj, sort_keys=True, indent=2)
24 def within_box(value, expect):
25 coord = [float(x) for x in expect.split(',')]
27 if isinstance(value, str):
28 value = value.split(',')
29 value = list(map(float, value))
32 return coord[0] <= value[0] <= coord[2] \
33 and coord[1] <= value[1] <= coord[3]
36 return value[0] >= coord[0] and value[1] <= coord[1] \
37 and value[2] >= coord[2] and value[3] <= coord[3]
39 raise ValueError("Not a coordinate or bbox.")
43 None: lambda val, exp: str(val) == exp,
44 'i': lambda val, exp: str(val).lower() == exp.lower(),
45 'fm': lambda val, exp: re.fullmatch(exp, val) is not None,
49 OSM_TYPE = {'node': 'n', 'way': 'w', 'relation': 'r'}
53 """ Returns the given attribute as a string.
55 The key parameter determines how the value is formatted before
56 returning. To refer to sub attributes, use '+' to add more keys
57 (e.g. 'name+ref' will access obj['name']['ref']). A '!' introduces
58 a formatting suffix. If no suffix is given, the value will be
59 converted using the str() function.
63 !:... - use a formatting expression according to Python Mini Format Spec
64 !i - make case-insensitive comparison
65 !fm - consider comparison string a regular expression and match full value
68 def __init__(self, obj, key):
71 self.key, self.fmt = key.rsplit('!', 1)
76 if self.key == 'object':
77 assert 'osm_id' in obj
78 assert 'osm_type' in obj
79 self.subobj = OSM_TYPE[obj['osm_type']] + str(obj['osm_id'])
83 self.subobj = self.obj
84 for sub in self.key.split('+'):
86 assert sub in self.subobj, \
87 f"Missing attribute {done}. Full object:\n{_pretty(self.obj)}"
88 self.subobj = self.subobj[sub]
90 def __eq__(self, other):
91 if not isinstance(other, str):
92 raise NotImplementedError()
94 # work around bad quoting by pytest-bdd
95 other = other.replace(r'\\', '\\')
97 if self.fmt in COMPARISON_FUNCS:
98 return COMPARISON_FUNCS[self.fmt](self.subobj, other)
100 if self.fmt.startswith(':'):
101 return other == f"{{{self.fmt}}}".format(self.subobj)
103 raise RuntimeError(f"Unknown format string '{self.fmt}'.")
106 k = self.key.replace('+', '][')
109 return f"result[{k}]({self.subobj})"