Advertisement
Guest User

Untitled

a guest
Sep 16th, 2019
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.73 KB | None | 0 0
  1. class DirtyTemplateMeta(type):
  2. '''
  3. Metaclass for :class:`DirtyTemplate`
  4. '''
  5. def __new__(cls, name, bases, dct, *, template_bases=()):
  6. new_cls = super().__new__(cls, name, bases, dct)
  7. new_cls.template_dict = dct
  8. new_cls.template_bases = template_bases
  9. return new_cls
  10.  
  11.  
  12. class DirtyTemplate(metaclass=DirtyTemplateMeta):
  13. '''
  14. Define a DirtyTemplate.
  15.  
  16. This is a horrible hack job that allows (in theory anyway) to define
  17. a 'Template' class which can be used a bit like templates in C++, in
  18. particular to define a class once that can be rendered into multiple
  19. defined classes each with different base classes.
  20.  
  21. This allows for some pretty wild stuff.
  22.  
  23. So we can define a template that bases ``dict``, implements a method, and
  24. overrides another.
  25.  
  26. Warning
  27. -------
  28. As I say, this is a terrible hack. I was experimenting with ways to be able
  29. to avoid repeating definitions but, due to some third-party dark magics,
  30. was unable to rely on mixin's or composition to get the job done.
  31.  
  32. I'm not saying it's a good idea, or that it'll work, just a bit of
  33. experimentation.
  34.  
  35. Examples
  36. --------
  37.  
  38. >>> class SomeTemplate(DirtyTemplate, template_bases=(dict,)):
  39. ... def some_method(self):
  40. ... return "hello world"
  41. ... def __getitem__(self, key):
  42. ... return "constant"
  43. ...
  44.  
  45. This shouldn't be instansiated directly. Instead it can be used to
  46. render different versions of the same class with different base classes.
  47.  
  48. >>> @SomeTemplate.template
  49. ... class ImplA:
  50. ... def some_method(self):
  51. ... return "not hello"
  52. ...
  53.  
  54. However, each rendering always has the template as the 'top class' rather
  55. than what you might expect.
  56.  
  57. >>> a = ImplA()
  58. >>> a.some_method()
  59. 'hello world'
  60. >>> a["thing"]
  61. 'constant'
  62.  
  63. It even has the base classes defined on the template;
  64.  
  65. >>> ImplA.__bases__
  66. (<class 'dirty_templates.ImplA'>, <class 'dict'>)
  67.  
  68. The behaviour from these base classes works as expected.
  69. >>> a["thing"] = 4
  70. >>> a.values()
  71. dict_values([4])
  72.  
  73.  
  74. In the next example we render a version of the template that inherits from
  75. a subclass of one of the bases of the template;
  76.  
  77. >>> import collections
  78. >>> @SomeTemplate.template
  79. ... class ImplB(collections.OrderedDict):
  80. ... pass
  81. ...
  82.  
  83.  
  84. Here again the template behaviour is used;
  85.  
  86. >>> b = ImplB()
  87. >>> b.some_method()
  88. 'hello world'
  89. >>> b["thing"]
  90. 'constant'
  91.  
  92. Note though, because the wrapped ``ImplB`` class already bases ``dict``
  93. (via OrderedDict) it's not added to the list of ``__bases__`` like it was
  94. in ``ImplA``
  95.  
  96. >>> ImplB.__bases__
  97. (<class 'dirty_templates.ImplB'>,)
  98.  
  99. but again the behaviour of ``OrderedDict`` is accessible;
  100.  
  101. >>> b["thing"] = 4
  102. >>> b.move_to_end("thing")
  103.  
  104. Finally we can obviously define new methods in the rendered classes;
  105.  
  106. >>> @SomeTemplate.template
  107. ... class ImplC(collections.OrderedDict):
  108. ... def some_new_method(self):
  109. ... return "new hello"
  110. ...
  111. ...
  112. >>> c = ImplC()
  113. >>> c.some_method()
  114. 'hello world'
  115. >>> c.some_new_method()
  116. 'new hello'
  117. >>> c["thing"]
  118. 'constant'
  119.  
  120. '''
  121. # This is set by metaclass
  122. template_dict = None
  123. template_bases = ()
  124.  
  125. @classmethod
  126. def template(cls, new_cls):
  127. '''
  128. Decorator to generate new templated classes.
  129. '''
  130. name = new_cls.__name__
  131. new_cls.__name__ = f'{name}_inner'
  132.  
  133. bases = [new_cls]
  134. bases.extend(
  135. base for base in cls.template_bases
  136. if not issubclass(new_cls, base)
  137. )
  138.  
  139. return type(name, tuple(bases), cls.template_dict)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement