anemoi-utils 0.4.17__py3-none-any.whl → 0.4.18__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.

Potentially problematic release.


This version of anemoi-utils might be problematic. Click here for more details.

anemoi/utils/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.4.17'
21
- __version_tuple__ = version_tuple = (0, 4, 17)
20
+ __version__ = version = '0.4.18'
21
+ __version_tuple__ = version_tuple = (0, 4, 18)
anemoi/utils/rules.py ADDED
@@ -0,0 +1,218 @@
1
+ # (C) Copyright 2025 Anemoi contributors.
2
+ #
3
+ # This software is licensed under the terms of the Apache Licence Version 2.0
4
+ # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
6
+ # In applying this licence, ECMWF does not waive the privileges and immunities
7
+ # granted to it by virtue of its status as an intergovernmental organisation
8
+ # nor does it submit to any jurisdiction.
9
+
10
+ from typing import Any
11
+ from typing import Dict
12
+ from typing import List
13
+ from typing import Mapping
14
+ from typing import Optional
15
+ from typing import Union
16
+
17
+
18
+ class Rule:
19
+
20
+ def __init__(self, match: Dict[str, Any], result: Any):
21
+ """Initialize a Rule object.
22
+
23
+ Parameters
24
+ ----------
25
+ match : Dict[str, Any]
26
+ A dictionary defining the conditions for the rule to match.
27
+ result : Any
28
+ The result to return if the rule matches.
29
+ """
30
+ self._match = match
31
+ self._result = result
32
+
33
+ def match(self, obj: Mapping[str, Any]) -> bool:
34
+ """Check if the rule matches the given object.
35
+
36
+ Parameters
37
+ ----------
38
+ obj : Mapping[str, Any]
39
+ The object to check against the rule's conditions.
40
+
41
+ Returns
42
+ -------
43
+ bool
44
+ True if the rule matches, False otherwise.
45
+ """
46
+ for key, value in self._match.items():
47
+ if key not in obj or obj[key] != value:
48
+ return False
49
+
50
+ return True
51
+
52
+ @property
53
+ def result(self) -> Any:
54
+ return self._result
55
+
56
+ @property
57
+ def condition(self) -> Dict[str, Any]:
58
+ return self._match
59
+
60
+
61
+ class RuleSet:
62
+
63
+ def __init__(self, rules: List[Union[Rule, Dict[str, Any], List[Any]]]):
64
+ """Initialize a RuleSet object.
65
+
66
+ Parameters
67
+ ----------
68
+ rules : List[Union[Rule, Dict[str, Any], List[Any]]]
69
+ A list of rules, where each rule can be a Rule object, a dictionary with
70
+ 'match' and 'result' keys, or a list with two elements (match and result).
71
+ """
72
+ assert isinstance(rules, list), "rules must be a list"
73
+
74
+ self.rules: List[Rule] = []
75
+
76
+ for rule in rules:
77
+ if isinstance(rule, Rule):
78
+ self.rules.append(rule)
79
+ continue
80
+
81
+ if isinstance(rule, dict):
82
+
83
+ assert len(rule) == 2, "Rule dictionary must contain exactly two key-value pair."
84
+
85
+ match = rule.get("match")
86
+ if match is None:
87
+ raise ValueError("Rule dictionary must contain a 'match' key.")
88
+
89
+ result = rule.get("result")
90
+ if result is None:
91
+ raise ValueError("Rule dictionary must contain a 'result' key.")
92
+
93
+ self.rules.append(Rule(match, result))
94
+ continue
95
+
96
+ if isinstance(rule, list):
97
+ assert len(rule) == 2, "Rule list must contain exactly two elements."
98
+ match = rule[0]
99
+ result = rule[1]
100
+ self.rules.append(Rule(match, result))
101
+ continue
102
+
103
+ raise ValueError(
104
+ "Rule must be either a Rule object, a dictionary with 'match' and 'result' keys, or a list with two elements."
105
+ )
106
+
107
+ @classmethod
108
+ def from_list(cls, rules: List[Any]) -> "RuleSet":
109
+ """Create a RuleSet from a list of rules.
110
+
111
+ Parameters
112
+ ----------
113
+ rules : List[Any]
114
+ A list of rules to initialize the RuleSet.
115
+
116
+ Returns
117
+ -------
118
+ RuleSet
119
+ A new RuleSet object.
120
+ """
121
+ return cls(rules)
122
+
123
+ @classmethod
124
+ def from_files(cls, path: str) -> "RuleSet":
125
+ """Create a RuleSet from a file.
126
+
127
+ Parameters
128
+ ----------
129
+ path : str
130
+ The path to the file containing the rules. Supported formats are .json and .yaml/.yml.
131
+
132
+ Returns
133
+ -------
134
+ RuleSet
135
+ A new RuleSet object.
136
+
137
+ Raises
138
+ ------
139
+ ValueError
140
+ If the file format is unsupported.
141
+ """
142
+ if path.endswith(".json"):
143
+ import json
144
+
145
+ with open(path, "r") as f:
146
+ return cls.from_list(json.load(f))
147
+
148
+ if path.endswith(".yaml") or path.endswith(".yml"):
149
+ import yaml
150
+
151
+ with open(path, "r") as f:
152
+ return cls.from_list(yaml.safe_load(f))
153
+
154
+ raise ValueError("Unsupported file format. Supported formats are .json and .yaml/.yml.")
155
+
156
+ @classmethod
157
+ def from_any(cls, rules: Union[str, List[Any]]) -> "RuleSet":
158
+ """Create a RuleSet from a list or a file path.
159
+
160
+ Parameters
161
+ ----------
162
+ rules : Union[str, List[Any]]
163
+ The rules to initialize the RuleSet, either as a list or a file path.
164
+
165
+ Returns
166
+ -------
167
+ RuleSet
168
+ A new RuleSet object.
169
+
170
+ Raises
171
+ ------
172
+ ValueError
173
+ If the rules format is unsupported.
174
+ """
175
+ if isinstance(rules, str):
176
+ return cls.from_files(rules)
177
+
178
+ if isinstance(rules, list):
179
+ return cls.from_list(rules)
180
+
181
+ raise ValueError("Unsupported rules format. Must be a list or a file path.")
182
+
183
+ def match(self, obj: Mapping[str, Any], strategy: str = "first-match") -> Optional[Rule]:
184
+ """Match an object against the rules in the RuleSet.
185
+
186
+ Parameters
187
+ ----------
188
+ obj : Mapping[str, Any]
189
+ The object to match against the rules.
190
+ strategy : str, optional
191
+ The matching strategy to use. Currently, only 'first-match' is supported.
192
+
193
+ Returns
194
+ -------
195
+ Optional[Rule]
196
+ The first matching rule, or None if no match is found.
197
+
198
+ Raises
199
+ ------
200
+ AssertionError
201
+ If an unsupported strategy is provided.
202
+ """
203
+ assert strategy == "first-match", "Only 'first-match' strategy is supported for now."
204
+ for rule in self.rules:
205
+ if rule.match(obj):
206
+ return rule
207
+
208
+ return None
209
+
210
+ def __iter__(self) -> iter:
211
+ """Return an iterator over the rules in the RuleSet.
212
+
213
+ Returns
214
+ -------
215
+ iter
216
+ An iterator over the Rule objects in the RuleSet.
217
+ """
218
+ return iter(self.rules)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anemoi-utils
3
- Version: 0.4.17
3
+ Version: 0.4.18
4
4
  Summary: A package to hold various functions to support training of ML models on ECMWF data.
