_structseq.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """
  2. Implementation helper: a struct that looks like a tuple. See timemodule
  3. and posixmodule for example uses.
  4. """
  5. class structseqfield(object):
  6. """Definition of field of a structseq. The 'index' is for positional
  7. tuple-like indexing. Fields whose index is after a gap in the numbers
  8. cannot be accessed like this, but only by name.
  9. """
  10. def __init__(self, index, doc=None, default=lambda self: None):
  11. self.__name__ = '?'
  12. self.index = index # patched to None if not positional
  13. self._index = index
  14. self.__doc__ = doc
  15. self._default = default
  16. def __repr__(self):
  17. return '<field %s (%s)>' % (self.__name__,
  18. self.__doc__ or 'undocumented')
  19. def __get__(self, obj, typ=None):
  20. if obj is None:
  21. return self
  22. if self.index is None:
  23. return obj.__dict__[self.__name__]
  24. else:
  25. return obj[self.index]
  26. def __set__(self, obj, value):
  27. raise TypeError("readonly attribute")
  28. class structseqtype(type):
  29. def __new__(metacls, classname, bases, dict):
  30. assert not bases
  31. fields_by_index = {}
  32. for name, field in dict.items():
  33. if isinstance(field, structseqfield):
  34. assert field._index not in fields_by_index
  35. fields_by_index[field._index] = field
  36. field.__name__ = name
  37. n_fields = len(fields_by_index)
  38. dict['n_fields'] = n_fields
  39. extra_fields = sorted(fields_by_index.items())
  40. n_sequence_fields = 0
  41. invis_fields = []
  42. if 'n_sequence_fields' in dict:
  43. n_sequence_fields = dict['n_sequence_fields']
  44. extra_fields = extra_fields[n_sequence_fields:]
  45. else:
  46. while extra_fields and extra_fields[0][0] == n_sequence_fields:
  47. extra_fields.pop(0)
  48. n_sequence_fields += 1
  49. dict['n_sequence_fields'] = n_sequence_fields
  50. dict['n_unnamed_fields'] = 0 # no fully anonymous fields in PyPy
  51. extra_fields = [field for index, field in extra_fields]
  52. for i,field in enumerate(extra_fields):
  53. field.index = None # no longer relevant
  54. assert '__new__' not in dict
  55. dict['_extra_fields'] = tuple(extra_fields)
  56. dict['__new__'] = structseq_new
  57. dict['__reduce__'] = structseq_reduce
  58. dict['__setattr__'] = structseq_setattr
  59. dict['__repr__'] = structseq_repr
  60. dict['_name'] = dict.get('name', '')
  61. return type.__new__(metacls, classname, (tuple,), dict)
  62. builtin_dict = dict
  63. def structseq_new(cls, sequence, dict={}):
  64. sequence = tuple(sequence)
  65. dict = builtin_dict(dict)
  66. # visible fields
  67. visible_count = cls.n_sequence_fields
  68. # total fields (unnamed are not yet supported, extra fields not included)
  69. real_count = cls.n_fields
  70. length = len(sequence)
  71. if length < visible_count:
  72. if visible_count < real_count:
  73. msg = "at least"
  74. else:
  75. msg = "exactly"
  76. raise TypeError("expected a sequence with %s %d items. has %d" % (
  77. msg, visible_count, length))
  78. if length > visible_count:
  79. if length > real_count:
  80. if visible_count < real_count:
  81. msg = "at most"
  82. else:
  83. msg = "exactly"
  84. raise TypeError("expected a sequence with %s %d items. has %d" \
  85. % (msg, real_count, length))
  86. for field, value in zip(cls._extra_fields, sequence[visible_count:]):
  87. name = field.__name__
  88. if name in dict:
  89. raise TypeError("duplicate value for %r" % (name,))
  90. dict[name] = value
  91. sequence = sequence[:visible_count]
  92. result = tuple.__new__(cls, sequence)
  93. object.__setattr__(result, '__dict__', dict)
  94. for field in cls._extra_fields:
  95. name = field.__name__
  96. if name not in dict:
  97. dict[name] = field._default(result)
  98. return result
  99. def structseq_reduce(self):
  100. return type(self), (tuple(self), self.__dict__)
  101. def structseq_setattr(self, attr, value):
  102. raise AttributeError("%r object has no attribute %r" % (
  103. self.__class__.__name__, attr))
  104. def structseq_repr(self):
  105. fields = {}
  106. visible_count = self.n_sequence_fields
  107. for field in type(self).__dict__.values():
  108. if isinstance(field, structseqfield) and \
  109. field._index <= visible_count:
  110. fields[field._index] = field
  111. parts = ["%s=%r" % (fields[index].__name__, value)
  112. for index, value in enumerate(self[:visible_count])]
  113. return "%s(%s)" % (self._name, ", ".join(parts))
  114. class SimpleNamespace:
  115. """A simple attribute-based namespace.
  116. SimpleNamespace(**kwargs)"""
  117. def __init__(self, **kwargs):
  118. self.__dict__.update(kwargs)
  119. def __repr__(self):
  120. ident = id(self)
  121. if ident in sns_recurse:
  122. return "namespace(...)"
  123. sns_recurse.add(ident)
  124. try:
  125. pairs = ('%s=%r' % item for item in sorted(self.__dict__.items()))
  126. return "namespace(%s)" % ', '.join(pairs)
  127. finally:
  128. sns_recurse.discard(ident)
  129. def __eq__(self, other):
  130. if issubclass(type(other), SimpleNamespace):
  131. return self.__dict__ == other.__dict__
  132. return NotImplemented
  133. def __ne__(self, other):
  134. if issubclass(type(other), SimpleNamespace):
  135. return self.__dict__ != other.__dict__
  136. return NotImplemented
  137. sns_recurse = set()
  138. # This class is not exposed in sys, but by the types module.
  139. SimpleNamespace.__module__ = 'types'