pybinny 0.3.0__tar.gz → 0.3.2__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 (106) hide show
  1. {pybinny-0.3.0/src/pybinny.egg-info → pybinny-0.3.2}/PKG-INFO +1 -1
  2. {pybinny-0.3.0 → pybinny-0.3.2}/pyproject.toml +10 -6
  3. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/api/nz_tomography.py +43 -13
  4. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/config_utils.py +30 -0
  5. pybinny-0.3.2/src/binny/surveys/survey_presets.py +67 -0
  6. {pybinny-0.3.0 → pybinny-0.3.2/src/pybinny.egg-info}/PKG-INFO +1 -1
  7. {pybinny-0.3.0 → pybinny-0.3.2}/src/pybinny.egg-info/SOURCES.txt +2 -0
  8. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_api_nz_tomography.py +90 -5
  9. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_surveys_config_utils.py +178 -0
  10. pybinny-0.3.2/tests/test_surveys_survey_presets.py +211 -0
  11. {pybinny-0.3.0 → pybinny-0.3.2}/LICENSE +0 -0
  12. {pybinny-0.3.0 → pybinny-0.3.2}/README.md +0 -0
  13. {pybinny-0.3.0 → pybinny-0.3.2}/setup.cfg +0 -0
  14. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/__init__.py +0 -0
  15. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/api/__init__.py +0 -0
  16. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/axes/__init__.py +0 -0
  17. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/axes/bin_edges.py +0 -0
  18. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/axes/grids.py +0 -0
  19. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/axes/mixed_edges.py +0 -0
  20. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/correlations/__init__.py +0 -0
  21. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/correlations/bin_combo_filter.py +0 -0
  22. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/correlations/filters.py +0 -0
  23. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/correlations/metrics.py +0 -0
  24. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/correlations/scores.py +0 -0
  25. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/correlations/topology.py +0 -0
  26. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz/__init__.py +0 -0
  27. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz/calibration.py +0 -0
  28. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz/models.py +0 -0
  29. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz/registry.py +0 -0
  30. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/__init__.py +0 -0
  31. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/_tomography_bins.py +0 -0
  32. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/between_sample_metrics.py +0 -0
  33. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/bin_similarity.py +0 -0
  34. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/bin_stats.py +0 -0
  35. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/binning_core.py +0 -0
  36. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/nz_tomo_utils.py +0 -0
  37. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/photoz.py +0 -0
  38. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/nz_tomo/specz.py +0 -0
  39. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/__init__.py +0 -0
  40. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/__init__.py +0 -0
  41. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/des_survey_specs.yaml +0 -0
  42. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/desi_survey_specs.yaml +0 -0
  43. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/euclid_survey_specs.yaml +0 -0
  44. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/example_full_photoz.yaml +0 -0
  45. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/example_full_specz.yaml +0 -0
  46. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/example_minimal_photoz.yaml +0 -0
  47. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/example_minimal_specz.yaml +0 -0
  48. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/example_tabulated.yaml +0 -0
  49. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/lsst_survey_specs.yaml +0 -0
  50. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/configs/roman_survey_specs.yaml +0 -0
  51. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/__init__.py +0 -0
  52. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/desi_elg_nz.txt +0 -0
  53. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/desi_lrg_nz.txt +0 -0
  54. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/example_tabulated_nz.txt +0 -0
  55. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/roman_n_eff20.nz +0 -0
  56. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/roman_n_eff30.nz +0 -0
  57. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/roman_n_eff40.nz +0 -0
  58. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/data/roman_n_eff50.nz +0 -0
  59. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/scripts/fit_roman_smail.py +0 -0
  60. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/surveys/sky.py +0 -0
  61. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/__init__.py +0 -0
  62. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/broadcasting.py +0 -0
  63. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/io.py +0 -0
  64. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/metadata.py +0 -0
  65. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/normalization.py +0 -0
  66. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/pairwise_metrics.py +0 -0
  67. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/relations.py +0 -0
  68. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/types.py +0 -0
  69. {pybinny-0.3.0 → pybinny-0.3.2}/src/binny/utils/validators.py +0 -0
  70. {pybinny-0.3.0 → pybinny-0.3.2}/src/pybinny.egg-info/dependency_links.txt +0 -0
  71. {pybinny-0.3.0 → pybinny-0.3.2}/src/pybinny.egg-info/requires.txt +0 -0
  72. {pybinny-0.3.0 → pybinny-0.3.2}/src/pybinny.egg-info/top_level.txt +0 -0
  73. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_api_nz_tomography_examples.py +0 -0
  74. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_api_nz_tomography_lsst_reference.py +0 -0
  75. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_axes_bin_edges.py +0 -0
  76. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_axes_grids.py +0 -0
  77. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_axes_mixed_edges.py +0 -0
  78. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_axes_mixed_edges_invariances_fast.py +0 -0
  79. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_axes_mixed_edges_invariances_slow.py +0 -0
  80. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_correlations_bin_combo_filter.py +0 -0
  81. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_correlations_filters.py +0 -0
  82. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_correlations_metrics.py +0 -0
  83. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_correlations_scores.py +0 -0
  84. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_correlations_topology.py +0 -0
  85. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_calibration.py +0 -0
  86. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_models.py +0 -0
  87. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_registry.py +0 -0
  88. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_between_sample_metrics.py +0 -0
  89. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_bin_similarity.py +0 -0
  90. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_bin_stats.py +0 -0
  91. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_bin_stats_center_invariance_fast.py +0 -0
  92. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_bin_stats_center_invariance_slow.py +0 -0
  93. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_bin_stats_invariances.py +0 -0
  94. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_binning_core.py +0 -0
  95. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_nz_tomo_utils.py +0 -0
  96. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_photoz.py +0 -0
  97. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_photoz_metadata.py +0 -0
  98. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_nz_tomo_specz.py +0 -0
  99. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_surveys_sky.py +0 -0
  100. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_utils_broadcasting.py +0 -0
  101. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_utils_io.py +0 -0
  102. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_utils_metadata.py +0 -0
  103. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_utils_normalization.py +0 -0
  104. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_utils_pairwise_metrics.py +0 -0
  105. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_utils_relations.py +0 -0
  106. {pybinny-0.3.0 → pybinny-0.3.2}/tests/test_utils_validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybinny
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Flexible tomographic binning algorithms for cosmology.
5
5
  Author-email: Nikolina Sarcevic <nikolina.sarcevic@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pybinny"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  description = "Flexible tomographic binning algorithms for cosmology."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -86,20 +86,24 @@ where = ["src"]
