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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ jobs:
run: >-
docker run --rm pts-framework-ci:${GITHUB_SHA}
python -m pytest tests -vv --durations=20

- name: Build documentation
run: >-
docker run --rm pts-framework-ci:${GITHUB_SHA}
python -m sphinx -W --keep-going -b html docs/source docs/build/html
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ WORKDIR /app
COPY pyproject.toml README.md ./
COPY src ./src
COPY tests ./tests
COPY docs ./docs

RUN python -m pip install --no-cache-dir --upgrade pip \
&& python -m pip install --no-cache-dir ".[test]" build
&& python -m pip install --no-cache-dir ".[test,doc]" build

CMD ["python", "-m", "pytest", "tests"]
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@

build
source/api/*
source/_generated/

# End of .gitignore content managed by acc-py
57 changes: 57 additions & 0 deletions docs/source/_examples/device_recipe_v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# SPDX-FileCopyrightText: 2026 CERN <home.cern>
# SPDX-License-Identifier: CC-BY-SA-4.0
---
name: Device acceptance recipe
version: "1.0"
recipe_version: 2.0.0
description: Configure a device, measure it, and disconnect safely.
main_sequence: Main
test_package: my_project.tests
globals:
device_address: COM3
test_voltage: 5.0
---
sequence_name: Main
description: Run the device acceptance flow.
parameters: {}
locals:
measurement: null
outputs: {}
setup_steps:
- steptype: PythonModuleStep
step_name: Configure device
description: Configure the device before measurement.
action_type: method
module: device_driver.py
method_name: setup_device
input_mapping:
port: {type: global, global_name: device_address}
voltage: {type: global, global_name: test_voltage}
output_mapping:
success: {type: passfail}
steps:
- steptype: WaitStep
step_name: Initial delay
description: Allow the device to settle.
input_mapping:
wait_time: {type: direct, value: 2}
output_mapping: {}
- steptype: PythonModuleStep
step_name: Take measurement
description: Read and validate the configured voltage.
action_type: method
module: device_driver.py
method_name: read_measurement
input_mapping: {}
output_mapping:
measured_value: {type: local, local_name: measurement}
status: {type: range, min: 4.8, max: 5.2}
teardown_steps:
- steptype: PythonModuleStep
step_name: Disconnect device
description: Close the device connection during teardown.
action_type: method
module: device_driver.py
method_name: disconnect
input_mapping: {}
output_mapping: {}
30 changes: 30 additions & 0 deletions docs/source/_examples/instrument_recipe_v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2026 CERN <home.cern>
# SPDX-License-Identifier: CC-BY-SA-4.0
---
name: Frequency stability test
version: "1.0"
recipe_version: 2.0.0
description: Read a CNT-91 through a packaged Python test method.
main_sequence: Main
test_package: my_project.tests
globals:
device_name: USB0::0x14EB::0x0091::205575::INSTR
---
sequence_name: Main
description: Run the frequency measurement.
parameters: {}
locals: {}
outputs: {}
setup_steps: []
steps:
- steptype: PythonModuleStep
step_name: Read CNT-91
description: Connect to the counter and evaluate its measurement.
action_type: method
module: instrument_tests.py
method_name: run_cnt91
input_mapping:
device_name: {type: global, global_name: device_name}
output_mapping:
output: {type: passfail}
teardown_steps: []
71 changes: 71 additions & 0 deletions docs/source/_examples/recipe_v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SPDX-FileCopyrightText: 2026 CERN <home.cern>
# SPDX-License-Identifier: CC-BY-SA-4.0
---
name: Recipe language 2 documentation example
version: "1.0"
recipe_version: 2.0.0
description: Demonstrate canonical version 2 syntax and typed mappings.
main_sequence: Main
continue_on_error: false
report: overwrite
report_name_include_serial: true
test_package: acceptance.tests
globals:
target: 12
saved_result: null
---
sequence_name: Main
description: Run a measurement and then the calibration sequence.
parameters: {}
outputs: {}
locals:
expected: 12
measured: null
setup_steps: []
steps:
- steptype: PythonModuleStep
step_name: Measure channels
description: Exercise every input mapping and representative outputs.
id: measure-channels
skip: false
critical: true
continue_on_error: false
action_type: method
module: measurements.py
method_name: measure
input_mapping:
channels: {type: direct, value: [0, 1], indexed: true}
expected: {type: local, local_name: expected}
target: {type: global, global_name: target}
transform: {type: method, value: normalize}
output_mapping:
passed: {type: passfail}
exact: {type: equals, value: 12}
bounded: {type: range, min: 10, max: 14}
measured: {type: local, local_name: measured}
saved: {type: global, global_name: saved_result}
chart: {type: image}
- steptype: SequenceStep
step_name: Calibrate
description: Run a nested sequence and use its aggregate verdict.
sequence: {type: internal, name: Calibration}
input_mapping: {}
output_mapping:
result: {type: passthrough}
stored: {type: local, local_name: measured}
teardown_steps: []
---
sequence_name: Calibration
description: Wait for the equipment to stabilize.
parameters: {}
outputs: {}
locals: {}
setup_steps: []
steps:
- steptype: WaitStep
step_name: Stabilize
description: Wait before returning to the caller.
input_mapping:
wait_time: {type: direct, value: 1}
output_mapping: {}
teardown_steps: []
45 changes: 12 additions & 33 deletions docs/source/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,24 +156,15 @@ The ``PythonModuleStep`` now uses a completely rewritten ``__load_module`` metho
* Removed module conflict detection (handled by Python's import system)
* Streamlined error handling with proper exception chaining

**Recipe Configuration Example**:
**Recipe configuration fields**:

.. code-block:: yaml

---
name: FSI PTS
version: 0.0.1
description: Test recipe for FSI version check
test_package: fsi_pts.tests # NEW: Specifies package containing test modules
globals: {}

---
sequence_name: Main
steps:
- steptype: PythonModuleStep
step_name: Get FSI Status
module: test_status.py # Resolved to fsi_pts.tests.test_status
method_name: test_status
# RecipeHeader fragment
test_package: fsi_pts.tests

# PythonModuleStep fragment
module: test_status.py # Resolves to fsi_pts.tests.test_status

**Benefits**:

Expand Down Expand Up @@ -310,21 +301,9 @@ For migrating to resource-based module loading:

.. code-block:: yaml

# Old (file-based)
---
name: My Recipe
globals: {}

steps:
- steptype: PythonModuleStep
module: tests/my_test.py # File path

# New (resource-based)
---
name: My Recipe
test_package: my_package.tests # Package containing test modules
globals: {}

steps:
- steptype: PythonModuleStep
module: my_test.py # Resolved to my_package.tests.my_test
# File-based module field
module: tests/my_test.py

# Resource-based header and module fields
test_package: my_package.tests
module: my_test.py # Resolves to my_package.tests.my_test
30 changes: 23 additions & 7 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
# SPDX-License-Identifier: LGPL-2.1-or-later

import datetime
import sys
from pathlib import Path

from pypts._version import __version__

ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))


project = "pypts"
author = "Alvaro Martinez Landete"
version = __version__

copyright = "{0}, CERN".format(datetime.datetime.now().year)
copyright = f"{datetime.datetime.now(datetime.UTC).year}, CERN"


# -- General configuration ----------------------------------------------------
Expand All @@ -20,7 +25,6 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'acc_py_sphinx.theme',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.doctest',
Expand All @@ -42,16 +46,28 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "acc_py"
html_theme = "alabaster"

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ["_static"]
html_show_sphinx = False
html_show_sourcelink = True


def _generate_recipe_language_docs(app):
"""Generate the schema and its human reference for this Sphinx build."""
from pypts.recipe_artifacts import write_artifacts

generated = Path(app.srcdir) / "_generated"
write_artifacts(
generated / "recipe_language.schema.json",
generated / "recipe_language_reference.rst",
)


def setup(app):
app.connect("builder-inited", _generate_recipe_language_docs)
return {"parallel_read_safe": True, "parallel_write_safe": True}


# -- Options for sphinx.ext.autosummary

autosummary_generate = True
Expand Down
Loading