1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
| import contextlib
class ValidatorLimit:
def __init__(self, name: str, mini, maxi) -> None:
if mini >= maxi:
raise ValueError("Limite non conforme!")
self.mini = mini
self.maxi = maxi
self.name = name
def test(self, value) -> tuple[bool, str, str]:
if self.mini is not None and value < self.mini:
return (
False,
self.name,
f"{self.name.upper()}: Valeur \"{value}\" trop petite, mini:{self.mini}"
)
if self.maxi is not None and value > self.maxi:
return (
False,
self.name,
f"{self.name.upper()}:Valeur \"{value}\" trop grande, maxi:{self.maxi}"
)
return True, self.name, ''
def __repr__(self):
return f"Limit.{self.name}: ({self.mini},{self.maxi})"
class Validator:
""" valider une valeur suivant des règles changeantes"""
LIMITS = ('min_max', 'min_max_error', 'min_max_alarm') # du plus critique au moins
TYPES = (bool, int, float, str)
RULES = LIMITS + ('init', 'default', 'type', 'exception')
def __init__(self, value, args: dict):
self._value = None
self.error = ''
self.rules = {'exception': False}
if args:
self.rules = self.rules | args
for item in self.LIMITS:
if rule := self.rules.get(item):
self.rules[item] = ValidatorLimit(item, rule[0], rule[1])
if diff := set(self.rules.keys()) - set(self.RULES):
raise ValueError(f"Règle de validation non valide: {diff}")
if value is None: value = self.rules['default'] # on assigne ou pas ?
self._value = value # on assigne ou pas ?
self.value = value
def __enter__(self):
#self.value = self._value
return self
def __exit__(self, *exc):
if self.rules.get('exception'):
return False
return True # stop toutes Exceptions dans ce contexte ...
def __str__(self):
rules = {k:v for k,v in self.rules.items() if v and not isinstance(v, tuple) or (isinstance(v, tuple) and set(v) != {None})}
err = f"\n # ERROR: {self.error}" if self.error else ''
return f" # Valeur: {self.value}\n # règles:{rules}{err}"
@property
def value(self):
return self._value
def ok(self, value=None) -> bool:
if value is not None:
self.value = value
return not bool(self.error)
@value.setter
def value(self, value):
self.error = ''
# test types
if not isinstance(value, self.TYPES):
raise TypeError("Type Invalide !", type(value))
if self.rules.get('type') and not isinstance(value, self.rules['type']):
raise TypeError('Type Invalide ! devrait être:', str(self.rules['type']))
# test limites si existent
ok, name, self.error = self.in_limits(value)
if ok:
self._value = value
else:
# en fonction du level de la limite, in fait des choses différentes
if name == 'min_max' and self.rules.get('exception'):
raise ValueError("si besoin métier, Valeur IMPOSSIBLE!")
if name == 'min_max_alarm':
# ici, que "alarm" dans les log ...
print(self.error)
# on force une valeur SI default existe
if 'pas-bon' != self.rules.get('default', 'pas-bon'):
self._value = self.rules['default']
if isinstance(value, str):
# pas compris le test..
pass
return self._value
def in_limits(self, value) -> tuple[bool, str, str]:
# test de la plus critique à la moins
rule: ValidatorLimit
for item in (self.LIMITS):
if rule := self.rules.get(item):
return rule.test(value)
return True, '', ''
#### UTILISATION
# on peut utiliser sans contexte:
x = 800
if not Validator(x,{'min_max': (0, 100)}).ok():
print('x est invalide')
x = 0
if Validator(x,{'min_max': (0, 100)}).ok():
print('x est valide')
x = 5
with Validator(x, {}) as val:
x = val.value
print("valeur après validation sans règle:", x)
print(val)
print()
x = None
with Validator(x, {'default':"C'est OK par défaut..."}) as val:
y = val.value
print("valeur initiale:", x, "valeur après validation:", y)
print(val)
print()
try:
Validator('toto', {'type': int})
except:
pass
x = 95
with Validator(x, {
'default': 25,
'min_max_alarm': (10, 80),
'min_max_error': (20, 90),
'min_max': (0, 100),
}) as val:
y = val.value
print("valeur initiale:", x, "valeur après validation:", y)
print(val)
print()
with Validator(50, {
#'default': 25, On ne force pas une valeur si erreur
'min_max_alarm': (10, 80),
'min_max_error': (20, 90),
'min_max': (0, 100),
#'exception': True, on désire exceptions ou pas ...
}) as val:
val.value = 200 # eventuellement déclanche exception si hors règle "min_max"
#val.value = val.value / 0 # test si exception est dans les règles
print("valeur après validation:", val.value)
print(val)
print()
print("valeur initiale: chaine vide")
with Validator('',) as val:
val.value += 7 # passe sans changer la valeur
print(val)
print()
with Validator('', {'exception': True}) as val:
val.value += -77 # passe pas, exception
print(val)
print()
# exemple plus réaliste
# ou utiliser methode ok()
import random
#regle_cpu = load_conf('mes_reglages.json.conf')['cpu']
regle_cpu = {'min_max_error': (0, 100)}
valid_cpu = Validator(0, regle_cpu)
for temperature_cpu in random.choices(range(50, 200), k=32):
print(temperature_cpu, "?")
#valid_cpu.value = temperature_cpu
#if not valid_cpu.ok():
if not valid_cpu.ok(temperature_cpu):
print(valid_cpu.error)
exit(4) |