86
86
  ]
87
87
 
88
88
  [tool.bumpversion]
89
- current_version = "0.3.0"
89
+ current_version = "0.3.2"
90
90
  commit = true
91
91
  tag = true
92
92
 
93
93
  [[tool.bumpversion.files]]
94
94
  filename = "pyproject.toml"
95
- search = 'version = "{current_version}"'
96
- replace = 'version = "{new_version}"'
95
+ search = 'current_version = "{current_version}"'
96
+ replace = 'current_version = "{new_version}"'
97
97
 
98
98
  [[tool.bumpversion.files]]
99
99
  filename = "pyproject.toml"
100
- search = 'current_version = "{current_version}"'
101
- replace = 'current_version = "{new_version}"'
100
+ search = 'version = "{current_version}"'
101
+ replace = 'version = "{new_version}"'
102
102
 
103
+ [[tool.bumpversion.files]]
104
+ filename = "CITATION.cff"
105
+ search = "version: {current_version}"
106
+ replace = "version: {new_version}"
103
107
 
104
108
  [tool.tox]
105
109
  requires = ["tox>=4.22"]
@@ -34,11 +34,19 @@ from binny.nz.registry import nz_model as _nz_model
34
34
  from binny.nz_tomo._tomography_bins import TomographyBins
35
35
  from binny.nz_tomo.bin_stats import population_stats as _population_stats
36
36
  from binny.nz_tomo.bin_stats import shape_stats as _shape_stats
37
+ from binny.surveys.survey_presets import (
38
+ list_survey_configs,
39
+ load_survey_config,
40
+ show_survey_config,
41
+ )
37
42
 
38
43
  __all__ = [
39
44
  "NZTomography",
40
45
  "available_metric_kernels",
41
46
  "register_metric_kernel",
47
+ "list_survey_configs",
48
+ "load_survey_config",
49
+ "show_survey_config",
42
50
  ]
43
51
 
44
52
 
@@ -93,23 +101,45 @@ class NZTomography:
93
101
  return _available_nz_models()
94
102
 
95
103
  @staticmethod
