anemoi-datasets 0.5.16__py3-none-any.whl → 0.5.17__py3-none-any.whl

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 (155) hide show
  1. anemoi/datasets/__init__.py +4 -1
  2. anemoi/datasets/__main__.py +12 -2
  3. anemoi/datasets/_version.py +9 -4
  4. anemoi/datasets/commands/cleanup.py +17 -2
  5. anemoi/datasets/commands/compare.py +18 -2
  6. anemoi/datasets/commands/copy.py +196 -14
  7. anemoi/datasets/commands/create.py +50 -7
  8. anemoi/datasets/commands/finalise-additions.py +17 -2
  9. anemoi/datasets/commands/finalise.py +17 -2
  10. anemoi/datasets/commands/init-additions.py +17 -2
  11. anemoi/datasets/commands/init.py +16 -2
  12. anemoi/datasets/commands/inspect.py +283 -62
  13. anemoi/datasets/commands/load-additions.py +16 -2
  14. anemoi/datasets/commands/load.py +16 -2
  15. anemoi/datasets/commands/patch.py +17 -2
  16. anemoi/datasets/commands/publish.py +17 -2
  17. anemoi/datasets/commands/scan.py +31 -3
  18. anemoi/datasets/compute/recentre.py +47 -11
  19. anemoi/datasets/create/__init__.py +612 -85
  20. anemoi/datasets/create/check.py +142 -20
  21. anemoi/datasets/create/chunks.py +64 -4
  22. anemoi/datasets/create/config.py +185 -21
  23. anemoi/datasets/create/filter.py +50 -0
  24. anemoi/datasets/create/filters/__init__.py +33 -0
  25. anemoi/datasets/create/filters/empty.py +37 -0
  26. anemoi/datasets/create/filters/legacy.py +93 -0
  27. anemoi/datasets/create/filters/noop.py +37 -0
  28. anemoi/datasets/create/filters/orog_to_z.py +58 -0
  29. anemoi/datasets/create/{functions/filters → filters}/pressure_level_relative_humidity_to_specific_humidity.py +33 -10
  30. anemoi/datasets/create/{functions/filters → filters}/pressure_level_specific_humidity_to_relative_humidity.py +32 -8
  31. anemoi/datasets/create/filters/rename.py +205 -0
  32. anemoi/datasets/create/{functions/filters → filters}/rotate_winds.py +43 -28
  33. anemoi/datasets/create/{functions/filters → filters}/single_level_dewpoint_to_relative_humidity.py +32 -9
  34. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_dewpoint.py +33 -9
  35. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_specific_humidity.py +55 -7
  36. anemoi/datasets/create/{functions/filters → filters}/single_level_specific_humidity_to_relative_humidity.py +98 -37
  37. anemoi/datasets/create/filters/speeddir_to_uv.py +95 -0
  38. anemoi/datasets/create/{functions/filters → filters}/sum.py +24 -27
  39. anemoi/datasets/create/filters/transform.py +53 -0
  40. anemoi/datasets/create/{functions/filters → filters}/unrotate_winds.py +27 -18
  41. anemoi/datasets/create/filters/uv_to_speeddir.py +94 -0
  42. anemoi/datasets/create/{functions/filters → filters}/wz_to_w.py +51 -33
  43. anemoi/datasets/create/input/__init__.py +76 -5
  44. anemoi/datasets/create/input/action.py +149 -13
  45. anemoi/datasets/create/input/concat.py +81 -10
  46. anemoi/datasets/create/input/context.py +39 -4
  47. anemoi/datasets/create/input/data_sources.py +72 -6
  48. anemoi/datasets/create/input/empty.py +21 -3
  49. anemoi/datasets/create/input/filter.py +60 -12
  50. anemoi/datasets/create/input/function.py +154 -37
  51. anemoi/datasets/create/input/join.py +86 -14
  52. anemoi/datasets/create/input/misc.py +67 -17
  53. anemoi/datasets/create/input/pipe.py +33 -6
  54. anemoi/datasets/create/input/repeated_dates.py +189 -41
  55. anemoi/datasets/create/input/result.py +202 -87
  56. anemoi/datasets/create/input/step.py +119 -22
  57. anemoi/datasets/create/input/template.py +100 -13
  58. anemoi/datasets/create/input/trace.py +62 -7
  59. anemoi/datasets/create/patch.py +52 -4
  60. anemoi/datasets/create/persistent.py +134 -17
  61. anemoi/datasets/create/size.py +15 -1
  62. anemoi/datasets/create/source.py +51 -0
  63. anemoi/datasets/create/sources/__init__.py +36 -0
  64. anemoi/datasets/create/{functions/sources → sources}/accumulations.py +296 -30
  65. anemoi/datasets/create/{functions/sources → sources}/constants.py +27 -2
  66. anemoi/datasets/create/{functions/sources → sources}/eccc_fstd.py +7 -3
  67. anemoi/datasets/create/sources/empty.py +37 -0
  68. anemoi/datasets/create/{functions/sources → sources}/forcings.py +25 -1
  69. anemoi/datasets/create/sources/grib.py +297 -0
  70. anemoi/datasets/create/{functions/sources → sources}/hindcasts.py +38 -4
  71. anemoi/datasets/create/sources/legacy.py +93 -0
  72. anemoi/datasets/create/{functions/sources → sources}/mars.py +168 -20
  73. anemoi/datasets/create/sources/netcdf.py +42 -0
  74. anemoi/datasets/create/sources/opendap.py +43 -0
  75. anemoi/datasets/create/{functions/sources/__init__.py → sources/patterns.py} +35 -4
  76. anemoi/datasets/create/sources/recentre.py +150 -0
  77. anemoi/datasets/create/{functions/sources → sources}/source.py +27 -5
  78. anemoi/datasets/create/{functions/sources → sources}/tendencies.py +64 -7
  79. anemoi/datasets/create/sources/xarray.py +92 -0
  80. anemoi/datasets/create/sources/xarray_kerchunk.py +36 -0
  81. anemoi/datasets/create/sources/xarray_support/README.md +1 -0
  82. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/__init__.py +109 -8
  83. anemoi/datasets/create/sources/xarray_support/coordinates.py +442 -0
  84. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/field.py +94 -16
  85. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/fieldlist.py +90 -25
  86. anemoi/datasets/create/sources/xarray_support/flavour.py +1036 -0
  87. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/grid.py +92 -31
  88. anemoi/datasets/create/sources/xarray_support/metadata.py +395 -0
  89. anemoi/datasets/create/sources/xarray_support/patch.py +91 -0
  90. anemoi/datasets/create/sources/xarray_support/time.py +391 -0
  91. anemoi/datasets/create/sources/xarray_support/variable.py +331 -0
  92. anemoi/datasets/create/sources/xarray_zarr.py +41 -0
  93. anemoi/datasets/create/{functions/sources → sources}/zenodo.py +34 -5
  94. anemoi/datasets/create/statistics/__init__.py +233 -44
  95. anemoi/datasets/create/statistics/summary.py +52 -6
  96. anemoi/datasets/create/testing.py +76 -0
  97. anemoi/datasets/create/{functions/filters/noop.py → typing.py} +6 -3
  98. anemoi/datasets/create/utils.py +97 -6
  99. anemoi/datasets/create/writer.py +26 -4
  100. anemoi/datasets/create/zarr.py +170 -23
  101. anemoi/datasets/data/__init__.py +51 -4
  102. anemoi/datasets/data/complement.py +191 -40
  103. anemoi/datasets/data/concat.py +141 -16
  104. anemoi/datasets/data/dataset.py +552 -61
  105. anemoi/datasets/data/debug.py +197 -26
  106. anemoi/datasets/data/ensemble.py +93 -8
  107. anemoi/datasets/data/fill_missing.py +165 -18
  108. anemoi/datasets/data/forwards.py +428 -56
  109. anemoi/datasets/data/grids.py +323 -97
  110. anemoi/datasets/data/indexing.py +112 -19
  111. anemoi/datasets/data/interpolate.py +92 -12
  112. anemoi/datasets/data/join.py +158 -19
  113. anemoi/datasets/data/masked.py +129 -15
  114. anemoi/datasets/data/merge.py +137 -23
  115. anemoi/datasets/data/misc.py +172 -16
  116. anemoi/datasets/data/missing.py +233 -29
  117. anemoi/datasets/data/rescale.py +111 -10
  118. anemoi/datasets/data/select.py +168 -26
  119. anemoi/datasets/data/statistics.py +67 -6
  120. anemoi/datasets/data/stores.py +149 -64
  121. anemoi/datasets/data/subset.py +159 -25
  122. anemoi/datasets/data/unchecked.py +168 -57
  123. anemoi/datasets/data/xy.py +168 -25
  124. anemoi/datasets/dates/__init__.py +191 -16
  125. anemoi/datasets/dates/groups.py +189 -47
  126. anemoi/datasets/grids.py +270 -31
  127. anemoi/datasets/testing.py +28 -1
  128. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.17.dist-info}/METADATA +9 -6
  129. anemoi_datasets-0.5.17.dist-info/RECORD +137 -0
  130. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.17.dist-info}/WHEEL +1 -1
  131. anemoi/datasets/create/functions/__init__.py +0 -66
  132. anemoi/datasets/create/functions/filters/__init__.py +0 -9
  133. anemoi/datasets/create/functions/filters/empty.py +0 -17
  134. anemoi/datasets/create/functions/filters/orog_to_z.py +0 -58
  135. anemoi/datasets/create/functions/filters/rename.py +0 -79
  136. anemoi/datasets/create/functions/filters/speeddir_to_uv.py +0 -78
  137. anemoi/datasets/create/functions/filters/uv_to_speeddir.py +0 -56
  138. anemoi/datasets/create/functions/sources/empty.py +0 -15
  139. anemoi/datasets/create/functions/sources/grib.py +0 -150
  140. anemoi/datasets/create/functions/sources/netcdf.py +0 -15
  141. anemoi/datasets/create/functions/sources/opendap.py +0 -15
  142. anemoi/datasets/create/functions/sources/recentre.py +0 -60
  143. anemoi/datasets/create/functions/sources/xarray/coordinates.py +0 -255
  144. anemoi/datasets/create/functions/sources/xarray/flavour.py +0 -472
  145. anemoi/datasets/create/functions/sources/xarray/metadata.py +0 -148
  146. anemoi/datasets/create/functions/sources/xarray/patch.py +0 -44
  147. anemoi/datasets/create/functions/sources/xarray/time.py +0 -177
  148. anemoi/datasets/create/functions/sources/xarray/variable.py +0 -188
  149. anemoi/datasets/create/functions/sources/xarray_kerchunk.py +0 -42
  150. anemoi/datasets/create/functions/sources/xarray_zarr.py +0 -15
  151. anemoi/datasets/utils/fields.py +0 -47
  152. anemoi_datasets-0.5.16.dist-info/RECORD +0 -129
  153. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.17.dist-info}/entry_points.txt +0 -0
  154. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.17.dist-info/licenses}/LICENSE +0 -0
  155. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.17.dist-info}/top_level.txt +0 -0
