1616See https://fmf.readthedocs.io/en/latest/modules.html#fmf.Tree.adjust
1717"""
1818
19+ from __future__ import annotations
20+
1921import re
22+ from collections .abc import Callable
2023
2124
2225class CannotDecide (Exception ):
@@ -34,7 +37,7 @@ class InvalidContext(Exception):
3437class ContextValue :
3538 """ Value for dimension """
3639
37- def __init__ (self , origin ):
40+ def __init__ (self , origin : str | tuple [ str , ...] ):
3841 """
3942 ContextValue("foo-1.2.3")
4043 ContextValue(["foo", "1", "2", "3"])
@@ -44,13 +47,13 @@ def __init__(self, origin):
4447 else :
4548 self ._to_compare = self ._split_to_version (origin )
4649
47- def __eq__ (self , other ):
50+ def __eq__ (self , other : ContextValue ):
4851 if isinstance (other , self .__class__ ):
4952 return self ._to_compare == other ._to_compare
5053 else :
5154 return False
5255
53- def __ne__ (self , other ):
56+ def __ne__ (self , other : ContextValue ):
5457 return not self .__eq__ (other )
5558
5659 def __str__ (self ):
@@ -59,7 +62,11 @@ def __str__(self):
5962 def __repr__ (self ):
6063 return "{}({})" .format (self .__class__ .__name__ , repr (self ._to_compare ))
6164
62- def version_cmp (self , other , minor_mode = False , ordered = True ):
65+ def version_cmp (
66+ self ,
67+ other : ContextValue ,
68+ minor_mode : bool = False ,
69+ ordered : bool = True ) -> int :
6370 """
6471 Comparing two ContextValue objects
6572
@@ -138,7 +145,7 @@ def version_cmp(self, other, minor_mode=False, ordered=True):
138145 return - 1 # other is larger (more pars)
139146
140147 @staticmethod
141- def compare (first , second ):
148+ def compare (first : str , second : str ):
142149 """ compare two version parts """
143150 # Ideally use `from packaging import version` but we need older
144151 # python support too so very rough
@@ -155,7 +162,7 @@ def compare(first, second):
155162 (first_version < second_version ))
156163
157164 @staticmethod
158- def _split_to_version (text ) :
165+ def _split_to_version (text : str ) -> tuple [ str , ...] :
159166 """
160167 Try to split text into name + version parts
161168
@@ -183,119 +190,121 @@ def __hash__(self):
183190
184191class Context :
185192 """ Represents https://fmf.readthedocs.io/en/latest/context.html """
193+
186194 # Operators' definitions
187195
188- def _op_defined (self , dimension_name , values ):
196+ def _op_defined (self , dimension_name : str , values : list [ ContextValue ] ):
189197 """ 'is defined' operator """
190198 return dimension_name in self ._dimensions
191199
192- def _op_not_defined (self , dimension_name , values ):
200+ def _op_not_defined (self , dimension_name : str , values : list [ ContextValue ] ):
193201 """ 'is not defined' operator """
194202 return dimension_name not in self ._dimensions
195203
196- def _op_eq (self , dimension_name , values ):
204+ def _op_eq (self , dimension_name : str , values : list [ ContextValue ] ):
197205 """ '=' operator """
198206
199- def comparator (dimension_value , it_val ) :
207+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
200208 return dimension_value .version_cmp (it_val , ordered = False ) == 0
201209
202210 return self ._op_core (dimension_name , values , comparator )
203211
204- def _op_not_eq (self , dimension_name , values ):
212+ def _op_not_eq (self , dimension_name : str , values : list [ ContextValue ] ):
205213 """ '!=' operator """
206214
207- def comparator (dimension_value , it_val ) :
215+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
208216 return dimension_value .version_cmp (it_val , ordered = False ) != 0
209217
210218 return self ._op_core (dimension_name , values , comparator )
211219
212- def _op_minor_eq (self , dimension_name , values ):
220+ def _op_minor_eq (self , dimension_name : str , values : list [ ContextValue ] ):
213221 """ '~=' operator """
214222
215- def comparator (dimension_value , it_val ) :
223+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
216224 return dimension_value .version_cmp (
217225 it_val , minor_mode = True , ordered = False ) == 0
218226
219227 return self ._op_core (dimension_name , values , comparator )
220228
221- def _op_minor_not_eq (self , dimension_name , values ):
229+ def _op_minor_not_eq (self , dimension_name : str , values : list [ ContextValue ] ):
222230 """ '~!=' operator """
223231
224- def comparator (dimension_value , it_val ) :
232+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
225233 return dimension_value .version_cmp (
226234 it_val , minor_mode = True , ordered = False ) != 0
227235
228236 return self ._op_core (dimension_name , values , comparator )
229237
230- def _op_minor_less_or_eq (self , dimension_name , values ):
238+ def _op_minor_less_or_eq (self , dimension_name : str , values : list [ ContextValue ] ):
231239 """ '~<=' operator """
232240
233- def comparator (dimension_value , it_val ) :
241+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
234242 return dimension_value .version_cmp (
235243 it_val , minor_mode = True , ordered = True ) <= 0
236244
237245 return self ._op_core (dimension_name , values , comparator )
238246
239- def _op_minor_less (self , dimension_name , values ):
247+ def _op_minor_less (self , dimension_name : str , values : list [ ContextValue ] ):
240248 """ '~<' operator """
241249
242- def comparator (dimension_value , it_val ) :
250+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
243251 return dimension_value .version_cmp (
244252 it_val , minor_mode = True , ordered = True ) < 0
245253
246254 return self ._op_core (dimension_name , values , comparator )
247255
248- def _op_less (self , dimension_name , values ):
256+ def _op_less (self , dimension_name : str , values : list [ ContextValue ] ):
249257 """ '<' operator """
250258
251- def comparator (dimension_value , it_val ) :
259+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
252260 return dimension_value .version_cmp (it_val , ordered = True ) < 0
253261
254262 return self ._op_core (dimension_name , values , comparator )
255263
256- def _op_less_or_equal (self , dimension_name , values ):
264+ def _op_less_or_equal (self , dimension_name : str , values : list [ ContextValue ] ):
257265 """ '<=' operator """
258266
259- def comparator (dimension_value , it_val ) :
267+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
260268 return dimension_value .version_cmp (it_val , ordered = True ) <= 0
261269
262270 return self ._op_core (dimension_name , values , comparator )
263271
264- def _op_greater_or_equal (self , dimension_name , values ):
272+ def _op_greater_or_equal (self , dimension_name : str , values : list [ ContextValue ] ):
265273 """ '>=' operator """
266274
267- def comparator (dimension_value , it_val ) :
275+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
268276 return dimension_value .version_cmp (it_val , ordered = True ) >= 0
269277
270278 return self ._op_core (dimension_name , values , comparator )
271279
272- def _op_minor_greater_or_equal (self , dimension_name , values ):
280+ def _op_minor_greater_or_equal (self , dimension_name : str , values : list [ ContextValue ] ):
273281 """ '~>=' operator """
274282
275- def comparator (dimension_value , it_val ) :
283+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
276284 return dimension_value .version_cmp (
277285 it_val , minor_mode = True , ordered = True ) >= 0
278286
279287 return self ._op_core (dimension_name , values , comparator )
280288
281- def _op_greater (self , dimension_name , values ):
289+ def _op_greater (self , dimension_name : str , values : list [ ContextValue ] ):
282290 """ '>' operator """
283291
284- def comparator (dimension_value , it_val ) :
292+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
285293 return dimension_value .version_cmp (it_val , ordered = True ) > 0
286294
287295 return self ._op_core (dimension_name , values , comparator )
288296
289- def _op_minor_greater (self , dimension_name , values ):
297+ def _op_minor_greater (self , dimension_name : str , values : list [ ContextValue ] ):
290298 """ '~>' operator """
291299
292- def comparator (dimension_value , it_val ) :
300+ def comparator (dimension_value : ContextValue , it_val : ContextValue ) -> bool :
293301 return dimension_value .version_cmp (
294302 it_val , minor_mode = True , ordered = True ) > 0
295303
296304 return self ._op_core (dimension_name , values , comparator )
297305
298- def _op_core (self , dimension_name , values , comparator ):
306+ def _op_core (self , dimension_name : str , values : list [ContextValue ],
307+ comparator : Callable [[ContextValue , ContextValue ], bool ]):
299308 """
300309 Evaluate value from dimension vs target values combination
301310
@@ -362,6 +371,7 @@ def _op_core(self, dimension_name, values, comparator):
362371
363372 # To split by 'or' operator
364373 re_or_split = re .compile (r'\bor\b' )
374+ _dimensions : dict [str ]
365375
366376 def __init__ (self , * args , ** kwargs ):
367377 """
@@ -393,8 +403,10 @@ def __init__(self, *args, **kwargs):
393403 [self .parse_value (val ) for val in values ]
394404 )
395405
406+ ExpressionType = tuple [str | None , str | bool , list [str ] | None ]
407+
396408 @staticmethod
397- def parse_rule (rule ) :
409+ def parse_rule (rule : str | bool ) -> list [ list [ ExpressionType ]] :
398410 """
399411 Parses rule into expressions
400412
@@ -437,12 +449,12 @@ def parse_rule(rule):
437449 return parsed_rule
438450
439451 @staticmethod
440- def parse_value (value ) :
452+ def parse_value (value : str ) -> ContextValue :
441453 """ Single place to convert to ContextValue """
442454 return ContextValue (str (value ))
443455
444456 @staticmethod
445- def split_rule_to_groups (rule ) :
457+ def split_rule_to_groups (rule : str ) -> list [ list [ str ]] :
446458 """
447459 Split rule into nested lists, no real parsing
448460
@@ -467,7 +479,7 @@ def split_rule_to_groups(rule):
467479 return rule_parts
468480
469481 @staticmethod
470- def split_expression (expression ) :
482+ def split_expression (expression : str ) -> ExpressionType :
471483 """
472484 Split expression to dimension name, operator and values
473485
@@ -500,7 +512,7 @@ def split_expression(expression):
500512 return (match .group (1 ), match .group (2 ), None )
501513 raise InvalidRule ("Cannot parse expression '{}'." .format (expression ))
502514
503- def matches (self , rule ) :
515+ def matches (self , rule : str | bool ) -> bool :
504516 """
505517 Does the rule match the current Context?
506518
@@ -570,7 +582,7 @@ def matches(self, rule):
570582 else :
571583 raise CannotDecide () # It's up to callee how to treat this
572584
573- def evaluate (self , expression ) :
585+ def evaluate (self , expression : ExpressionType ) -> bool :
574586 dimension_name , operator , values = expression
575587 if isinstance (operator , bool ):
576588 return operator
0 commit comments