Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

_element.py 5.8KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. # -*- test-case-name: twisted.web.test.test_template -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. from typing import TYPE_CHECKING, Callable, List, Optional, TypeVar, Union, overload
  5. from zope.interface import implementer
  6. from twisted.web.error import (
  7. MissingRenderMethod,
  8. MissingTemplateLoader,
  9. UnexposedMethodError,
  10. )
  11. from twisted.web.iweb import IRenderable, IRequest, ITemplateLoader
  12. if TYPE_CHECKING:
  13. from twisted.web.template import Flattenable, Tag
  14. T = TypeVar("T")
  15. class Expose:
  16. """
  17. Helper for exposing methods for various uses using a simple decorator-style
  18. callable.
  19. Instances of this class can be called with one or more functions as
  20. positional arguments. The names of these functions will be added to a list
  21. on the class object of which they are methods.
  22. """
  23. def __call__(self, *funcObjs: Callable) -> Callable:
  24. """
  25. Add one or more functions to the set of exposed functions.
  26. This is a way to declare something about a class definition, similar to
  27. L{zope.interface.implementer}. Use it like this::
  28. magic = Expose('perform extra magic')
  29. class Foo(Bar):
  30. def twiddle(self, x, y):
  31. ...
  32. def frob(self, a, b):
  33. ...
  34. magic(twiddle, frob)
  35. Later you can query the object::
  36. aFoo = Foo()
  37. magic.get(aFoo, 'twiddle')(x=1, y=2)
  38. The call to C{get} will fail if the name it is given has not been
  39. exposed using C{magic}.
  40. @param funcObjs: One or more function objects which will be exposed to
  41. the client.
  42. @return: The first of C{funcObjs}.
  43. """
  44. if not funcObjs:
  45. raise TypeError("expose() takes at least 1 argument (0 given)")
  46. for fObj in funcObjs:
  47. exposedThrough: List[Expose] = getattr(fObj, "exposedThrough", [])
  48. exposedThrough.append(self)
  49. setattr(fObj, "exposedThrough", exposedThrough)
  50. return funcObjs[0]
  51. _nodefault = object()
  52. @overload
  53. def get(self, instance: object, methodName: str) -> Callable:
  54. ...
  55. @overload
  56. def get(self, instance: object, methodName: str, default: T) -> Union[Callable, T]:
  57. ...
  58. def get(
  59. self, instance: object, methodName: str, default: object = _nodefault
  60. ) -> object:
  61. """
  62. Retrieve an exposed method with the given name from the given instance.
  63. @raise UnexposedMethodError: Raised if C{default} is not specified and
  64. there is no exposed method with the given name.
  65. @return: A callable object for the named method assigned to the given
  66. instance.
  67. """
  68. method = getattr(instance, methodName, None)
  69. exposedThrough = getattr(method, "exposedThrough", [])
  70. if self not in exposedThrough:
  71. if default is self._nodefault:
  72. raise UnexposedMethodError(self, methodName)
  73. return default
  74. return method
  75. def exposer(thunk: Callable) -> Expose:
  76. expose = Expose()
  77. expose.__doc__ = thunk.__doc__
  78. return expose
  79. @exposer
  80. def renderer() -> None:
  81. """
  82. Decorate with L{renderer} to use methods as template render directives.
  83. For example::
  84. class Foo(Element):
  85. @renderer
  86. def twiddle(self, request, tag):
  87. return tag('Hello, world.')
  88. <div xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
  89. <span t:render="twiddle" />
  90. </div>
  91. Will result in this final output::
  92. <div>
  93. <span>Hello, world.</span>
  94. </div>
  95. """
  96. @implementer(IRenderable)
  97. class Element:
  98. """
  99. Base for classes which can render part of a page.
  100. An Element is a renderer that can be embedded in a stan document and can
  101. hook its template (from the loader) up to render methods.
  102. An Element might be used to encapsulate the rendering of a complex piece of
  103. data which is to be displayed in multiple different contexts. The Element
  104. allows the rendering logic to be easily re-used in different ways.
  105. Element returns render methods which are registered using
  106. L{twisted.web._element.renderer}. For example::
  107. class Menu(Element):
  108. @renderer
  109. def items(self, request, tag):
  110. ....
  111. Render methods are invoked with two arguments: first, the
  112. L{twisted.web.http.Request} being served and second, the tag object which
  113. "invoked" the render method.
  114. @ivar loader: The factory which will be used to load documents to
  115. return from C{render}.
  116. """
  117. loader: Optional[ITemplateLoader] = None
  118. def __init__(self, loader: Optional[ITemplateLoader] = None):
  119. if loader is not None:
  120. self.loader = loader
  121. def lookupRenderMethod(
  122. self, name: str
  123. ) -> Callable[[Optional[IRequest], "Tag"], "Flattenable"]:
  124. """
  125. Look up and return the named render method.
  126. """
  127. method = renderer.get(self, name, None)
  128. if method is None:
  129. raise MissingRenderMethod(self, name)
  130. return method
  131. def render(self, request: Optional[IRequest]) -> "Flattenable":
  132. """
  133. Implement L{IRenderable} to allow one L{Element} to be embedded in
  134. another's template or rendering output.
  135. (This will simply load the template from the C{loader}; when used in a
  136. template, the flattening engine will keep track of this object
  137. separately as the object to lookup renderers on and call
  138. L{Element.renderer} to look them up. The resulting object from this
  139. method is not directly associated with this L{Element}.)
  140. """
  141. loader = self.loader
  142. if loader is None:
  143. raise MissingTemplateLoader(self)
  144. return loader.load()