@@ -8,11 +8,17 @@
8
8
  # nor does it submit to any jurisdiction.
9
9
 
10
10
  import logging
11
+ import warnings
11
12
  from copy import deepcopy
13
+ from typing import Any
14
+ from typing import Dict
15
+ from typing import List
16
+ from typing import Optional
17
+ from typing import Type
12
18
 
13
19
  from .action import Action
20
+ from .action import ActionContext
14
21
  from .context import Context
15
- from .misc import is_function
16
22
  from .result import Result
17
23
  from .template import notify_result
18
24
  from .trace import trace_datasource
@@ -22,28 +28,75 @@ LOG = logging.getLogger(__name__)
22
28
 
23
29
 
24
30
  class StepResult(Result):
25
- def __init__(self, context, action_path, group_of_dates, action, upstream_result):
31
+ """Represents the result of a step in the data processing pipeline."""
32
+
33
+ def __init__(
34
+ self, context: Context, action_path: List[str], group_of_dates: Any, action: Action, upstream_result: Result
35
+ ) -> None:
36
+ """Initialize a StepResult instance.
37
+
38
+ Parameters
39
+ ----------
40
+ context
41
+ The context in which the step is executed.
42
+ action_path
43
+ The path of actions leading to this step.
44
+ group_of_dates
45
+ The group of dates associated with this step.
46
+ action
47
+ The action associated with this step.
48
+ upstream_result
49
+ The result of the upstream step.
50
+ """
26
51
  super().__init__(context, action_path, group_of_dates)
