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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
"""
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
n_fields = len(fields_by_index)
dict['n_fields'] = n_fields
extra_fields = sorted(fields_by_index.items())
n_sequence_fields = 0
invis_fields = []
if 'n_sequence_fields' in dict:
n_sequence_fields = dict['n_sequence_fields']
extra_fields = extra_fields[n_sequence_fields:]
else:
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 i,field in enumerate(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)
# visible fields
visible_count = cls.n_sequence_fields
# total fields (unnamed are not yet supported, extra fields not included)
real_count = cls.n_fields
length = len(sequence)
if length < visible_count:
if visible_count < real_count:
msg = "at least"
else:
msg = "exactly"
raise TypeError("expected a sequence with %s %d items. has %d" % (
msg, visible_count, length))
if length > visible_count:
if length > real_count:
if visible_count < real_count:
msg = "at most"
else:
msg = "exactly"
raise TypeError("expected a sequence with %s %d items. has %d" \
% (msg, real_count, length))
for field, value in zip(cls._extra_fields, sequence[visible_count:]):
name = field.__name__
if name in dict:
raise TypeError("duplicate value for %r" % (name,))
dict[name] = value
sequence = sequence[:visible_count]
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 = {}
visible_count = self.n_sequence_fields
for field in type(self).__dict__.values():
if isinstance(field, structseqfield) and \
field._index <= visible_count:
fields[field._index] = field
parts = ["%s=%r" % (fields[index].__name__, value)
for index, value in enumerate(self[:visible_count])]
return "%s(%s)" % (self._name, ", ".join(parts))
class SimpleNamespace:
"""A simple attribute-based namespace.
SimpleNamespace(**kwargs)"""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
ident = id(self)
if ident in sns_recurse:
return "namespace(...)"
sns_recurse.add(ident)
try:
pairs = ('%s=%r' % item for item in sorted(self.__dict__.items()))
return "namespace(%s)" % ', '.join(pairs)
finally:
sns_recurse.discard(ident)
def __eq__(self, other):
if issubclass(type(other), SimpleNamespace):
return self.__dict__ == other.__dict__
return NotImplemented
def __ne__(self, other):
if issubclass(type(other), SimpleNamespace):
return self.__dict__ != other.__dict__
return NotImplemented
sns_recurse = set()
# This class is not exposed in sys, but by the types module.
SimpleNamespace.__module__ = 'types'
|