essreduce 25.11.4__tar.gz → 25.11.6__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 (149) hide show
  1. {essreduce-25.11.4 → essreduce-25.11.6}/PKG-INFO +1 -1
  2. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/data/_registry.py +110 -44
  3. {essreduce-25.11.4 → essreduce-25.11.6}/src/essreduce.egg-info/PKG-INFO +1 -1
  4. {essreduce-25.11.4 → essreduce-25.11.6}/.copier-answers.ess.yml +0 -0
  5. {essreduce-25.11.4 → essreduce-25.11.6}/.copier-answers.yml +0 -0
  6. {essreduce-25.11.4 → essreduce-25.11.6}/.github/ISSUE_TEMPLATE/high-level-requirement.yml +0 -0
  7. {essreduce-25.11.4 → essreduce-25.11.6}/.github/dependabot.yml +0 -0
  8. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/ci.yml +0 -0
  9. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/docs.yml +0 -0
  10. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/nightly_at_main.yml +0 -0
  11. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/nightly_at_main_lower_bound.yml +0 -0
  12. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/nightly_at_release.yml +0 -0
  13. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/python-version-ci +0 -0
  14. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/release.yml +0 -0
  15. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/test.yml +0 -0
  16. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/unpinned.yml +0 -0
  17. {essreduce-25.11.4 → essreduce-25.11.6}/.github/workflows/weekly_windows_macos.yml +0 -0
  18. {essreduce-25.11.4 → essreduce-25.11.6}/.gitignore +0 -0
  19. {essreduce-25.11.4 → essreduce-25.11.6}/.pre-commit-config.yaml +0 -0
  20. {essreduce-25.11.4 → essreduce-25.11.6}/.python-version +0 -0
  21. {essreduce-25.11.4 → essreduce-25.11.6}/CODE_OF_CONDUCT.md +0 -0
  22. {essreduce-25.11.4 → essreduce-25.11.6}/CONTRIBUTING.md +0 -0
  23. {essreduce-25.11.4 → essreduce-25.11.6}/LICENSE +0 -0
  24. {essreduce-25.11.4 → essreduce-25.11.6}/MANIFEST.in +0 -0
  25. {essreduce-25.11.4 → essreduce-25.11.6}/README.md +0 -0
  26. {essreduce-25.11.4 → essreduce-25.11.6}/docs/_static/anaconda-icon.js +0 -0
  27. {essreduce-25.11.4 → essreduce-25.11.6}/docs/_static/favicon.svg +0 -0
  28. {essreduce-25.11.4 → essreduce-25.11.6}/docs/_static/logo-dark.svg +0 -0
  29. {essreduce-25.11.4 → essreduce-25.11.6}/docs/_static/logo.svg +0 -0
  30. {essreduce-25.11.4 → essreduce-25.11.6}/docs/_templates/class-template.rst +0 -0
  31. {essreduce-25.11.4 → essreduce-25.11.6}/docs/_templates/doc_version.html +0 -0
  32. {essreduce-25.11.4 → essreduce-25.11.6}/docs/_templates/module-template.rst +0 -0
  33. {essreduce-25.11.4 → essreduce-25.11.6}/docs/about/index.md +0 -0
  34. {essreduce-25.11.4 → essreduce-25.11.6}/docs/api-reference/index.md +0 -0
  35. {essreduce-25.11.4 → essreduce-25.11.6}/docs/conf.py +0 -0
  36. {essreduce-25.11.4 → essreduce-25.11.6}/docs/developer/coding-conventions.md +0 -0
  37. {essreduce-25.11.4 → essreduce-25.11.6}/docs/developer/dependency-management.md +0 -0
  38. {essreduce-25.11.4 → essreduce-25.11.6}/docs/developer/getting-started.md +0 -0
  39. {essreduce-25.11.4 → essreduce-25.11.6}/docs/developer/gui.ipynb +0 -0
  40. {essreduce-25.11.4 → essreduce-25.11.6}/docs/developer/index.md +0 -0
  41. {essreduce-25.11.4 → essreduce-25.11.6}/docs/index.md +0 -0
  42. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/index.md +0 -0
  43. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/installation.md +0 -0
  44. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/reduction-workflow-guidelines.md +0 -0
  45. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/tof/dream.ipynb +0 -0
  46. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/tof/frame-unwrapping.ipynb +0 -0
  47. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/tof/index.md +0 -0
  48. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/tof/wfm.ipynb +0 -0
  49. {essreduce-25.11.4 → essreduce-25.11.6}/docs/user-guide/widget.md +0 -0
  50. {essreduce-25.11.4 → essreduce-25.11.6}/pyproject.toml +0 -0
  51. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/base.in +0 -0
  52. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/base.txt +0 -0
  53. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/basetest.in +0 -0
  54. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/basetest.txt +0 -0
  55. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/ci.in +0 -0
  56. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/ci.txt +0 -0
  57. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/dev.in +0 -0
  58. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/dev.txt +0 -0
  59. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/docs.in +0 -0
  60. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/docs.txt +0 -0
  61. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/make_base.py +0 -0
  62. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/mypy.in +0 -0
  63. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/mypy.txt +0 -0
  64. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/nightly.in +0 -0
  65. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/nightly.txt +0 -0
  66. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/static.in +0 -0
  67. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/static.txt +0 -0
  68. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/test.in +0 -0
  69. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/test.txt +0 -0
  70. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/wheels.in +0 -0
  71. {essreduce-25.11.4 → essreduce-25.11.6}/requirements/wheels.txt +0 -0
  72. {essreduce-25.11.4 → essreduce-25.11.6}/resources/logo.svg +0 -0
  73. {essreduce-25.11.4 → essreduce-25.11.6}/setup.cfg +0 -0
  74. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/__init__.py +0 -0
  75. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/data/__init__.py +0 -0
  76. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/live/__init__.py +0 -0
  77. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/live/raw.py +0 -0
  78. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/live/roi.py +0 -0
  79. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/live/workflow.py +0 -0
  80. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/logging.py +0 -0
  81. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/nexus/__init__.py +0 -0
  82. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/nexus/_nexus_loader.py +0 -0
  83. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/nexus/json_generator.py +0 -0
  84. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/nexus/json_nexus.py +0 -0
  85. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/nexus/types.py +0 -0
  86. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/nexus/workflow.py +0 -0
  87. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/normalization.py +0 -0
  88. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/parameter.py +0 -0
  89. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/py.typed +0 -0
  90. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/scripts/grow_nexus.py +0 -0
  91. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/streaming.py +0 -0
  92. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/__init__.py +0 -0
  93. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/eto_to_tof.py +0 -0
  94. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/fakes.py +0 -0
  95. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/interpolator_numba.py +0 -0
  96. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/interpolator_scipy.py +0 -0
  97. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/lut.py +0 -0
  98. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/resample.py +0 -0
  99. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/types.py +0 -0
  100. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/time_of_flight/workflow.py +0 -0
  101. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/ui.py +0 -0
  102. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/uncertainty.py +0 -0
  103. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/__init__.py +0 -0
  104. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_base.py +0 -0
  105. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_binedges_widget.py +0 -0
  106. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_bounds_widget.py +0 -0
  107. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_config.py +0 -0
  108. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_filename_widget.py +0 -0
  109. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_linspace_widget.py +0 -0
  110. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_optional_widget.py +0 -0
  111. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_spinner.py +0 -0
  112. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_string_widget.py +0 -0
  113. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_switchable_widget.py +0 -0
  114. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/widgets/_vector_widget.py +0 -0
  115. {essreduce-25.11.4 → essreduce-25.11.6}/src/ess/reduce/workflow.py +0 -0
  116. {essreduce-25.11.4 → essreduce-25.11.6}/src/essreduce.egg-info/SOURCES.txt +0 -0
  117. {essreduce-25.11.4 → essreduce-25.11.6}/src/essreduce.egg-info/dependency_links.txt +0 -0
  118. {essreduce-25.11.4 → essreduce-25.11.6}/src/essreduce.egg-info/entry_points.txt +0 -0
  119. {essreduce-25.11.4 → essreduce-25.11.6}/src/essreduce.egg-info/requires.txt +0 -0
  120. {essreduce-25.11.4 → essreduce-25.11.6}/src/essreduce.egg-info/top_level.txt +0 -0
  121. {essreduce-25.11.4 → essreduce-25.11.6}/tests/accumulators_test.py +0 -0
  122. {essreduce-25.11.4 → essreduce-25.11.6}/tests/conftest.py +0 -0
  123. {essreduce-25.11.4 → essreduce-25.11.6}/tests/live/raw_test.py +0 -0
  124. {essreduce-25.11.4 → essreduce-25.11.6}/tests/live/roi_test.py +0 -0
  125. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_generator_test.py +0 -0
  126. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_examples/array_dataset.json +0 -0
  127. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_examples/dataset.json +0 -0
  128. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_examples/detector.json +0 -0
  129. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_examples/entry.json +0 -0
  130. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_examples/event_data.json +0 -0
  131. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_examples/instrument.json +0 -0
  132. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_examples/log.json +0 -0
  133. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/json_nexus_test.py +0 -0
  134. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/nexus_loader_test.py +0 -0
  135. {essreduce-25.11.4 → essreduce-25.11.6}/tests/nexus/workflow_test.py +0 -0
  136. {essreduce-25.11.4 → essreduce-25.11.6}/tests/normalization_test.py +0 -0
  137. {essreduce-25.11.4 → essreduce-25.11.6}/tests/package_test.py +0 -0
  138. {essreduce-25.11.4 → essreduce-25.11.6}/tests/scripts/test_grow_nexus.py +0 -0
  139. {essreduce-25.11.4 → essreduce-25.11.6}/tests/streaming_test.py +0 -0
  140. {essreduce-25.11.4 → essreduce-25.11.6}/tests/time_of_flight/interpolator_test.py +0 -0
  141. {essreduce-25.11.4 → essreduce-25.11.6}/tests/time_of_flight/lut_test.py +0 -0
  142. {essreduce-25.11.4 → essreduce-25.11.6}/tests/time_of_flight/resample_tests.py +0 -0
  143. {essreduce-25.11.4 → essreduce-25.11.6}/tests/time_of_flight/unwrap_test.py +0 -0
  144. {essreduce-25.11.4 → essreduce-25.11.6}/tests/time_of_flight/wfm_test.py +0 -0
  145. {essreduce-25.11.4 → essreduce-25.11.6}/tests/time_of_flight/workflow_test.py +0 -0
  146. {essreduce-25.11.4 → essreduce-25.11.6}/tests/uncertainty_test.py +0 -0
  147. {essreduce-25.11.4 → essreduce-25.11.6}/tests/widget_test.py +0 -0
  148. {essreduce-25.11.4 → essreduce-25.11.6}/tools/shrink_nexus.py +0 -0
  149. {essreduce-25.11.4 → essreduce-25.11.6}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: essreduce
