annotate doc/tutorial.rst @ 71:88adf10be709 draft

Add tests.
author David Barts <n5jrn@me.com>
date Mon, 15 Jul 2019 13:16:31 -0700
parents 682cd33e564c
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
60
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
1 ********
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
2 Tutorial
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
3 ********
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
4
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
5 .. highlight:: none
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
6
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
7 ==============================================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
8 First Step: Create a Directory for Your Webapp
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
9 ==============================================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
10
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
11 TinCan is a lot like a web server; it serves a directory of files. There is
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
12 one catch, though: that directory must contain a ``WEB-INF`` subdirectory.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
13 It's OK if ``WEB-INF`` ends up being empty (as it can, for a really simple
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
14 webapp). If ``WEB-INF`` is missing, TinCan will conclude the directory
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
15 doesn't contain a webapp and will refuse to serve it. (This is a deliberate
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
16 feature, added to prevent serving directories that don't contain webapps.)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
17
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
18 So the first thing we must do is create a directory to hold our new webapp.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
19 Let's call it ``demo``::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
20 $ mkdir demo demo/WEB-INF
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
21
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
22 ===================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
23 Adding Some Content
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
24 ===================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
25 Use your favorite editor to create a file called ``hello.pspx``, and insert
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
26 the following content into it::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
27
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
28 <!DOCTYPE html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
29 <html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
30 <head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
31 <title>Hello</title>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
32 </head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
33 <body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
34 <h1>Hello, World</h1>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
35 <p>This page was called on the route ${page.request.environ['PATH_INFO']}.</p>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
36 </body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
37 </html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
38
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
39 Save the file to disk, and start up a test server::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
40
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
41 $ launch demo
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
42
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
43 You should see something like the following::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
44
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
45 adding route: /hello.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
46 Bottle v0.12.16 server starting up (using WSGIRefServer())...
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
47 Listening on http://localhost:8080/
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
48 Hit Ctrl-C to quit.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
49
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
50 If you already have a server listening on port 8080, this naturally won't work.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
51 Use the ``--port`` option to tell ``launch`` to listen on another port, e.g.::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
52
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
53 $ launch --port=8000
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
54
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
55 When you have a working test server, point your browser at
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
56 ``http://localhost:8080/hello.pspx``
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
57 and you should see something like the following:
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
58
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
59 .. image:: hello1.png
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
60
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
61 What just happened?
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
62
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
63 #. TinCan found the ``hello.pspx`` file.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
64
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
65 #. In that file, it found no special header directives. It also found no ``hello.py`` file.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
66
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
67 #. So TinCan fell back to its default behavior, and created an instance of the ``tincan.Page`` class (the base class of all code-behind logic), and associated the template code in ``hello.pspx`` with it.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
68
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
69 #. The ``${page.request.environ['PATH_INFO']}`` expression references objects found in that standard page object to return the part of the HTTP request path that is being interpreted as a route within the webapp itself.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
70
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
71 ==============================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
72 Adding Some Content of Our Own
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
73 ==============================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
74
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
75 Use Control-C to force the test server to quit, then add two more files. First,
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
76 a template, ``hello2.pspx``::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
77
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
78 <!DOCTYPE html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
79 <html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
80 <head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
81 <title>Hello</title>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
82 </head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
83 <body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
84 <h1>Hello Again, World</h1>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
85 <p>This page was called on the route ${page.request.environ['PATH_INFO']}.</p>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
86 <p>The current time on the server is ${time}.</p>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
87 </body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
88 </html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
89
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
90 Next, some code-behind in ``hello2.py``::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
91
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
92 import time
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
93 import tincan
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
94
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
95 class Hello2(tincan.Page):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
96 def handle(self):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
97 self.time = time.ctime()
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
98
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
99 This time, when you launch the test server, you should notice a second route
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
100 being created::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
101
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
102 $ ./launch demo
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
103 adding route: /hello.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
104 adding route: /hello2.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
105 Bottle v0.12.16 server starting up (using WSGIRefServer())...
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
106 Listening on http://localhost:8080/
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
107 Hit Ctrl-C to quit.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
108
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
109 When you visit the new page, you should see something like this:
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
110
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
111 .. image:: hello2.png
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
112
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
113 What new happened this time?
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
114
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
115 #. You created a code-behind file with the same name as its associated template (only the extensions differ).
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
116
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
117 #. Because the file names match, TinCan deduced that the two files were related, one containing the code-behind for the associated template.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
118
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
119 #. TinCan looked in the code-behind file for a something subclassing ``tincan.Page``, and created an instance of that class, complete with the standard ``request`` and ``response`` instance variables.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
120
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
121 #. It then called the ``handle(self)`` method of that class, which defined yet another instance variable.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
122
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
123 #. Because that instance variable *did not* start with an underscore, TinCan considered it to be exportable, and exported it as a template variable.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
124
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
125 Suppose you had written the following code-behind instead::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
126
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
127 from time import ctime
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
128 from tincan import Page
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
129
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
130 class Hello2(Page):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
131 def handle(self):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
132 self.time = ctime()
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
133
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
134 Every class is a subclass of itself, so what stops TinCan from getting all confused now that there are two identifiers in this module, ``Page`` and ``Hello2``, both of which are subclasses of ``tincan.Page``? For that matter, what if you had defined your own subclass of ``tincan.Page`` and subclassed it further, e.g.::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
135
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
136 from time import ctime
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
137 from mymodule import MyPage
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
138
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
139 class Hello2(MyPage):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
140 def handle(self):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
141 self.time = ctime()
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
142
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
143 The answer is that the code would still have worked (you might want to try the first example above just to prove it). When deciding what class to use, TinCan looks for *the deepest subclass of tincan.Page it can find* in the code-behind. So in a code-behind file that contains both a reference to ``tincan.Page`` itself, and a subclass of ``tincan.Page``, the subclass will always "win." Likewise, a subclass of a subclass will always "win" over a direct subclass of the parent class.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
144
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
145 A code-behind file need not share the same file name as its associated
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
146 template. If you use the ``#python`` header directive, you can tell TinCan
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
147 exactly which code-behind file to use. For example::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
148
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
149 #python other.py
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
150 <!DOCTYPE html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
151 <html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
152 <head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
153 <title>Hello</title>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
154 </head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
155 <body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
156 <h1>Hello Again, World</h1>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
157 <p>This page was called on the route ${page.request.environ['PATH_INFO']}.</p>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
158 <p>The current time on the server is ${time}.</p>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
159 </body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
160 </html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
161
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
162 What happens if you edit ``hello2.pspx`` to look like the above but *do not* rename ``hello2.py`` to ``other.py``? What happens if you make ``hello2.pspx`` run with ``hello.py`` (and vice versa)? Try it and see!
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
163
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
164 \(Note that it is generally not a good idea to gratuitously name things inconsistently, as it makes it hard for other people — or even you, at a later time — to easily figure out what is going on.\)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
165
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
166 ===========
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
167 Error Pages
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
168 ===========
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
169
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
170 If you tried the exercises in the paragraph immediately above, some of them let you see what happens when something goes wrong in TinCan. What if that standard error page is not to your liking, and you want to display something customized? TinCan lets you do that. Create a file called ``500.pspx``::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
171
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
172 #errors 500
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
173 <!DOCTYPE html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
174 <html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
175 <head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
176 <title>${error.status_line}</title>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
177 </head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
178 <body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
179 <h1>${error.status_line}</h1>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
180 <p>How embarrassing! It seems there was an internal error serving
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
181 <kbd>${request.url}</kbd></p>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
182 </body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
183 </html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
184
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
185 Now when you visit a page that doesn't work due to server-side errors (such as a template referencing an undefined variable), you'll see something like:
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
186
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
187 .. image:: 500.png
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
188
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
189 Error pages work a little differently from normal pages. They are based on subclasses of ``tincan.ErrorPage``. You get two standard template variables "for free:" ``error``, containing an instance of a ``bottle.HTTPError`` object pertaining to the error, and ``request``, a ``bottle.HTTPRequest`` object pertaining to the request that triggered the error.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
190
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
191 It is not as common for error pages to have code-behind as it is for normal, non-error pages, but it is possible and allowed. As alluded to above, the class to subclass is ``tincan.ErrorPage``; such classes have a ``handle()`` method which may define instance variables which get exported to the associated template using the same basic rules as for normal pages.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
192
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
193 If an error page itself causes an error, TinCan (the underlying Bottle framework, actually) will ignore it and display a fallback error page. This is to avoid triggering an infinite loop.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
194
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
195 ====================================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
196 Index Pages and Server-Side Forwards
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
197 ====================================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
198
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
199 Remove any ``#python`` header directives you added to ``hello.pspx`` and ``hello2.pspx`` during your previous experiments and create ``index.pspx``::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
200
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
201 #forward hello.pspx
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
202
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
203 That's it, just one line! When you launch the modified webapp, you should notice a couple new routes being created::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
204
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
205 $ launch demo
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
206 adding route: /hello.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
207 adding route: /hello2.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
208 adding route: /index.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
209 adding route: / (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
210 Bottle v0.12.16 server starting up (using WSGIRefServer())...
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
211 Listening on http://localhost:8080/
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
212 Hit Ctrl-C to quit.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
213
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
214 And if you navigate to ``http://localhost:8080/``, you should see our old friend the first page we created in this tutorial.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
215
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
216 Just like how a web server recognizes ``index.html`` as special, and routes requests for its containing directory to it, TinCan recognizes ``index.pspx`` as special.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
217
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
218 ``#forward`` creates what is known as a *server-side forward*. Unlike with an HTTP forward, all the action happens on the server side. The client never sees any intermediate 3xx response and never has to make a second HTTP request to resolve the URL. Any TinCan template file containing a ``#forward`` header directive will have *everything else in it* ignored, and otherwise act as if it were an exact copy of the route referenced in the ``#forward`` header.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
219
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
220 It is permitted for a page to ``#forward`` to another ``#forward`` page (but you probably should think twice before doing this). ``#forward`` loops are not permitted and attempts to create them will be rejected and cause an error message at launch time.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
221
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
222 =====================================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
223 Form Data and Requests Other Than GET
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
224 =====================================
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
225
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
226 As a final example, here's a sample page, ``name.pspx``, that processes form data via a POST request, and which uses some `Chameleon TAL <https://chameleon.readthedocs.io/en/latest/reference.html#basics-tal>`_ to render and style the response page, call it::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
227
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
228 #rem A page that accepts both GET and POST requests
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
229 #methods GET POST
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
230 <!DOCTYPE html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
231 <html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
232 <head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
233 <title>Form Test</title>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
234 </head>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
235 <body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
236 <h1>Form Test</h1>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
237 <h2>Enter Your Name Below</h2>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
238 <form method="post">
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
239 <input type="text" name="name"/>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
240 <input type="submit" value="Submit"/>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
241 </form>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
242 <if tal:condition="message is not None" tal:omit-tag="True">
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
243 <h2 tal:content="message.subject"></h2>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
244 <p tal:attributes="style message.style" tal:content="message.body"></p>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
245 </if>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
246 </body>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
247 </html>
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
248
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
249 Here's the code-behind for that page::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
250
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
251 from tincan import Page
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
252 from jsdict import JSDict
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
253
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
254 class Name(Page):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
255 def handle(self):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
256 self._error_message = JSDict({"subject": "Error", "style": "color: red;"})
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
257 if "name" not in self.request.forms:
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
258 if self.request.method == "GET":
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
259 self.message = None
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
260 else:
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
261 self.message = self.error("This should not happen!")
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
262 else:
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
263 name = self.request.forms["name"]
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
264 if name.strip() == "":
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
265 self.message = self.error("Please enter your name above.")
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
266 else:
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
267 self.message = JSDict({
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
268 "subject": "Hello, {0}".format(name.split()[0]),
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
269 "style": None,
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
270 "body": "Pleased to meet you!"
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
271 })
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
272
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
273 def error(self, message):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
274 self._error_message.body = message
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
275 return self._error_message
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
276
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
277 This example requires you to create a third file, in ``WEB-INF/lib/jsdict.py``::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
278
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
279 class JSDict(dict):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
280 def __getattr__(self, name):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
281 return self[name]
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
282
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
283 def __setattr__(self, name, value):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
284 self[name] = value
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
285
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
286 def __delattr__(self, name):
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
287 del self[name]
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
288
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
289 ``WEB-INF/lib`` is the directory where webapp-specific library routines live. It gets added to ``sys.path`` automatically when your webapp is launched.
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
290
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
291 This example introduces two new header directives, ``#rem`` and ``#methods``. The former introduces a remark (comment); the latter specifies the request methods that this page will respond to. Not specifying a ``#methods`` line is equivalent to specifying ``#methods GET``. When you launch the webapp after adding this new page, you shound notice TinCan announce that it is creating a route that supports both GET and POST requests for it::
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
292
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
293 $ launch demo
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
294 adding route: /hello.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
295 adding route: /hello2.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
296 adding route: /index.pspx (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
297 adding route: / (GET)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
298 adding route: /name.pspx (GET,POST)
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
299 Bottle v0.12.16 server starting up (using WSGIRefServer())...
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
300 Listening on http://localhost:8080/
682cd33e564c Documentation (incomplete).
David Barts <n5jrn@me.com>
parents:
diff changeset
301 Hit Ctrl-C to quit.