File: bimap.py

package info (click to toggle)
python-dnslib 0.9.25-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 680 kB
  • sloc: python: 3,522; sh: 31; makefile: 7
file content (123 lines) | stat: -rw-r--r-- 3,797 bytes parent folder | download | duplicates (2)
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
# -*- coding: utf-8 -*-

"""
    Bimap - bidirectional mapping between code/value
"""

import sys,types

class BimapError(Exception):
    pass

class Bimap(object):

    """
        Bi-directional mapping between code/text.

        Initialised using:

            name:   Used for exceptions
            dict:   Dict mapping from code (numeric) to text
            error:  Error type to raise if key not found
                    _or_ callable which either generates mapping
                    return error

        The class provides:

            * A 'forward' map (code->text) which is accessed through
              __getitem__ (bimap[code])
            * A 'reverse' map (code>value) which is accessed through
              __getattr__ (bimap.text)
            * A 'get' method which does a forward lookup (code->text)
              and returns a textual version of code if there is no
              explicit mapping (or default provided)

        >>> class TestError(Exception):
        ...     pass

        >>> TEST = Bimap('TEST',{1:'A', 2:'B', 3:'C'},TestError)
        >>> TEST[1]
        'A'
        >>> TEST.A
        1
        >>> TEST.X
        Traceback (most recent call last):
        ...
        TestError: TEST: Invalid reverse lookup: [X]
        >>> TEST[99]
        Traceback (most recent call last):
        ...
        TestError: TEST: Invalid forward lookup: [99]
        >>> TEST.get(99)
        '99'

        # Test with callable error
        >>> def _error(name,key,forward):
        ...     if forward:
        ...         try:
        ...             return "TEST%d" % (key,)
        ...         except:
        ...             raise TestError("%s: Invalid forward lookup: [%s]" % (name,key))
        ...     else:
        ...         if key.startswith("TEST"):
        ...             try:
        ...                 return int(key[4:])
        ...             except:
        ...                 pass
        ...         raise TestError("%s: Invalid reverse lookup: [%s]" % (name,key))
        >>> TEST2 = Bimap('TEST2',{1:'A', 2:'B', 3:'C'},_error)
        >>> TEST2[1]
        'A'
        >>> TEST2[9999]
        'TEST9999'
        >>> TEST2['abcd']
        Traceback (most recent call last):
        ...
        TestError: TEST2: Invalid forward lookup: [abcd]
        >>> TEST2.A
        1
        >>> TEST2.TEST9999
        9999
        >>> TEST2.X
        Traceback (most recent call last):
        ...
        TestError: TEST2: Invalid reverse lookup: [X]

    """

    def __init__(self,name,forward,error=AttributeError):
        self.name = name
        self.error = error
        self.forward = forward.copy()
        self.reverse = dict([(v,k) for (k,v) in list(forward.items())])

    def get(self,k,default=None):
        try:
            return self.forward[k]
        except KeyError as e:
            return default or str(k)

    def __getitem__(self,k):
        try:
            return self.forward[k]
        except KeyError as e:
            if isinstance(self.error,types.FunctionType):
                return self.error(self.name,k,True)
            else:
                raise self.error("%s: Invalid forward lookup: [%s]" % (self.name,k))

    def __getattr__(self,k):
        try:
            # Python 3.7 inspect module (called by doctest) checks for __wrapped__ attribute
            if k == "__wrapped__":
                raise AttributeError()
            return self.reverse[k]
        except KeyError as e:
            if isinstance(self.error,types.FunctionType):
                return self.error(self.name,k,False)
            else:
                raise self.error("%s: Invalid reverse lookup: [%s]" % (self.name,k))

if __name__ == '__main__':
    import doctest,sys
    sys.exit(0 if doctest.testmod().failed == 0 else 1)