27
52
  assert isinstance(upstream_result, Result), type(upstream_result)
28
- self.upstream_result = upstream_result
29
- self.action = action
53
+ self.upstream_result: Result = upstream_result
54
+ self.action: Action = action
30
55
 
31
56
  @property
32
57
  @notify_result
33
58
  @trace_datasource
34
- def datasource(self):
59
+ def datasource(self) -> Any:
60
+ """Retrieve the datasource associated with this step result."""
35
61
  raise NotImplementedError(f"Not implemented in {self.__class__.__name__}")
36
62
 
37
63
 
38
64
  class StepAction(Action):
39
- result_class = None
40
-
41
- def __init__(self, context, action_path, previous_step, *args, **kwargs):
65
+ """Represents an action that is part of a step in the data processing pipeline."""
66
+
67
+ result_class: Optional[Type[StepResult]] = None
68
+
69
+ def __init__(
70
+ self, context: ActionContext, action_path: List[str], previous_step: Any, *args: Any, **kwargs: Any
71
+ ) -> None:
72
+ """Initialize a StepAction instance.
73
+
74
+ Parameters
75
+ ----------
76
+ context
77
+ The context in which the action is executed.
78
+ action_path
79
+ The path of actions leading to this step.
80
+ previous_step
81
+ The previous step in the pipeline.
82
+ """
42
83
  super().__init__(context, action_path, *args, **kwargs)