3
- Version: 25.11.4
3
+ Version: 25.11.6
4
4
  Summary: Common data reduction tools for the ESS facility
5
5
  Author: Scipp contributors
6
6
  License-Expression: BSD-3-Clause
@@ -3,14 +3,14 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import dataclasses
6
7
  import hashlib
7
8
  import os
8
9
  from abc import ABC, abstractmethod
9
10
  from collections.abc import Mapping
10
- from dataclasses import dataclass
11
11
  from functools import cache
12
12
  from pathlib import Path
13
- from typing import Any
13
+ from typing import Any, Literal
14
14
 
15
15
  _LOCAL_CACHE_ENV_VAR = "SCIPP_DATA_DIR"
16
16
  _LOCAL_REGISTRY_ENV_VAR = "SCIPP_OVERRIDE_DATA_DIR"
@@ -28,8 +28,8 @@ def make_registry(
28
28
 
29
29
  By default, this function creates a :class:`PoochRegistry` to download files
30
30
  via HTTP from an online file store.
31
- This can be overridden by setting the environment variable `SCIPP_DATA_DIR` to a
32
- path on the local file system.
31
+ This can be overridden by setting the environment variable
32
+ ``SCIPP_OVERRIDE_DATA_DIR`` to a path on the local file system.
33
33
  In this case, a :class:`LocalRegistry` is returned.
34
34
 
35
35
  Files are specified as a dict using either the Pooch string format explicitly
@@ -40,7 +40,11 @@ def make_registry(
40
40
  ... "file1.dat": "md5:1234567890abcdef",
41
41
  ... "file2.csv": Entry(alg="md5", chk="abcdef123456789"),
42
42
  ... "folder/nested.dat": "blake2b:1234567890abcdef",
43
- ... "zipped.zip": Entry(alg="blake2b", chk="abcdef123456789", unzip=True),
43
+ ... "zipped.zip": Entry(
44
+ ... alg="blake2b",
45
+ ... chk="abcdef123456789",
46
+ ... extractor="unzip"
47
+ ... ),
44
48
  ... }
45
49
 
46
50
  In the example above, the specifications for ``file1.dat`` and ``file2.csv`` are
@@ -49,10 +53,11 @@ def make_registry(
49
53
  Paths like this must always use forward slashes (/) even on Windows.
50
54
 
51
55
  As shown above, it is possible to automatically unzip
52
- files by specifying ``unzip=True``.
56
+ files by specifying ``extractor="unzip"``.
53
57
  When calling ``registry.get_path("zipped.zip")`` the file will be unzipped and
54
58
  a path to the content is returned.
55
- This expects that there is only a single file in the zip archive.
59
+ Similarly, ``extractor="untar"`` specifies that a file needs to be untarred
60
+ (and possibly un-gzipped).
56
61
 
57
62
  The complete path to the source file is constructed as follows:
58
63
 
@@ -111,7 +116,7 @@ def _check_local_override_path(override: str) -> Path:
111
116
  return path
112
117
 
113
118
 
114
- @dataclass(frozen=True, slots=True)
119
+ @dataclasses.dataclass(frozen=True, slots=True)
115
120
  class Entry:
116
121
  """An entry in a registry."""
117
122
 
@@ -119,9 +124,18 @@ class Entry:
119
124
  """Checksum."""
120
125
  alg: str
121
126
  """Checksum algorithm."""
122
- unzip: bool = False
127
+ extractor: Literal["unzip", "untar"] | None = None
128
+ """Processor to extract file contents."""
129
+
130
+ unzip: dataclasses.InitVar[bool] = False
123
131
  """Whether to unzip the file."""
124
132
 
133
+ def __post_init__(self, unzip: bool) -> None:
134
+ if self.extractor is not None and unzip:
135
+ raise TypeError("Set either the 'unzip' argument or 'extractor', not both.")
136
+ if self.extractor is None and unzip:
137
+ object.__setattr__(self, "extractor", "unzip")
138
+
125
139
  @classmethod
126
140
  def from_pooch_string(cls, pooch_string: str) -> Entry:
127
141
  alg, chk = pooch_string.split(":")
@@ -132,7 +146,7 @@ class Registry(ABC):
132
146
  def __init__(self, files: Mapping[str, str | Entry]) -> None:
133
147
  self._files = _to_file_entries(files)
134
148
 
135
- @abstractmethod
149
+ @cache # noqa: B019
136
150
  def get_path(self, name: str) -> Path:
137
151
  """Get the path to a file in the registry.
138
152
 
@@ -154,9 +168,60 @@ class Registry(ABC):
154
168
  :
155
169
  The Path to the file.
156
170
  """
171
+ return Path(
172
+ _expect_single(
173
+ self._fetch(name, extractor=self._extractor_processor(name)),
174
+ name,
175
+ )
176
+ )
157
177
 
158
- def _needs_unzip(self, name: str) -> bool:
159
- return self._files[name].unzip
178
+ @cache # noqa: B019
179
+ def get_paths(self, name: str) -> list[Path]:
180
+ """Get the paths to unpacked files from the registry.
181
+
182
+ This method downloads the given file, extracts its contents, and returns
183
+ the paths to all extracted contents.
184
+ Unlike :meth:`get_path`, this method requires an extractor processor
185
+ (unzip or untar).
186
+
187
+ Depending on the implementation, the file is downloaded if necessary.
188
+
189
+ Note that implementations are allowed to cache return values of this method
190
+ to avoid recomputing potentially expensive checksums.
191
+ This usually means that the ``Registry`` object itself gets stored until the
192
+ Python interpreter shuts down.
193
+ However, registries are small and do not own resources.
194
+
195
+ Parameters
196
+ ----------
197
+ name:
198
+ Name of the zipped or tarred file to get the path for.
199
+
200
+ Returns
201
+ -------
202
+ :
203
+ The Paths to the files.
204
+ """
205
+ if (extractor := self._extractor_processor(name)) is None:
206
+ raise ValueError(f"File '{name}' is not zipped or tarred.")
207
+ return [Path(path) for path in self._fetch(name, extractor=extractor)]
208
+
209
+ def _extractor_processor_type(self, name: str) -> Any:
210
+ match self._files[name].extractor:
211
+ case "unzip":
212
+ return _pooch_unzip_processor_class()
213
+ case "untar":
214
+ return _pooch_untar_processor_class()
215
+ case None:
216
+ return None
217
+
218
+ @abstractmethod
219
+ def _extractor_processor(self, name: str) -> Any:
220
+ """Return an instance of a processor for the given file."""
221
+
222
+ @abstractmethod
223
+ def _fetch(self, name: str, extractor: Any) -> list[str] | str:
224
+ """Fetch the given file from the registry."""
160
225
 
161
226
 
162
227
  class PoochRegistry(Registry):
@@ -178,24 +243,15 @@ class PoochRegistry(Registry):
178
243
  )
179
244
  super().__init__(files)
180
245
 
181
- @cache # noqa: B019
182
- def get_path(self, name: str) -> Path:
183
- """Get the path to a file in the registry.
184
-
185
- Downloads the file if necessary.
186
- """
187
- if self._needs_unzip(name):
188
- paths: list[str] = self._registry.fetch( # type: ignore[assignment]
189
- name, processor=self._unzip_processor
190
- )
191
- return Path(_expect_single_unzipped(paths, name))
192
- return Path(self._registry.fetch(name))
246
+ def _fetch(self, name: str, extractor: Any) -> list[str] | str:
247
+ return self._registry.fetch(name, processor=extractor)
193
248
 
194
- @property
195
- def _unzip_processor(self) -> Any:
249
+ def _extractor_processor(self, name: str) -> Any:
196
250
  # Create a new processor on demand because reusing the same processor would
197
251
  # reuse the same output path for every file.
198
- return _import_pooch().Unzip()
252
+ if (cls := self._extractor_processor_type(name=name)) is not None:
253
+ return cls()
254
+ return None
199
255
 
200
256
 
201
257
  class LocalRegistry(Registry):
@@ -217,12 +273,11 @@ class LocalRegistry(Registry):
217
273
  base_url=base_url,
218
274
  retry_if_failed=retry_if_failed,
219
275
  )
