1#
2# This file is part of pyasn1 software.
3#
4# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
5# License: https://pyasn1.readthedocs.io/en/latest/license.html
6#
7# ASN.1 named integers
8#
9from pyasn1 import error
10
11__all__ = ['NamedValues']
12
13
14class NamedValues(object):
15 """Create named values object.
16
17 The |NamedValues| object represents a collection of string names
18 associated with numeric IDs. These objects are used for giving
19 names to otherwise numerical values.
20
21 |NamedValues| objects are immutable and duck-type Python
22 :class:`dict` object mapping ID to name and vice-versa.
23
24 Parameters
25 ----------
26 *args: variable number of two-element :py:class:`tuple`
27
28 name: :py:class:`str`
29 Value label
30
31 value: :py:class:`int`
32 Numeric value
33
34 Keyword Args
35 ------------
36 name: :py:class:`str`
37 Value label
38
39 value: :py:class:`int`
40 Numeric value
41
42 Examples
43 --------
44
45 .. code-block:: pycon
46
47 >>> nv = NamedValues('a', 'b', ('c', 0), d=1)
48 >>> nv
49 >>> {'c': 0, 'd': 1, 'a': 2, 'b': 3}
50 >>> nv[0]
51 'c'
52 >>> nv['a']
53 2
54 """
55 def __init__(self, *args, **kwargs):
56 self.__names = {}
57 self.__numbers = {}
58
59 anonymousNames = []
60
61 for namedValue in args:
62 if isinstance(namedValue, (tuple, list)):
63 try:
64 name, number = namedValue
65
66 except ValueError:
67 raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,))
68
69 else:
70 anonymousNames.append(namedValue)
71 continue
72
73 if name in self.__names:
74 raise error.PyAsn1Error('Duplicate name %s' % (name,))
75
76 if number in self.__numbers:
77 raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
78
79 self.__names[name] = number
80 self.__numbers[number] = name
81
82 for name, number in kwargs.items():
83 if name in self.__names:
84 raise error.PyAsn1Error('Duplicate name %s' % (name,))
85
86 if number in self.__numbers:
87 raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
88
89 self.__names[name] = number
90 self.__numbers[number] = name
91
92 if anonymousNames:
93
94 number = self.__numbers and max(self.__numbers) + 1 or 0
95
96 for name in anonymousNames:
97
98 if name in self.__names:
99 raise error.PyAsn1Error('Duplicate name %s' % (name,))
100
101 self.__names[name] = number
102 self.__numbers[number] = name
103
104 number += 1
105
106 def __repr__(self):
107 representation = ', '.join(['%s=%d' % x for x in self.items()])
108
109 if len(representation) > 64:
110 representation = representation[:32] + '...' + representation[-32:]
111
112 return '<%s object, enums %s>' % (
113 self.__class__.__name__, representation)
114
115 def __eq__(self, other):
116 return dict(self) == other
117
118 def __ne__(self, other):
119 return dict(self) != other
120
121 def __lt__(self, other):
122 return dict(self) < other
123
124 def __le__(self, other):
125 return dict(self) <= other
126
127 def __gt__(self, other):
128 return dict(self) > other
129
130 def __ge__(self, other):
131 return dict(self) >= other
132
133 def __hash__(self):
134 return hash(self.items())
135
136 # Python dict protocol (read-only)
137
138 def __getitem__(self, key):
139 try:
140 return self.__numbers[key]
141
142 except KeyError:
143 return self.__names[key]
144
145 def __len__(self):
146 return len(self.__names)
147
148 def __contains__(self, key):
149 return key in self.__names or key in self.__numbers
150
151 def __iter__(self):
152 return iter(self.__names)
153
154 def values(self):
155 return iter(self.__numbers)
156
157 def keys(self):
158 return iter(self.__names)
159
160 def items(self):
161 for name in self.__names:
162 yield name, self.__names[name]
163
164 # support merging
165
166 def __add__(self, namedValues):
167 return self.__class__(*tuple(self.items()) + tuple(namedValues.items()))
168
169 # XXX clone/subtype?
170
171 def clone(self, *args, **kwargs):
172 new = self.__class__(*args, **kwargs)
173 return self + new
174
175 # legacy protocol
176
177 def getName(self, value):
178 if value in self.__numbers:
179 return self.__numbers[value]
180
181 def getValue(self, name):
182 if name in self.__names:
183 return self.__names[name]
184
185 def getValues(self, *names):
186 try:
187 return [self.__names[name] for name in names]
188
189 except KeyError:
190 raise error.PyAsn1Error(
191 'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),)
192 )