96
- def list_surveys() -> list[str]:
97
- """List available shipped survey preset names.
104
+ def load_survey_config(survey: str) -> dict[str, Any]:
105
+ """Load a built-in survey configuration by survey name.
106
+
107
+ Args:
108
+ survey: Survey preset name, such as ``"lsst"``, ``"desi"``, or
109
+ ``"roman"``.
110
+
111
+ Returns:
112
+ Parsed survey configuration dictionary.
113
+ """
114
+ return load_survey_config(survey)
115
+
116
+ @staticmethod
117
+ def show_survey_config(
118
+ survey: str,
119
+ *,
120
+ print_output: bool = True,
121
+ ) -> str:
122
+ """Show a built-in survey configuration as YAML text.
98
123
 
99
- Survey presets are YAML configuration files shipped with the package,
100
- following the naming convention ``<preset>_survey_specs.yaml``. This
101
- method returns the available preset base names (without the suffix),
102
- suitable for passing to :meth:`build_survey_bins`.
124
+ Args:
125
+ survey: Survey preset name, such as ``"lsst"``, ``"desi"``, or
126
+ ``"roman"``.
127
+ print_output: Whether to print the YAML text before returning it.
128
+
129
+ Returns:
130
+ The selected survey configuration formatted as YAML.
131
+ """
132
+ return show_survey_config(survey, print_output=print_output)
133
+
134
+ @staticmethod
135
+ def list_surveys() -> list[str]:
136
+ """List available built-in survey preset names.
103
137
 
104
138
  Returns:
105
- A sorted list of available survey preset base names.
139
+ A sorted list of available survey preset names, such as ``"lsst"``,
140
+ ``"desi"``, or ``"roman"``.
106
141
  """
107
- presets: list[str] = []
108
- for name in cu.list_configs():
109
- s = str(name)
110
- if s.endswith("_survey_specs.yaml"):
111
- presets.append(s.removesuffix("_survey_specs.yaml"))
112
- return sorted(set(presets))
142
+ return list_survey_configs()
113
143
 
114
144
  @property
115
145
  def z(self) -> np.ndarray:
@@ -83,6 +83,7 @@ from binny.nz.registry import nz_model
83
83
  __all__ = [
84
84
  "config_path",
85
85
  "list_configs",
86
+ "load_config",
86
87
  ]
87
88
 
88
89
  _CONFIG_PKG = "binny.surveys.configs"
@@ -668,3 +669,32 @@ def _builder_kwargs_from_spec(spec: Mapping[str, Any]) -> dict[str, Any]:
668
669
 
669
670
  kwargs.update(params)
670
671
  return kwargs
672
+
673
+
674
+ def load_config(path: str | Path) -> Mapping[Any, Any]:
675
+ """Load a survey configuration from a YAML file.
676
+
677
+ Args:
678
+ path: Path to a YAML file, or the filename of a shipped Binny survey
679
+ configuration.
680
+
681
+ Returns:
682
+ Parsed survey configuration mapping.
683
+
684
+ Raises:
685
+ ValueError: If the YAML root is not a mapping or the config has no
686
+ tomography list.
687
+ FileNotFoundError: If ``path`` is neither an existing file nor a
688
+ shipped config filename.
689
+ """
690
+ p = Path(path)
691
+
692
+ if not p.exists():
693
+ p = config_path(str(path))
694
+
695
+ cfg = _load_yaml_mapping(p)
696
+
697
+ if "tomography" not in cfg:
698
+ raise ValueError("Config must contain a 'tomography' list.")
699
+
700
+ return cfg
@@ -0,0 +1,67 @@
1
+ """Helpers for working with built-in Binny survey configurations.
2
+
3
+ This module provides a small public interface for discovering, loading, and
4
+ displaying the survey YAML files distributed with Binny. These helpers let users
5
+ access survey presets such as LSST, DESI, or Roman by name, without needing to
6
+ know where the YAML files live inside the installed package.
7
+
8
+ The returned configurations use the same dictionary structure as the YAML files
9
+ and can be passed directly to :class:`binny.NZTomography`.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ import yaml
17
+
18
+ from binny.surveys.config_utils import list_configs, load_config
19
+
20
+
21
+ def load_survey_config(survey: str) -> dict[str, Any]:
22
+ """Load a built-in survey configuration by survey name.
23
+
24
+ Args:
25
+ survey: Survey preset name, such as ``"lsst"``, ``"desi"``, or
26
+ ``"roman"``.
27
+
28
+ Returns:
29
+ Parsed survey configuration dictionary.
30
+ """
31
+ name = str(survey).strip().lower()
32
+ return dict(load_config(f"{name}_survey_specs.yaml"))
33
+
34
+
35
+ def list_survey_configs() -> list[str]:
36
+ """List built-in survey configuration names."""
37
+ surveys = []
38
+
39
+ for filename in list_configs():
40
+ if filename.endswith("_survey_specs.yaml"):
41
+ surveys.append(filename.removesuffix("_survey_specs.yaml"))
42
+
43
+ return sorted(set(surveys))
44
+
45
+
46
+ def show_survey_config(
47
+ survey: str,
48
+ *,
49
+ print_output: bool = True,
50
+ ) -> str:
51
+ """Show a built-in survey configuration as YAML text.
52
+
53
+ Args:
54
+ survey: Survey preset name, such as ``"lsst"``, ``"desi"``, or
55
+ ``"roman"``.
56
+ print_output: Whether to print the YAML text before returning it.
57
+
58
+ Returns:
59
+ The selected survey configuration formatted as YAML.
60
+ """
61
+ cfg = load_survey_config(survey)
62
+ text = yaml.safe_dump(cfg, sort_keys=False)
63
+
64
+ if print_output:
65
+ print(text)
66
+
67
+ return text
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybinny
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Flexible tomographic binning algorithms for cosmology.
5
5
  Author-email: Nikolina Sarcevic <nikolina.sarcevic@gmail.com>
