MCPcopy
hub / github.com/github/spec-kit / _evaluate_simple_expression

Function _evaluate_simple_expression

src/specify_cli/workflows/expressions.py:249–408  ·  view source on GitHub ↗

Evaluate a simple expression against the namespace. Supports: - Dot-path access: ``steps.specify.output.file`` - Comparisons: ``==``, ``!=``, ``>``, ``<``, ``>=``, ``<=`` - Boolean operators: ``and``, ``or``, ``not`` - ``in``, ``not in`` - Pipe filters: ``| default('...')``,

(expr: str, namespace: dict[str, Any])

Source from the content-addressed store, hash-verified

247
248
249def _evaluate_simple_expression(expr: str, namespace: dict[str, Any]) -> Any:
250 """Evaluate a simple expression against the namespace.
251
252 Supports:
253 - Dot-path access: ``steps.specify.output.file``
254 - Comparisons: ``==``, ``!=``, ``>``, ``<``, ``>=``, ``<=``
255 - Boolean operators: ``and``, ``or``, ``not``
256 - ``in``, ``not in``
257 - Pipe filters: ``| default('...')``, ``| join(', ')``, ``| contains('...')``, ``| from_json``, ``| map('...')``
258 - String and numeric literals
259 """
260 expr = expr.strip()
261
262 # String literal — only when the WHOLE expression is one quoted string,
263 # i.e. the opening quote's matching close is the final character. Checking
264 # startswith/endswith alone would also grab `'a' == 'b'` and strip it to the
265 # garbage `a' == 'b`; a genuine single literal short-circuits here so quoted
266 # strings containing `|` or operator keywords are not mis-parsed downstream.
267 if expr[:1] in ("'", '"') and expr.find(expr[0], 1) == len(expr) - 1:
268 return expr[1:-1]
269
270 # Handle pipe filters. Detect the pipe at the top level only, so a literal
271 # '|' inside a quoted operand (e.g. `inputs.x == 'a|b'`) or nested brackets is
272 # not mistaken for a filter separator — mirroring the operator parsing below.
273 pipe_idx = _find_top_level(expr, "|")
274 if pipe_idx != -1:
275 value = _evaluate_simple_expression(expr[:pipe_idx].strip(), namespace)
276 filter_expr = expr[pipe_idx + 1:].strip()
277
278 # `from_json` is strict: it takes no arguments and tolerates no
279 # trailing tokens. Match on the leading filter name and require the
280 # whole filter to be exactly `from_json`, so every mis-wired form
281 # (`from_json()`, `from_json('x')`, `from_json)`, `from_json extra`)
282 # fails loudly instead of silently falling through to the
283 # unknown-filter path and returning the unparsed value. (filter_expr
284 # is already stripped above.)
285 leading = re.match(r"\w+", filter_expr)
286 if leading and leading.group(0) == "from_json":
287 if filter_expr != "from_json":
288 raise ValueError(
289 "from_json: expected '| from_json' with no arguments or "
290 f"trailing tokens, got '| {filter_expr}'"
291 )
292 return _filter_from_json(value)
293
294 # Parse filter name and argument
295 filter_match = re.match(r"(\w+)\((.+)\)", filter_expr)
296 if filter_match:
297 fname = filter_match.group(1)
298 farg = _evaluate_simple_expression(filter_match.group(2).strip(), namespace)
299 if fname == "default":
300 return _filter_default(value, farg)
301 if fname == "join":
302 return _filter_join(value, farg)
303 if fname == "map":
304 return _filter_map(value, farg)
305 if fname == "contains":
306 return _filter_contains(value, farg)

Callers 2

evaluate_expressionFunction · 0.85
_replacerFunction · 0.85

Calls 9

_find_top_levelFunction · 0.85
_filter_from_jsonFunction · 0.85
_filter_defaultFunction · 0.85
_filter_joinFunction · 0.85
_filter_mapFunction · 0.85
_filter_containsFunction · 0.85
_safe_compareFunction · 0.85
_split_top_level_commasFunction · 0.85
_resolve_dot_pathFunction · 0.85

Tested by

no test coverage detected