5
5
  Author-email: "European Centre for Medium-Range Weather Forecasts (ECMWF)" <software.support@ecmwf.int>
6
6
  License: Apache License
@@ -1,6 +1,6 @@
1
1
  anemoi/utils/__init__.py,sha256=uVhpF-VjIl_4mMywOVtgTutgsdIsqz-xdkwxeMhzuag,730
2
2
  anemoi/utils/__main__.py,sha256=6LlE4MYrPvqqrykxXh7XMi50UZteUY59NeM8P9Zs2dU,910
3
- anemoi/utils/_version.py,sha256=UwtfX--7I9I_2cUoyH0-NALXkddhkqIyGBlxSkUZCdU,513
3
+ anemoi/utils/_version.py,sha256=k9MQ8BJ_knOnasrd-KVQ3E0l9YzHo7DymdQPhaKLLNU,513
4
4
  anemoi/utils/caching.py,sha256=rXbeAmpBcMbbfN4EVblaHWKicsrtx1otER84FEBtz98,6183
5
5
  anemoi/utils/checkpoints.py,sha256=N4WpAZXa4etrpSEKhHqUUtG2-x9w3FJMHcLO-dDAXPY,9600
6
6
  anemoi/utils/cli.py,sha256=IyZfnSw0u0yYnrjOrzvm2RuuKvDk4cVb8pf8BkaChgA,6209