6
6
  License: MIT
@@ -30,6 +30,7 @@ src/binny/nz_tomo/specz.py
30
30
  src/binny/surveys/__init__.py
31
31
  src/binny/surveys/config_utils.py
32
32
  src/binny/surveys/sky.py
33
+ src/binny/surveys/survey_presets.py
33
34
  src/binny/surveys/configs/__init__.py
34
35
  src/binny/surveys/configs/des_survey_specs.yaml
35
36
  src/binny/surveys/configs/desi_survey_specs.yaml
@@ -93,6 +94,7 @@ tests/test_nz_tomo_photoz_metadata.py
93
94
  tests/test_nz_tomo_specz.py
94
95
  tests/test_surveys_config_utils.py
95
96
  tests/test_surveys_sky.py
97
+ tests/test_surveys_survey_presets.py
96
98
  tests/test_utils_broadcasting.py
97
99
  tests/test_utils_io.py
98
100
  tests/test_utils_metadata.py
@@ -554,17 +554,30 @@ def test_list_nz_models_delegates(monkeypatch):
554
554
  assert NZTomography.list_nz_models() == ["a", "b"]
555
555
 
556
556
 
557
- def test_list_survey_presets_filters_suffix(monkeypatch):
558
- """Tests that list_survey_presets filters out non-survey configs."""
557
+ def test_list_survey_presets_delegates(monkeypatch):
558
+ """Tests that list_surveys delegates to the survey preset helper."""
559
559
  monkeypatch.setattr(
560
- cu,
561
- "list_configs",
562
- lambda: ["lsst_survey_specs.yaml", "README.txt", "hsc_survey_specs.yaml", "foo.yaml"],
560
+ "binny.api.nz_tomography.list_survey_configs",
561
+ lambda: ["hsc", "lsst"],
563
562
  raising=True,
564
563
  )
564
+
565
565
  assert NZTomography.list_surveys() == ["hsc", "lsst"]
566
566
 
567
567
 
568
+ def test_module_exports_survey_config_helpers():
569
+ """Tests that survey config helpers are importable from the public API module."""
570
+ from binny.api.nz_tomography import (
571
+ list_survey_configs,
572
+ load_survey_config,
573
+ show_survey_config,
574
+ )
575
+
576
+ assert callable(list_survey_configs)
577
+ assert callable(load_survey_config)
578
+ assert callable(show_survey_config)
579
+
580
+
568
581
  def test_registry_is_callable():
569
582
  """Tests that registry objects are callable."""
570
583
  from binny.nz.registry import nz_model
@@ -1034,3 +1047,75 @@ def test_between_sample_stats_delegates_to_between_sample_metrics(monkeypatch):
1034
1047
  assert calls["pearson"]["bins_a"] == t0.bins
1035
1048
  assert calls["pearson"]["bins_b"] == t1.bins
1036
1049
  assert calls["pearson"]["kw"] == {"clip": True}