220
- self._extract_dir = pooch_registry.path
276
+ self._extract_base_dir = pooch_registry.path
221
277
  self._source_path = source_path.resolve().joinpath(*prefix.split("/"), version)
222
278
  super().__init__(files)
223
279
 
224
- @cache # noqa: B019
225
- def get_path(self, name: str) -> Path:
280
+ def _fetch(self, name: str, extractor: Any) -> list[str] | str:
226
281
  """Get the path to a file in the registry."""
227
282
  try:
228
283
  entry = self._files[name]
@@ -238,24 +293,24 @@ class LocalRegistry(Registry):
238
293
 
239
294
  _check_hash(name, path, entry)
240
295
 
241
- if self._needs_unzip(name):
242
- return Path(
243
- _expect_single_unzipped(
244
- self._unzip_processor(os.fspath(path), "download", None), path
245
- )
246
- )
247
- return path
296
+ if extractor is not None:
297
+ return extractor(os.fspath(path), "download", None)
298
+ return os.fspath(path)
248
299
 
249
300
  def _local_path(self, name: str) -> Path:
250
301
  # Split on "/" because `name` is always a POSIX-style path, but the return
251
302
  # value is a system path, i.e., it can be a Windows-style path.
252
303
  return self._source_path.joinpath(*name.split("/"))
