diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index d2e9e74..dd0bf30 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -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. diff --git a/docs/source/yaml_format.rst b/docs/source/yaml_format.rst index 32ea332..a2431ae 100644 --- a/docs/source/yaml_format.rst +++ b/docs/source/yaml_format.rst @@ -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 ---------------------- diff --git a/pyproject.toml b/pyproject.toml index e177cc0..4993994 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", diff --git a/src/pypts/validate_recipe.py b/src/pypts/validate_recipe.py new file mode 100644 index 0000000..65ec715 --- /dev/null +++ b/src/pypts/validate_recipe.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2026 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 +""" + +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())