From 6c0a8b320bf40bd193f13b19d8e8b6d150e1ce2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Gr=C3=B8nbech?= Date: Fri, 13 Feb 2026 23:37:51 +0100 Subject: [PATCH 1/6] fix: client.simulators.routines.revisions.list or retrieve no longer returns a corrupted routine revision if the revision has a zero valued input in the configuration --- .../client/data_classes/simulators/routine_revisions.py | 2 +- tests/tests_unit/test_data_classes/test_simulators.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cognite/client/data_classes/simulators/routine_revisions.py b/cognite/client/data_classes/simulators/routine_revisions.py index 20e2c6c0e8..fbe68c793f 100644 --- a/cognite/client/data_classes/simulators/routine_revisions.py +++ b/cognite/client/data_classes/simulators/routine_revisions.py @@ -83,7 +83,7 @@ def _load( is_constant = resource.get("value") is_timeseries = resource.get("sourceExternalId") - type_ = "constant" if is_constant else "timeseries" if is_timeseries else None + type_ = "constant" if is_constant is not None else "timeseries" if is_timeseries else None if type_ is None: return UnknownCogniteObject(resource) # type: ignore[return-value] input_class = _INPUT_CLASS_BY_TYPE.get(type_) diff --git a/tests/tests_unit/test_data_classes/test_simulators.py b/tests/tests_unit/test_data_classes/test_simulators.py index 33c211680a..c228113eb0 100644 --- a/tests/tests_unit/test_data_classes/test_simulators.py +++ b/tests/tests_unit/test_data_classes/test_simulators.py @@ -11,6 +11,7 @@ from cognite.client.data_classes.simulators.routine_revisions import ( SimulationValueUnitInput, SimulatorRoutineConfiguration, + SimulatorRoutineInput, SimulatorRoutineInputConstant, SimulatorRoutineInputTimeseries, SimulatorRoutineOutput, @@ -489,3 +490,11 @@ def test_error_handling(self) -> None: SimulationRunWrite( routine_revision_external_id="routine_revision_external_id_1", ) + + +class TestSimulatorRoutineInput: + def test_load_zero_value(self) -> None: + routine_input = SimulatorRoutineInput._load( + {"name": "input", "referenceId": "input", "value": 0, "valueType": "DOUBLE"} + ) + assert isinstance(routine_input, SimulatorRoutineInput) From 7f2b2f403d11cc27f97f1d1d4bea8e1707f9d964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Gr=C3=B8nbech?= <42722577+MortGron@users.noreply.github.com> Date: Sat, 14 Feb 2026 08:43:38 +0100 Subject: [PATCH 2/6] Do not fail on empty string external ID in revision inputs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- cognite/client/data_classes/simulators/routine_revisions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cognite/client/data_classes/simulators/routine_revisions.py b/cognite/client/data_classes/simulators/routine_revisions.py index fbe68c793f..1ae51ade37 100644 --- a/cognite/client/data_classes/simulators/routine_revisions.py +++ b/cognite/client/data_classes/simulators/routine_revisions.py @@ -83,7 +83,7 @@ def _load( is_constant = resource.get("value") is_timeseries = resource.get("sourceExternalId") - type_ = "constant" if is_constant is not None else "timeseries" if is_timeseries else None + type_ = "constant" if is_constant is not None else "timeseries" if is_timeseries is not None else None if type_ is None: return UnknownCogniteObject(resource) # type: ignore[return-value] input_class = _INPUT_CLASS_BY_TYPE.get(type_) From 4323e16cffef7886fe7bea2d5175ae5e2bde2242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Gr=C3=B8nbech?= <42722577+MortGron@users.noreply.github.com> Date: Sat, 14 Feb 2026 08:44:02 +0100 Subject: [PATCH 3/6] Add test Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- tests/tests_unit/test_data_classes/test_simulators.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/tests_unit/test_data_classes/test_simulators.py b/tests/tests_unit/test_data_classes/test_simulators.py index c228113eb0..bd300d5f6b 100644 --- a/tests/tests_unit/test_data_classes/test_simulators.py +++ b/tests/tests_unit/test_data_classes/test_simulators.py @@ -497,4 +497,12 @@ def test_load_zero_value(self) -> None: routine_input = SimulatorRoutineInput._load( {"name": "input", "referenceId": "input", "value": 0, "valueType": "DOUBLE"} ) - assert isinstance(routine_input, SimulatorRoutineInput) + assert isinstance(routine_input, SimulatorRoutineInputConstant) + assert routine_input.value == 0 + + def test_load_empty_string_source_external_id(self) -> None: + routine_input = SimulatorRoutineInput._load( + {"name": "input", "referenceId": "input", "sourceExternalId": ""} + ) + assert isinstance(routine_input, SimulatorRoutineInputTimeseries) + assert routine_input.source_external_id == "" From 5fa6b0553905892b21b3dfa29ff16d5391389c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Gr=C3=B8nbech?= Date: Sat, 14 Feb 2026 08:51:23 +0100 Subject: [PATCH 4/6] Run ruff --- tests/tests_unit/test_data_classes/test_simulators.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/tests_unit/test_data_classes/test_simulators.py b/tests/tests_unit/test_data_classes/test_simulators.py index bd300d5f6b..10e40c6a50 100644 --- a/tests/tests_unit/test_data_classes/test_simulators.py +++ b/tests/tests_unit/test_data_classes/test_simulators.py @@ -501,8 +501,6 @@ def test_load_zero_value(self) -> None: assert routine_input.value == 0 def test_load_empty_string_source_external_id(self) -> None: - routine_input = SimulatorRoutineInput._load( - {"name": "input", "referenceId": "input", "sourceExternalId": ""} - ) + routine_input = SimulatorRoutineInput._load({"name": "input", "referenceId": "input", "sourceExternalId": ""}) assert isinstance(routine_input, SimulatorRoutineInputTimeseries) assert routine_input.source_external_id == "" From 4a97e403c1277428feea4a788dfde01cb3066e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Gr=C3=B8nbech?= Date: Tue, 17 Feb 2026 13:19:38 +0100 Subject: [PATCH 5/6] docs: add data modelling debugging data classes to the docs --- docs/source/data_modeling.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/data_modeling.rst b/docs/source/data_modeling.rst index 11de51e619..9406669399 100644 --- a/docs/source/data_modeling.rst +++ b/docs/source/data_modeling.rst @@ -315,5 +315,11 @@ Core Data Model Extractor Extensions -------------------- .. automodule:: cognite.client.data_classes.data_modeling.extractor_extensions.v1 + :members: + :show-inheritance: + +Debugging Data Classes +------------ +.. automodule:: cognite.client.data_classes.data_modeling.debug :members: :show-inheritance: \ No newline at end of file From 45b60c197d7c08515ffe54e9478ab060943b44bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Gr=C3=B8nbech?= Date: Wed, 18 Feb 2026 14:32:55 +0100 Subject: [PATCH 6/6] Add check for invalid input --- .../client/data_classes/simulators/routine_revisions.py | 9 +++++++-- tests/tests_unit/test_data_classes/test_simulators.py | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cognite/client/data_classes/simulators/routine_revisions.py b/cognite/client/data_classes/simulators/routine_revisions.py index 1ae51ade37..e7d62116e4 100644 --- a/cognite/client/data_classes/simulators/routine_revisions.py +++ b/cognite/client/data_classes/simulators/routine_revisions.py @@ -83,8 +83,13 @@ def _load( is_constant = resource.get("value") is_timeseries = resource.get("sourceExternalId") - type_ = "constant" if is_constant is not None else "timeseries" if is_timeseries is not None else None - if type_ is None: + if is_constant is not None and is_timeseries is not None: + raise ValueError("Invalid routine input, cannot contain both 'value' and 'sourceExternalId'") + elif is_constant is not None: + type_ = "constant" + elif is_timeseries is not None: + type_ = "timeseries" + else: return UnknownCogniteObject(resource) # type: ignore[return-value] input_class = _INPUT_CLASS_BY_TYPE.get(type_) if input_class is None: diff --git a/tests/tests_unit/test_data_classes/test_simulators.py b/tests/tests_unit/test_data_classes/test_simulators.py index 10e40c6a50..9b98789d09 100644 --- a/tests/tests_unit/test_data_classes/test_simulators.py +++ b/tests/tests_unit/test_data_classes/test_simulators.py @@ -504,3 +504,10 @@ def test_load_empty_string_source_external_id(self) -> None: routine_input = SimulatorRoutineInput._load({"name": "input", "referenceId": "input", "sourceExternalId": ""}) assert isinstance(routine_input, SimulatorRoutineInputTimeseries) assert routine_input.source_external_id == "" + + def test_load_both_value_and_time_series(self) -> None: + with pytest.raises( + ValueError, + match="Invalid routine input, cannot contain both 'value' and 'sourceExternalId'", + ): + SimulatorRoutineInput._load({"name": "input", "referenceId": "input", "sourceExternalId": "", "value": 0})