File: ranges.py

package info (click to toggle)
python-dnslib 0.9.7%2Bhg20170303-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 412 kB
  • ctags: 506
  • sloc: python: 2,776; sh: 16; makefile: 5
file content (141 lines) | stat: -rw-r--r-- 4,067 bytes parent folder | download
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)