43
- self.previous_step = previous_step
84
+ self.previous_step: Any = previous_step
44
85
 
45
86
  @trace_select
46
- def select(self, group_of_dates):
87
+ def select(self, group_of_dates: Any) -> StepResult:
88
+ """Select the result for a given group of dates.
89
+
90
+ Parameters
91
+ ----------
92
+ group_of_dates
93
+ The group of dates to select the result for.
94
+
95
+ Returns
96
+ -------
97
+ unknown
98
+ The result of the step.
99
+ """
47
100
  return self.result_class(
48
101
  self.context,
49
102
  self.action_path,
@@ -52,12 +105,36 @@ class StepAction(Action):
52
105
  self.previous_step.select(group_of_dates),
53
106
  )
54
107
 
55
- def __repr__(self):
56
- # raise NotImplementedError(f"Not implemented in {self.__class__.__name__}")
57
- return super().__repr__(self.previous_step, _inline_=str(self.kwargs))
58
-
59
-
60
- def step_factory(config, context, action_path, previous_step):
108
+ def __repr__(self) -> str:
109
+ """Return a string representation of the StepAction instance.
110
+
111
+ Returns
112
+ -------
113
+ unknown
114
+ String representation of the instance.
115
+ """
116
+ return self._repr(self.previous_step, _inline_=str(self.kwargs))
117
+
118
+
119
+ def step_factory(config: Dict[str, Any], context: ActionContext, action_path: List[str], previous_step: Any) -> Any:
120
+ """Factory function to create a step action based on the given configuration.
121
+
122
+ Parameters
123
+ ----------
124
+ config
125
+ The configuration dictionary for the step.
126
+ context
127
+ The context in which the step is executed.
128
+ action_path
129
+ The path of actions leading to this step.
130
+ previous_step
131
+ The previous step in the pipeline.
132
+
133
+ Returns
134
+ -------
135
+ unknown
136
+ An instance of a step action.
137
+ """
61
138
 
62
139
  from .filter import FilterStepAction
63
140
  from .filter import FunctionStepAction
@@ -85,10 +162,30 @@ def step_factory(config, context, action_path, previous_step):
85
162
  if isinstance(config[key], str):
