climate-ref-core 0.5.2__tar.gz → 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/PKG-INFO +3 -1
  2. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/pyproject.toml +3 -1
  3. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/dataset_registry.py +1 -1
  4. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/diagnostics.py +39 -22
  5. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/test_cmec_metric.py +9 -0
  6. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_dataset_registry/test_dataset_registry.py +2 -2
  7. climate_ref_core-0.5.3/tests/unit/test_datasets/dataset_collection_obs4mips_hash.yml +2 -0
  8. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_datasets.py +3 -3
  9. climate_ref_core-0.5.2/tests/unit/test_datasets/dataset_collection_obs4mips_hash.yml +0 -2
  10. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/.gitignore +0 -0
  11. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/LICENCE +0 -0
  12. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/NOTICE +0 -0
  13. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/README.md +0 -0
  14. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/__init__.py +0 -0
  15. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/constraints.py +0 -0
  16. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/datasets.py +0 -0
  17. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/env.py +0 -0
  18. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/exceptions.py +0 -0
  19. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/executor.py +0 -0
  20. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/logging.py +0 -0
  21. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/metric_values/__init__.py +0 -0
  22. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/metric_values/typing.py +0 -0
  23. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/providers.py +0 -0
  24. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/py.typed +0 -0
  25. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/pycmec/README.md +0 -0
  26. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/pycmec/__init__.py +0 -0
  27. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/pycmec/controlled_vocabulary.py +0 -0
  28. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/pycmec/cv_cmip7_aft.yaml +0 -0
  29. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/pycmec/metric.py +0 -0
  30. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/src/climate_ref_core/pycmec/output.py +0 -0
  31. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/metric_values/test_typing.py +0 -0
  32. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/cmec_testdata/cmec_metric_sample.json +0 -0
  33. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/cmec_testdata/cmec_output_sample.json +0 -0
  34. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/cmec_testdata/cv_sample.yaml +0 -0
  35. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/cmec_testdata/test_metric_json_schema.yml +0 -0
  36. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/cmec_testdata/test_output_json_schema.yml +0 -0
  37. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/conftest.py +0 -0
  38. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/test_cmec_output.py +0 -0
  39. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/pycmec/test_controlled_vocabulary.py +0 -0
  40. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_constraints.py +0 -0
  41. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_datasets/dataset_collection_hash.yml +0 -0
  42. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_datasets/metric_dataset_hash.yml +0 -0
  43. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_exceptions.py +0 -0
  44. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_executor.py +0 -0
  45. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_logging.py +0 -0
  46. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_metrics.py +0 -0
  47. {climate_ref_core-0.5.2 → climate_ref_core-0.5.3}/tests/unit/test_providers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: climate-ref-core
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: Core library for the CMIP Rapid Evaluation Framework
5
5
  Author-email: Jared Lewis <jared.lewis@climate-resource.com>, Mika Pflueger <mika.pflueger@climate-resource.com>, Bouwe Andela <b.andela@esciencecenter.nl>, Jiwoo Lee <lee1043@llnl.gov>, Min Xu <xum1@ornl.gov>, Nathan Collier <collierno@ornl.gov>, Dora Hegedus <dora.hegedus@stfc.ac.uk>
6
6
  License: Apache-2.0
@@ -18,8 +18,10 @@ Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Topic :: Scientific/Engineering
19
19
  Requires-Python: >=3.11
20
20
  Requires-Dist: attrs>=23.2.0
21
+ Requires-Dist: loguru>=0.7.0
21
22
  Requires-Dist: numpy>=1.25.0
22
23
  Requires-Dist: pandas>=2.1.0
24
+ Requires-Dist: pooch<2,>=1.8.0
23
25
  Requires-Dist: pydantic>=2.10.6
24
26
  Requires-Dist: requests