1050
+
1051
+
1052
+ def test_load_survey_config_delegates(monkeypatch):
1053
+ """Tests that NZTomography.load_survey_config delegates to the survey preset helper."""
1054
+ called = {"survey": None}
1055
+
1056
+ def fake_load_survey_config(survey):
1057
+ called["survey"] = survey
1058
+ return {"name": survey, "tomography": []}
1059
+
1060
+ monkeypatch.setattr(
1061
+ "binny.api.nz_tomography.load_survey_config",
1062
+ fake_load_survey_config,
1063
+ raising=True,
1064
+ )
1065
+
1066
+ out = NZTomography.load_survey_config("lsst")
1067
+
1068
+ assert out == {"name": "lsst", "tomography": []}
1069
+ assert called["survey"] == "lsst"
1070
+
1071
+
1072
+ def test_show_survey_config_delegates(monkeypatch):
1073
+ """Tests that NZTomography.show_survey_config delegates to the survey preset helper."""
1074
+ called = {"survey": None, "print_output": None}
1075
+
1076
+ def fake_show_survey_config(survey, *, print_output=True):
1077
+ called["survey"] = survey
1078
+ called["print_output"] = print_output
1079
+ return "name: lsst\n"
1080
+
1081
+ monkeypatch.setattr(
1082
+ "binny.api.nz_tomography.show_survey_config",
1083
+ fake_show_survey_config,
1084
+ raising=True,
1085
+ )
1086
+
1087
+ out = NZTomography.show_survey_config("lsst", print_output=False)
1088
+
1089
+ assert out == "name: lsst\n"
1090
+ assert called == {"survey": "lsst", "print_output": False}
1091
+
1092
+
1093
+ def test_list_surveys_matches_list_survey_configs(monkeypatch):
1094
+ """Tests that list_surveys exposes the same values as list_survey_configs."""
1095
+ monkeypatch.setattr(
1096
+ "binny.api.nz_tomography.list_survey_configs",
1097
+ lambda: ["desi", "lsst", "roman"],
1098
+ raising=True,
1099
+ )
1100
+
1101
+ assert NZTomography.list_surveys() == ["desi", "lsst", "roman"]
1102
+
1103
+
1104
+ def test_module_all_exports_survey_config_helpers():
1105
+ """Tests that survey config helpers are included in __all__."""
1106
+ from binny.api import nz_tomography
1107
+
1108
+ assert "list_survey_configs" in nz_tomography.__all__
1109
+ assert "load_survey_config" in nz_tomography.__all__
1110
+ assert "show_survey_config" in nz_tomography.__all__
1111
+
1112
+
1113
+ @pytest.mark.parametrize("survey", ["lsst", "desi", "roman"])
1114
+ def test_builtin_survey_configs_can_be_loaded(survey):
1115
+ """Tests that representative built-in survey configs can be loaded."""
1116
+ cfg = NZTomography.load_survey_config(survey)
1117
+
1118
+ assert cfg["name"] == survey
1119
+ assert "tomography" in cfg
1120
+ assert isinstance(cfg["tomography"], list)
1121
+ assert len(cfg["tomography"]) > 0
@@ -12,6 +12,7 @@ import yaml
12
12
 
