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
|
"""
Implementation helper: a struct that looks like a tuple. See timemodule
and posixmodule for example uses.
"""
class structseqfield(object):
"""Definition of field of a structseq. The 'index' is for positional
tuple-like indexing. Fields whose index is after a gap in the numbers
cannot be accessed like this, but only by name.
"""
def __init__(self, index, doc=None, default=lambda self: None):
self.__name__ = '?'
self.index = index # patched to None if not positional
self._index = index
self.__doc__ = doc
self._default = default
def __repr__(self):
return '<field %s (%s)>' % (self.__name__,
self.__doc__ or 'undocumented')
def __get__(self, obj, typ=None):
if obj is None:
return self
if self.index is None:
return obj.__dict__[self.__name__]
else:
return obj[self.index]
def __set__(self, obj, value):
raise TypeError("readonly attribute")
class structseqtype(type):
def __new__(metacls, classname, bases, dict):
assert not bases
fields_by_index = {}
for name, field in dict.items():
if isinstance(field, structseqfield):
assert field._index not in fields_by_index
fields_by_index[field._index] = field
field.__name__ = name
dict['n_fields'] = len(fields_by_index)
extra_fields = sorted(fields_by_index.iteritems())
n_sequence_fields = 0
while extra_fields and extra_fields[0][0] == n_sequence_fields:
extra_fields.pop(0)
n_sequence_fields += 1
dict['n_sequence_fields'] = n_sequence_fields
dict['n_unnamed_fields'] = 0 # no fully anonymous fields in PyPy
extra_fields = [field for index, field in extra_fields]
for field in extra_fields:
field.index = None # no longer relevant
assert '__new__' not in dict
dict['_extra_fields'] = tuple(extra_fields)
dict['__new__'] = structseq_new
dict['__reduce__'] = structseq_reduce
dict['__setattr__'] = structseq_setattr
dict['__repr__'] = structseq_repr
dict['_name'] = dict.get('name', '')
return type.__new__(metacls, classname, (tuple,), dict)
builtin_dict = dict
def structseq_new(cls, sequence, dict={}):
sequence = tuple(sequence)
dict = builtin_dict(dict)
N = cls.n_sequence_fields
if len(sequence) < N:
if N < cls.n_fields:
msg = "at least"
else:
msg = "exactly"
raise TypeError("expected a sequence with %s %d items" % (
msg, N))
if len(sequence) > N:
if len(sequence) > cls.n_fields:
if N < cls.n_fields:
msg = "at most"
else:
msg = "exactly"
raise TypeError("expected a sequence with %s %d items" % (
msg, cls.n_fields))
for field, value in zip(cls._extra_fields, sequence[N:]):
name = field.__name__
if name in dict:
raise TypeError("duplicate value for %r" % (name,))
dict[name] = value
sequence = sequence[:N]
result = tuple.__new__(cls, sequence)
object.__setattr__(result, '__dict__', dict)
for field in cls._extra_fields:
name = field.__name__
if name not in dict:
dict[name] = field._default(result)
return result
def structseq_reduce(self):
return type(self), (tuple(self), self.__dict__)
def structseq_setattr(self, attr, value):
if attr not in type(self).__dict__:
raise AttributeError("%r object has no attribute %r" % (
self.__class__.__name__, attr))
else:
raise TypeError("readonly attribute")
def structseq_repr(self):
fields = {}
for field in type(self).__dict__.values():
if isinstance(field, structseqfield):
fields[field._index] = field
parts = ["%s=%r" % (fields[index].__name__, value)
for index, value in enumerate(self)]
return "%s(%s)" % (self._name, ", ".join(parts))
|