PageRenderTime 36ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/_unsorted/_core_new/_util/wrapper.py

https://bitbucket.org/ericsnowcurrently/commonlib
Python | 213 lines | 179 code | 21 blank | 13 comment | 15 complexity | 66695de9517863865491eeb5a0e6c2f3 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. """wrapper module
  2. """
  3. __all__ = (
  4. "wrapper", "wrap",
  5. "WrapperMeta", "Wrapper",
  6. "WrapperAttribute", "PassHandler",
  7. "MissingInstanceError", "MissingHandlerError",
  8. )
  9. from error import Error
  10. #################################################
  11. # sequence wrapper
  12. MAX_ITERATIONS = 2**15 # 32768
  13. class MaxIterationError(RuntimeError):
  14. def __init__(self, max=MAX_ITERATIONS, *args, **kwargs):
  15. #def __init__(self, max=MAX_ITERATIONS, *args, msg=None):
  16. kwargs, msg = emulate_kwonly(kwargs, (), (("msg", None),))
  17. if msg is None:
  18. msg = "maximum iterations reached (%s)" % max
  19. super(type(self), self).__init__(msg, *args)
  20. class SequenceWrapper(object):
  21. """A Sequence object that exposes the iterable as a sequence."""
  22. def __new__(cls, iterable, maxiter=MAX_ITERATIONS)
  23. if isinstance(iterable, Sequence):
  24. return iterable
  25. return super(cls, cls).__new__(cls)
  26. def __init__(self, iterable, maxiter=MAX_ITERATIONS):
  27. self.iterable = iterable
  28. self.maxiter = maxiter
  29. self._tracking = []
  30. def __getattr__(self, name):
  31. return getattr(self.iterable, name)
  32. def __iter__(self):
  33. if self.iterable is self._tracking:
  34. return iter(self.iterable)
  35. iterable = self._tracking
  36. for item in iterable:
  37. yield item
  38. # now get back to where we left off
  39. maxiter = self.maxiter
  40. count = 0
  41. for item in self.iterable: # iterator?
  42. if count >= maxiter:
  43. raise MaxIterationError(maxiter)
  44. yield item
  45. iterable.append(item)
  46. count += 1
  47. self.iterable =
  48. self._fixed = True
  49. def __len__(self):
  50. if hasattr(self.iterable, "__len__"):
  51. return len(self.iterable)
  52. return len(tuple(self.iterable))
  53. def __getitem__(self, index):
  54. if hasattr(self.iterable, "__getitem__"):
  55. return self.iterable[index]
  56. index -= 1
  57. count = 0
  58. for value in self.iterable:
  59. if count == index:
  60. return value
  61. count += 1
  62. raise IndexError
  63. def __contains__(self, value):
  64. ...
  65. def as_sequence(iterable, maxiter=MAX_ITERATIONS):
  66. return SequenceWrapper(iterable, maxiter)
  67. #################################################
  68. # generic wrapper class
  69. class MissingInstanceError(Error):
  70. MSG = "_WRAP_INSTANCE not set on class"
  71. class MissingHandlerError(Error):
  72. MSG = "expected a handler, found None"
  73. class WrapperAttribute(object):
  74. def __init__(self, name):
  75. self.name = name
  76. def _handler(self, cls):
  77. handler = cls._WRAP_HANDLERS.get(self.name)
  78. if handler is None:
  79. handler = cls._WRAP_DEFAULT_HANDLER
  80. if handler is None:
  81. raise MissingHandlerError(self.name)
  82. return handler(self.name)
  83. def __get__(self, obj, cls):
  84. if cls._WRAP_INSTANCE is None:
  85. raise MissingInstanceError(self.name)
  86. return self._handler(cls).__get__(obj, cls)
  87. #return getattr(cls._WRAP_INSTANCE, self.name)
  88. def __set__(self, obj, value):
  89. self._handler(type(obj)).__set__(obj, value)
  90. def __del__(self, obj):
  91. raise NotImplementedError
  92. class PassHandler(WrapperAttribute):
  93. def __get__(self, obj, cls):
  94. return getattr(cls._WRAP_INSTANCE, name)
  95. def __set__(self, obj, value):
  96. setattr(cls._WRAP_INSTANCE, name, value)
  97. def __del__(self, obj, cls):
  98. delattr(cls._WRAP_INSTANCE, name)
  99. class WrapperMeta(type):
  100. """The metaclass for the base wrapper class.
  101. >>> NewWrapper = WrapperMeta("NewWrapper", (list,), {})
  102. >>> NewWrapper = Wrapper.from_base(list, {})
  103. >>> class NewWrapper(list):
  104. ... __metaclass__ = WrapperMeta
  105. ...
  106. >>> class NewWrapper(list):
  107. ... __metaclass__ = WrapperMeta
  108. ... DEFAULT_HANDLER = PassHandler()
  109. ... __getitem__ = AnotherHandler()
  110. ...
  111. >>> class NewWrapper(Wrapper, list):
  112. ... DEFAULT_HANDLER = PassHandler()
  113. ... __getitem__ = AnotherHandler()
  114. ...
  115. >>> obj = NewWrapper([1,2,3,4,5])
  116. >>> obj = NewWrapper.from_instance([1,2,3,4,5])
  117. >>>
  118. >>> spam = [1,2,3,4,5]
  119. >>> spam = Wrapper.from_base(type(spam), {}).from_instance(spam)
  120. """
  121. def __new__(meta, name, bases, namespace):
  122. for base in bases:
  123. if not isinstance(base, meta):
  124. break
  125. default = namespace.pop("DEFAULT_HANDLER", None)
  126. handlers = namespace.pop("HANDLERS", {})
  127. handlers.update(namespace)
  128. return meta.from_base(base, handlers, default)
  129. @classmethod
  130. def from_base(meta, base, handlers, default=None):
  131. name = "%sWrapper" % base.__name__.capitalize()
  132. cls = type.__new__(meta, name, (base,), {})
  133. cls._WRAP_BASE = base
  134. cls._WRAP_HANDLERS = handlers
  135. cls._WRAP_DEFAULT = default
  136. cls._WRAP_INSTANCE = None
  137. for name in handlers:
  138. setattr(cls, name, WrapperAttribute(name))
  139. return cls
  140. def __call__(cls, *args, **kwargs):
  141. original = cls._WRAP_BASE(*args, **kwargs)
  142. return cls.from_instance(original)
  143. def from_instance(cls, original):
  144. subclass = type.__new__(type(cls), cls.__name__, (cls,), {})
  145. subclass._WRAP_INSTANCE = original
  146. obj = subclass.__new__(subclass)
  147. return obj
  148. class Wrapper(object):
  149. __metaclass__ = WrapperMeta
  150. DEFAULT_HANDLER = PassHandler
  151. wrapper = Wrapper.from_base
  152. def wrap(obj, names=None, attrmap=None, defaulthandler=None):
  153. """create an instance of a new wrapper class for this obj."""
  154. if names is not None and not names and not attrmap:
  155. raise TypeError("expected either names or attrmap")
  156. if attrmap is None:
  157. attrmap = {}
  158. for key, value in attrmap.items():
  159. if value is None:
  160. attrmap[key] = default_handler
  161. if names is None:
  162. names = set(dir(obj)) - set(attrmap)
  163. for name in names:
  164. if name in attrmap:
  165. raise TypeError("name collision between names and attrmap")
  166. attrmap[name] = default_handler
  167. try:
  168. cls = wrapper(type(obj), attrmap)
  169. except MissingHandlerError:
  170. if default_handler is None:
  171. raise TypeError("expected default_handler to be passed")
  172. else:
  173. raise
  174. # register abstract base classes too?
  175. return cls.from_instance(obj)