8
8
from openhtf.util import measurements
9
9
10
10
class MyLessThanValidator(ValidatorBase):
11
- def __init__(self, limit):
11
+ def __init__(self, limit) -> None :
12
12
self.limit = limit
13
13
14
14
# This will be invoked to test if the measurement is 'PASS' or 'FAIL'.
15
- def __call__(self, value):
15
+ def __call__(self, value) -> bool :
16
16
return value < self.limit
17
17
18
18
# Name defaults to the validator's __name__ attribute unless overridden.
@@ -31,12 +31,12 @@ def MyPhase(test):
31
31
For simpler validators, you don't need to register them at all, you can
32
32
simply attach them to the Measurement with the .with_validator() method:
33
33
34
- def LessThan4(value):
34
+ def LessThan4(value) -> bool :
35
35
return value < 4
36
36
37
37
@measurements.measures(
38
38
measurements.Measurement('my_measurement).with_validator(LessThan4))
39
- def MyPhase(test) :
39
+ def MyPhase(test: htf.TestApi) -> None :
40
40
test.measurements.my_measurement = 5 # Will also 'FAIL'
41
41
42
42
Notes:
@@ -58,37 +58,43 @@ def MyPhase(test):
58
58
import math
59
59
import numbers
60
60
import re
61
+ from typing import Callable , Dict , Optional , Type , TypeVar , Union
62
+
61
63
from openhtf import util
62
64
63
- _VALIDATORS = {}
64
65
66
+ class ValidatorBase (abc .ABC ):
67
+
68
+ @abc .abstractmethod
69
+ def __call__ (self , value ) -> bool :
70
+ """Should validate value, returning a boolean result."""
71
+
72
+
73
+ _ValidatorT = TypeVar ("_ValidatorT" , bound = ValidatorBase )
74
+ _ValidatorFactoryT = Union [Type [_ValidatorT ], Callable [..., _ValidatorT ]]
75
+ _VALIDATORS : Dict [str , _ValidatorFactoryT ] = {}
65
76
66
- def register (validator , name = None ):
77
+
78
+ def register (validator : _ValidatorFactoryT ,
79
+ name : Optional [str ] = None ) -> _ValidatorFactoryT :
67
80
name = name or validator .__name__
68
81
if name in _VALIDATORS :
69
82
raise ValueError ('Duplicate validator name' , name )
70
83
_VALIDATORS [name ] = validator
71
84
return validator
72
85
73
86
74
- def has_validator (name ) :
87
+ def has_validator (name : str ) -> bool :
75
88
return name in _VALIDATORS
76
89
77
90
78
- def create_validator (name , * args , ** kwargs ):
91
+ def create_validator (name : str , * args , ** kwargs ) -> _ValidatorT :
79
92
return _VALIDATORS [name ](* args , ** kwargs )
80
93
81
94
82
95
_identity = lambda x : x
83
96
84
97
85
- class ValidatorBase (abc .ABC ):
86
-
87
- @abc .abstractmethod
88
- def __call__ (self , value ):
89
- """Should validate value, returning a boolean result."""
90
-
91
-
92
98
class RangeValidatorBase (ValidatorBase , abc .ABC ):
93
99
94
100
@abc .abstractproperty
@@ -120,7 +126,7 @@ def __init__(self,
120
126
minimum ,
121
127
maximum ,
122
128
marginal_minimum = None ,
123
- marginal_maximum = None ):
129
+ marginal_maximum = None ) -> None :
124
130
super (AllInRangeValidator , self ).__init__ ()
125
131
if minimum is None and maximum is None :
126
132
raise ValueError ('Must specify minimum, maximum, or both' )
@@ -168,7 +174,7 @@ def marginal_minimum(self):
168
174
def marginal_maximum (self ):
169
175
return self ._marginal_maximum
170
176
171
- def __call__ (self , values ):
177
+ def __call__ (self , values ) -> bool :
172
178
within_maximum = self ._maximum is None or all (
173
179
value <= self .maximum for value in values )
174
180
within_minimum = self ._minimum is None or all (
@@ -204,18 +210,18 @@ def __str__(self):
204
210
class AllEqualsValidator (ValidatorBase ):
205
211
"""Validator to verify a list of values are equal to the expected value."""
206
212
207
- def __init__ (self , spec ):
213
+ def __init__ (self , spec ) -> None :
208
214
super (AllEqualsValidator , self ).__init__ ()
209
215
self ._spec = spec
210
216
211
217
@property
212
218
def spec (self ):
213
219
return self ._spec
214
220
215
- def __call__ (self , values ):
221
+ def __call__ (self , values ) -> bool :
216
222
return all ([value == self .spec for value in values ])
217
223
218
- def __str__ (self ):
224
+ def __str__ (self ) -> str :
219
225
return "'x' is equal to '%s'" % self ._spec
220
226
221
227
@@ -242,7 +248,7 @@ def __init__(self,
242
248
maximum = None ,
243
249
marginal_minimum = None ,
244
250
marginal_maximum = None ,
245
- type = None ): # pylint: disable=redefined-builtin
251
+ type = None ) -> None : # pylint: disable=redefined-builtin
246
252
super (InRange , self ).__init__ ()
247
253
248
254
if minimum is None and maximum is None :
@@ -292,7 +298,7 @@ def marginal_minimum(self):
292
298
return converter (self ._marginal_minimum )
293
299
294
300
@property
295
- def marginal_maximum (self ):
301
+ def marginal_maximum (self ) -> str :
296
302
converter = self ._type if self ._type is not None else _identity
297
303
return converter (self ._marginal_maximum )
298
304
@@ -305,7 +311,7 @@ def with_args(self, **kwargs):
305
311
type = self ._type ,
306
312
)
307
313
308
- def __call__ (self , value ):
314
+ def __call__ (self , value ) -> bool :
309
315
if value is None :
310
316
return False
311
317
if math .isnan (value ):
@@ -329,7 +335,7 @@ def is_marginal(self, value) -> bool:
329
335
return True
330
336
return False
331
337
332
- def __str__ (self ):
338
+ def __str__ (self ) -> str :
333
339
assert self ._minimum is not None or self ._maximum is not None
334
340
if (self ._minimum is not None and self ._maximum is not None and
335
341
self ._minimum == self ._maximum ):
@@ -347,13 +353,13 @@ def __str__(self):
347
353
string_repr += ' <= {}' .format (self ._maximum )
348
354
return string_repr
349
355
350
- def __eq__ (self , other ):
356
+ def __eq__ (self , other ) -> bool :
351
357
return (isinstance (other , type (self )) and self .minimum == other .minimum and
352
358
self .maximum == other .maximum and
353
359
self .marginal_minimum == other .marginal_minimum and
354
360
self .marginal_maximum == other .marginal_maximum )
355
361
356
- def __ne__ (self , other ):
362
+ def __ne__ (self , other ) -> bool :
357
363
return not self == other
358
364
359
365
@@ -373,10 +379,10 @@ def equals(value, type=None): # pylint: disable=redefined-builtin
373
379
return Equals (value , type = type )
374
380
375
381
376
- class Equals (object ):
382
+ class Equals (ValidatorBase ):
377
383
"""Validator to verify an object is equal to the expected value."""
378
384
379
- def __init__ (self , expected , type = None ): # pylint: disable=redefined-builtin
385
+ def __init__ (self , expected , type = None ) -> None : # pylint: disable=redefined-builtin
380
386
self ._expected = expected
381
387
self ._type = type
382
388
@@ -388,21 +394,21 @@ def expected(self):
388
394
def __call__ (self , value ):
389
395
return value == self .expected
390
396
391
- def __str__ (self ):
397
+ def __str__ (self ) -> str :
392
398
return f"'x' is equal to '{ self ._expected } '"
393
399
394
- def __eq__ (self , other ):
400
+ def __eq__ (self , other ) -> bool :
395
401
return isinstance (other , type (self )) and self .expected == other .expected
396
402
397
403
398
- class RegexMatcher (object ):
404
+ class RegexMatcher (ValidatorBase ):
399
405
"""Validator to verify a string value matches a regex."""
400
406
401
- def __init__ (self , regex , compiled_regex ):
407
+ def __init__ (self , regex , compiled_regex ) -> None :
402
408
self ._compiled = compiled_regex
403
409
self .regex = regex
404
410
405
- def __call__ (self , value ):
411
+ def __call__ (self , value ) -> bool :
406
412
return self ._compiled .match (str (value )) is not None
407
413
408
414
def __deepcopy__ (self , dummy_memo ):
@@ -414,7 +420,7 @@ def __str__(self):
414
420
def __eq__ (self , other ):
415
421
return isinstance (other , type (self )) and self .regex == other .regex
416
422
417
- def __ne__ (self , other ):
423
+ def __ne__ (self , other ) -> bool :
418
424
return not self == other
419
425
420
426
@@ -426,7 +432,7 @@ def matches_regex(regex):
426
432
class WithinPercent (RangeValidatorBase ):
427
433
"""Validates that a number is within percent of a value."""
428
434
429
- def __init__ (self , expected , percent , marginal_percent = None ):
435
+ def __init__ (self , expected , percent , marginal_percent = None ) -> None :
430
436
super (WithinPercent , self ).__init__ ()
431
437
if percent < 0 :
432
438
raise ValueError ('percent argument is {}, must be >0' .format (percent ))
@@ -465,7 +471,7 @@ def marginal_maximum(self):
465
471
return (self .expected -
466
472
self ._applied_marginal_percent if self .marginal_percent else None )
467
473
468
- def __call__ (self , value ):
474
+ def __call__ (self , value ) -> bool :
469
475
return self .minimum <= value <= self .maximum
470
476
471
477
def is_marginal (self , value ) -> bool :
@@ -475,17 +481,17 @@ def is_marginal(self, value) -> bool:
475
481
return (self .minimum < value <= self .marginal_minimum or
476
482
self .marginal_maximum <= value < self .maximum )
477
483
478
- def __str__ (self ):
484
+ def __str__ (self ) -> str :
479
485
return "'x' is within {}% of {}. Marginal: {}% of {}" .format (
480
486
self .percent , self .expected , self .marginal_percent , self .expected )
481
487
482
- def __eq__ (self , other ):
488
+ def __eq__ (self , other ) -> bool :
483
489
return (isinstance (other , type (self )) and
484
490
self .expected == other .expected and
485
491
self .percent == other .percent and
486
492
self .marginal_percent == other .marginal_percent )
487
493
488
- def __ne__ (self , other ):
494
+ def __ne__ (self , other ) -> bool :
489
495
return not self == other
490
496
491
497
@@ -497,14 +503,14 @@ def within_percent(expected, percent):
497
503
class DimensionPivot (ValidatorBase ):
498
504
"""Runs a validator on each actual value of a dimensioned measurement."""
499
505
500
- def __init__ (self , sub_validator ):
506
+ def __init__ (self , sub_validator ) -> None :
501
507
super (DimensionPivot , self ).__init__ ()
502
508
self ._sub_validator = sub_validator
503
509
504
- def __call__ (self , dimensioned_value ):
510
+ def __call__ (self , dimensioned_value ) -> bool :
505
511
return all (self ._sub_validator (row [- 1 ]) for row in dimensioned_value )
506
512
507
- def __str__ (self ):
513
+ def __str__ (self ) -> str :
508
514
return 'All values pass: {}' .format (str (self ._sub_validator ))
509
515
510
516
@@ -516,11 +522,11 @@ def dimension_pivot_validate(sub_validator):
516
522
class ConsistentEndDimensionPivot (ValidatorBase ):
517
523
"""If any rows validate, all following rows must also validate."""
518
524
519
- def __init__ (self , sub_validator ):
525
+ def __init__ (self , sub_validator ) -> None :
520
526
super (ConsistentEndDimensionPivot , self ).__init__ ()
521
527
self ._sub_validator = sub_validator
522
528
523
- def __call__ (self , dimensioned_value ):
529
+ def __call__ (self , dimensioned_value ) -> bool :
524
530
for index , row in enumerate (dimensioned_value ):
525
531
if self ._sub_validator (row [- 1 ]):
526
532
i = index
@@ -529,7 +535,7 @@ def __call__(self, dimensioned_value):
529
535
return False
530
536
return all (self ._sub_validator (rest [- 1 ]) for rest in dimensioned_value [i :])
531
537
532
- def __str__ (self ):
538
+ def __str__ (self ) -> str :
533
539
return 'Once pass, rest must also pass: {}' .format (str (self ._sub_validator ))
534
540
535
541
0 commit comments