253
304
 
254
- @property
255
- def _unzip_processor(self) -> Any:
305
+ def _extract_dir(self, name: str) -> Path:
306
+ return self._extract_base_dir / name
307
+
308
+ def _extractor_processor(self, name: str) -> Any:
256
309
  # Create a new processor on demand because reusing the same processor would
257
310
  # reuse the same output path for every file.
258
- return _import_pooch().Unzip(self._extract_dir)
311
+ if (cls := self._extractor_processor_type(name=name)) is not None:
312
+ return cls(extract_dir=self._extract_dir(name))
313
+ return None
259
314
 
260
315
 
261
316
  def _import_pooch() -> Any:
@@ -288,19 +343,30 @@ def _create_pooch(
288
343
  )
289
344
 
290
345
 
291
- def _pooch_unzip_processor(extract_dir: Path) -> Any:
346
+ def _pooch_unzip_processor_class() -> Any:
292
347
  try:
293
348
  import pooch
294
349
  except ImportError:
295
350
  raise ImportError("You need to install Pooch to unzip files.") from None
296
351
 
297
- return pooch.processors.Unzip(extract_dir=os.fspath(extract_dir))
352
+ return pooch.processors.Unzip
353
+
354
+
355
+ def _pooch_untar_processor_class() -> Any:
356
+ try:
357
+ import pooch
358
+ except ImportError:
359
+ raise ImportError("You need to install Pooch to untar files.") from None
360
+
361
+ return pooch.processors.Untar
298
362
 
299
363
 
300
- def _expect_single_unzipped(paths: list[str], archive: str | os.PathLike) -> str:
364
+ def _expect_single(paths: list[str] | str, archive: str | os.PathLike) -> str:
365
+ if isinstance(paths, str):
366
+ return paths
301
367
  if len(paths) != 1:
302
368
  raise ValueError(
303
- f"Expected exactly one file to unzip, got {len(paths)} in "
369
+ f"Expected exactly one extracted file, got {len(paths)} in "
304
370
  f"'{os.fspath(archive)}'."
305
371
  )
306
372
  return paths[0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: essreduce
3
- Version: 25.11.4
3
+ Version: 25.11.6
4
4
  Summary: Common data reduction tools for the ESS facility
5
5
  Author: Scipp contributors
6
6
  License-Expression: BSD-3-Clause
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
File without changes
File without changes