86
163
  args, kwargs = [config[key]], {}
87
164
 
88
- if cls is None:
89
- if not is_function(key, "filters"):
90
- raise ValueError(f"Unknown step {key}")
91
- cls = FunctionStepAction
92
- args = [key] + args
165
+ if cls is not None:
166
+ return cls(context, action_path, previous_step, *args, **kwargs)
167
+
168
+ # Try filters from datasets filter registry
169
+ from anemoi.transform.filters import filter_registry as transform_filter_registry
170
+
171
+ from ..filters import create_filter as create_datasets_filter
172
+ from ..filters import filter_registry as datasets_filter_registry
173
+
174
+ if datasets_filter_registry.is_registered(key):
175
+
176
+ if transform_filter_registry.is_registered(key):
177
+ warnings.warn(f"Filter `{key}` is registered in both datasets and transform filter registries")
178
+
179
+ filter = create_datasets_filter(None, config)
180
+ return FunctionStepAction(context, action_path + [key], previous_step, key, filter)
181
+
182
+ # Use filters from transform registry
183
+
184
+ if transform_filter_registry.is_registered(key):
185
+ from ..filters.transform import TransformFilter
186
+
187
+ return FunctionStepAction(
188
+ context, action_path + [key], previous_step, key, TransformFilter(context, key, config)
189
+ )
93
190
 
94
- return cls(context, action_path, previous_step, *args, **kwargs)
191
+ raise ValueError(f"Unknown step action `{key}`")
@@ -10,35 +10,108 @@
10
10
 
11
11
  import logging
12
12
  import re
13
+ from abc import ABC
14
+ from abc import abstractmethod
13
15
  from functools import wraps
16
+ from typing import Any
17
+ from typing import Callable
18
+ from typing import List
19
+
20
+ from .context import Context
14
21
 
15
22
  LOG = logging.getLogger(__name__)
16
23
 
17
24
 
18
- def notify_result(method):
25
+ def notify_result(method: Callable[..., Any]) -> Callable[..., Any]:
26
+ """Decorator to notify the context of the result of the method call.
27
+
28
+ Parameters
29
+ ----------
30
+ method : Callable[..., Any]
31
+ The method to wrap.
32
+
33
+ Returns
34
+ -------
35
+ Callable[..., Any]
36
+ The wrapped method.
37
+ """
38
+
19
39
  @wraps(method)
20
- def wrapper(self, *args, **kwargs):
21
- result = method(self, *args, **kwargs)
40
+ def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
41
+ result: Any = method(self, *args, **kwargs)
22
42
  self.context.notify_result(self.action_path, result)
23
43
  return result
24
44
 
25
45
  return wrapper
26
46
 
27
47
 
28
- class Substitution:
29
- pass
48
+ class Substitution(ABC):
49
+ """Abstract base class for substitutions in templates."""
30
50
 
51
+ @abstractmethod
52
+ def resolve(self, context: Context) -> Any:
53
+ """Resolve the substitution using the given context.
31
54
 
32
- class Reference(Substitution):
33
- def __init__(self, context, action_path):
34
- self.context = context
35
- self.action_path = action_path
55
+ Parameters
56
+ ----------
57
+ context : Context
58
+ The context to use for resolution.
59
+
60
+ Returns
61
+ -------
62
+ Any
63
+ The resolved value.
64
+ """
65
+ pass
36
66
 
37
- def resolve(self, context):
67
+
68
+ class Reference(Substitution):
69
+ """A class to represent a reference to another value in the context."""
70
+
71
+ def __init__(self, context: Any, action_path: List[str]) -> None:
72
+ """Initialize a Reference instance.
73
+
74
+ Parameters
75
+ ----------
76
+ context : Any
77
+ The context in which the reference exists.
78
+ action_path : list of str
79
+ The action path to resolve.
80
+ """
81
+ self.context: Any = context
82
+ self.action_path: List[str] = action_path
83
+
84
+ def resolve(self, context: Context) -> Any:
85
+ """Resolve the reference using the given context.
86
+
87
+ Parameters
88
+ ----------
89
+ context : Context
90
+ The context to use for resolution.
91
+
92
+ Returns
93
+ -------
94
+ Any
95
+ The resolved value.
96
+ """
38
97
  return context.get_result(self.action_path)
