summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--homepage/pages/docs.md388
1 files changed, 297 insertions, 91 deletions
diff --git a/homepage/pages/docs.md b/homepage/pages/docs.md
index b755670..9c86dff 100644
--- a/homepage/pages/docs.md
+++ b/homepage/pages/docs.md
@@ -1,15 +1,29 @@
[TOC]
-Bottle Documentation
-====================
+ [apache]: http://www.apache.org/
+ [cherrypy]: http://www.cherrypy.org/
+ [decorator]: http://docs.python.org/glossary.html#term-decorator
+ [fapws3]: http://github.com/william-os4y/fapws3
+ [flup]: http://trac.saddi.com/flup
+ [http_code]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ [http_method]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+ [mako]: http://www.makotemplates.org/
+ [mod_wsgi]: http://code.google.com/p/modwsgi/
+ [paste]: http://pythonpaste.org/
+ [wsgi]: http://www.wsgi.org/wsgi/
-This document is a work in progress. If you have questions not answered here,
-please file a ticket at bottle's [issue
-tracker](http://github.com/defnull/bottle/issues).
-## Basic Routes
+# Bottle Documentation
-Routes are used to map request URLs to callables that generate the response content. Bottle has a `route()` decorator to do that.
+__This document is a work in progress__ and intended to be a tutorial, howto and an api documentation at the same time. If you have questions not answered here,
+please file a ticket at bottles [issue tracker](http://github.com/defnull/bottle/issues).
+
+
+
+
+## "Hello World" in a Bottle
+
+Lets start with a very basic example: Hello World
#!Python
from bottle import route, run
@@ -18,50 +32,89 @@ Routes are used to map request URLs to callables that generate the response cont
return "Hello World!"
run() # This starts the HTTP server
-Run this script, visit <http://localhost:8080/hello> and you will see "Hello World!" in your Browser.
+Run this script, visit <http://localhost:8080/hello> and you will see "Hello World!" in your Browser. So, what happened here?
+
+ 1. First we imported some bottle components. The `route()` decorator and the `run()` function.
+ 2. The `route()` [decorator][] is used do bind a piece of code to an URL. In this example we want to answer requests to the `/hello` URL.
+ 3. This function will be called every time someone hits the `/hello` URL on the web server. It is called a __handler function__.
+ 4. The return value of a handler function will be sent back to the Browser.
+ 5. Now it is time to start the actual HTTP server. The default is a development server running on *localhost* port *8080* and serving requests until you hit __Ctrl-C__
-### GET, POST, HEAD, ...
-The route decorator has an optional keyword argument `method` which defaults to `method='GET'`.
-Possible values are `POST`, `PUT`, `DELETE`, `HEAD` or any other HTTP request method you want to listen to.
+# Routing
+
+Routes are used to map URLs to __handler callbacks__ that generate the content for the specific URL. Bottle has a `route()` decorator to do that. You can add any number of routes to a callback.
+
+ #!Python
+ from bottle import route
+ @route('/')
+ @route('/index.html')
+ def index():
+ return "<a href='/hello'>Go to Hello World page</a>"
+
+ @route('/hello')
+ def hello():
+ return "Hello World!"
+
+As you can see, URLs and routes have nothing to do with actual files on the web server. Routes are unique names for your handler callbacks, nothing more and nothing less.
+
+URLs not matching any routes are answered by a 404 error. Exceptions within your handler callbacks will cause 500 errors.
+
+
+
+
+
+## Request Methods
+
+The `route()` decorator has an optional keyword argument `method` which defaults to `method='GET'`, so only GET requests get answered.
+Possible values are `POST`, `PUT`, `DELETE`, `HEAD` or any other [HTTP request method][http_method] you want to listen to.
#!Python
from bottle import route, request
@route('/form/submit', method='POST')
def form_submit():
- form_data = request.POST
- do_something(form_data)
+ form_data = request.POST # see "GET and POST values"
+ do_something_with(form_data)
return "Done"
-
-
-
+In this example we used `request.POST` which is described [here](#get-and-post-values)
## Dynamic Routes
-You can extract parts of the URL and create dynamic routes with an easy syntax.
+Static routes are fine, but URLs may carry information as well. Let's add a `:name` placeholder to our route.
#!Python
+ from bottle import route
@route('/hello/:name')
def hello(name):
return "Hello %s!" % name
-By default, a `:placeholder` matches everything up to the next
-slash. To change that, you can add some regular expression in between `#`s:
+This dynamic route matches `/hello/alice` as well as `/hello/bob`. In fact, the `:name` part of the route matches everything but a slash (`/`), so any name is possible. `/hello/bob/and/alice` or `/hellobob` won't match.
+
+Each part of the URL covered by a placeholder is provided to your handler callback as a keyword parameter.
+
+
+
+
+### Regular Expressions
+
+The default placeholder matches everything up to the next slash. To change that, you can add some regular expression:
#!Python
+ from bottle import route
@route('/get_object/:id#[0-9]+#')
def get(id):
return "Object ID: %d" % int(id)
-or even use full features regular expressions with named groups:
+or even use full featured regular expressions with named groups:
#!Python
+ from bottle import route
@route('/get_object/(?P<id>[0-9]+)')
def get(id):
return "Object ID: %d" % int(id)
@@ -70,12 +123,14 @@ As you can see, URL parameters remain strings, even if they are
configured to only match digits. You have to explicitly cast them into
the type you need.
-### The @validate() decorator
+
+
+
+## The @validate() decorator
Bottle offers a handy decorator called `validate()` to check and manipulate URL parameters.
-It takes callables as keyword arguments and filters every URL parameter
-through the corresponding callable before they are passed to your
-request handler.
+It takes callables (function or class objects) as keyword arguments and filters every URL parameter
+through the corresponding callable before they are passed to your request handler.
#!Python
from bottle import route, validate
@@ -85,54 +140,103 @@ request handler.
def validate_test(i, f, csv):
return "Int: %d, Float:%f, List:%s" % (i, f, repr(csv))
-You may raise `ValueError` in your custom callable if the parameter
+You may raise `ValueError` in your custom callable if a parameter
does not validate.
+# Generating content
+
+TODO
+
+
+
+## Output Casting
+
+The [WSGI specification][wsgi] expects an iterable list of byte strings to be returned from your application and can't handle file objects, unicode, dictionaries or exceptions.
+
+ #!Python
+ from bottle import route
+ @route('/wsgi')
+ def wsgi():
+ return ['WSGI','wants a','list of','strings']
+
+Bottle automatically tries to convert anything to a WSGI supported type, so you
+don't have to. The following examples will work with Bottle, but won't work with
+pure WSGI.
-## Returning files and JSON
+### Strings and Unicode
-The WSGI specification expects an iterable list of strings and can't handle file
-objects or plain strings. Bottle automatically converts them to iterables, so
-you don't have to. The following example will work with Bottle, but won't work
-with pure WSGI.
+Returning strings (bytes) is not a problem. Unicode however needs to be encoded into a byte stream before
+the webserver can send it to the client. Ths default encoding is utf-8, so if that fits your needs, you can
+simply return unicode or unicode iterables.
#!Python
- @route('/get_string')
+ from bottle import route, response
+ @route('/string')
def get_string():
- return "This is not a list of strings, but a single string"
+ return 'Bottle converts strings to iterables'
+
+ @route('/unicode')
+ def get_unicode():
+ return u'Unicode is encoded with UTF-8 by default'
+
+You can change Bottles default encoding by setting `response.content_type` to a value
+containing a `charset=...` parameter or by changing `response.charset` directly.
+
+ #!Python
+ from bottle import route, response
+ @route('/iso')
+ def get_iso():
+ response.charset = 'ISO-8859-15'
+ return u'This will be sent with ISO-8859-15 encoding.'
+
+ @route('/latin9')
+ def get_latin():
+ response.content_type = 'text/html; charset=latin9'
+ return u'ISO-8859-15 is also known as latin9.'
+
+In some rare cases the Python encoding names differ from the names supported by the HTTP specification.
+Then, you have to do both: First set the `response.content_type` header (which is sent to the client
+unchanged) and then set the `response.charset` option (which is used to decode unicode).
+
+
+### File Objects and Streams
+
+Bottle wrapps everything that has a `read()` method (file objects) with the `wsgi.file_wrapper`
+provided by your WSGI server implementation. This wrapper should use highly optimised system calls for
+your operating system (`sendfile` on UNIX) to transfer the file.
+
+ #!Python
@route('/file')
def get_file():
return open('some/file.txt','r')
+
+
+### JSON
+
Even dictionaries are allowed. They are converted to
[json](http://de.wikipedia.org/wiki/JavaScript_Object_Notation) and returned
-as `Content-Type: application/json`.
+with `Content-Type` header set to `application/json`. To disable this feature (and pass dicts to your
+middleware) you can set `bottle.default_app().autojson` to `False`.
#!Python
@route('/api/status')
def api_status():
return {'status':'online', 'servertime':time.time()}
-You can turn off this feature: `bottle.default_app().autojson = False`
-## HTTP errors and redirects
- #!Python
- from bottle import redirect, abort
-
- @route('/wrong/url')
- def wrong():
- redirect("/right/url")
-
- @route('/restricted')
- def restricted():
- abort(401, "Sorry, access denied.")
+### Static Files
-## Static files
+You can directly return file objects, but `bottle.send_file()` is the recommended way to serve static files.
+It automatically guesses a mime-type, adds a `Last-Modified` header, restricts paths to a `root` directory
+for security reasons and generates appropriate error pages (401 on permission errors, 404 on missing files). It
+even supports the `If-Modified-Since` header and eventually generates a `304 Not modified` response.
+You can pass a custom mimetype to disable mimetype guessing.
#!Python
from bottle import send_file
@@ -141,6 +245,45 @@ You can turn off this feature: `bottle.default_app().autojson = False`
def static_file(filename):
send_file(filename, root='/path/to/static/files')
+ @route('/images/:filename#.*\.png#')
+ def static_image(filename):
+ send_file(filename, root='/path/to/image/files', mimetype='image/png')
+
+
+
+
+## HTTP Errors and Redirects
+
+The `bottle.abort(code[, message])` function is used to generate [HTTP error pages][http_code].
+
+ #!Python
+ from bottle import route, redirect, abort
+ @route('/restricted')
+ def restricted():
+ abort(401, "Sorry, access denied.")
+
+To redirect a client to a different URL, you can send a `307 Temporary Redirect` response
+with the `Location` header set to the new URL. `bottle.redirect(url[, code])` does that for you.
+You may provide a different HTTP status code as a second parameter.
+
+ #!Python
+ from bottle import route, redirect, abort
+ @route('/wrong/url')
+ def wrong():
+ redirect("/right/url")
+
+Both functions interrupt your handler code (by throwing a `bottle.HTTPError` exception) so you don't
+have to return anything.
+
+All unhandled exceptions other than `bottle.HTTPError` will result in a `500 Internal Server Error`
+response, so they won't crash your WSGI server.
+
+
+
+# HTTP Stuff
+
+TODO
+
## Cookies
@@ -164,13 +307,14 @@ To set the `max-age` attribute (which is not a valid Python parameter name) you
+## GET and POST values
+TODO
-
-## Templates
+# Templates
Bottle uses its own little template engine by default. You can use a template by
calling `template(template_name, **template_arguments)` and returning
@@ -189,23 +333,22 @@ The `hello_template.tpl` file could look like this:
<h1>Hello {{username}}</h1>
<p>How are you?</p>
-### Template search path
-The list `bottle.TEMPLATE_PATH` is used to map template names to actual
-file names. By default, this list contains `['./%s.tpl', './views/%s.tpl']`.
-### Template caching
-
-Templates are cached in memory after compilation. Modifications made to
-the template file will have no affect until you clear the template
-cache. Call `bottle.TEMPLATES.clear()` to do so.
+## Template search path
+The list `bottle.TEMPLATE_PATH` is used to map template names to actual
+file names. By default, this list contains `['./%s.tpl', './views/%s.tpl']`.
+## Template caching
+Templates are cached in memory after compilation. Modifications made to
+the template file will have no affect until you clear the template
+cache. Call `bottle.TEMPLATES.clear()` to do so.
@@ -250,16 +393,9 @@ Example:
+# Key/Value Databases
-
-
-
-
-
-
-
-
-## Key/Value Databases
+<div style="color:darkred">Warning: The included key/value database is depreciated.</div> Please switch to a [real](http://code.google.com/p/redis/) [key](http://couchdb.apache.org/) [value](http://www.mongodb.org/) [database](http://docs.python.org/library/anydbm.html).
Bottle (>0.4.6) offers a persistent key/value database accessible through the
`bottle.db` module variable. You can use key or attribute syntax to store or
@@ -277,14 +413,20 @@ Printing a bucket object doesn't print the keys and values, and the
`items()` and `values()` methods are not supported. Missing keys will raise
`KeyError` as expected.
-### Persistence
+
+
+
+## Persistence
During a request live-cycle, all changes are cached in thread-local memory. At
the end of the request, the changes are saved automatically so the next request
will have access to the updated values. Each bucket is stored in a separate file
in `bottle.DB_PATH`. Be sure to allow write-access to this path and use bucket
names that are allowed in filenames.
-### Race conditions
+
+
+
+## Race conditions
You don't have do worry about file corruption but race conditions are still a
problem in multi-threaded or forked environments. You can call
`bottle.db.save()` or `botle.db.bucket_name.save()` to flush the thread-local
@@ -292,7 +434,10 @@ memory cache to disk, but there is no way to detect database changes made in
other threads until these threads call `bottle.db.save()` or leave the current
request cycle.
-### Example
+
+
+
+## Example
#!Python
from bottle import route, db
@@ -306,13 +451,7 @@ request cycle.
-
-
-
-
-
-
-## Using WSGI and Middleware
+# Using WSGI and Middleware
A call to `bottle.default_app()` returns your WSGI application. After applying as many WSGI middleware modules as you like, you can tell
`bottle.run()` to use your wrapped application, instead of the default one.
@@ -323,7 +462,10 @@ A call to `bottle.default_app()` returns your WSGI application. After applying a
newapp = YourMiddleware(app)
run(app=newapp)
-### How default_app() works
+
+
+
+## How default_app() works
Bottle creates a single instance of `bottle.Bottle()` and uses it as a default for most of the modul-level decorators and the `bottle.run()` routine.
`bottle.default_app()` returns (or changes) this default. You may, however, create your own instances of `bottle.Bottle()`.
@@ -337,7 +479,61 @@ Bottle creates a single instance of `bottle.Bottle()` and uses it as a default f
run(app=mybottle)
-## Deployment
+
+
+# Development
+Bottle has two features that may be helpfull during development.
+
+## Debug Mode
+
+In debug mode, bottle is much more verbose and tries to help you finding
+bugs. You should never use debug mode in production environments.
+
+ #!Python
+ import bottle
+ bottle.debug(True)
+
+This does the following:
+
+ * Exceptions will print a stacktrace
+ * Error pages will contain that stacktrace
+ * Templates will not be cached.
+
+
+
+
+## Auto Reloading
+
+During development, you have to restart the server a lot to test your
+recent changes. The auto reloader can do this for you. Every time you
+edit a module file, the reloader restarts the server process and loads
+the newest version of your code.
+
+ #!Python
+ from bottle import run
+ run(reloader=True)
+
+How it works: The main process will not start a server, but spawn a new
+child process using the same command line agruments used to start the
+main process. All module level code is executed at least twice! Be
+carefull.
+
+The child process will have `os.environ['BOTTLE_CHILD']` set to `true`
+and start as a normal non-reloading app server. As soon as any of the
+loaded modules changes, the child process is terminated and respawned by
+the main process. Changes in template files will not trigger a reload.
+Please use debug mode to deactivate template caching.
+
+The reloading depends on the ability to stop the child process. If you are
+running on Windows or any other operating system not supporting
+`signal.SIGINT` (which raises `KeyboardInterrupt` in Python),
+`signal.SIGTERM` is used to kill the child. Note that exit handlers and
+finally clauses, etc., are not executed after a `SIGTERM`.
+
+
+
+
+# Deployment
Bottle uses the build-in `wsgiref.SimpleServer` by default. This non-threading
HTTP server is perfectly fine for development and early production,
@@ -349,7 +545,10 @@ There are three ways to eliminate this bottleneck:
* Spread the load between multiple bottle instances
* Do both
-### Multi-Threaded Server
+
+
+
+## Multi-Threaded Server
The easiest way to increase performance is to install a multi-threaded and
WSGI-capable HTTP server like [Paste][paste], [flup][flup], [cherrypy][cherrypy]
@@ -369,11 +568,14 @@ the server settings, you may want to manually set up your HTTP server and use
from paste import httpserver
httpserver.serve(myapp, host=host, port=port)
-### Multiple Server Processes
-A single Python process can only utilize one CPU at a time, even if
+
+
+## Multiple Server Processes
+
+A single Python process can only utilise one CPU at a time, even if
there are more CPU cores available. The trick is to balance the load
-between multiple independent Python processes to utilize all of your
+between multiple independent Python processes to utilise all of your
CPU cores.
Instead of a single Bottle application server, you start one instances
@@ -395,7 +597,10 @@ One of the fastest load balancer available is [pound](http://www.apsis.ch/pound/
I'll add examples for [lighttpd](http://www.lighttpd.net/) and
[Apache](http://www.apache.org/) web servers soon.
-### Apache mod_wsgi
+
+
+
+## Apache mod_wsgi
Instead of running your own HTTP server from within Bottle, you can
attach Bottle applications to an [Apache server][apache] using
@@ -434,7 +639,12 @@ The Apache configuration may look like this:
</VirtualHost>
-### Google AppEngine
+
+
+## Google AppEngine
+
+I didn't test this myself but several Bottle users reported that this
+works just fine.
#!Python
import bottle
@@ -443,7 +653,10 @@ The Apache configuration may look like this:
# Do NOT use bottle.run() with AppEngine
util.run_wsgi_app(bottle.default_app())
-### Good old CGI
+
+
+
+## Good old CGI
CGI is slow as hell, but it works.
@@ -452,10 +665,3 @@ CGI is slow as hell, but it works.
# ... add or import your bottle app code here ...
bottle.run(server=bottle.CGIServer)
-[mako]: http://www.makotemplates.org/
-[cherrypy]: http://www.cherrypy.org/
-[flup]: http://trac.saddi.com/flup
-[paste]: http://pythonpaste.org/
-[fapws3]: http://github.com/william-os4y/fapws3
-[apache]: http://www.apache.org/
-[mod_wsgi]: http://code.google.com/p/modwsgi/