13
13
  from binny.surveys.config_utils import (
14
14
  _build_parent_nz,
15
+ _builder_kwargs_from_spec,
15
16
  _extract_survey_meta,
16
17
  _extract_z_grid,
17
18
  _iter_tomography_entries,
@@ -26,6 +27,7 @@ from binny.surveys.config_utils import (
26
27
  _tabulated_params_from_config,
27
28
  config_path,
28
29
  list_configs,
30
+ load_config,
29
31
  )
30
32
 
31
33
 
@@ -546,3 +548,179 @@ def test_parse_entry_accepts_tabulated_source_block() -> None:
546
548
  assert spec["nz"]["model"] == "tabulated"
547
549
  assert spec["nz"]["source"]["path"] == "desi_lrg_nz.txt"
548
550
  assert np.allclose(spec["bins"]["edges"], [0.4, 1.0])
551
+
552
+
553
+ def test_load_config_loads_yaml_file(tmp_path) -> None:
554
+ """Tests that load_config loads a YAML config from a file path."""
555
+
556
+ cfg = {
557
+ "name": "x",
558
+ "tomography": [
559
+ {
560
+ "role": "source",
561
+ "kind": "photoz",
562
+ "nz": {"model": "smail", "params": {}},
563
+ "bins": {"scheme": "equidistant", "n_bins": 2},
564
+ }
565
+ ],
566
+ }
567
+
568
+ p = tmp_path / "cfg.yaml"
569
+ p.write_text(yaml.safe_dump(cfg), encoding="utf-8")
570
+
571
+ out = load_config(p)
572
+
573
+ assert isinstance(out, Mapping)
574
+ assert out["name"] == "x"
575
+ assert "tomography" in out
576
+
577
+
578
+ def test_load_config_rejects_missing_tomography(tmp_path) -> None:
579
+ """Tests that load_config rejects configs without tomography."""
580
+
581
+ p = tmp_path / "cfg.yaml"
582
+ p.write_text(yaml.safe_dump({"name": "x"}), encoding="utf-8")
583
+
584
+ with pytest.raises(ValueError, match="must contain a 'tomography' list"):
585
+ load_config(p)
586
+
587
+
588
+ def test_select_entries_filters_by_scenario() -> None:
589
+ """Tests that _select_entries can distinguish scenarios."""
590
+ entries = [
591
+ {"role": "source", "scenario": "hls_optimistic"},
592
+ {"role": "source", "scenario": "hls_conservative"},
593
+ ]
594
+
595
+ sel = _select_entries(
596
+ entries,
597
+ role="source",
598
+ scenario="hls_optimistic",
599
+ )
600
+
601
+ assert len(sel) == 1
602
+ assert sel[0]["scenario"] == "hls_optimistic"
603
+
604
+
605
+ def test_select_entries_filters_by_year_scenario_and_sample() -> None:
606
+ """Tests that _select_entries matches all provided selectors."""
607
+ entries = [
608
+ {"role": "lens", "year": "1", "scenario": "hls", "sample": "wide"},
609
+ {"role": "lens", "year": "1", "scenario": "wide", "sample": "wide"},
610
+ {"role": "lens", "year": "10", "scenario": "hls", "sample": "wide"},
611
+ ]
612
+
613
+ sel = _select_entries(
614
+ entries,
615
+ role="lens",
616
+ year="1",
617
+ scenario="hls",
618
+ sample="wide",
619
+ )
620
+
621
+ assert len(sel) == 1
622
+ assert sel[0]["year"] == "1"
623
+ assert sel[0]["scenario"] == "hls"
624
+ assert sel[0]["sample"] == "wide"
625
+
626
+
627
+ def test_parse_bins_with_scheme_and_range() -> None:
628
+ """Tests that _parse_bins preserves scheme bin ranges."""
629
+ bins = _parse_bins(
630
+ {
631
+ "scheme": "equidistant",
632
+ "n_bins": 3,
633
+ "range": [0.2, 1.4],
634
+ }
635
+ )
636
+
637
+ assert bins["scheme"] == "equidistant"
638
+ assert bins["n_bins"] == 3
639
+ assert bins["range"] == (0.2, 1.4)
640
+
641
+
642
+ def test_parse_bins_rejects_missing_n_bins() -> None:
643
+ """Tests that _parse_bins requires n_bins for scheme-based bins."""
644
+ with pytest.raises(ValueError, match="bins.n_bins is required"):
645
+ _parse_bins({"scheme": "equidistant"})
646
+
647
+
648
+ def test_parse_bins_rejects_missing_edges_or_scheme() -> None:
649
+ """Tests that _parse_bins requires either edges or scheme."""
650
+ with pytest.raises(ValueError, match="must provide either 'edges' or 'scheme'"):
651
+ _parse_bins({})
652
+
653
+
654
+ def test_builder_kwargs_from_spec_uses_edges() -> None:
655
+ """Tests that _builder_kwargs_from_spec converts explicit edges."""
656
+ spec = {
657
+ "bins": {"edges": np.array([0.0, 0.5, 1.0])},
658
+ "uncertainties": {"sigma_z": 0.05},
659
+ }
660
+
661
+ kwargs = _builder_kwargs_from_spec(spec)
662
+
663
+ assert np.allclose(kwargs["bin_edges"], [0.0, 0.5, 1.0])
664
+ assert kwargs["binning_scheme"] is None
665
+ assert kwargs["n_bins"] is None
666
+ assert kwargs["sigma_z"] == pytest.approx(0.05)
667
+
668
+
669
+ def test_builder_kwargs_from_spec_uses_scheme_and_range() -> None:
670
+ """Tests that _builder_kwargs_from_spec converts scheme bins."""
671
+
672
+ spec = {
673
+ "bins": {
674
+ "scheme": "equidistant",
675
+ "n_bins": 4,
676
+ "range": (0.2, 1.2),
677
+ },
678
+ "uncertainties": {"scatter_scale": 0.03},
679
+ }
680
+
681
+ kwargs = _builder_kwargs_from_spec(spec)
682
+
683
+ assert kwargs["bin_edges"] is None
684
+ assert kwargs["binning_scheme"] == "equidistant"
685
+ assert kwargs["n_bins"] == 4
686
+ assert kwargs["bin_range"] == (0.2, 1.2)
687
+ assert kwargs["scatter_scale"] == pytest.approx(0.03)
688
+
689
+
690
+ def test_builder_kwargs_from_spec_preserves_explicit_bin_range_uncertainty() -> None:
691
+ """Tests that uncertainties can explicitly override bin_range."""
692
+
693
+ spec = {
694
+ "bins": {
695
+ "scheme": "equidistant",
696
+ "n_bins": 4,
697
+ "range": (0.2, 1.2),
698
+ },
699
+ "uncertainties": {"bin_range": (0.3, 1.1)},
700
+ }
701
+
702
+ kwargs = _builder_kwargs_from_spec(spec)
703
+
704
+ assert kwargs["bin_range"] == (0.3, 1.1)
705
+
706
+
707
+ def test_survey_meta_preserves_scenario_and_survey_meta(
708
+ minimal_cfg: Mapping[str, Any],
709
+ ) -> None:
710
+ """Tests that _survey_meta includes scenario and top-level metadata."""
711
+ meta = _survey_meta(
712
+ cfg=minimal_cfg,
713
+ resolved_key="k",
714
+ role="lens",
715
+ year="1",
716
+ scenario="test_scenario",
717
+ sample="samplename",
718
+ )
719
+
720
+ assert meta["survey"] == "test"
721
+ assert meta["key"] == "k"
722
+ assert meta["role"] == "lens"
723
+ assert meta["year"] == "1"
724
+ assert meta["scenario"] == "test_scenario"
725
+ assert meta["sample"] == "samplename"
726
+ assert meta["survey_meta"] == {"a": 1}
@@ -0,0 +1,211 @@
1
+ """Unit tests for ``binny.surveys.survey_presets``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import pytest
8
+ import yaml
9
+
10
+ from binny.surveys import survey_presets
11
+ from binny.surveys.survey_presets import (
12
+ list_survey_configs,
13
+ load_survey_config,
14
+ show_survey_config,
15
+ )
16
+
17
+
18
+ def test_load_survey_config_normalizes_name(monkeypatch) -> None:
19
+ """Tests that load_survey_config normalizes survey names."""
20
+ called = {"path": None}
21
+
22
+ def fake_load_config(path: str) -> dict[str, Any]:
23
+ called["path"] = path
24
+ return {"name": "lsst", "tomography": []}
25
+
26
+ monkeypatch.setattr(
27
+ survey_presets,
28
+ "load_config",
29
+ fake_load_config,
30
+ raising=True,
31
+ )
32
+
33
+ cfg = load_survey_config(" LSST ")
34
+
35
+ assert cfg == {"name": "lsst", "tomography": []}
36
+ assert called["path"] == "lsst_survey_specs.yaml"
37
+
38
+
39
+ def test_load_survey_config_returns_plain_dict(monkeypatch) -> None:
40
+ """Tests that load_survey_config returns a plain dictionary."""
41
+
42
+ class MappingLike(dict):
43
+ """Small dict subclass used to check dict conversion."""
44
+
45
+ monkeypatch.setattr(
46
+ survey_presets,
47
+ "load_config",
48
+ lambda path: MappingLike({"name": "desi", "tomography": []}),
49
+ raising=True,
50
+ )
51
+
52
+ cfg = load_survey_config("desi")
53
+
54
+ assert type(cfg) is dict
55
+ assert cfg["name"] == "desi"
56
+ assert cfg["tomography"] == []
57
+
58
+
59
+ def test_load_survey_config_propagates_missing_config(monkeypatch) -> None:
60
+ """Tests that load_survey_config propagates missing-config errors."""
61
+
62
+ def fake_load_config(path: str) -> dict[str, Any]:
63
+ raise FileNotFoundError("missing config")
64
+
65
+ monkeypatch.setattr(
66
+ survey_presets,
67
+ "load_config",
68
+ fake_load_config,
69
+ raising=True,
70
+ )
71
+
72
+ with pytest.raises(FileNotFoundError, match="missing config"):
73
+ load_survey_config("not-a-survey")
74
+
75
+
76
+ def test_list_survey_configs_filters_suffix(monkeypatch) -> None:
77
+ """Tests that list_survey_configs filters survey config filenames."""
78
+ monkeypatch.setattr(
79
+ survey_presets,
80
+ "list_configs",
81
+ lambda: [
82
+ "lsst_survey_specs.yaml",
83
+ "README.txt",
84
+ "hsc_survey_specs.yaml",
85
+ "foo.yaml",
86
+ "roman_survey_specs.yml",
87
+ "desi_survey_specs.yaml",
88
+ ],
89
+ raising=True,
90
+ )
91
+
92
+ assert list_survey_configs() == ["desi", "hsc", "lsst"]
93
+
94
+
95
+ def test_list_survey_configs_returns_unique_sorted_names(monkeypatch) -> None:
96
+ """Tests that list_survey_configs returns sorted unique survey names."""
97
+ monkeypatch.setattr(
98
+ survey_presets,
99
+ "list_configs",
100
+ lambda: [
101
+ "roman_survey_specs.yaml",
102
+ "lsst_survey_specs.yaml",
103
+ "roman_survey_specs.yaml",
104
+ "desi_survey_specs.yaml",
105
+ ],
106
+ raising=True,
107
+ )
108
+
109
+ assert list_survey_configs() == ["desi", "lsst", "roman"]
110
+
111
+
112
+ def test_list_survey_configs_returns_empty_list_when_no_presets(monkeypatch) -> None:
113
+ """Tests that list_survey_configs returns an empty list when no presets exist."""
114
+ monkeypatch.setattr(
115
+ survey_presets,
116
+ "list_configs",
117
+ lambda: [
118
+ "README.txt",
119
+ "example.yaml",
120
+ "example_survey_specs.yml",
121
+ ],
122
+ raising=True,
123
+ )
124
+
125
+ assert list_survey_configs() == []
126
+
127
+
128
+ def test_show_survey_config_returns_yaml_text(monkeypatch) -> None:
129
+ """Tests that show_survey_config returns YAML text."""
130
+ cfg = {
131
+ "name": "lsst",
132
+ "tomography": [
133
+ {
134
+ "role": "source",
135
+ "kind": "photoz",
136
+ }
137
+ ],
138
+ }
139
+
140
+ monkeypatch.setattr(
141
+ survey_presets,
142
+ "load_survey_config",
143
+ lambda survey: cfg,
144
+ raising=True,
145
+ )
146
+
147
+ text = show_survey_config("lsst", print_output=False)
148
+
149
+ assert isinstance(text, str)
150
+ assert yaml.safe_load(text) == cfg
151
+ assert "name: lsst" in text
152
+ assert "tomography:" in text
153
+
154
+
155
+ def test_show_survey_config_does_not_print_when_disabled(
156
+ monkeypatch,
157
+ capsys,
158
+ ) -> None:
159
+ """Tests that show_survey_config can suppress printing."""
160
+ monkeypatch.setattr(
161
+ survey_presets,
162
+ "load_survey_config",
163
+ lambda survey: {"name": "lsst", "tomography": []},
164
+ raising=True,
165
+ )
166
+
167
+ text = show_survey_config("lsst", print_output=False)
168
+ captured = capsys.readouterr()
169
+
170
+ assert text
171
+ assert captured.out == ""
172
+
173
+
174
+ def test_show_survey_config_prints_by_default(
175
+ monkeypatch,
176
+ capsys,
177
+ ) -> None:
178
+ """Tests that show_survey_config prints YAML by default."""
179
+ monkeypatch.setattr(
180
+ survey_presets,
181
+ "load_survey_config",
182
+ lambda survey: {"name": "roman", "tomography": []},
183
+ raising=True,
184
+ )
185
+
186
+ text = show_survey_config("roman")
187
+ captured = capsys.readouterr()
188
+
189
+ assert captured.out == text + "\n"
190
+ assert "name: roman" in captured.out
191
+ assert "tomography:" in captured.out
192
+
193
+
194
+ def test_show_survey_config_forwards_survey_name(monkeypatch) -> None:
195
+ """Tests that show_survey_config forwards the survey name to the loader."""
196
+ called = {"survey": None}
197
+
198
+ def fake_load_survey_config(survey: str) -> dict[str, Any]:
199
+ called["survey"] = survey
200
+ return {"name": "desi", "tomography": []}
201
+
202
+ monkeypatch.setattr(
203
+ survey_presets,
204
+ "load_survey_config",
205
+ fake_load_survey_config,
206
+ raising=True,
207
+ )
208
+
209
+ show_survey_config("DESI", print_output=False)
210
+
211
+ assert called["survey"] == "DESI"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes