Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/source/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ missing description, or the removed sequence ``serial_number`` field. See
:ref:`yaml_format` for migration guidance and the generated
:doc:`_generated/recipe_language_reference` for exact fields.

**Validating a recipe without launching the GUI**

Run the recipe through the production parser from the command line to get the
same diagnostics the GUI would report, without opening it:

.. code-block:: bash

python -m pypts.validate_recipe my_recipe.yml

The command prints one line per diagnostic (``[code] file:line:col (field.path): message``),
a final ``OK``/``FAILED`` summary, and exits with a non-zero status code if any
errors were found — convenient for pre-commit hooks or CI.

**ModuleNotFoundError**

Ensure test_package is properly named in the recipe and that the method_name properly name the specific function to run.
Expand Down
4 changes: 4 additions & 0 deletions docs/source/yaml_format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ unsafe YAML tags, missing required fields, invalid sequence references, and
unsupported versions produce diagnostics. PyPTS does not repair or silently
normalize legacy syntax.

Run ``python -m pypts.validate_recipe my_recipe.yml`` to check a recipe file
against these rules from the command line, without launching the GUI. See
:ref:`troubleshooting` for details and exit codes.

Variables and mappings
----------------------

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dependencies = [
"nidmm==1.4.8",
"numpy",
"PySide6==6.9.1",
"PyYAML==6.0.2",
"PyYAML==6.0.3",
"ruamel.yaml",
"pymeasure==0.15.0",
"pyserial",
Expand Down
44 changes: 44 additions & 0 deletions src/pypts/validate_recipe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# SPDX-FileCopyrightText: 2026 CERN <home.cern>
#
# SPDX-License-Identifier: LGPL-2.1-or-later
"""Command-line validation of recipe-language 2.0.0 YAML files.

Usage:
python -m pypts.validate_recipe <recipe.yml>
"""

from __future__ import annotations

import argparse
import sys
from collections.abc import Sequence as ArgSequence

from pypts.recipe_parser import parse_recipe_file
from pypts.YamVIEW.verify_recipe import format_diagnostic


def main(argv: ArgSequence[str] | None = None) -> int:
"""Validate the given recipe file and print diagnostics; return a process exit code."""
parser = argparse.ArgumentParser(
prog="python -m pypts.validate_recipe",
description="Validate a recipe-language 2.0.0 YAML file and report diagnostics.",
)
parser.add_argument("recipe", help="Path to the recipe YAML file to validate.")
args = parser.parse_args(argv)

result = parse_recipe_file(args.recipe)

for diagnostic in result.diagnostics:
stream = sys.stderr if diagnostic.severity == "error" else sys.stdout
print(format_diagnostic(diagnostic), file=stream)

if result.is_valid:
print(f"OK: {args.recipe} is a valid recipe-language 2.0.0 recipe.")
return 0

print(f"FAILED: {len(result.errors)} error(s), {len(result.warnings)} warning(s).", file=sys.stderr)
return 1


if __name__ == "__main__":
sys.exit(main())