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
|
"""
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):
raise AttributeError("%r object has no attribute %r" % (
self.__class__.__name__, attr))
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))
|