parselglossy.validation_plumbing module

Plumbing functions powering our validation facilities.

parselglossy.validation_plumbing.check_keyword()[source]

Checks that a template keyword is well-formed.

In this function, a template keyword is well-formed if it has:

  • An allowed type.
  • A non-empty docstring.

The two additional criteria:

  • A default callable that is valid Python, if present.
  • Predicates that are valid Python, if present.

can only be checked meaningfully later, as they might depend on keyword(s)/section(s) of the input that are only known after merging.

Parameters:
  • template (JSONDict) –
  • address (Tuple) –
Returns:

errors

Return type:

List[Error]

parselglossy.validation_plumbing.rec_check_predicates()[source]

Run predicates on input tree with fixed defaults.

Parameters:
  • incoming (JSONDict) – The input dict. This is supposed to be the result of fix_defaults().
  • predicates (JSONDict) – A view-by-predicates of the template dict.
  • start_dict (JSONDict) – The dict we start recursion from.
  • address (Tuple[str]) – A tuple of keys need to index the current value in the recursion.
Returns:

errors – A list of keys to access elements in the dict that raised an error.

Return type:

List[Error]

parselglossy.validation_plumbing.rec_fix_defaults()[source]

Fix default value and perform type checking.

Parameters:
  • incoming (JSONDict) – The input dict. This is supposed to be the one obtained by merging user and template dict-s.
  • types (JSONDict) – Types of all keywords in the input. Generated from view_by_types().
  • start_dict (JSONDict) – The dict we start recursion from. This parameter is needed to keep around a copy of the full dict during the recursion.
  • address (Tuple[str]) – A tuple of keys need to index the current value in the recursion. See Notes.
Returns:

  • outgoing (JSONDict) – A dictionary with all default values fixed.
  • errors (List[Error]) – A list of keys to access elements in the dict that raised an error. See Notes.

Notes

Since we allow callables to appear as defaults, we need to run them to determine the actual default values.

This operation must be done with some care, to avoid false negatives or ambiguous type checks. For example:

  • If the type is str and the default a callable, the type will match, but the default will make no sense.
  • If the type is numerical, e.g. int, the type will not match.

However, by design the callables must refer to some other field in the input tree, hence they must contain the reserved token “user”. This allows us to disambiguate a callable as default from a value as default.

The final strategy adopted is then:

1. Perform type checking with type_matches(). If successful, we coerce the type. 2. If types did not match, we further check whether the value is a string, containing the reserved token “value”. This means the default value is actually a callable. We run the callable, which internally coerces the type of the result to the expected one. 3. If even this check was unsuccessful, types really were unmatched. We report the error and move on.

parselglossy.validation_plumbing.rec_is_template_valid()[source]

Checks a template dict is well-formed.

A template dict is well-formed if:

  • All keywords have:

    • An allowed type.
    • A non-empty docstring.
    • A default callable that is valid Python, if present.
    • Predicates that are valid Python, if present.

    Note that the latter two criteria can only be checked later on.

  • No sections are nested under keywords.

  • All sections have a non-empty docstring.

Parameters:
  • template (JSONDict) –
  • address (Tuple[str]) –
Returns:

errors

Return type:

List[Error]

parselglossy.validation_plumbing.rec_merge_ours()[source]

Recursively merge two dict-s with “ours” strategy.

Parameters:
  • theirs (JSONDict) –
  • ours (JSONDict) –
  • address (Tuple) –
Returns:

  • outgoing (JSONDict)
  • errors (List[Error])

Notes

The theirs dictionary is supposed to be the view by defaults of the validation specification, whereas ours is the dictionary from user input. The recursive merge action will generate a complete, but not validated, input dictionary by using default values where these are not overridden by user input, hence the naming “ours” for the merge strategy.

parselglossy.validation_plumbing.run_callable(f: str, d: Dict[str, Any], *, t: str) → Tuple[str, Optional[Any]][source]

Run a callable encoded as a string.

A callable is any function of the input tree.

Parameters:
  • f (str) – Callable to checked as a string
  • d (JSONDict) – The input dict.
  • t (str) – Expected type.
Returns:

retval – The error message, if any, and the result of the callable, if any.

Return type:

Tuple[str, Optional[Any]]

Notes

The input tree is called user. The callable is turned into a lambda function and executed using eval, to ensure that the syntax of callable actions is correct and that the callable returns correctly.

We need to pass the full incoming dictionary as argument to eval, because we allow indexing in the global dict. That is, since it is allowed to define defaults in a given section based on defaults in other section we must be able to access the full input at any point.

parselglossy.validation_plumbing.run_predicate(predicate: str, where: str, user: Dict[str, Any]) → Tuple[str, bool][source]

Run a predicate to check whether it is satisfied.

Parameters:
  • predicate (str) –
  • where (str) –
  • user (JSONDict) –

Notes

We replace the convenience placeholder “value” with its full “address” in user.

parselglossy.validation_plumbing.undocumented(x: Dict[str, Any]) → bool[source]
parselglossy.validation_plumbing.untyped(x: Dict[str, Any]) → bool[source]