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
|
# -*- coding: utf-8 -*-
"""
Wrapper around property builtin to restrict attribute to defined
integer value range (throws ValueError).
Intended to ensure that values packed with struct are in the
correct range
>>> class T(object):
... a = range_property('a',-100,100)
... b = B('b')
... c = H('c')
... d = I('d')
... e = instance_property('e',(int,bool))
>>> t = T()
>>> for i in [0,100,-100]:
... t.a = i
... assert t.a == i
>>> t.a = 101
Traceback (most recent call last):
...
ValueError: Attribute 'a' must be between -100-100 [101]
>>> t.a = -101
Traceback (most recent call last):
...
ValueError: Attribute 'a' must be between -100-100 [-101]
>>> t.a = 'blah'
Traceback (most recent call last):
...
ValueError: Attribute 'a' must be between -100-100 [blah]
>>> t.e = 999
>>> t.e = False
>>> t.e = None
Traceback (most recent call last):
...
ValueError: Attribute 'e' must be instance of ...
>>> check_range("test",123,0,255)
>>> check_range("test",999,0,255)
Traceback (most recent call last):
...
ValueError: Attribute 'test' must be between 0-255 [999]
>>> check_instance("test",123,int)
>>> check_instance("test","xxx",int)
Traceback (most recent call last):
...
ValueError: Attribute 'test' must be instance of ...
"""
import sys
if sys.version < '3':
int_types = (int, long,)
byte_types = (str,bytearray)
else:
int_types = (int,)
byte_types = (bytes,bytearray)
def check_instance(name,val,types):
if not isinstance(val,types):
raise ValueError("Attribute '%s' must be instance of %s [%s]" %
(name,types,type(val)))
def check_bytes(name,val):
return check_instance(name,val,byte_types)
def instance_property(attr,types):
def getter(obj):
return getattr(obj,"_%s" % attr)
def setter(obj,val):
if isinstance(val,types):
setattr(obj,"_%s" % attr,val)
else:
raise ValueError("Attribute '%s' must be instance of %s [%s]" %
(attr,types,type(val)))
return property(getter,setter)
def BYTES(attr):
return instance_property(attr,byte_types)
def check_range(name,val,min,max):
if not (isinstance(val,int_types) and min <= val <= max):
raise ValueError("Attribute '%s' must be between %d-%d [%s]" %
(name,min,max,val))
def range_property(attr,min,max):
def getter(obj):
return getattr(obj,"_%s" % attr)
def setter(obj,val):
if isinstance(val,int_types) and min <= val <= max:
setattr(obj,"_%s" % attr,val)
else:
raise ValueError("Attribute '%s' must be between %d-%d [%s]" %
(attr,min,max,val))
return property(getter,setter)
def B(attr):
"""
Unsigned Byte
"""
return range_property(attr,0,255)
def H(attr):
"""
Unsigned Short
"""
return range_property(attr,0,65535)
def I(attr):
"""
Unsigned Long
"""
return range_property(attr,0,4294967295)
def ntuple_range(attr,n,min,max):
f = lambda x : isinstance(x,int_types) and min <= x <= max
def getter(obj):
return getattr(obj,"_%s" % attr)
def setter(obj,val):
if len(val) != n:
raise ValueError("Attribute '%s' must be tuple with %d elements [%s]" %
(attr,n,val))
if all(map(f,val)):
setattr(obj,"_%s" % attr,val)
else:
raise ValueError("Attribute '%s' elements must be between %d-%d [%s]" %
(attr,min,max,val))
return property(getter,setter)
def IP4(attr):
return ntuple_range(attr,4,0,255)
def IP6(attr):
return ntuple_range(attr,16,0,255)
if __name__ == '__main__':
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS)
|