39
98
 
40
99
 
41
- def resolve(context, x):
100
+ def resolve(context: Context, x: Any) -> Any:
101
+ """Recursively resolve substitutions in the given structure using the context.
102
+
103
+ Parameters
104
+ ----------
105
+ context : Context
106
+ The context to use for resolution.
107
+ x : Union[tuple, list, dict, Substitution, Any]
108
+ The structure to resolve.
109
+
110
+ Returns
111
+ -------
112
+ Any
113
+ The resolved structure.
114
+ """
42
115
  if isinstance(x, tuple):
43
116
  return tuple([resolve(context, y) for y in x])
44
117
 
@@ -54,7 +127,21 @@ def resolve(context, x):
54
127
  return x
55
128
 
56
129
 
57
- def substitute(context, x):
130
+ def substitute(context: Context, x: Any) -> Any:
131
+ """Recursively substitute references in the given structure using the context.
132
+
133
+ Parameters
134
+ ----------
135
+ context : Context
136
+ The context to use for substitution.
137
+ x : Union[tuple, list, dict, str, Any]
138
+ The structure to substitute.
139
+
140
+ Returns
141
+ -------
142
+ Any
143
+ The substituted structure.
144
+ """
58
145
  if isinstance(x, tuple):
59
146
  return tuple([substitute(context, y) for y in x])
60
147
 
@@ -67,7 +154,7 @@ def substitute(context, x):
67
154
  if not isinstance(x, str):
68
155
  return x
69
156
 
70
- if re.match(r"^\${[\.\w]+}$", x):
157
+ if re.match(r"^\${[\.\w\-]+}$", x):
71
158
  path = x[2:-1].split(".")
72
159
  context.will_need_reference(path)
73
160
  return Reference(context, path)
@@ -12,6 +12,8 @@ import logging
12
12
  import textwrap
13
13
  import threading
14
14
  from functools import wraps
15
+ from typing import Any
16
+ from typing import Callable
15
17
 
16
18
  LOG = logging.getLogger(__name__)
17
19
 
@@ -20,17 +22,44 @@ thread_local = threading.local()
20
22
  TRACE = 0
21
23
 
22
24
 
23
- def enable_trace(on_off):
25
+ def enable_trace(on_off: int) -> None:
26
+ """Enables or disables tracing.
27
+
28
+ Parameters
29
+ ----------
30
+ on_off : int
31
+ 1 to enable tracing, 0 to disable.
32
+ """
24
33
  global TRACE
25
34
  TRACE = on_off
26
35
 
27
36
 
28
- def step(action_path):
37
+ def step(action_path: list[str]) -> str:
38
+ """Returns a formatted string representing the action path.
39
+
40
+ Parameters
41
+ ----------
42
+ action_path : list of str
43
+ The action path.
44
+
45
+ Returns
46
+ -------
47
+ str
48
+ The formatted action path.
49
+ """
29
50
  return f"[{'.'.join(action_path)}]"
30
51
 
31
52
 
32
- def trace(emoji, *args):
53
+ def trace(emoji: str, *args: Any) -> None:
54
+ """Logs a trace message with an emoji.
33
55
 
56
+ Parameters
57
+ ----------
58
+ emoji : str
59
+ The emoji to use.
60
+ *args : Any
61
+ The arguments to log.
62
+ """
34
63
  if not TRACE:
35
64
  return
36
65
 
@@ -40,9 +69,22 @@ def trace(emoji, *args):
40
69
  print(emoji, " " * thread_local.TRACE_INDENT, *args)
41
70
 
42
71
 
43
- def trace_datasource(method):
72
+ def trace_datasource(method: Callable) -> Callable:
73
+ """Decorator to trace the datasource method.
74
+
75
+ Parameters
76
+ ----------
77
+ method : Callable
78
+ The method to decorate.
79
+
80
+ Returns
81
+ -------
82
+ Callable
83
+ The wrapped method.
84
+ """
85
+
44
86
  @wraps(method)