25
27
  Requires-Dist: rich
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "climate-ref-core"
3
- version = "0.5.2"
3
+ version = "0.5.3"
4
4
  description = "Core library for the CMIP Rapid Evaluation Framework"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -31,6 +31,8 @@ dependencies = [
31
31
  "typing_extensions",
32
32
  "requests",
33
33
  "rich",
34
+ "loguru>=0.7.0",
35
+ "pooch>=1.8.0,<2",
34
36
 
35
37
  # SPEC 0000 constraints
36
38
  # We follow [SPEC-0000](https://scientific-python.org/specs/spec-0000/)
@@ -148,7 +148,7 @@ class DatasetRegistryManager:
148
148
  path=pooch.os_cache(cache_name),
149
149
  base_url=base_url,
150
150
  version=version,
151
- retry_if_failed=5,
151
+ retry_if_failed=10,
152
152
  env="REF_DATASET_CACHE_DIR",
153
153
  )
154
154
  registry.load_registry(str(importlib.resources.files(package) / resource))
@@ -5,7 +5,6 @@ Diagnostic interface
5
5
  from __future__ import annotations
6
6
 
7
7
  import pathlib
8
- from abc import abstractmethod
9
8
  from collections.abc import Iterable, Sequence
10
9
  from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
11
10
 
@@ -438,12 +437,28 @@ class AbstractDiagnostic(Protocol):
438
437
  The provider that provides the diagnostic.
439
438
  """
440
439
 
441
- def run(self, definition: ExecutionDefinition) -> ExecutionResult:
440
+ def execute(self, definition: ExecutionDefinition) -> None:
442
441
  """
443
- Run the diagnostic on the given configuration.
442
+ Execute the diagnostic on the given configuration.
444
443
 
445
444
  The implementation of this method is left to the diagnostic providers.
445
+ The results should be written to the output directory of the execution definition.
446
+ These are later used to build the output bundle and the diagnostic bundle.
447
+
448
+ This may occur in a separate process (or python environment in the case of a `CommandLineDiagnostic`).
449
+
450
+ Parameters
451
+ ----------
452
+ definition
453
+ The configuration to run the diagnostic on.
454
+ """
455
+ ...
456
+
457
+ def build_execution_result(self, definition: ExecutionDefinition) -> ExecutionResult:
458
+ """
459
+ Build the result from running the diagnostic on the given configuration.
446
460
 
461
+ This can be replayed later to build the result from the output execution.
447
462
 
448
463
  Parameters
449
464
  ----------
@@ -455,6 +470,7 @@ class AbstractDiagnostic(Protocol):
455
470
  :
456
471
  The result of running the diagnostic.
457
472
  """
473
+ ...
458
474
 
459
475
 
460
476
  class Diagnostic(AbstractDiagnostic):
@@ -506,6 +522,24 @@ class Diagnostic(AbstractDiagnostic):
506
522
  def provider(self, value: DiagnosticProvider) -> None:
507
523
  self._provider = value
508
524
 
525
+ def run(self, definition: ExecutionDefinition) -> ExecutionResult:
526
+ """
527
+ Run the diagnostic on the given configuration.
528
+
529
+ This executes the diagnostic and builds the result from the output bundle.
530
+
531
+ Parameters
532
+ ----------
533
+ definition
534
+ The configuration to run the diagnostic on.
535
+ """
536
+ # Execute the diagnostic
537
+ # This may be run in a separate process (or python environment)
538
+ self.execute(definition)
539
+
540
+ # Build the result from the output bundle
541
+ return self.build_execution_result(definition)
542
+
509
543
 
510
544
  class CommandLineDiagnostic(Diagnostic):
511
545
  """
@@ -514,7 +548,6 @@ class CommandLineDiagnostic(Diagnostic):
514
548
 
515
549
  provider: CommandLineDiagnosticProvider
516
550
 
517
- @abstractmethod
518
551
  def build_cmd(self, definition: ExecutionDefinition) -> Iterable[str]:
519
552
  """
520
553
  Build the command to run the diagnostic on the given configuration.
@@ -529,24 +562,9 @@ class CommandLineDiagnostic(Diagnostic):
529
562
  :
530
563
  A command that can be run with :func:`subprocess.run`.
531
564
  """
565
+ return []
532
566
 
533
- @abstractmethod
534
- def build_execution_result(self, definition: ExecutionDefinition) -> ExecutionResult:
535
- """
536
- Build the result from running the diagnostic on the given configuration.
537
-
538
- Parameters
539
- ----------
540
- definition
541
- The configuration to run the diagnostic on.
542
-
543
- Returns
544
- -------
545
- :
546
- The result of running the diagnostic.
547
- """
548
-
549
- def run(self, definition: ExecutionDefinition) -> ExecutionResult:
567
+ def execute(self, definition: ExecutionDefinition) -> None:
550
568
  """
551
569
  Run the diagnostic on the given configuration.
552
570
 
@@ -562,4 +580,3 @@ class CommandLineDiagnostic(Diagnostic):
562
580
  """
563
581
  cmd = self.build_cmd(definition)
564
582
  self.provider.run(cmd)
565
- return self.build_execution_result(definition)
@@ -477,6 +477,15 @@ def test_remove():
477
477
  }
478
478
 
479
479
 
480
+ def test_iter_results(cmec_metric):
481
+ assert list(cmec_metric.iter_results())
482
+
483
+
484
+ def test_iter_results_empty(cmec_right_metric_dict):
485
+ cmec_metric = CMECMetric.model_validate(CMECMetric.create_template())
486
+ assert not list(cmec_metric.iter_results())
487
+
488
+
480
489
  @pytest.mark.xfail(reason="No need to currently support removing deeper dimensions")
481
490
  def test_remove_not_first(cmec_right_metric_dict):
482
491
  remove_dimensions(cmec_right_metric_dict, ["metric"])
@@ -107,7 +107,7 @@ def test_fetch_all_files(mocker, tmp_path, symlink):
107
107
  registry.fetch = mocker.MagicMock(return_value=downloaded_file)
108
108
 
109
109
  fetch_all_files(registry, "obs4ref", tmp_path, symlink=symlink)
110
- assert registry.fetch.call_count == 2
110
+ assert registry.fetch.call_count == 36
111
111
 
112
112
  expected_file = (
113
113
  tmp_path / "obs4REF/MOHC/HadISST-1-1/mon/ts/gn/v20210727/ts_mon_HadISST-1-1_PCMDI_gn_187001-201907.nc"
@@ -123,4 +123,4 @@ def test_fetch_all_files_no_output(mocker):
123
123
  registry.fetch = mocker.MagicMock()
124
124
 
125
125
  fetch_all_files(registry, "obs4ref", None)
126
- assert registry.fetch.call_count == 2
126
+ assert registry.fetch.call_count == 36
@@ -120,11 +120,11 @@ class TestDatasetCollectionObs4MIPs:
120
120
  assert dataset_collection_obs4mips.instance_id.equals(expected)
121
121
 
122
122
  def test_hash(self, dataset_collection_obs4mips, obs4mips_data_catalog, data_regression):
123
- ta_datasets = obs4mips_data_catalog[obs4mips_data_catalog.variable_id == "ta"]
124
- dataset_hash = hash(DatasetCollection(ta_datasets, "instance_id"))
123
+ ts_datasets = obs4mips_data_catalog[obs4mips_data_catalog.variable_id == "ts"]
124
+ dataset_hash = hash(DatasetCollection(ts_datasets, "instance_id"))
125
125
  assert isinstance(dataset_hash, int)
126
126
 
127
- assert dataset_hash != hash(DatasetCollection(ta_datasets.iloc[[0, 0]], "instance_id"))
127
+ assert dataset_hash != hash(DatasetCollection(ts_datasets.iloc[[0, 0]], "instance_id"))
128
128
 
129
129
  # This hash will change if the data catalog changes
130
130
  # Specifically if more tas datasets are provided