From c23124fea8123534ff91c93b41d6510230855eb8 Mon Sep 17 00:00:00 2001 From: Robrecht Cannoodt Date: Wed, 29 Jul 2026 15:14:02 +0200 Subject: [PATCH 1/4] order the task graph topologically * Sort the api graph with kahn's algorithm seeded with all roots, instead of a bfs from a single root * Tasks with more than one raw dataset (e.g. task_predict_modality) no longer strand all but the first at the very end of the readme * A component is no longer documented before the files it consumes --- packages/python/openproblems/CHANGELOG.md | 6 ++ .../project/docs/read_task_metadata.py | 64 ++++++++++--------- .../tests/test_docs_render_task_readme_qmd.py | 51 +++++++++++++++ 3 files changed, 90 insertions(+), 31 deletions(-) diff --git a/packages/python/openproblems/CHANGELOG.md b/packages/python/openproblems/CHANGELOG.md index ffdddac..6f19718 100644 --- a/packages/python/openproblems/CHANGELOG.md +++ b/packages/python/openproblems/CHANGELOG.md @@ -22,6 +22,12 @@ * Improve diagnostic print messages in `check_config` and `run_and_check_output` to be more descriptive. +## BUG FIXES + +* `read_task_metadata`: Order the task graph topologically instead of by a breadth-first search from a single root. + Tasks with more than one raw dataset no longer strand all but the first at the end of the README, + and a component is never documented before the files it consumes. + # openproblems core Python v0.1.1 ## NEW FUNCTIONALITY diff --git a/packages/python/openproblems/src/openproblems/project/docs/read_task_metadata.py b/packages/python/openproblems/src/openproblems/project/docs/read_task_metadata.py index 69412e6..52c2bd9 100644 --- a/packages/python/openproblems/src/openproblems/project/docs/read_task_metadata.py +++ b/packages/python/openproblems/src/openproblems/project/docs/read_task_metadata.py @@ -2,7 +2,6 @@ import glob import os import re -import warnings from collections import deque @@ -10,7 +9,8 @@ def read_task_metadata(path: str) -> dict: """Read all API files in a task directory and return structured metadata. Scans ``path`` recursively for ``comp_*.yaml`` and ``file_*.yaml`` files, - builds a directed task graph, and runs a BFS to determine render order. + builds a directed task graph, and topologically sorts it to determine + render order. Args: path: Path to the task directory (or ``api/`` subdirectory). A @@ -25,8 +25,9 @@ def read_task_metadata(path: str) -> dict: * ``file_info`` / ``comp_info`` – flat lists of info dicts * ``file_expected_format`` / ``comp_args`` – flat lists * ``task_graph`` – ``networkx.DiGraph`` - * ``task_graph_root`` – name of the root node - * ``task_graph_order`` – BFS-ordered list of node names + * ``task_graph_roots`` – names of the nodes without any inputs + * ``task_graph_root`` – name of the first root node + * ``task_graph_order`` – topologically ordered list of node names """ from .. import find_project_root from .read_task_config import read_task_config @@ -62,8 +63,8 @@ def read_task_metadata(path: str) -> dict: } task_graph = _build_graph(files, comps) - task_graph_root = _get_root(task_graph) - task_graph_order = _bfs_order(task_graph, task_graph_root) + task_graph_roots = _get_roots(task_graph) + task_graph_order = _topological_order(task_graph, task_graph_roots) comp_info = [c["info"] for c in comps.values()] comp_args = [arg for c in comps.values() for arg in c["args"]] @@ -82,7 +83,8 @@ def read_task_metadata(path: str) -> dict: "comp_info": comp_info, "comp_args": comp_args, "task_graph": task_graph, - "task_graph_root": task_graph_root, + "task_graph_roots": task_graph_roots, + "task_graph_root": task_graph_roots[0] if task_graph_roots else None, "task_graph_order": task_graph_order, } @@ -114,32 +116,32 @@ def _build_graph(files: dict, comps: dict): return G -def _get_root(G) -> str: +def _get_roots(G) -> list[str]: + """Nodes without inputs, i.e. the raw datasets a task starts from.""" roots = [n for n, d in G.in_degree() if d == 0] - if not roots: - return next(iter(G.nodes())) - if len(roots) > 1: - warnings.warn( - f"Multiple root nodes with in-degree 0: {roots}. Using first.", - stacklevel=4, - ) - return roots[0] - - -def _bfs_order(G, root: str) -> list[str]: - """BFS from root; unreachable nodes are appended afterwards (mirrors igraph).""" - visited: list[str] = [] - seen: set[str] = set() - queue: deque[str] = deque([root]) + return roots if roots else list(G.nodes())[:1] + + +def _topological_order(G, roots: list[str]) -> list[str]: + """Order the graph so every node comes after the nodes it consumes. + + Kahn's algorithm with a FIFO queue seeded with *all* roots, so a task with + several raw datasets keeps them together at the start instead of stranding + all but the first at the end. Nodes in a cycle are appended afterwards. + """ + pending = {n: d for n, d in G.in_degree()} + order: list[str] = [] + seen: set[str] = set(roots) + queue: deque[str] = deque(roots) while queue: node = queue.popleft() - if node not in seen: - seen.add(node) - visited.append(node) - for nbr in G.successors(node): - if nbr not in seen: - queue.append(nbr) + order.append(node) + for nbr in G.successors(node): + pending[nbr] -= 1 + if pending[nbr] <= 0 and nbr not in seen: + seen.add(nbr) + queue.append(nbr) for node in G.nodes(): if node not in seen: - visited.append(node) - return visited + order.append(node) + return order diff --git a/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py b/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py index c1ea848..9543f02 100644 --- a/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py +++ b/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py @@ -91,3 +91,54 @@ def test_render_task_readme_qmd_from_path(): result = render_task_readme_qmd(EXAMPLE_PROJECT) assert "## API" in result + + +def test_task_graph_order_is_topological(task_metadata): + G = task_metadata["task_graph"] + order = task_metadata["task_graph_order"] + + assert sorted(order) == sorted(G.nodes) + for node in order: + for pred in G.predecessors(node): + msg = f"{node} is rendered before its input {pred}" + assert order.index(pred) < order.index(node), msg + + +def test_task_graph_order_keeps_multiple_roots_up_front(): + import networkx as nx + from openproblems.project.docs.read_task_metadata import ( + _get_roots, + _topological_order, + ) + + # a multimodal task: two raw datasets feeding a single processor + G = nx.DiGraph() + G.add_edges_from( + [ + ("file_mod1", "comp_process"), + ("file_mod2", "comp_process"), + ("comp_process", "file_train"), + ] + ) + + roots = _get_roots(G) + order = _topological_order(G, roots) + + assert roots == ["file_mod1", "file_mod2"] + assert order == ["file_mod1", "file_mod2", "comp_process", "file_train"] + + +def test_task_graph_order_includes_cyclic_nodes(): + import networkx as nx + from openproblems.project.docs.read_task_metadata import ( + _get_roots, + _topological_order, + ) + + G = nx.DiGraph() + G.add_edges_from([("a", "b"), ("b", "c"), ("c", "b")]) + + order = _topological_order(G, _get_roots(G)) + + assert sorted(order) == ["a", "b", "c"] + assert order[0] == "a" From cac1a9b39d147c68adbbd8b07d5c2a814aee6605 Mon Sep 17 00:00:00 2001 From: Robrecht Cannoodt Date: Wed, 29 Jul 2026 15:17:07 +0200 Subject: [PATCH 2/4] render non-file arguments in the component spec * Stop filtering the arguments table down to `type: file`, which dropped `--seed` from the process_dataset spec entirely * Fall back to an argument's `description` when it has no `summary` -- only file arguments get a summary, through `__merge__` * Add a `--seed` to the example project to cover both --- packages/python/openproblems/CHANGELOG.md | 3 +++ .../project/docs/render_component_spec.py | 11 ++++++----- .../example_project/api/comp_data_processor.yaml | 4 ++++ .../tests/test_docs_render_task_readme_qmd.py | 12 ++++++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/python/openproblems/CHANGELOG.md b/packages/python/openproblems/CHANGELOG.md index 6f19718..2ef79a5 100644 --- a/packages/python/openproblems/CHANGELOG.md +++ b/packages/python/openproblems/CHANGELOG.md @@ -28,6 +28,9 @@ Tasks with more than one raw dataset no longer strand all but the first at the end of the README, and a component is never documented before the files it consumes. +* `render_component_spec`: Include non-file arguments (e.g. `--seed`) in the arguments table, + and fall back to an argument's `description` when it has no `summary`. + # openproblems core Python v0.1.1 ## NEW FUNCTIONALITY diff --git a/packages/python/openproblems/src/openproblems/project/docs/render_component_spec.py b/packages/python/openproblems/src/openproblems/project/docs/render_component_spec.py index 5e9719e..c047f88 100644 --- a/packages/python/openproblems/src/openproblems/project/docs/render_component_spec.py +++ b/packages/python/openproblems/src/openproblems/project/docs/render_component_spec.py @@ -39,12 +39,11 @@ def render_component_spec(spec: dict | str) -> str: def _format_arguments(args: list[dict]) -> str: from ._markdown import format_markdown_table - file_args = [a for a in args if a.get("type") == "file"] - if not file_args: + if not args: return "" rows = [] - for arg in file_args: + for arg in args: tags = [] if not arg.get("required", True): tags.append("Optional") @@ -52,7 +51,9 @@ def _format_arguments(args: list[dict]) -> str: tags.append("Output") tag_str = f"(_{', '.join(tags)}_) " if tags else "" - summary = re.sub(r" *\n *", " ", (arg.get("summary") or "").strip()).rstrip(".") + # file arguments carry a summary via __merge__, plain ones a description + text = arg.get("summary") or arg.get("description") or "" + text = re.sub(r" *\n *", " ", text.strip()).rstrip(".") default = arg.get("default") default_str = f" Default: `{default}`." if default is not None else "" @@ -60,7 +61,7 @@ def _format_arguments(args: list[dict]) -> str: [ f"`--{arg['arg_name']}`", f"`{arg.get('type', '')}`", - f"{tag_str}{summary}.{default_str}", + f"{tag_str}{text}.{default_str}", ] ) diff --git a/packages/python/openproblems/tests/data/example_project/api/comp_data_processor.yaml b/packages/python/openproblems/tests/data/example_project/api/comp_data_processor.yaml index 1ed53bd..fc933f0 100644 --- a/packages/python/openproblems/tests/data/example_project/api/comp_data_processor.yaml +++ b/packages/python/openproblems/tests/data/example_project/api/comp_data_processor.yaml @@ -23,6 +23,10 @@ arguments: __merge__: file_solution.yaml direction: output required: true + - name: "--seed" + type: integer + default: 1 + description: "The seed for determining the train/test split." test_resources: - path: /resources_test/common/cxg_mouse_pancreas_atlas dest: resources_test/common/cxg_mouse_pancreas_atlas diff --git a/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py b/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py index 9543f02..da434ff 100644 --- a/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py +++ b/packages/python/openproblems/tests/test_docs_render_task_readme_qmd.py @@ -142,3 +142,15 @@ def test_task_graph_order_includes_cyclic_nodes(): assert sorted(order) == ["a", "b", "c"] assert order[0] == "a" + + +def test_render_component_spec_non_file_arguments(task_metadata): + from openproblems.project.docs import render_component_spec + + result = render_component_spec(task_metadata["comps"]["comp_data_processor"]) + + # non-file arguments are part of the API too, and describe themselves + # through `description` rather than the `summary` a __merge__ pulls in + assert "`--seed`" in result + assert "The seed for determining the train/test split" in result + assert "Default: `1`" in result From c6845b4d599caef933121768f9d8f67af21acba0 Mon Sep 17 00:00:00 2001 From: Robrecht Cannoodt Date: Wed, 29 Jul 2026 15:21:28 +0200 Subject: [PATCH 3/4] deprecate openproblems.docs * Add `.deprecate_docs()`, which warns once per session per function * Deprecate all eight exported functions; the readme pipeline (`common/scripts/create_task_readme`) runs the python implementation, so `openproblems.project.docs` is the source of truth now * `render_json_schema_example()` has no python replacement -- say so * Note in the changelog that the two readme bugs fixed in python core v0.2.0 were not backported here --- packages/r/openproblems.docs/CHANGELOG.md | 13 +++++++++ packages/r/openproblems.docs/DESCRIPTION | 3 +- packages/r/openproblems.docs/R/deprecated.R | 28 +++++++++++++++++++ .../R/openproblems.docs-package.R | 6 ++++ .../openproblems.docs/R/read_component_spec.R | 6 ++++ .../r/openproblems.docs/R/read_file_format.R | 6 ++++ .../r/openproblems.docs/R/read_task_config.R | 6 ++++ .../openproblems.docs/R/read_task_metadata.R | 6 ++++ .../R/render_component_spec.R | 6 ++++ .../openproblems.docs/R/render_file_format.R | 6 ++++ .../R/render_json_schema_example.R | 6 ++++ .../R/render_task_readme_qmd.R | 6 ++++ .../man/openproblems.docs-package.Rd | 10 ++++++- .../man/read_component_spec.Rd | 6 ++++ .../openproblems.docs/man/read_file_format.Rd | 6 ++++ .../openproblems.docs/man/read_task_config.Rd | 6 ++++ .../man/read_task_metadata.Rd | 6 ++++ .../man/render_component_spec.Rd | 6 ++++ .../man/render_file_format.Rd | 6 ++++ .../man/render_json_schema_example.Rd | 6 ++++ .../man/render_task_readme_qmd.Rd | 6 ++++ .../tests/testthat/test-deprecated.R | 14 ++++++++++ 22 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 packages/r/openproblems.docs/R/deprecated.R create mode 100644 packages/r/openproblems.docs/tests/testthat/test-deprecated.R diff --git a/packages/r/openproblems.docs/CHANGELOG.md b/packages/r/openproblems.docs/CHANGELOG.md index 0866178..45dea12 100644 --- a/packages/r/openproblems.docs/CHANGELOG.md +++ b/packages/r/openproblems.docs/CHANGELOG.md @@ -1,3 +1,16 @@ +# openproblems.docs R v0.2.0 + +## DEPRECATIONS + +* All exported functions are deprecated and will be removed in a future release. + `common/scripts/create_task_readme` runs the Python implementation, so + `openproblems.project.docs` in the Python `openproblems` package is the source + of truth for task documentation. `render_json_schema_example` has no + replacement there. + + Note that the two README rendering bugs fixed in Python core v0.2.0 + (task graph ordering and missing non-file arguments) were not backported here. + # openproblems.docs R v0.1.0 Initial release diff --git a/packages/r/openproblems.docs/DESCRIPTION b/packages/r/openproblems.docs/DESCRIPTION index 6a2fe1a..1f75b39 100644 --- a/packages/r/openproblems.docs/DESCRIPTION +++ b/packages/r/openproblems.docs/DESCRIPTION @@ -14,7 +14,8 @@ Authors@R: c( role = c("aut"), comment = c(ORCID = "0009-0003-8555-1361") )) -Description: OpenProblems Documentation R helper functions. +Description: OpenProblems Documentation R helper functions. Deprecated: superseded + by the `openproblems.project.docs` module of the Python `openproblems` package. License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) diff --git a/packages/r/openproblems.docs/R/deprecated.R b/packages/r/openproblems.docs/R/deprecated.R new file mode 100644 index 0000000..5126fe8 --- /dev/null +++ b/packages/r/openproblems.docs/R/deprecated.R @@ -0,0 +1,28 @@ +#' Warn that a documentation helper is deprecated +#' +#' The README rendering pipeline (`common/scripts/create_task_readme`) runs the +#' Python implementation, so this package is no longer the source of truth. +#' +#' @param what Name of the deprecated function +#' @param replacement Name of the Python function that supersedes it, or `NULL` +#' if there is none +#' +#' @noRd +.deprecate_docs <- function(what, replacement = what) { + advice <- + if (is.null(replacement)) { + "It has no replacement in the Python `openproblems` package." + } else { + paste0("Use `openproblems.project.docs.", replacement, "()` from the Python `openproblems` package instead.") + } + + rlang::warn( + c( + paste0("`", what, "()` is deprecated and will be removed in a future release."), + i = advice + ), + class = "deprecatedWarning", + .frequency = "once", + .frequency_id = paste0("openproblems.docs::", what) + ) +} diff --git a/packages/r/openproblems.docs/R/openproblems.docs-package.R b/packages/r/openproblems.docs/R/openproblems.docs-package.R index df8bb51..b98a2d5 100644 --- a/packages/r/openproblems.docs/R/openproblems.docs-package.R +++ b/packages/r/openproblems.docs/R/openproblems.docs-package.R @@ -1,4 +1,10 @@ #' @keywords internal +#' +#' @section Deprecated: +#' This package is deprecated and will be removed in a future release. The +#' README rendering pipeline (`common/scripts/create_task_readme`) runs the +#' Python `openproblems` package, so `openproblems.project.docs` is the source +#' of truth for task documentation. "_PACKAGE" ## usethis namespace: start diff --git a/packages/r/openproblems.docs/R/read_component_spec.R b/packages/r/openproblems.docs/R/read_component_spec.R index f793f2f..b58a3fb 100644 --- a/packages/r/openproblems.docs/R/read_component_spec.R +++ b/packages/r/openproblems.docs/R/read_component_spec.R @@ -5,6 +5,10 @@ #' @param path Path to a component spec yaml, usually in `src/api/comp_*.yaml` #' @return A list with compontent info and arguments #' +#' @section Deprecated: +#' Superseded by `openproblems.project.docs.read_component_spec()` in the Python +#' `openproblems` package, which is what `create_task_readme` runs. +#' #' @export #' @examples #' path <- system.file( @@ -14,6 +18,8 @@ #' #' read_component_spec(path) read_component_spec <- function(path) { + .deprecate_docs("read_component_spec") + data <- openproblems::read_nested_yaml(path) tryCatch( diff --git a/packages/r/openproblems.docs/R/read_file_format.R b/packages/r/openproblems.docs/R/read_file_format.R index 2687313..a2fedb8 100644 --- a/packages/r/openproblems.docs/R/read_file_format.R +++ b/packages/r/openproblems.docs/R/read_file_format.R @@ -5,6 +5,10 @@ #' @param path Path to a file format yaml, usually in `src/api/file_*.yaml` #' @return A list with file format info and expected_format #' +#' @section Deprecated: +#' Superseded by `openproblems.project.docs.read_file_format()` in the Python +#' `openproblems` package, which is what `create_task_readme` runs. +#' #' @export #' @examples #' path <- system.file( @@ -14,6 +18,8 @@ #' #' read_file_format(path) read_file_format <- function(path) { + .deprecate_docs("read_file_format") + data <- openproblems::read_nested_yaml(path) tryCatch( diff --git a/packages/r/openproblems.docs/R/read_task_config.R b/packages/r/openproblems.docs/R/read_task_config.R index 2ff3529..6735868 100644 --- a/packages/r/openproblems.docs/R/read_task_config.R +++ b/packages/r/openproblems.docs/R/read_task_config.R @@ -2,6 +2,10 @@ #' #' @param path Path to a project config file #' +#' @section Deprecated: +#' Superseded by `openproblems.project.docs.read_task_config()` in the Python +#' `openproblems` package, which is what `create_task_readme` runs. +#' #' @importFrom cli cli_inform #' @importFrom openproblems.utils validate_object #' @@ -15,6 +19,8 @@ #' #' read_task_config(path) read_task_config <- function(path) { + .deprecate_docs("read_task_config") + proj_conf <- openproblems::read_nested_yaml(path) tryCatch( diff --git a/packages/r/openproblems.docs/R/read_task_metadata.R b/packages/r/openproblems.docs/R/read_task_metadata.R index 8190c9c..27385fb 100644 --- a/packages/r/openproblems.docs/R/read_task_metadata.R +++ b/packages/r/openproblems.docs/R/read_task_metadata.R @@ -5,6 +5,10 @@ #' @param path Path to the API directory of a task #' @return A list with the api info #' +#' @section Deprecated: +#' Superseded by `openproblems.project.docs.read_task_metadata()` in the Python +#' `openproblems` package, which is what `create_task_readme` runs. +#' #' @importFrom cli cli_inform cli_abort #' #' @export @@ -15,6 +19,8 @@ #' #' task_metadata read_task_metadata <- function(path) { + .deprecate_docs("read_task_metadata") + cli::cli_inform(paste0("Looking for project root in '", path, "'")) project_path <- openproblems::find_project_root(path) if (is.null(project_path)) { diff --git a/packages/r/openproblems.docs/R/render_component_spec.R b/packages/r/openproblems.docs/R/render_component_spec.R index 47e39aa..f138448 100644 --- a/packages/r/openproblems.docs/R/render_component_spec.R +++ b/packages/r/openproblems.docs/R/render_component_spec.R @@ -3,6 +3,10 @@ #' @param spec file spec #' @return string #' +#' @section Deprecated: +#' Superseded by `openproblems.project.docs.render_component_spec()` in the Python +#' `openproblems` package, which is what `create_task_readme` runs. +#' #' @export #' @examples #' path <- system.file( @@ -14,6 +18,8 @@ #' #' render_component_spec(spec) render_component_spec <- function(spec) { + .deprecate_docs("render_component_spec") + if (is.character(spec)) { spec <- read_component_spec(spec) } diff --git a/packages/r/openproblems.docs/R/render_file_format.R b/packages/r/openproblems.docs/R/render_file_format.R index 4bb6532..5326dd4 100644 --- a/packages/r/openproblems.docs/R/render_file_format.R +++ b/packages/r/openproblems.docs/R/render_file_format.R @@ -3,6 +3,10 @@ #' @param spec file spec #' @return string #' +#' @section Deprecated: +#' Superseded by `openproblems.project.docs.render_file_format()` in the Python +#' `openproblems` package, which is what `create_task_readme` runs. +#' #' @export #' @examples #' path <- system.file( @@ -14,6 +18,8 @@ #' #' render_file_format(spec) render_file_format <- function(spec) { + .deprecate_docs("render_file_format") + if (is.character(spec)) { spec <- read_file_format(spec) } diff --git a/packages/r/openproblems.docs/R/render_json_schema_example.R b/packages/r/openproblems.docs/R/render_json_schema_example.R index 0eb09d7..dffeaca 100644 --- a/packages/r/openproblems.docs/R/render_json_schema_example.R +++ b/packages/r/openproblems.docs/R/render_json_schema_example.R @@ -5,6 +5,10 @@ #' @param json_schema JSON schema as a list #' @return YAML string #' +#' @section Deprecated: +#' Deprecated along with the rest of this package; it has no replacement in the +#' Python `openproblems` package. +#' #' @export #' #' @examples @@ -15,6 +19,8 @@ #' #' render_json_schema_example(json_schema) render_json_schema_example <- function(json_schema) { + .deprecate_docs("render_json_schema_example", replacement = NULL) + if (!"properties" %in% names(json_schema)) { return("") } diff --git a/packages/r/openproblems.docs/R/render_task_readme_qmd.R b/packages/r/openproblems.docs/R/render_task_readme_qmd.R index 260800a..ae879c0 100644 --- a/packages/r/openproblems.docs/R/render_task_readme_qmd.R +++ b/packages/r/openproblems.docs/R/render_task_readme_qmd.R @@ -5,6 +5,10 @@ #' #' @return A qmd documentation string #' +#' @section Deprecated: +#' Superseded by `openproblems.project.docs.render_task_readme_qmd()` in the Python +#' `openproblems` package, which is what `create_task_readme` runs. +#' #' @export #' #' @examples @@ -14,6 +18,8 @@ #' #' render_task_readme_qmd(task_metadata) render_task_readme_qmd <- function(task_metadata, add_instructions = FALSE) { + .deprecate_docs("render_task_readme_qmd") + cli::cli_inform("Render authors") authors_str <- .render_task_authors(task_metadata) diff --git a/packages/r/openproblems.docs/man/openproblems.docs-package.Rd b/packages/r/openproblems.docs/man/openproblems.docs-package.Rd index fc5478e..310054f 100644 --- a/packages/r/openproblems.docs/man/openproblems.docs-package.Rd +++ b/packages/r/openproblems.docs/man/openproblems.docs-package.Rd @@ -6,8 +6,16 @@ \alias{openproblems.docs-package} \title{openproblems.docs: OpenProblems Documentation} \description{ -OpenProblems Documentation R helper functions. +OpenProblems Documentation R helper functions. Deprecated: superseded by the \code{openproblems.project.docs} module of the Python \code{openproblems} package. } +\section{Deprecated}{ + +This package is deprecated and will be removed in a future release. The +README rendering pipeline (\code{common/scripts/create_task_readme}) runs the +Python \code{openproblems} package, so \code{openproblems.project.docs} is the source +of truth for task documentation. +} + \seealso{ Useful links: \itemize{ diff --git a/packages/r/openproblems.docs/man/read_component_spec.Rd b/packages/r/openproblems.docs/man/read_component_spec.Rd index 340234a..a354967 100644 --- a/packages/r/openproblems.docs/man/read_component_spec.Rd +++ b/packages/r/openproblems.docs/man/read_component_spec.Rd @@ -15,6 +15,12 @@ A list with compontent info and arguments \description{ This function reads a component spec from a yaml file. } +\section{Deprecated}{ + +Superseded by \code{openproblems.project.docs.read_component_spec()} in the Python +\code{openproblems} package, which is what \code{create_task_readme} runs. +} + \examples{ path <- system.file( "extdata", "example_project", "api", "comp_method.yaml", diff --git a/packages/r/openproblems.docs/man/read_file_format.Rd b/packages/r/openproblems.docs/man/read_file_format.Rd index 7316b8b..9f58249 100644 --- a/packages/r/openproblems.docs/man/read_file_format.Rd +++ b/packages/r/openproblems.docs/man/read_file_format.Rd @@ -15,6 +15,12 @@ A list with file format info and expected_format \description{ This function reads a file format spec from a yaml file. } +\section{Deprecated}{ + +Superseded by \code{openproblems.project.docs.read_file_format()} in the Python +\code{openproblems} package, which is what \code{create_task_readme} runs. +} + \examples{ path <- system.file( "extdata", "example_project", "api", "file_train_h5ad.yaml", diff --git a/packages/r/openproblems.docs/man/read_task_config.Rd b/packages/r/openproblems.docs/man/read_task_config.Rd index 0858b1b..ca9e698 100644 --- a/packages/r/openproblems.docs/man/read_task_config.Rd +++ b/packages/r/openproblems.docs/man/read_task_config.Rd @@ -12,6 +12,12 @@ read_task_config(path) \description{ Read project config } +\section{Deprecated}{ + +Superseded by \code{openproblems.project.docs.read_task_config()} in the Python +\code{openproblems} package, which is what \code{create_task_readme} runs. +} + \examples{ path <- system.file( "extdata", "example_project", "_viash.yaml", diff --git a/packages/r/openproblems.docs/man/read_task_metadata.Rd b/packages/r/openproblems.docs/man/read_task_metadata.Rd index 3bd91d4..b035071 100644 --- a/packages/r/openproblems.docs/man/read_task_metadata.Rd +++ b/packages/r/openproblems.docs/man/read_task_metadata.Rd @@ -15,6 +15,12 @@ A list with the api info \description{ This function reads the api files in a task and returns a list with the api info } +\section{Deprecated}{ + +Superseded by \code{openproblems.project.docs.read_task_metadata()} in the Python +\code{openproblems} package, which is what \code{create_task_readme} runs. +} + \examples{ path <- system.file("extdata", "example_project", "api", package = "openproblems.docs") diff --git a/packages/r/openproblems.docs/man/render_component_spec.Rd b/packages/r/openproblems.docs/man/render_component_spec.Rd index e9125fa..80deb14 100644 --- a/packages/r/openproblems.docs/man/render_component_spec.Rd +++ b/packages/r/openproblems.docs/man/render_component_spec.Rd @@ -15,6 +15,12 @@ string \description{ Render component section } +\section{Deprecated}{ + +Superseded by \code{openproblems.project.docs.render_component_spec()} in the Python +\code{openproblems} package, which is what \code{create_task_readme} runs. +} + \examples{ path <- system.file( "extdata", "example_project", "api", "comp_method.yaml", diff --git a/packages/r/openproblems.docs/man/render_file_format.Rd b/packages/r/openproblems.docs/man/render_file_format.Rd index 5160f50..f7337c5 100644 --- a/packages/r/openproblems.docs/man/render_file_format.Rd +++ b/packages/r/openproblems.docs/man/render_file_format.Rd @@ -15,6 +15,12 @@ string \description{ Render file section } +\section{Deprecated}{ + +Superseded by \code{openproblems.project.docs.render_file_format()} in the Python +\code{openproblems} package, which is what \code{create_task_readme} runs. +} + \examples{ path <- system.file( "extdata", "example_project", "api", "file_train_h5ad.yaml", diff --git a/packages/r/openproblems.docs/man/render_json_schema_example.Rd b/packages/r/openproblems.docs/man/render_json_schema_example.Rd index 1846d96..e521679 100644 --- a/packages/r/openproblems.docs/man/render_json_schema_example.Rd +++ b/packages/r/openproblems.docs/man/render_json_schema_example.Rd @@ -15,6 +15,12 @@ YAML string \description{ This function takes a JSON schema and renders an example YAML string. } +\section{Deprecated}{ + +Deprecated along with the rest of this package; it has no replacement in the +Python \code{openproblems} package. +} + \examples{ library(openproblems) example <- system.file("extdata", "example_schema.json", package = "openproblems.docs") diff --git a/packages/r/openproblems.docs/man/render_task_readme_qmd.Rd b/packages/r/openproblems.docs/man/render_task_readme_qmd.Rd index bbe476d..de7acca 100644 --- a/packages/r/openproblems.docs/man/render_task_readme_qmd.Rd +++ b/packages/r/openproblems.docs/man/render_task_readme_qmd.Rd @@ -17,6 +17,12 @@ A qmd documentation string \description{ Render the README.qmd file for a task } +\section{Deprecated}{ + +Superseded by \code{openproblems.project.docs.render_task_readme_qmd()} in the Python +\code{openproblems} package, which is what \code{create_task_readme} runs. +} + \examples{ path <- system.file("extdata", "example_project", package = "openproblems.docs") diff --git a/packages/r/openproblems.docs/tests/testthat/test-deprecated.R b/packages/r/openproblems.docs/tests/testthat/test-deprecated.R new file mode 100644 index 0000000..2961407 --- /dev/null +++ b/packages/r/openproblems.docs/tests/testthat/test-deprecated.R @@ -0,0 +1,14 @@ +test_that(".deprecate_docs points at the Python replacement", { + # a name unused elsewhere, so the once-per-session cache does not swallow it + expect_warning( + .deprecate_docs("a_superseded_function"), + class = "deprecatedWarning" + ) +}) + +test_that(".deprecate_docs handles a missing replacement", { + expect_warning( + .deprecate_docs("a_function_without_replacement", replacement = NULL), + "no replacement" + ) +}) From 988d97bde666c886309df44ba023ec88982d3c2a Mon Sep 17 00:00:00 2001 From: Robrecht Cannoodt Date: Wed, 29 Jul 2026 15:40:27 +0200 Subject: [PATCH 4/4] apply styler * styler 1.11.0 puts one argument per line at a single indent with the closing `) {` on its own line, where the older styler used a hanging double indent -- lintr 3.4.0 flags the latter with indentation_linter * Pre-existing on main; the R workflow only triggers on `packages/r/**`, so this pr is simply the first in a while to run it --- .../r/openproblems.utils/R/validate_object.R | 25 ++++++++++--------- packages/r/openproblems/R/read_viash_config.R | 5 ++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/r/openproblems.utils/R/validate_object.R b/packages/r/openproblems.utils/R/validate_object.R index 9e88625..28eadf2 100644 --- a/packages/r/openproblems.utils/R/validate_object.R +++ b/packages/r/openproblems.utils/R/validate_object.R @@ -31,18 +31,19 @@ #' ) #' validate_object(task_config, what = "task_config") validate_object <- function( - obj, - what = c( - "api_component_spec", - "api_file_format", - "task_config", - "task_control_method", - "task_method", - "task_metric" - ), - obj_source = NULL, - engine = c("ajv", "imjv"), - error = TRUE) { + obj, + what = c( + "api_component_spec", + "api_file_format", + "task_config", + "task_control_method", + "task_method", + "task_metric" + ), + obj_source = NULL, + engine = c("ajv", "imjv"), + error = TRUE +) { what <- match.arg(what) engine <- match.arg(engine) diff --git a/packages/r/openproblems/R/read_viash_config.R b/packages/r/openproblems/R/read_viash_config.R index 69fcbba..a2c56e5 100644 --- a/packages/r/openproblems/R/read_viash_config.R +++ b/packages/r/openproblems/R/read_viash_config.R @@ -19,8 +19,9 @@ #' #' @export read_viash_config <- function( - target_config_path, - project_root_dir = find_project_root(target_config_path)) { + target_config_path, + project_root_dir = find_project_root(target_config_path) +) { # note: if this config was not generated by viash, use `viash config view` first? config <- read_nested_yaml(target_config_path)