45
- def wrapper(self, *args, **kwargs):
87
+ def wrapper(self, *args: Any, **kwargs: Any) -> Any:
46
88
 
47
89
  if not hasattr(thread_local, "TRACE_INDENT"):
48
90
  thread_local.TRACE_INDENT = 0
@@ -67,9 +109,22 @@ def trace_datasource(method):
67
109
  return wrapper
68
110
 
69
111
 
70
- def trace_select(method):
112
+ def trace_select(method: Callable) -> Callable:
113
+ """Decorator to trace the select method.
114
+
115
+ Parameters
116
+ ----------
117
+ method : Callable
118
+ The method to decorate.
119
+
120
+ Returns
121
+ -------
122
+ Callable
123
+ The wrapped method.
124
+ """
125
+
71
126
  @wraps(method)
72
- def wrapper(self, *args, **kwargs):
127
+ def wrapper(self, *args: Any, **kwargs: Any) -> Any:
73
128
  if not hasattr(thread_local, "TRACE_INDENT"):
74
129
  thread_local.TRACE_INDENT = 0
75
130
  trace(
@@ -10,13 +10,26 @@
10
10
  import json
11
11
  import logging
12
12
  import os
13
+ from typing import Union
13
14
 
14
15
  import zarr
15
16
 
16
17
  LOG = logging.getLogger(__name__)
17
18
 
18
19
 
19
- def fix_order_by(order_by):
20
+ def fix_order_by(order_by: Union[dict, list]) -> list[dict]:
21
+ """Fix the order_by attribute to ensure it is a list of dictionaries.
22
+
23
+ Parameters
24
+ ----------
25
+ order_by : dict or list
26
+ The order_by attribute to fix.
27
+
28
+ Returns
29
+ -------
30
+ list[dict]
31
+ The fixed order_by attribute.
32
+ """
20
33
  if isinstance(order_by, list):
21
34
  return order_by
22
35
 
@@ -29,14 +42,38 @@ def fix_order_by(order_by):
29
42
  return lst
30
43
 
31
44
 
32
- def fix_history(history):
45
+ def fix_history(history: list[dict]) -> list[dict]:
46
+ """Fix the history attribute by removing specific actions.
47
+
48
+ Parameters
49
+ ----------
50
+ history : list[dict]
51
+ The history attribute to fix.
52
+
53
+ Returns
54
+ -------
55
+ list[dict]
56
+ The fixed history attribute.
57
+ """
33
58
  new = history
34
59
  new = [d for d in new if d.get("action") != "loading_data_start"]
35
60
  new = [d for d in new if d.get("action") != "loading_data_end"]
36
61
  return new
37
62
 
38
63
 
39
- def fix_provenance(provenance):
64
+ def fix_provenance(provenance: dict) -> dict:
65
+ """Fix the provenance attribute by adding missing fields and removing unnecessary ones.
66
+
67
+ Parameters
68
+ ----------
69
+ provenance : dict
70
+ The provenance attribute to fix.
71
+
72
+ Returns
73
+ -------
74
+ dict
75
+ The fixed provenance attribute.
76
+ """
40
77
  if "python" not in provenance:
41
78
  provenance["python"] = provenance["platform"]["python_version"]
42
79
 
@@ -79,7 +116,18 @@ def fix_provenance(provenance):
79
116
  return provenance
80
117
 
81
118
 
82
- def apply_patch(path, verbose=True, dry_run=False):
119
+ def apply_patch(path: str, verbose: bool = True, dry_run: bool = False) -> None:
120
+ """Apply a patch to the dataset at the given path.
121
+
122
+ Parameters
123
+ ----------
124
+ path : str
125
+ The path to the dataset.
126
+ verbose : bool, optional
127
+ Whether to log detailed information. Defaults to True.
128
+ dry_run : bool, optional
129
+ If True, do not actually apply the patch. Defaults to False.
130
+ """
83
131
  LOG.debug("====================")
84
132
  LOG.debug(f"Patching {path}")
85
133
  LOG.debug("====================")