Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / web / _element.py
1 # -*- test-case-name: twisted.web.test.test_template -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 from zope.interface import implements
6
7 from twisted.web.iweb import IRenderable
8
9 from twisted.web.error import MissingRenderMethod, UnexposedMethodError
10 from twisted.web.error import MissingTemplateLoader
11
12
13 class Expose(object):
14     """
15     Helper for exposing methods for various uses using a simple decorator-style
16     callable.
17
18     Instances of this class can be called with one or more functions as
19     positional arguments.  The names of these functions will be added to a list
20     on the class object of which they are methods.
21
22     @ivar attributeName: The attribute with which exposed methods will be
23     tracked.
24     """
25     def __init__(self, doc=None):
26         self.doc = doc
27
28
29     def __call__(self, *funcObjs):
30         """
31         Add one or more functions to the set of exposed functions.
32
33         This is a way to declare something about a class definition, similar to
34         L{zope.interface.implements}.  Use it like this::
35
36             magic = Expose('perform extra magic')
37             class Foo(Bar):
38                 def twiddle(self, x, y):
39                     ...
40                 def frob(self, a, b):
41                     ...
42                 magic(twiddle, frob)
43
44         Later you can query the object::
45
46             aFoo = Foo()
47             magic.get(aFoo, 'twiddle')(x=1, y=2)
48
49         The call to C{get} will fail if the name it is given has not been
50         exposed using C{magic}.
51
52         @param funcObjs: One or more function objects which will be exposed to
53         the client.
54
55         @return: The first of C{funcObjs}.
56         """
57         if not funcObjs:
58             raise TypeError("expose() takes at least 1 argument (0 given)")
59         for fObj in funcObjs:
60             fObj.exposedThrough = getattr(fObj, 'exposedThrough', [])
61             fObj.exposedThrough.append(self)
62         return funcObjs[0]
63
64
65     _nodefault = object()
66     def get(self, instance, methodName, default=_nodefault):
67         """
68         Retrieve an exposed method with the given name from the given instance.
69
70         @raise UnexposedMethodError: Raised if C{default} is not specified and
71         there is no exposed method with the given name.
72
73         @return: A callable object for the named method assigned to the given
74         instance.
75         """
76         method = getattr(instance, methodName, None)
77         exposedThrough = getattr(method, 'exposedThrough', [])
78         if self not in exposedThrough:
79             if default is self._nodefault:
80                 raise UnexposedMethodError(self, methodName)
81             return default
82         return method
83
84
85     @classmethod
86     def _withDocumentation(cls, thunk):
87         """
88         Slight hack to make users of this class appear to have a docstring to
89         documentation generators, by defining them with a decorator.  (This hack
90         should be removed when epydoc can be convinced to use some other method
91         for documenting.)
92         """
93         return cls(thunk.__doc__)
94
95
96 # Avoid exposing the ugly, private classmethod name in the docs.  Luckily this
97 # namespace is private already so this doesn't leak further.
98 exposer = Expose._withDocumentation
99
100 @exposer
101 def renderer():
102     """
103     Decorate with L{renderer} to use methods as template render directives.
104
105     For example::
106
107         class Foo(Element):
108             @renderer
109             def twiddle(self, request, tag):
110                 return tag('Hello, world.')
111
112         <div xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
113             <span t:render="twiddle" />
114         </div>
115
116     Will result in this final output::
117
118         <div>
119             <span>Hello, world.</span>
120         </div>
121     """
122
123
124
125 class Element(object):
126     """
127     Base for classes which can render part of a page.
128
129     An Element is a renderer that can be embedded in a stan document and can
130     hook its template (from the loader) up to render methods.
131
132     An Element might be used to encapsulate the rendering of a complex piece of
133     data which is to be displayed in multiple different contexts.  The Element
134     allows the rendering logic to be easily re-used in different ways.
135
136     Element returns render methods which are registered using
137     L{twisted.web.element.renderer}.  For example::
138
139         class Menu(Element):
140             @renderer
141             def items(self, request, tag):
142                 ....
143
144     Render methods are invoked with two arguments: first, the
145     L{twisted.web.http.Request} being served and second, the tag object which
146     "invoked" the render method.
147
148     @type loader: L{ITemplateLoader} provider
149     @ivar loader: The factory which will be used to load documents to
150         return from C{render}.
151     """
152     implements(IRenderable)
153     loader = None
154
155     def __init__(self, loader=None):
156         if loader is not None:
157             self.loader = loader
158
159
160     def lookupRenderMethod(self, name):
161         """
162         Look up and return the named render method.
163         """
164         method = renderer.get(self, name, None)
165         if method is None:
166             raise MissingRenderMethod(self, name)
167         return method
168
169
170     def render(self, request):
171         """
172         Implement L{IRenderable} to allow one L{Element} to be embedded in
173         another's template or rendering output.
174
175         (This will simply load the template from the C{loader}; when used in a
176         template, the flattening engine will keep track of this object
177         separately as the object to lookup renderers on and call
178         L{Element.renderer} to look them up.  The resulting object from this
179         method is not directly associated with this L{Element}.)
180         """
181         loader = self.loader
182         if loader is None:
183             raise MissingTemplateLoader(self)
184         return loader.load()
185