@@ -15,6 +15,7 @@ anemoi/utils/humanize.py,sha256=pjnFJAKHbEAOfcvn8c48kt-8eFy6FGW_U2ruJvfamrA,2518
15
15
  anemoi/utils/logs.py,sha256=naTgrmPwWHD4eekFttXftS4gtcAGYHpCqG4iwYprNDA,1804
16
16
  anemoi/utils/provenance.py,sha256=xC6mTstF7f_asqtPSrulC7c34xjOSuAxWhkwc3yKhHg,14629
17
17
  anemoi/utils/registry.py,sha256=vIaHMT66m0_w8lrY3u5GmT70tIRNyA_Z_p_T7pTFx_k,9637
18
+ anemoi/utils/rules.py,sha256=xYCiUV_HXTGFe93diqMLQsMJCWGi5umd_bWEeYP8XFY,6318
18
19
  anemoi/utils/s3.py,sha256=xMT48kbcelcjjqsaU567WI3oZ5eqo88Rlgyx5ECszAU,4074
19
20
  anemoi/utils/sanitise.py,sha256=ZYGdSX6qihQANr3pHZjbKnoapnzP1KcrWdW1Ul1mOGk,3668
20
21
  anemoi/utils/sanitize.py,sha256=43ZKDcfVpeXSsJ9TFEc9aZnD6oe2cUh151XnDspM98M,462
@@ -30,9 +31,9 @@ anemoi/utils/mars/requests.py,sha256=VFMHBVAAl0_2lOcMBa1lvaKHctN0lDJsI6_U4BucGew
30
31
  anemoi/utils/remote/__init__.py,sha256=swPWHQoh-B6Xq9R489tPw0FykMue7f-bJ8enneFYSYE,20776
31
32
  anemoi/utils/remote/s3.py,sha256=spQ8l0rwQjLZh9dZu5cOsYIvNwKihQfCJ6YsFYegeqI,17339
32
33
  anemoi/utils/remote/ssh.py,sha256=xNtsawh8okytCKRehkRCVExbHZj-CRUQNormEHglfuw,8088
33
- anemoi_utils-0.4.17.dist-info/licenses/LICENSE,sha256=8HznKF1Vi2IvfLsKNE5A2iVyiri3pRjRPvPC9kxs6qk,11354
34
- anemoi_utils-0.4.17.dist-info/METADATA,sha256=XkTQFxHQ9BRkUl77BycvPb3F6yPGxqS9x5w-aRQlLYc,15360
35
- anemoi_utils-0.4.17.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
36
- anemoi_utils-0.4.17.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
37
- anemoi_utils-0.4.17.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
38
- anemoi_utils-0.4.17.dist-info/RECORD,,
34
+ anemoi_utils-0.4.18.dist-info/licenses/LICENSE,sha256=8HznKF1Vi2IvfLsKNE5A2iVyiri3pRjRPvPC9kxs6qk,11354
35
+ anemoi_utils-0.4.18.dist-info/METADATA,sha256=JfNRSTBhVM7aDRWBmhpDWtrkCoTVT1fUwGYyWcR45BU,15360
36
+ anemoi_utils-0.4.18.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
37
+ anemoi_utils-0.4.18.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
38
+ anemoi_utils-0.4.18.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
39
+ anemoi_utils-0.4.18.dist-info/RECORD,,