From 9dda8e56618d6803dbff13e6bfd92c2a8267700c Mon Sep 17 00:00:00 2001 From: Jennifer Pollack Date: Tue, 18 Aug 2026 15:06:03 +0200 Subject: [PATCH 1/6] Add resource dependency helper with tests --- src/wf_psf/quality_control/config.py | 28 +++++++++++ .../tests/test_quality_control/config_test.py | 46 ++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/wf_psf/quality_control/config.py b/src/wf_psf/quality_control/config.py index b9e9e074..d91a1cf7 100644 --- a/src/wf_psf/quality_control/config.py +++ b/src/wf_psf/quality_control/config.py @@ -276,6 +276,7 @@ def parse_resources_config( return ResourcesConfig(available=dict(config)) +# validators for internal consistency of config sections def validate_quality_control_config(config: QualityControlConfig) -> None: """Validate internal consistency of a quality control configuration. @@ -358,6 +359,33 @@ def validate_rejection_policy_metrics(config: QualityControlConfig) -> None: ) +# Resource dependency helpers +def get_required_resources( + config: QualityControlConfig, +) -> set[str]: + """Return resources required by enabled quality metrics. + + Parameters + ---------- + config : QualityControlConfig + Validated quality control configuration. + + Returns + ------- + Unique resource identifiers required by enabled quality metrics. + + Notes + ----- + This configuration is assumed to have been validated for internal consistency between resource requirements and available resources. + """ + return { + resource + for metric in config.metrics.values() + if metric.enabled + for resource in metric.required_resources + } + + SECTION_PARSERS = { "metrics": parse_metrics_config, "rejection": parse_rejection_policy_config, diff --git a/src/wf_psf/tests/test_quality_control/config_test.py b/src/wf_psf/tests/test_quality_control/config_test.py index e06668c8..ac28c143 100644 --- a/src/wf_psf/tests/test_quality_control/config_test.py +++ b/src/wf_psf/tests/test_quality_control/config_test.py @@ -18,6 +18,7 @@ ResourcesConfig, ) from wf_psf.quality_control.config import ( + get_required_resources, parse_resources_config, validate_metric_resources, validate_rejection_policy_metrics, @@ -256,9 +257,52 @@ def test_validate_rejection_policy_metrics_metric_not_enabled(qc_config_factory) validate_rejection_policy_metrics(config) -# Integration tests +# Resource dependency helper tests +def test_get_required_resources(qc_config_factory): + config = qc_config_factory( + metrics={ + "mask_obscuration": QualityMetricConfig( + enabled=True, + required_resources=[], + ), + "goodness_of_fit": QualityMetricConfig( + enabled=True, + required_resources=["psf_models.standard"], + ), + "shapes": QualityMetricConfig( + enabled=False, + required_resources=["psf_models.oversampled"], + ), + } + ) + + assert get_required_resources(config) == {"psf_models.standard"} + +def test_get_required_resources_combines_unique_resources(qc_config_factory): + config = qc_config_factory( + metrics={ + "metric_a": QualityMetricConfig( + enabled=True, + required_resources=["psf_models.standard"], + ), + "metric_b": QualityMetricConfig( + enabled=True, + required_resources=[ + "psf_models.standard", + "psf_models.oversampled", + ], + ), + } + ) + assert get_required_resources(config) == { + "psf_models.standard", + "psf_models.oversampled", + } + + +# Integration tests def test_load_config_validates_configuration_pass(): with does_not_raise(): load_config("valid/quality_control.yaml") From 8d31c734b65773dd30c2ab04402939bbae888bc0 Mon Sep 17 00:00:00 2001 From: Jennifer Pollack Date: Thu, 20 Aug 2026 13:33:59 +0200 Subject: [PATCH 2/6] Add resource dependency helpers with tests - Add resource module with helper to determine resources required by enabled metrics - Add helper to identify ready-to-use resources and those requiring preparation - Add resource_test module with tests for both helpers - Delete resource dependency related code from config and config test modules - Move qc_config_factory fixture to new conftest.py in quality_control test package --- src/wf_psf/quality_control/config.py | 27 ----- src/wf_psf/quality_control/resources.py | 75 +++++++++++++ .../tests/test_quality_control/config_test.py | 89 --------------- .../tests/test_quality_control/conftest.py | 50 +++++++++ .../test_quality_control/resources_test.py | 105 ++++++++++++++++++ 5 files changed, 230 insertions(+), 116 deletions(-) create mode 100644 src/wf_psf/quality_control/resources.py create mode 100644 src/wf_psf/tests/test_quality_control/conftest.py create mode 100644 src/wf_psf/tests/test_quality_control/resources_test.py diff --git a/src/wf_psf/quality_control/config.py b/src/wf_psf/quality_control/config.py index d91a1cf7..b0166250 100644 --- a/src/wf_psf/quality_control/config.py +++ b/src/wf_psf/quality_control/config.py @@ -359,33 +359,6 @@ def validate_rejection_policy_metrics(config: QualityControlConfig) -> None: ) -# Resource dependency helpers -def get_required_resources( - config: QualityControlConfig, -) -> set[str]: - """Return resources required by enabled quality metrics. - - Parameters - ---------- - config : QualityControlConfig - Validated quality control configuration. - - Returns - ------- - Unique resource identifiers required by enabled quality metrics. - - Notes - ----- - This configuration is assumed to have been validated for internal consistency between resource requirements and available resources. - """ - return { - resource - for metric in config.metrics.values() - if metric.enabled - for resource in metric.required_resources - } - - SECTION_PARSERS = { "metrics": parse_metrics_config, "rejection": parse_rejection_policy_config, diff --git a/src/wf_psf/quality_control/resources.py b/src/wf_psf/quality_control/resources.py new file mode 100644 index 00000000..602a2cf1 --- /dev/null +++ b/src/wf_psf/quality_control/resources.py @@ -0,0 +1,75 @@ +"""Quality control pipeline resources. + +Defines resource dependency helpers required by the quality control framework's quality metrics. + +:Authors: + Jennifer Pollack + +""" + +from __future__ import annotations +from collections.abc import Mapping +from typing import Any +from wf_psf.quality_control.config import QualityControlConfig + + +def get_required_resources( + config: QualityControlConfig, +) -> set[str]: + """Return resources required by enabled quality metrics. + + Parameters + ---------- + config : QualityControlConfig + Validated quality control configuration. + + Returns + ------- + Unique resource identifiers required by enabled quality metrics. + + Notes + ----- + This configuration is assumed to have been validated for internal consistency between resource requirements and available resources. + """ + return { + resource + for metric in config.metrics.values() + if metric.enabled + for resource in metric.required_resources + } + + +def resolve_resources( + required: set[str], + provided: Mapping[str, Any], +) -> tuple[dict[str, Any], set[str]]: + """Separate supplied resources from resources requiring preparation. + + Parameters + ---------- + required : set[str] + Unique resource identifiers required by enabled quality metrics. + + provided : Mapping[str, Any] + Ready-to-use resources provided by the quality control pipeline caller, keyed by resource identifier. + + Returns + ------- + tuple[dict[str, Any], set[str]] + A tuple containing: + + - a dictionary of required resources that were provided, keyed by + resource identifier; + - a set of required resource identifiers that were not provided and + therefore require preparation. + + Notes + ----- + This function only resolves which required resources are already + available. It does not prepare or otherwise acquire missing resources. + """ + resolved = { + resource: provided[resource] for resource in required if resource in provided + } + missing = required.difference(resolved) + return resolved, missing diff --git a/src/wf_psf/tests/test_quality_control/config_test.py b/src/wf_psf/tests/test_quality_control/config_test.py index ac28c143..32dde632 100644 --- a/src/wf_psf/tests/test_quality_control/config_test.py +++ b/src/wf_psf/tests/test_quality_control/config_test.py @@ -18,7 +18,6 @@ ResourcesConfig, ) from wf_psf.quality_control.config import ( - get_required_resources, parse_resources_config, validate_metric_resources, validate_rejection_policy_metrics, @@ -30,49 +29,6 @@ def load_config(config_file: str) -> QualityControlConfig: return handler.load() -@pytest.fixture -def qc_config_factory(): - def factory( - *, - required_resources=None, - rejection_metric=None, - resources=None, - metrics=None, - rejection=None, - ): - metric_default = { - "goodness_of_fit": QualityMetricConfig( - enabled=True, - required_resources=required_resources or [], - ) - } - - resources_default = ResourcesConfig( - available={ - "psf_models": { - "standard": { - "inference_config": "inference_standard.yaml", - } - } - } - ) - - rejection_default = { - rejection_metric or "goodness_of_fit": RejectionPolicyConfig( - enabled=True, - threshold=0.25, - ) - } - - return QualityControlConfig( - metrics=metric_default if metrics is None else metrics, - resources=resources_default if resources is None else resources, - rejection=rejection_default if rejection is None else rejection, - ) - - return factory - - # Test for config loading and parsers def test_quality_control_config_loading(): config = load_config("valid/quality_control.yaml") @@ -257,51 +213,6 @@ def test_validate_rejection_policy_metrics_metric_not_enabled(qc_config_factory) validate_rejection_policy_metrics(config) -# Resource dependency helper tests -def test_get_required_resources(qc_config_factory): - config = qc_config_factory( - metrics={ - "mask_obscuration": QualityMetricConfig( - enabled=True, - required_resources=[], - ), - "goodness_of_fit": QualityMetricConfig( - enabled=True, - required_resources=["psf_models.standard"], - ), - "shapes": QualityMetricConfig( - enabled=False, - required_resources=["psf_models.oversampled"], - ), - } - ) - - assert get_required_resources(config) == {"psf_models.standard"} - - -def test_get_required_resources_combines_unique_resources(qc_config_factory): - config = qc_config_factory( - metrics={ - "metric_a": QualityMetricConfig( - enabled=True, - required_resources=["psf_models.standard"], - ), - "metric_b": QualityMetricConfig( - enabled=True, - required_resources=[ - "psf_models.standard", - "psf_models.oversampled", - ], - ), - } - ) - - assert get_required_resources(config) == { - "psf_models.standard", - "psf_models.oversampled", - } - - # Integration tests def test_load_config_validates_configuration_pass(): with does_not_raise(): diff --git a/src/wf_psf/tests/test_quality_control/conftest.py b/src/wf_psf/tests/test_quality_control/conftest.py new file mode 100644 index 00000000..6d76caca --- /dev/null +++ b/src/wf_psf/tests/test_quality_control/conftest.py @@ -0,0 +1,50 @@ +import pytest +from wf_psf.quality_control.config import ( + QualityControlConfig, + QualityMetricConfig, + RejectionPolicyConfig, + ResourcesConfig, +) + + +@pytest.fixture +def qc_config_factory(): + def factory( + *, + required_resources=None, + rejection_metric=None, + resources=None, + metrics=None, + rejection=None, + ): + metric_default = { + "goodness_of_fit": QualityMetricConfig( + enabled=True, + required_resources=required_resources or [], + ) + } + + resources_default = ResourcesConfig( + available={ + "psf_models": { + "standard": { + "inference_config": "inference_standard.yaml", + } + } + } + ) + + rejection_default = { + rejection_metric or "goodness_of_fit": RejectionPolicyConfig( + enabled=True, + threshold=0.25, + ) + } + + return QualityControlConfig( + metrics=metric_default if metrics is None else metrics, + resources=resources_default if resources is None else resources, + rejection=rejection_default if rejection is None else rejection, + ) + + return factory diff --git a/src/wf_psf/tests/test_quality_control/resources_test.py b/src/wf_psf/tests/test_quality_control/resources_test.py new file mode 100644 index 00000000..8f9e2f75 --- /dev/null +++ b/src/wf_psf/tests/test_quality_control/resources_test.py @@ -0,0 +1,105 @@ +"""UNIT TESTS FOR PACKAGE MODULE: Quality Control Resources + +This module contains unit tests for the quality control resources module. + +:Author: Jennifer Pollack + +""" + +import pytest +from wf_psf.quality_control.config import QualityMetricConfig +from wf_psf.quality_control.resources import get_required_resources, resolve_resources + + +def test_get_required_resources(qc_config_factory): + config = qc_config_factory( + metrics={ + "mask_obscuration": QualityMetricConfig( + enabled=True, + required_resources=[], + ), + "goodness_of_fit": QualityMetricConfig( + enabled=True, + required_resources=["psf_models.standard"], + ), + "shapes": QualityMetricConfig( + enabled=False, + required_resources=["psf_models.oversampled"], + ), + } + ) + + assert get_required_resources(config) == {"psf_models.standard"} + + +def test_get_required_resources_combines_unique_resources(qc_config_factory): + config = qc_config_factory( + metrics={ + "metric_a": QualityMetricConfig( + enabled=True, + required_resources=["psf_models.standard"], + ), + "metric_b": QualityMetricConfig( + enabled=True, + required_resources=[ + "psf_models.standard", + "psf_models.oversampled", + ], + ), + } + ) + + assert get_required_resources(config) == { + "psf_models.standard", + "psf_models.oversampled", + } + + +@pytest.mark.parametrize( + ("required", "provided", "expected_resolved", "expected_missing"), + [ + ( + {"psf_models.standard"}, + {"psf_models.standard": [1, 1, 1, 1]}, + {"psf_models.standard": [1, 1, 1, 1]}, + set(), + ), + ( + {"psf_models.standard"}, + {}, + {}, + {"psf_models.standard"}, + ), + ( + {"psf_models.standard", "psf_models.oversampled"}, + {"psf_models.standard": [1, 1, 1, 1]}, + {"psf_models.standard": [1, 1, 1, 1]}, + {"psf_models.oversampled"}, + ), + ( + {"psf_models.standard"}, + { + "psf_models.standard": [1, 1, 1, 1], + "psf_models.oversampled": [2, 2, 2, 2], + }, + {"psf_models.standard": [1, 1, 1, 1]}, + set(), + ), + ( + set(), + {}, + {}, + set(), + ), + ], +) +def test_resolve_resources( + required, + provided, + expected_resolved, + expected_missing, +): + assert resolve_resources(required, provided) == ( + expected_resolved, + expected_missing, + ) From 3cc3545b752e6e61699640ed4978704968d8419d Mon Sep 17 00:00:00 2001 From: Jennifer Pollack Date: Thu, 20 Aug 2026 13:51:30 +0200 Subject: [PATCH 3/6] docs: clarify working version of resource module in doc string --- src/wf_psf/quality_control/resources.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wf_psf/quality_control/resources.py b/src/wf_psf/quality_control/resources.py index 602a2cf1..7bdedbba 100644 --- a/src/wf_psf/quality_control/resources.py +++ b/src/wf_psf/quality_control/resources.py @@ -1,10 +1,10 @@ -"""Quality control pipeline resources. +"""Resource dependency handling for the quality control pipeline. -Defines resource dependency helpers required by the quality control framework's quality metrics. +Defines helpers for identifying resources required by enabled quality metrics +and resolving those requirements against resources supplied by the caller. :Authors: Jennifer Pollack - """ from __future__ import annotations From 194147ec49e89675a2e4dd5ae6ec013482332136 Mon Sep 17 00:00:00 2001 From: Jennifer Pollack Date: Fri, 21 Aug 2026 11:12:24 +0200 Subject: [PATCH 4/6] Add resource resolution orchestrator with tests - Add helper to resolve resources required by enabled metrics - Raise an explicit error when required resources are unavailable - Add composition tests for resolved, missing, and unrequired resources --- src/wf_psf/quality_control/resources.py | 42 +++++++++++++++++++ .../test_quality_control/resources_test.py | 37 +++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/wf_psf/quality_control/resources.py b/src/wf_psf/quality_control/resources.py index 7bdedbba..4cb57ae7 100644 --- a/src/wf_psf/quality_control/resources.py +++ b/src/wf_psf/quality_control/resources.py @@ -73,3 +73,45 @@ def resolve_resources( } missing = required.difference(resolved) return resolved, missing + + +def resolve_required_resources( + config: QualityControlConfig, + provided: Mapping[str, Any] | None = None, +) -> dict[str, Any]: + """Resolve resources required by enabled quality metrics. + + Parameters + ---------- + config : QualityControlConfig + Quality control configuration. + + provided : Mapping[str, Any] or None + Ready-to-use resources supplied by the pipeline caller. + + Returns + ------- + dict[str, Any] + Resources required by enabled quality metrics and supplied by the + caller. + + Raises + ------ + NotImplementedError + If required resources are not supplied by the caller. Preparation of + missing resources is not yet implemented. + """ + required = get_required_resources(config) + provided = {} if provided is None else provided + + resolved, missing = resolve_resources( + required=required, + provided=provided, + ) + + if missing: + raise NotImplementedError( + f"Required resources are not available: {sorted(missing)}" + ) + + return resolved diff --git a/src/wf_psf/tests/test_quality_control/resources_test.py b/src/wf_psf/tests/test_quality_control/resources_test.py index 8f9e2f75..b17642d2 100644 --- a/src/wf_psf/tests/test_quality_control/resources_test.py +++ b/src/wf_psf/tests/test_quality_control/resources_test.py @@ -8,7 +8,11 @@ import pytest from wf_psf.quality_control.config import QualityMetricConfig -from wf_psf.quality_control.resources import get_required_resources, resolve_resources +from wf_psf.quality_control.resources import ( + get_required_resources, + resolve_resources, + resolve_required_resources, +) def test_get_required_resources(qc_config_factory): @@ -103,3 +107,34 @@ def test_resolve_resources( expected_resolved, expected_missing, ) + + +# Resource resolution orchestration tests +def test_resolve_required_resources(qc_config_factory): + config = qc_config_factory( + required_resources=["psf_models.standard"], + ) + model_psfs = [1, 1, 1, 1] + provided = {"psf_models.standard": model_psfs} + + assert resolve_required_resources(config, provided) == { + "psf_models.standard": model_psfs, + } + + +def test_resolve_required_resources_missing(qc_config_factory): + config = qc_config_factory( + required_resources=["psf_models.standard"], + ) + + with pytest.raises( + NotImplementedError, + match="Required resources are not available", + ): + resolve_required_resources(config) + + +def test_resolve_required_resources_no_resources_required(qc_config_factory): + config = qc_config_factory(required_resources=[]) + + assert resolve_required_resources(config) == {} From 3e869c838e7551375c09f64aba8e6fc156d5adbe Mon Sep 17 00:00:00 2001 From: Jennifer Pollack Date: Mon, 24 Aug 2026 13:23:28 +0200 Subject: [PATCH 5/6] Introduce Resources class as resource API boundary - Define Resources class as the resource management API boundary - Move and consolidate resource helpers into class methods - Update resources unit test module --- src/wf_psf/quality_control/resources.py | 153 +++++++----------- .../test_quality_control/resources_test.py | 71 +++----- 2 files changed, 75 insertions(+), 149 deletions(-) diff --git a/src/wf_psf/quality_control/resources.py b/src/wf_psf/quality_control/resources.py index 4cb57ae7..dc06fea3 100644 --- a/src/wf_psf/quality_control/resources.py +++ b/src/wf_psf/quality_control/resources.py @@ -13,105 +13,66 @@ from wf_psf.quality_control.config import QualityControlConfig -def get_required_resources( - config: QualityControlConfig, -) -> set[str]: - """Return resources required by enabled quality metrics. - - Parameters - ---------- - config : QualityControlConfig - Validated quality control configuration. - - Returns - ------- - Unique resource identifiers required by enabled quality metrics. +class Resources: + """Manage resources required by quality control metrics. - Notes - ----- - This configuration is assumed to have been validated for internal consistency between resource requirements and available resources. + Assesses resource requirements and availability for a validated quality + control configuration. """ - return { - resource - for metric in config.metrics.values() - if metric.enabled - for resource in metric.required_resources - } - - -def resolve_resources( - required: set[str], - provided: Mapping[str, Any], -) -> tuple[dict[str, Any], set[str]]: - """Separate supplied resources from resources requiring preparation. - - Parameters - ---------- - required : set[str] - Unique resource identifiers required by enabled quality metrics. - - provided : Mapping[str, Any] - Ready-to-use resources provided by the quality control pipeline caller, keyed by resource identifier. - - Returns - ------- - tuple[dict[str, Any], set[str]] - A tuple containing: - - a dictionary of required resources that were provided, keyed by - resource identifier; - - a set of required resource identifiers that were not provided and - therefore require preparation. + def __init__(self, config: QualityControlConfig): + self.config = config - Notes - ----- - This function only resolves which required resources are already - available. It does not prepare or otherwise acquire missing resources. - """ - resolved = { - resource: provided[resource] for resource in required if resource in provided - } - missing = required.difference(resolved) - return resolved, missing - - -def resolve_required_resources( - config: QualityControlConfig, - provided: Mapping[str, Any] | None = None, -) -> dict[str, Any]: - """Resolve resources required by enabled quality metrics. - - Parameters - ---------- - config : QualityControlConfig - Quality control configuration. - - provided : Mapping[str, Any] or None - Ready-to-use resources supplied by the pipeline caller. - - Returns - ------- - dict[str, Any] - Resources required by enabled quality metrics and supplied by the - caller. - - Raises - ------ - NotImplementedError - If required resources are not supplied by the caller. Preparation of - missing resources is not yet implemented. - """ - required = get_required_resources(config) - provided = {} if provided is None else provided + def get_required(self) -> set[str]: + """Return resources required by enabled quality metrics. - resolved, missing = resolve_resources( - required=required, - provided=provided, - ) - - if missing: - raise NotImplementedError( - f"Required resources are not available: {sorted(missing)}" - ) + Returns + ------- + Unique resource identifiers required by enabled quality metrics. - return resolved + Notes + ----- + This configuration is assumed to have been validated for internal consistency between resource requirements and available resources. + + """ + return { + resource + for metric in self.config.metrics.values() + if metric.enabled + for resource in metric.required_resources + } + + def resolve( + self, + provided: Mapping[str, Any] | None = None, + ) -> dict[str, Any]: + """Resolve resources required by enabled quality metrics. + + Parameters + ---------- + provided : Mapping[str, Any] or None + Ready-to-use resources supplied by the pipeline caller. + + Returns + ------- + dict[str, Any] + Resources required by enabled quality metrics and supplied by the + caller. + + Raises + ------ + NotImplementedError + If required resources are not supplied by the caller. Preparation of + missing resources is not yet implemented. + """ + required = self.get_required() + provided = {} if provided is None else provided + + missing = required - provided.keys() + + if missing: + raise NotImplementedError( + f"Required resources are not available: {sorted(missing)}" + ) + + return {resource: provided[resource] for resource in required} diff --git a/src/wf_psf/tests/test_quality_control/resources_test.py b/src/wf_psf/tests/test_quality_control/resources_test.py index b17642d2..f7d14a8f 100644 --- a/src/wf_psf/tests/test_quality_control/resources_test.py +++ b/src/wf_psf/tests/test_quality_control/resources_test.py @@ -8,14 +8,10 @@ import pytest from wf_psf.quality_control.config import QualityMetricConfig -from wf_psf.quality_control.resources import ( - get_required_resources, - resolve_resources, - resolve_required_resources, -) +from wf_psf.quality_control.resources import Resources -def test_get_required_resources(qc_config_factory): +def test_get_required(qc_config_factory): config = qc_config_factory( metrics={ "mask_obscuration": QualityMetricConfig( @@ -32,11 +28,11 @@ def test_get_required_resources(qc_config_factory): ), } ) - - assert get_required_resources(config) == {"psf_models.standard"} + resources = Resources(config) + assert resources.get_required() == {"psf_models.standard"} -def test_get_required_resources_combines_unique_resources(qc_config_factory): +def test_get_required_combines_unique_resources(qc_config_factory): config = qc_config_factory( metrics={ "metric_a": QualityMetricConfig( @@ -53,32 +49,21 @@ def test_get_required_resources_combines_unique_resources(qc_config_factory): } ) - assert get_required_resources(config) == { + resources = Resources(config) + assert resources.get_required() == { "psf_models.standard", "psf_models.oversampled", } +# Resource resolution orchestration tests @pytest.mark.parametrize( - ("required", "provided", "expected_resolved", "expected_missing"), + ("required_resources", "provided", "expected_resolved"), [ ( {"psf_models.standard"}, {"psf_models.standard": [1, 1, 1, 1]}, {"psf_models.standard": [1, 1, 1, 1]}, - set(), - ), - ( - {"psf_models.standard"}, - {}, - {}, - {"psf_models.standard"}, - ), - ( - {"psf_models.standard", "psf_models.oversampled"}, - {"psf_models.standard": [1, 1, 1, 1]}, - {"psf_models.standard": [1, 1, 1, 1]}, - {"psf_models.oversampled"}, ), ( {"psf_models.standard"}, @@ -87,54 +72,34 @@ def test_get_required_resources_combines_unique_resources(qc_config_factory): "psf_models.oversampled": [2, 2, 2, 2], }, {"psf_models.standard": [1, 1, 1, 1]}, - set(), ), ( - set(), + [], {}, {}, - set(), ), ], ) def test_resolve_resources( - required, + qc_config_factory, + required_resources, provided, expected_resolved, - expected_missing, ): - assert resolve_resources(required, provided) == ( - expected_resolved, - expected_missing, - ) - - -# Resource resolution orchestration tests -def test_resolve_required_resources(qc_config_factory): - config = qc_config_factory( - required_resources=["psf_models.standard"], - ) - model_psfs = [1, 1, 1, 1] - provided = {"psf_models.standard": model_psfs} + config = qc_config_factory(required_resources=required_resources) + resources = Resources(config) - assert resolve_required_resources(config, provided) == { - "psf_models.standard": model_psfs, - } + assert resources.resolve(provided) == expected_resolved -def test_resolve_required_resources_missing(qc_config_factory): +def test_resolve_resources_missing(qc_config_factory): config = qc_config_factory( required_resources=["psf_models.standard"], ) + resources = Resources(config) with pytest.raises( NotImplementedError, match="Required resources are not available", ): - resolve_required_resources(config) - - -def test_resolve_required_resources_no_resources_required(qc_config_factory): - config = qc_config_factory(required_resources=[]) - - assert resolve_required_resources(config) == {} + resources.resolve() From ae5a3af760fa55393d851070dcd779621291548d Mon Sep 17 00:00:00 2001 From: Jennifer Pollack Date: Mon, 24 Aug 2026 15:57:58 +0200 Subject: [PATCH 6/6] Add debug logging for resource resolution --- src/wf_psf/quality_control/resources.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/wf_psf/quality_control/resources.py b/src/wf_psf/quality_control/resources.py index dc06fea3..556d9375 100644 --- a/src/wf_psf/quality_control/resources.py +++ b/src/wf_psf/quality_control/resources.py @@ -12,6 +12,10 @@ from typing import Any from wf_psf.quality_control.config import QualityControlConfig +import logging + +logger = logging.getLogger(__name__) + class Resources: """Manage resources required by quality control metrics. @@ -68,11 +72,24 @@ def resolve( required = self.get_required() provided = {} if provided is None else provided + resolved = { + resource: provided[resource] + for resource in required + if resource in provided + } missing = required - provided.keys() + unused = provided.keys() - required + + logger.debug( + "Resource resolution: resolved=%s, missing=%s, unused=%s", + sorted(resolved), + sorted(missing), + sorted(unused), + ) if missing: raise NotImplementedError( f"Required resources are not available: {sorted(missing)}" ) - return {resource: provided[resource] for resource in required} + return resolved