Commits

Anonymous committed 463e625 Merge

Merge.

  • Participants
  • Parent commits 28fe8ec, 964610c
  • Branches trunk

Comments (0)

Files changed (41)

 - implemented `find_modules`
 - refactored request and response objects into base objects, mixins and
   full featured subclasses that implement all mixins.
+- werkzeug's routing raises `MethodNotAllowed` now if it matches a
+  rule but for a different method.
 
 
 Version 0.1

File docs/src/api_stability.txt

 change over time.  The following modules will very likely change in one of
 the next release versions:
 
-`werkzeug.routing`
-    Currently the path converter does not sort properly and we will change
-    the way sorting works there.  Try to avoid `path` as much as possible
-    for the time being.
-
 `werkzeug.contrib`
     The community-contributed modules are yet undocumented and we expect
     some upcoming changes there.
         this 302 code, use ``response = redirect(e.new_url, 302)``.
     -   `lazy_property` is now called `cached_property`.  The alias for
         the old name will disappear in Werkzeug 0.3.
+    -   `match` can now raise `MethodNotAllowed` if configured for
+        methods and there was no method for that request.

File docs/src/debug.txt

 
     application = DebuggedApplication(application, evalex=True)
     
-    run_simple('localhost', 4000, application)
+    run_simple('localhost', 5000, application)
 
-This code spawns a debugging server on `localhost:4000` with the debugger
+This code spawns a debugging server on `localhost:5000` with the debugger
 enabled.  If you set `evalex` to `False`, the debugger is disabled.
 
 .. warning::

File docs/src/exceptions.txt

 HTTP Exceptions
 ===============
 
-The `werkzeug.exceptions` module implements a number of Python exceptions you
-can raise from within your views to trigger a standard, non-200 response.
-
-
-Usage Example
-=============
-
-.. sourcecode:: python
-
-    from werkzeug import BaseRequest, responder
-    from werkzeug.exceptions import HTTPException, NotFound
-
-    def view(request):
-        raise NotFound()
-
-    @responder
-    def application(environ, start_response):
-        request = BaseRequest(environ)
-        try:
-            return view(request)
-        except HTTPException, e:
-            return e
-
-
-As you can see from this example, those exceptions are callable WSGI
-applications.  Because of Python 2.3/2.4 compatibility, those do not extend
-from the response objects, but only from the Python exception class.
-
-As a matter of fact they are not Werkzeug response objects.  However, you
-can get a response object by calling ``get_response()`` on a HTTP exception.
-
-Keep in mind that you have to pass an environment to ``get_response()``
-because some errors fetch additional information from the WSGI environment.
-
-If you want to hook in a different exception page to, say, an 404 status code,
-you can add a second `except` statement for a specific subclass of an error:
-
-.. sourcecode:: python
-
-    @responder
-    def application(environ, start_response):
-        request = BaseRequest(environ)
-        try:
-            return view(request)
-        except NotFound, e:
-            return not_found(request)
-        except HTTPException, e:
-            return e
+.. module:: werkzeug.exceptions
 
 
 Error Classes
 
 The following error classes exist in Werkzeug:
 
-*400* `BadRequest`
-    Raise if the browser sent something to the application the application or
-    server cannot handle.
+.. class:: werkzeug.exceptions.BadRequest
+.. class:: werkzeug.exceptions.Unauthorized
+.. class:: werkzeug.exceptions.Forbidden
+.. class:: werkzeug.exceptions.NotFound
+.. class:: werkzeug.exceptions.MethodNotAllowed
+.. class:: werkzeug.exceptions.NotAcceptable
+.. class:: werkzeug.exceptions.RequestTimeout
+.. class:: werkzeug.exceptions.Gone
+.. class:: werkzeug.exceptions.LengthRequired
+.. class:: werkzeug.exceptions.PreconditionFailed
+.. class:: werkzeug.exceptions.RequestEntityTooLarge
+.. class:: werkzeug.exceptions.RequestURITooLarge
+.. class:: werkzeug.exceptions.UnsupportedMediaType
+.. class:: werkzeug.exceptions.InternalServerError
+.. class:: werkzeug.exceptions.NotImplemented
+.. class:: werkzeug.exceptions.BadGateway
+.. class:: werkzeug.exceptions.ServiceUnavailable
 
-*401* `Unauthorized`
-    Raise if the user is not authorized.  Also used if you want to use HTTP
-    basic auth.
 
-*403* `Forbidden`
-    Raise if the user doesn't have the permission for the requested resource
-    but was authenticated.
+Baseclass
+=========
 
-*404* `NotFound`
-    Raise if a resource does not exist and never existed.
+All the exceptions implement this common interface:
 
-*405* `MethodNotAllowed`
-    Raise if the server used a method the resource does not handle.  For
-    example, `POST` if the resource is view only.  Especially useful for REST.
+.. class:: werkzeug.exceptions.HTTPException
 
-    The first argument for this exception should be a list of allowed methods.
-    Strictly speaking, the response would be invalid if you don't provide
-    valid methods in the header, which is what you can do with that list.
+    .. def:: werkzeug.exceptions.HTTPException.get_response
+    .. def:: werkzeug.exceptions.HTTPException.__call__
 
-*406* `NotAcceptable`
-    Raise if the server can't return any content conforming to the `Accept`
-    headers of the client.
 
-*408* `RequestTimeout`
-    Raise to signalize a timeout.
+Simple Aborting
+===============
 
-*410* `Gone`
-    Raise if a resource existed previously and went away without new location.
+Sometimes it's convenient to just raise an exception by the error code,
+without importing the exception and looking up the name etc.  For this
+purpose there is the `abort` function.
 
-*411* `LengthRequired`
-    Raise if the browser submitted data but no `Content-Length` header which
-    is required for the kind of processing the server does.
+It can be passed a WSGI application or a status code.  If a status code
+is given it's looked up in the list of exceptions from above and will
+raise that exception, if passed a WSGI application it will wrap it in
+a proxy WSGI exception and raise that::
 
-*412* `PreconditionFailed` 
-    Status code used in combination with `If-Match`, `If-None-Match`, or
-    `If-Unmodified-Since`.
+    abort(404)
+    abort(Response('Hello World'))
 
-*413* `RequestEntityTooLarge`
-    The status code one should return if the data submitted exceeded a given
-    limit.
+If you want to use this functionality with custom excetions you can
+create an instance of the aborter class:
 
-*414* `RequestURITooLarge`
-    Like *413*, but for too long URLs.
-
-*415* `UnsupportedMediaType`
-    The status code returned if the server is unable to handle the media type
-    the client transmitted.
-
-*500* `InternalServerError`
-    Raise if an internal server error occured.  This is a good fallback if an
-    unknown error occured in the dispatcher.
-
-*501* `NotImplemented`
-    Raise if the application does not support the action requested by the
-    browser.
-
-*502* `BadGateway`
-    If you do proxing in your application, you should return this status code
-    if you received an invalid response from the upstream server it accessed
-    in attempting to fulfill the request.
-
-*503* `ServiceUnavailable`
-    The status code you should return if a service is temporarily unavailable.
+.. class:: werkzeug.exceptions.Aborter
 
 
 Custom Errors
 =============
 
 As you can see from the list above not all status codes are available as
-errors.  Especially redirects and other non-200 status codes that represent
-do not represent errors are missing.  For redirects you can use the `redirect`
-function from the utilities.
+errors.  Especially redirects and ather non 200 status codes that
+represent do not represent errors are missing.  For redirects you can use
+the `redirect` function from the utilities.
 
-If you want to add an error yourself you can subclass `HTTPException`:
-
-.. sourcecode:: python
+If you want to add an error yourself you can subclass `HTTPException`::
 
     from werkzeug.exceptions import HTTPException
 
         description = '<p>Payment required.</p>'
 
 This is the minimal code you need for your own exception.  If you want to
-add more logic to the errors, you can override the `get_description()`,
-`get_body()`, `get_headers()`, and `get_response()` methods.  In any case,
+add more logic to the errors you can override the `get_description()`,
+`get_body()`, `get_headers()` and `get_response()` methods.  In any case
 you should have a look at the sourcecode of the exceptions module.
 
-**New in Werkzeug 0.2** You can override the default description in the
-constructor with the `description` parameter (it's the first argument for
-all exceptions except of the `MethodNotAllowed` which accepts a list of
-allowed methods as first argument).
+You can override the default description in the constructor with the
+`description` parameter (it's the first argument for all exceptions
+except of the `MethodNotAllowed` which accepts a list of allowed methods
+as first argument)::
 
-.. sourcecode:: python
-
-    raise BadRequest('Request failed because the X parameter was not present.')
+    raise BadRequest('Request failed because X was not present')

File docs/src/index.txt

 Welcome to the Werkzeug documentation.
 
 
-.. admonition:: Note
-
-    The documentation reflects the status of Werkzeug 0.2 as of
-    HG revision `7ed8e75a17bc`.  Currently we do some code cleanup
-    in the mercurial repository, if you are using newer revisions
-    of Werkzeug you should participate in ``#pocoo`` to not miss
-    the latest changes.  Documentation will be update before the
-    release.
-
-
 Reference
 =========
 
-Documentation regarding the Werkzeug modules and Werkzeug as such.
+Documentation regarding the Werkzeug modules:
 
 - `Installing Werkzeug <installation.txt>`_
 
-- `Tutorial <tutorial.txt>`_ - getting started with Werkzeug by
-  implementing a simple `TinyURL`_ clone.
+- `Important Terms <terms.txt>`_ --- read this before reading the
+  documentation
 
-- `Important Terms <terms.txt>`_ - read this before reading the
-  Werkzeug documentation.
+- `Tutorial <tutorial.txt>`_ --- getting started with Werkzeug
 
-- `Wrappers <wrappers.txt>`_ - wrap `environ` and `start_response`
-  in request/response objects.
+- `Wrappers <wrappers.txt>`_ --- request and response objects
 
-- `Routing System <routing.txt>`_ - a powerful URL dispatcher
-  and builder.
+- `Routing System <routing.txt>`_ --- a powerful URL dispatcher
+  and builder
 
-- `Mini Templates <templates.txt>`_ - a minimal templating system.
+- `Mini Templates <templates.txt>`_ --- a minimal templating system
 
-- `Management Script Utilities <script.txt>`_ - tools to write simple
-  management scripts.
+- `Management Script Utilities <script.txt>`_ --- tools to write simple
+  management scripts
 
-- `Test Utilities <test.txt>`_ - send requests to your application
-  from the Python interpreter or unit tests.
+- `Test Utilities <test.txt>`_ --- unittest support tools
 
-- `HTTP Exceptions <exceptions.txt>`_ - exception classes that work
-  like response objects for non-200 HTTP status codes.
+- `HTTP Exceptions <exceptions.txt>`_ --- exceptions for HTTP
 
-- `Utilities <utils.txt>`_ - useful things like multi value dicts,
-  a data structure for headers etc.
+- `Utilities <utils.txt>`_ --- useful classes and functions
 
-- `Context Locals <local.txt>`_ - a WSGI centric version of `thread.local`.
+- `Context Locals <local.txt>`_ --- a WSGI centric version of `thread.local`.
 
-- `Debugging System <debug.txt>`_ - an interactive debugger.
+- `Debugging System <debug.txt>`_ --- an interactive debugger.
 
-- `API Stability <api_stability.txt>`_ - API stability
+- `API Stability <api_stability.txt>`_ --- API stability
 
 
 General Development Information
 applications.  This is also interesting if you don't want to use
 Werkzeug but other WSGI utilities; the ideas are the same.
 
-- `Serving WSGI Applications <serving.txt>`_ - serving WSGI applications.
+- `Serving WSGI Applications <serving.txt>`_ --- serving WSGI applications.
 
-- `How WSGI Works <wsgihowto.txt>`_ - short introduction to WSGI and
+- `How WSGI Works <wsgihowto.txt>`_ --- short introduction to WSGI and
   Werkzeug.
 
-- `Organizing Code <organizing.txt>`_ - gives you an idea how you can
+- `Organizing Code <organizing.txt>`_ --- gives you an idea how you can
   organize your code when using Werkzeug.
 
-- `Other Libraries <libraries.txt>`_ - links to other libraries you
+- `Other Libraries <libraries.txt>`_ --- links to other libraries you
   can use with Werkzeug.
 
-- `Deploying WSGI Applications <deploying.txt>`_ - ready for production?
+- `Deploying WSGI Applications <deploying.txt>`_ --- ready for production?
   This page covers all the details you have to know to deploy your
   application on various webservers.
 

File docs/src/libraries.txt

     lets you construct SQL queries using Python expressions.  It also provides
     connection pools and plays nicely with the WSGI standard.
 
+`Elixir <http://elixir.ematia.de/>`_
+    Elixir is a declarative layer on top of the SQLAlchemy library.  It is a
+    fairly thin wrapper, which provides the ability to create simple Python
+    classes that map directly to relational database tables (this pattern is
+    often referred to as the Active Record design pattern), providing many of
+    the benefits of traditional databases without losing the convenience of
+    Python objects.
+
+`Storm <https://storm.canonical.com/>`_
+    Storm is an object-relational mapper (ORM) for Python developed at
+    Canonical.  It has been in development for more than a year for use in
+    Canonical projects such as Launchpad, and has been released as an
+    open-source product.
 
 Template Engines
 ================
 
 Here some form validation packages for WSGI applications:
 
+`What The Forms <http://dev.simplecodes.com/projects/wtforms>`_
+    WTForms is a HTTP/HTML forms handling library, written in Python.  It
+    handles definition, validation and rendering in a flexible and i18n
+    friendly way. It heavily reduces boilerplate and is completely unicode
+    aware.
+
 `Newforms Extracted <http://code.google.com/p/newforms-extracted/>`_
     This is a project to extract Django's newforms and make that package
     usable by other projects, since Django doesn't seem interested in making
     Various small WSGI utilities like a minimal traceback or auth
     middleware.  It also includes an SCGI server.
 
-`paste <http://pythonpaste.org/>`_
+`Paste <http://pythonpaste.org/>`_
     Many tools for use with WSGI: dispatching, composition, simple
     applications (e.g., file serving), and more.
 
 `Routes <http://routes.groovie.org/>`_
     A port of the Rails URL mapping system.
 
+`Python OpenID <http://openidenabled.com/python-openid/>`_
+    The OpenID library with batteries included.
+
+`AuthKit <http://authkit.org/>`_
+    WSGI Authentication and Authorization Tools.  Built in support for
+    HTTP basic, HTTP digest, form, cookie and OpenID authentication methods
+    plus others.
+
 You can find a more complete list on the `wsgi.org`_ webpage.
 
 

File docs/src/local.txt

 Context Locals
 ==============
 
-Sooner or later you have some things you want to have in every single view or
-helper function or whatever.  In PHP, the way to go are global variables.
-However, that is not possible in WSGI applications without a major drawback:
-As soon as you operate on the global namespace your application is not
-thread-safe any longer.
+.. module:: werkzeug.local
 
-The Python standard library comes with an utility called "thread-locals".  A
-thread-local is a global object you can put stuff on and get back from it
-later in a thread-safe way.  That means whenever you set or get an object
-to/from a thread-local object, the thread-local object checks in which thread
-you are and delivers the correct value.
 
-This has a few disadvantages, though.  For example beside threads there are
-other ways to handle concurrency in Python.  A very popular approach are
-`greenlets`_.  And also if every request gets its own thread, it is not
-guaranteed in WSGI.  It could be that a request is reusing a thread from
-before and data is left in the thread-local object.
+Objects
+=======
 
-`werkzeug.local` fills this gap.
+.. class:: werkzeug.local.LocalManager
 
+    .. def:: werkzeug.local.LocalManager.cleanup
+    .. def:: werkzeug.local.LocalManager.make_middleware
+    .. def:: werkzeug.local.LocalManager.middleware
+    .. def:: werkzeug.local.LocalManager.get_ident
 
-Nutshell
-========
+.. class:: werkzeug.local.LocalProxy
 
-Here is a simple example on how you can use `werkzeug.local`:
+    Keep in mind that ``repr()`` is also forwarded, so if you want to find
+    out if you are dealing with a proxy you can do an ``isinstance()`` check:
 
-.. sourcecode:: python
+    .. sourcecode:: pycon
 
-    from werkzeug import Local, LocalManager
+        >>> from werkzeug import LocalProxy
+        >>> isinstance(request, LocalProxy)
+        True
 
-    local = Local()
-    local_manager = LocalManager([local])
-
-    def application(environ, start_response):
-        local.request = request = Request(environ)
-        # ...
-
-    application = local_manager.make_middleware(application)
-
-What this code does is binding request to `local.request`.  Every other piece
-of code executed after this assignment in the same context can safely access
-`local.request` and will get the same request object.  The `make_middleware`
-method on the local manager ensures that everything is cleaned up after the
-request.
-
-The same context means the same greenlet (if you're using greenlets) in the
-same thread and same process.
-
-If a request object is not yet set on the local object and you try to access
-it you will get an `AttributeError`.  You can use `getattr` to avoid that:
-
-.. sourcecode:: python
-
-    def get_request():
-        return getattr(local, 'request', None)
-
-This will try to get the request or return `None` if the request is not (yet?)
-available.
-
-
-Manager Objects
-===============
-
-Local objects cannot manage themselves. For that you need a local manager.
-You can pass a local manager multiple locals or add them later by appending
-them to `manager.locals`.  Everytime the manager cleans up it, will clean up
-all the data left in the locals for this context.
-
-The following methods exist:
-
-`cleanup()`
-    Manually clean up the data in the locals for this context.  Call this at
-    the end of the request or use `make_middleware()`.
-
-`make_middleware(app)`
-    Encapsulate the application and call `cleanup()` at the end of a request.
-
-`middleware()`
-    Like `make_middleware`, but works as decorator.  The main difference is
-    that the returned object will have the original docstring etc.
+    You can also create proxy objects by hand:
 
     .. sourcecode:: python
 
-        @manager.middleware
-        def application(environ, start_response):
-            # ...
-
-`get_ident()`
-    Return the context identifier the local objects use internally for this
-    context.  You cannot override this method to change the behavior but use
-    it to link other context local objects (such as SQLAlchemy's scoped
-    sessions) to the Werkzeug locals.
-
-
-Proxies
-=======
-
-Werkzeug can also create proxy objects for you.  A proxy object forwards nearly
-all operations to an object bound to an object on a local.  This is for example
-useful if you want a global request object:
-
-.. sourcecode:: python
-
-    from werkzeug import Local
-    local = Local()
-    request = local('request')
-    user = local('user')
-
-Whenever you access anything on `user` it will forward the operation to the
-object `local.user`.  This affects all operations except of any sort of
-assignment.
-
-Keep in mind that ``repr()`` is also forwarded, so if you want to find out if
-you are dealing with a proxy you can do an ``isinstance()`` check:
-
-.. sourcecode:: pycon
-
-    >>> from werkzeug import LocalProxy
-    >>> isinstance(request, LocalProxy)
-    True
-
-You can also create proxy objects by hand:
-
-.. sourcecode:: python
-
-    from werkzeug import Local, LocalProxy
-    local = Local()
-    request = LocalProxy(local, 'request')
-
-
-.. _greenlets: http://codespeak.net/py/dist/greenlet.html
+        from werkzeug import Local, LocalProxy
+        local = Local()
+        request = LocalProxy(local, 'request')

File docs/src/organizing.txt

 
 .. sourcecode:: python
 
+    from werkzeug import Request, Response, import_string
     from werkzeug.exceptions import HTTPException
     from werkzeug.routing import RequestRedirect
-    from mypackage.urls import map, not_found
-    from mypackage.utils import Request, Response
+    from mypackage.urls import url_map
 
     def application(environ, start_response):
-        urls = map.bind_to_environ(environ)
-        req = Request(environ, urls)
+        url_adapter = map.bind_to_environ(environ)
+        req = Request(environ)
         try:
-            endpoint, args = urls.match(req.path)
-            module, func = endpoint.split('/', 1)
-            mod = __import__('mypackage.views.' + module, None,
-                             None, [''])
-            view = getattr(mod, func)
+            endpoint, values = url_adapter.match()
+            view = import_string('mypackage.views.' + endpoint)
             resp = view(req, **args)
         except (RequestRedirect, HTTPException), e:
             resp = e
 explained in the routing documentation.
 
 This will look for the controller functions in `mypackage.views.module`.
-If the URL is configured with an endpoint of ``'static/index'``, the module
+If the URL is configured with an endpoint of ``'static.index'``, the module
 `mypackage.views.static` is loaded and `index(req)` is called.
 
 The URL rule parameters are passed to the function as keyword arguments then.
 .. sourcecode:: python
 
     from jinja import Environment, PackageLoader
-    from werkzeug import BaseRequest, BaseResponse
+    from werkzeug import Response
 
     env = Environment(loader=PackageLoader('mypackage', 'templates'))
 
-    class Request(BaseRequest):
-
-        def __init__(self, environ, urls):
-            self.environ = environ
-            self.urls = urls
-
-        def url_for(self, endpoint, **values):
-            return self.urls.build(endpoint, values)
-
-    class Response(BaseResponse):
-        pass
-
     class TemplateResponse(Response):
 
         def __init__(self, template_name, **values):
             tmpl = env.get_template(template_name)
             output = tmpl.render(values)
-            super(TemplateResponse, self).__init__(output, mimetype='text/html')
+            Response.__init__(self, output, mimetype='text/html')
 
 **Note**: Templates in this example are saved in the `templates` folder inside
 the `mypackage` package.  The way template loading and rendering works depends
 second response subclass that is used to render templates.  It's used in the
 example view below.
 
-``req.url_for`` can be used to get an URL to another view.
-
 
 URLs
 ====
 
     from werkzeug.routing import Map, Rule
 
-    map = Map([
-        Rule('/', 'static/index')
+    url_map = Map([
+        Rule('/', 'static.index')
     ])
 
 This is just one small URL rule for one view.
 with the help of Werkzeug.
 
 
-Generating Boilerplate Code
-===========================
-
-Getting the application started is usually a quite boring process because you
-write the same code again and again.  Because of that, Werkzeug comes with a
-tiny script that can generate a package structure from a package template.
-
-Have a look at the `bootstrap`_ documentation for more information about
-this topic.
-
-
 .. _SQLAlchemy: http://www.sqlalchemy.org/
 .. _routing system: routing.txt
 .. _Jinja: http://jinja.pocoo.org/
-.. _bootstrap: bootstrap.txt

File docs/src/routing.txt

         Rule('/<int:year>/', endpoint='blog/archive'),
         Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),
         Rule('/<int:year>/<int:month>/<int:day>/', endpoint='blog/archive'),
-        Rule('/<int:year>/<int:month>/<int:day>/<slug>', endpoint='blog/show_post'),
+        Rule('/<int:year>/<int:month>/<int:day>/<slug>',
+             endpoint='blog/show_post'),
         Rule('/about', endpoint='blog/about_me'),
         Rule('/feeds/', endpoint='blog/feeds'),
         Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
     >>> m = Map([
     ...     Rule('/', endpoint='index'),
     ...     Rule('/downloads/', endpoint='downloads/index'), 
-    ...     Rule('/downloads/<int:download_id>', endpoint='downloads/show')
+    ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
     ... ])
     >>> urls = m.bind("example.com", "/")
     >>> urls.match("/", "GET")
     ('index', {})
     >>> urls.match("/downloads/42")
-    ('downloads/show', {'download_id': 42})
+    ('downloads/show', {'id': 42})
 
 And here is what happens on redirect and missing URLs:
 
 
     >>> urls.match("/downloads")
     Traceback (most recent call last):
-      File "<stdin>", line 1, in <module>
-      File "werkzeug/routing.py", line 660, in match
-        path_info.lstrip('/')
+      ...
     werkzeug.routing.RequestRedirect: http://example.com/downloads/
     >>> urls.match("/missing")
     Traceback (most recent call last):
-      File "<stdin>", line 1, in <module>
-      File "werkzeug/routing.py", line 676, in match
-        raise NotFound(path_info)
+      ...
     werkzeug.routing.NotFound: /missing
 
 The second parameter to the `match` method can be a string that specifies
 
     >>> urls.build("index", {})
     '/'
-    >>> urls.build("downloads/show", {'download_id': 42})
+    >>> urls.build("downloads/show", {'id': 42})
     '/downloads/42'
-    >>> urls.build("downloads/show", {'download_id': 42}, force_external=True)
+    >>> urls.build("downloads/show", {'id': 42}, force_external=True)
     'http://example.com/downloads/42'
 
 Because URLs cannot contain non-ASCII data, you will always get byte strings

File docs/src/script.txt

 Management Script Utilities
 ===========================
 
-Most of the time you have recurring tasks while writing an application such as
-starting up an interactive Python interpreter with some prefilled imports,
-starting the development server, initializing the database or something
-similar.
 
-For that purpose Werkzeug provides the `werkzeug.script` module which helps
-you writing such scripts.
-
-
-Basic Usage
-===========
-
-The following snippet is roughly the same in every Werkzeug script:
-
-.. sourcecode:: python
-
-    #!/usr/bin/env python
-    # -*- coding: utf-8 -*-
-    from werkzeug import script
-
-    # actions go here
-
-    if __name__ == '__main__':
-        script.run()
-
-Starting this script now does nothing because no actions are defined yet.  An
-action is a function in the same module starting with ``"action_"`` which
-takes a number of arguments where every argument has a default.  The type of
-the default value specifies the type of the argument.
-
-Arguments can then be passed by position or using ``--name=value`` from the
-shell.
-
-Because a `runserver` and `shell` command is pretty common, there are two
-factory functions that create such commands:
-
-.. sourcecode:: python
-
-    def make_app():
-        from yourapplication import YourApplication
-        return YourApplication(...)
-
-    action_runserver = script.make_runserver(make_app, use_reloader=True)
-    action_shell = script.make_shell(lambda: {'app': make_app()})
-
-
-Using The Scripts
-=================
-
-The script from above can be used like this from the shell now::
-
-    $ ./manage.py --help
-    $ ./manage.py runserver localhost 8080 --debugger --no-reloader
-    $ ./manage.py runserver -p 4000
-    $ ./manage.py shell
-
-As you can see it's possible to pass parameters as positional arguments
-or as named parameters, pretty much like Python function calls.
-
-With Werkzeug 0.1, calling of boolean arguments worked a bit different, so you
-had to call ``--use-reloader yes``.
+.. module:: werkzeug.script
 
 
 Writing Actions
 
 An additional feature is the definition of tuples as defaults.  The first item
 in the tuple could be a short name for the command and the second the default
-value:
-
-.. sourcecode:: python
+value::
 
     def action_add_user(username=('u', ''), password=('p', '')):
         """Docstring goes here."""
 ================
 
 Per default, the `run` function looks up variables in the current locals.
-That means if no arguments are provided, it implicitly assumes this call:
-
-.. sourcecode:: python
+That means if no arguments are provided, it implicitly assumes this call::
 
     script.run(locals(), 'action_')
 
 If you don't want to use an action discovery, you can set the prefix to an
-empty string and pass a dict with functions:
-
-.. sourcecode:: python
+empty string and pass a dict with functions::
 
     script.run(dict(
         runserver=script.make_runserver(make_app, use_reloader=True),

File docs/src/templates.txt

 It it however not recommended to use this template system for anything else
 than simple content generation.
 
+.. module:: werkzeug.templates
 
-Usage
-=====
+The Template Class
+==================
 
-In the application you can pass the `Template` class a string with the
-template code and then call `render()` (or `substituate()` for compatibility
-with `string.Template`) with either a dict or some keyword arguments to render
-the template:
+.. class:: werkzeug.templates.Template
 
-.. sourcecode:: python
+    .. def:: werkzeug.templates.Template.render
+    .. def:: werkzeug.templates.Template.from_file
 
-    from werkzeug import Template
+    Besides the normal global functions and objects, the following functions
+    are added to every namespace: `escape`, `url_encode`, `url_quote`, and
+    `url_quote_plus`.  You can change those by subclassing `Template` and
+    overriding the `default_context` dict::
 
-    tmpl = Template(u'''\
-    <h1>$escape(title)</h1>
-    <ul>
-    <% for href, caption in links %>
-      <li><a href="$escape(href)">$escape(caption)</a></li>
-    <% endfor %>
-    </ul>''')
-
-    print tmpl.render(
-        title='Foo',
-        links=[
-            ('/', 'Index'),
-            ('/about', 'About')
-        ]
-    )
-
-This will then generate this:
-
-.. sourcecode:: html
-
-    <h1>Foo</h1>
-    <ul>
-      <li><a href="/">Index</a></li>
-      <li><a href="/about">About</a></li>
-    </ul>
-
-You can also create templates from files:
-
-.. sourcecode:: python
-
-    t = Template.from_file('filename.html')
-
-This also improves the error messages because then the Python traceback system
-is able to locate the source for the error messages.
-
-
-Template Syntax
-===============
-
-The syntax elements are a mixture of Django, Genshi text and mod_python
-templates and used internally in Werkzeug components.
-
-We do not recommend using this template engine in a real environment because
-it is quite slow and does not provide any advanced features.  For simple
-applications (like in CGI scripts) this can however be sufficient.
-
-Printing Variables::
-
-    $variable
-    $variable.attribute[item](some, function)(calls)
-    ${expression} or <%py print expression %>
-
-Keep in mind that the `print` statement adds a newline after the call or a
-whitespace if it ends with a comma.
-
-For loops::
-
-    <% for item in seq %>
-        ...
-    <% endfor %>
-
-While loops::
-
-    <% while expression %>
-        <%py break / continue %>
-    <% endwhile %>
-
-If conditions::
-
-    <% if expression %>
-        ...
-    <% elif expression %>
-        ...
-    <% else %>
-        ...
-    <% endif %>
-
-Python expressions::
-
-    <%py
-        ...
-    %>
-
-    <%python
-        ...
-    %>
-
-Note on Python expressions:  You cannot start a loop in a Python block and
-continue it in another one.  This example does *not* work::
-
-    <%python
-        for item in seq:
-    %>
-        ...
-
-Comments::
-
-    <%#
-        This is a comment
-    %>
-
-
-Missing Variables
-=================
-
-If you try to access a missing variable you will get back an `Undefined`
-object.  You can iterate over such an object or print it and it won't fail.
-However, every other operation will raise an error.  To test if a variable is
-undefined, you can use this expression::
-
-    <% if variable is Undefined %>
-        ...
-    <% endif %>
-
-
-Global Functions
-================
-
-Besides the normal global functions and objects, the following functions are
-added to every namespace: `escape`, `url_encode`, `url_quote`, and
-`url_quote_plus`.  You can change those by subclassing `Template` and
-overriding the `default_context` dict:
-
-.. sourcecode:: python
-
-    class MyTemplate(Template):
-        default_namespace = {
-            'ueber_func':       ueber_func
-        }
-        # Now add the old functions, too, because they are useful.
-        default_namespace.update(Template.default_namespace)
+        class MyTemplate(Template):
+            default_namespace = {
+                'ueber_func':       ueber_func
+            }
+            # Now add the old functions, too, because they are useful.
+            default_namespace.update(Template.default_namespace)

File docs/src/test.txt

 you can use them as response wrapper, ideally by subclassing them and hooking
 in test functionality.
 
-.. sourcecode:: python
-
-    >>> from werkzeug import Client, BaseResponse, test_app
-    >>> c = Client(test_app, BaseResponse)
-    >>> resp = c.get('/')
-    >>> resp.status_code
-    200
-    >>> resp.headers
-    Headers([('Content-Type', 'text/html; charset=utf-8')])
-    >>> resp.response_body.splitlines()[:2]
-    ['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"',
-     '  "http://www.w3.org/TR/html4/loose.dtd">']
+>>> from werkzeug import Client, BaseResponse, test_app
+>>> c = Client(test_app, BaseResponse)
+>>> resp = c.get('/')
+>>> resp.status_code
+200
+>>> resp.headers
+Headers([('Content-Type', 'text/html; charset=utf-8')])
+>>> resp.response_body.splitlines()[:2]
+['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"',
+ '  "http://www.w3.org/TR/html4/loose.dtd">']
 
 Or without a wrapper defined:
 
-.. sourcecode:: python
+>>> c = Client(test_app)
+>>> app_iter, status, headers = c.get('/')
+>>> status
+'200 OK'
+>>> headers
+[('Content-Type', 'text/html; charset=utf-8')]
+>>> ''.join(app_iter).splitlines()[:2]
+['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"',
+ '  "http://www.w3.org/TR/html4/loose.dtd">']
 
-    >>> from werkzeug import Client, test_app
-    >>> c = Client(test_app)
-    >>> app_iter, status, headers = c.get('/')
-    >>> status
-    '200 OK'
-    >>> headers
-    [('Content-Type', 'text/html; charset=utf-8')]
-    >>> ''.join(app_iter).splitlines()[:2]
-    ['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"',
-     '  "http://www.w3.org/TR/html4/loose.dtd">']
+The Client
+==========
 
+.. class:: werkzeug.test.Client
 
-Available Functions
-===================
-
-The following functions are available:
-
-`open(path, base_url, query_string, **options)`
-    Takes the same arguments as the `create_environ` function from the utility
-    module with some additions.
-    
-    The first parameter should be the path of the request which defaults to
-    '/'.  The second one can either be an absolute path (in that case the URL
-    host is `localhost:80`) or a full path to the request with scheme, netloc
-    port and the path to the script.
-    
-    If `path` contains a query string it will be used, even if the
-    `query_string` parameter was given.  If it does not contain one, the
-    `query_string` parameter is used as query string.  In that case it can
-    either be a dict, `MultiDict` or string.
-
-    The following options exist:
-
-    `method`
-        The request method.  Defaults to `GET`
-
-    `input_stream`
-        The input stream.  Defaults to an empty read-only stream.
-
-    `data`
-        The data you want to transmit.  You can set this to a string and
-        define a content type instead of specifying an input stream.
-        Additionally you can pass a dict with the form data.  The values
-        could then be strings (not unicode objects!), which are then
-        URL-encoded, or file objects.
-
-        A file object for this method is either a file descriptor with
-        an additional `name` attribute (like a file descriptor returned
-        by the `open`/`file` function), a tuple in the form
-        ``(fd, filename, mimetype)`` (all arguments except fd optional)
-        or a dict with those keys and values.
-
-        Additionally you can instanciate the `werkzeug.test.File` object
-        (or a subclass of it) and pass it as value.
-
-    `content_type`
-        The content type for this request.  Default is an empty content type.
-
-    `content_length`
-        The value for the content length header.  Defaults to 0.
-
-    `errors_stream`
-        The `wsgi.errors` stream.  Defaults to `sys.stderr`.
-
-    `multithread`
-        The multithreaded flag for the WSGI environment.  Defaults to `False`.
-
-    `multiprocess`
-        The multiprocess flag for the WSGI environment.  Defaults to `False`.
-
-    `run_once`
-        The `run_once` flag for the WSGI environment.  Defaults to `False`.
-
-`get(...)`
-    Like `open()`, but enforces `GET` as method.
-
-`post(...)`
-    Like `open()`, but enforces `POST` as method.
-
-`head(...)`
-    Like `open()`, but enforces `HEAD` as method.
-
-`put(...)`
-    Like `open()`, but enforces `PUT` as method.
-
-`delete(...)`
-    Like `open()`, but enforces `DELETE` as method.
+    .. def:: werkzeug.test.Client.__init__
+    .. def:: werkzeug.test.Client.open
+    .. def:: werkzeug.test.Client.get
+    .. def:: werkzeug.test.Client.head
+    .. def:: werkzeug.test.Client.post
+    .. def:: werkzeug.test.Client.put
+    .. def:: werkzeug.test.Client.delete

File docs/src/tutorial.txt

 Werkzeug Tutorial
 =================
 
+.. admonition:: Translations
+
+    This tutorial is available in the following languages:
+
+    -   **English**
+    -   `Deutsch <tutorial_de.txt>`_
+
 Welcome to the Werkzeug 0.2 tutorial in which we will create a TinyURL clone
 that stores URLs in a database.  The libraries we will use for this
 applications are `Jinja`_ for the templates, `SQLAlchemy`_ for the database
 .. sourcecode:: python
 
     from sqlalchemy import create_engine
-    from werkzeug import BaseRequest, ClosingIterator
+    from werkzeug import Request, ClosingIterator
     from werkzeug.exceptions import HTTPException
 
-    from shorty.utils import Session, metadata, local, local_manager, url_map
+    from shorty.utils import session, metadata, local, local_manager, url_map
     from shorty import views
     import shorty.models
 
 
         def __call__(self, environ, start_response):
             local.application = self
-            request = BaseRequest(environ)
+            request = Request(environ)
             local.url_adapter = adapter = url_map.bind_to_environ(environ)
             try:
-                endpoint, values = adapter.match(request.path)
+                endpoint, values = adapter.match()
                 handler = getattr(views, endpoint)
                 response = handler(request, **values)
             except HTTPException, e:
                 response = e
             return ClosingIterator(response(environ, start_response),
-                                   [Session.remove, local_manager.cleanup])
+                                   [session.remove, local_manager.cleanup])
 
 That's a lot of code for the beginning!  Let's go through it step by step.
 First we have a couple of imports:  From SQLAlchemy we import a factory
 function that creates a new database engine for us.  A database engine holds
 a pool of connections for us and manages them.  The next few imports pull some
 objects into the namespace Werkzeug provides: a request object, a special
-iterator class that helps us cleaning up stuff at the request end, a helper
-function that imports objects based on a string and finally the base class for
-all HTTP exceptions.
+iterator class that helps us cleaning up stuff at the request end and finally
+the base class for all HTTP exceptions.
 
 The next five imports are not working because we don't have the utils module
 written yet.  However we should cover some of the objects already.  The
-`Session` object pulled from there is not a PHP-like session object but a
+`session` object pulled from there is not a PHP-like session object but a
 SQLAlchemy database session object.  Basically a database session object keeps
 track of yet uncommited objects for the database.  Unlike Django, an
 instanciated SQLAlchemy model is already tracked by the session!  The metadata
 tables we use.
 
 And then comes the request dispatching function.  In there we create a new
-request object by passing the environment to the `BaseRequest` constructor.
+request object by passing the environment to the `Request` constructor.
 Once again we bind the application to the local object, this time, however,
 we have to do this, otherwise things will break soon.
 
     application = local('application')
 
     metadata = MetaData()
-    Session = scoped_session(lambda: create_session(application.database_engine,
+    session = scoped_session(lambda: create_session(application.database_engine,
                              transactional=True), local_manager.get_ident)
 
     url_map = Map()
 
     from datetime import datetime
     from sqlalchemy import Table, Column, String, Boolean, DateTime
-    from shorty.utils import Session, metadata, url_for, get_random_uid
+    from shorty.utils import session, metadata, url_for, get_random_uid
 
     url_table = Table('urls', metadata,
         Column('uid', String(140), primary_key=True),
         def __repr__(self):
             return '<URL %r>' % self.uid
 
-    Session.mapper(URL, url_table)
+    session.mapper(URL, url_table)
 
 This module is pretty straightforward.  We import all the stuff we need from
 SQLAlchemy and create a table.  Then we add a class for this table and we map
 .. sourcecode:: pycon
 
     Interactive Werkzeug Shell
-    >>> from shorty.models import Session, URL
+    >>> from shorty.models import session, URL
 
 Now we can add some URLs to the database:
 
     >>> urls = [URL('http://example.org/'), URL('http://localhost:5000/')]
     >>> URL.query.all()
     []
-    >>> Session.commit()
+    >>> session.commit()
     >>> URL.query.all()
     [<URL '5cFbsk'>, <URL 'mpugsT'>]
 
 .. sourcecode:: pycon
 
     >>> URL('http://werkzeug.pocoo.org/', False, 'werkzeug-webpage')
-    >>> Session.commit()
+    >>> session.commit()
 
 And query them all:
 
 
     from werkzeug import redirect
     from werkzeug.exceptions import NotFound
-    from shorty.utils import Session, render_template, expose, validate_url, \
+    from shorty.utils import session, render_template, expose, validate_url, \
          url_for
     from shorty.models import URL
 
                     error = 'The alias you have requested exists already'
             if not error:
                 uid = URL(url, 'private' not in request.form, alias).uid
-                Session.commit()
+                session.commit()
                 return redirect(url_for('display', uid=uid))
         return render_template('new.html', error=error, url=url)
 
 
     for os import path    
     from urlparse import urlparse
+    from werkzeug import Response
     from jinja import Environment, FileSystemLoader
 
     ALLOWED_SCHEMES = frozenset(['http', 'https', 'ftp', 'ftps'])
     jinja_env.globals['url_for'] = url_for
 
     def render_template(template, **context):
-        return BaseResponse(jinja_env.get_template(template).render(**context),
-                            mimetype='text/html')
+        return Response(jinja_env.get_template(template).render(**context),
+                        mimetype='text/html')
 
     def validate_url(url):
         return urlparse(url)[0] in ALLOWED_SCHEMES

File docs/src/tutorial_de.txt

+=================
+Werkzeug Tutorial
+=================
+
+.. admonition:: Hinweis
+
+    Dies ist die Deutsche Übersetzung des `Tutorials <tutorial.txt>`_.  Auf
+    Grund der Tatsache, dass die Entwicklung rum um Werkzeug nie still steht
+    und sich Verbesserungen an der Bibliotheken auch auf das Tutorial auswirken
+    ist die Originalversion möglicherweise aktueller.
+
+Willkommen zum Werkzeug 0.2 Tutorial.  In diesem Tutorial werden wir einen
+kleinen TinyURL Klon programmieren, der die URLs in einer Datenbank speichert.
+Die verwendeten Bibliotheken für diese Anwendung sind `Jinja`_ für die Templates,
+`SQLAlchemy`_ für die Datenbank und natürlich Werkzeug für WSGI.
+
+Der Grund warum wir uns hier für diese Bibliotheken entschieden haben ist, dass
+wir einen Django-ähnlichen Grundaufbau nachstellen wollen.  Zum Beispiel
+view-Funktionen und nicht Rails-/Pylons-ähnliche Controller-Klassen mit
+action-Methoden oder Designer-freundliche Templates.
+
+Im Werkzeug `Beispiel-Ordner`_ befinden sich einige Anwendungen die andere
+Konzepte, Template Engines etc. verfolgen.  Dort findet sich auch der
+Quellcode dieser Anwendung.
+
+Du kannst SQLAlchemy verwenden um Jinja und SQLAlchemy zu installieren
+sollest du diese Bibliotheken noch nicht besitzen::
+
+    sudo easy_install Jinja
+    sudo easy_install SQLAlchemy
+
+Sofern du auf einem Windows Computer mit Administratorrechten arbeitest
+kannst du das "sudo" fallen lassen.  Als OS X Benutzer könntest du die
+Bibliotheken auch via port installieren, Linux Benutzer finden diese Pakete
+möglicherweise auch im Packagemanager.
+
+Wenn du neugirig bist kannst du auch die `Online Demo`_ der Anwendung
+ansehen.
+
+Noch ein kleiner Hinweis: Dieses Tutorial erfordert Python 2.4.
+
+.. _Jinja: http://jinja.pocoo.org/
+.. _SQLAlchemy: http://sqlalchemy.org/
+.. _Beispiel-Ordner: http://dev.pocoo.org/projects/werkzeug/browser/example
+.. _Online Demo: http://werkzeug.pocoo.org/e/shorty/
+
+
+Teil 0: Die Ordnerstruktur
+==========================
+
+Bevor wir beginnen können müssen wir ein Python Paket für unsere Werkzeug
+Anwendung erstellen.  Dort werden wir dann die Anwendung, die Templates
+und die statischen Dateien ablegen.
+
+Die Anwendung dieses Tutorials nennen wir `shorty` und die Struktur für
+unsere Anwendung sieht etwa so aus::
+
+    manage.py
+    shorty/
+        __init__.py
+        templates/
+        static/
+
+Die ``__init__.py`` und ``manage.py`` Dateien lassen wir für die Moment einmal
+leer.  Die erste dieser Dateien macht den ``shorty`` Order ein Python Package
+und die zweite werden wir später für unsere Verwaltungsfunktionen nutzen.
+
+
+Teil 1: Die WSGI Anwendung
+==========================
+
+Im Gegensatz zu Django oder ähnlichen Frameworks arbeitet Werkzeug direkt
+auf der WSGI Schicht.  Es gibt keine schicke Magie, die die zentrale WSGI
+Anwendung für uns implementiert.  Das bedeutet, dass wir als erstes eben
+diese WSGI Anwendung programmieren müssen.  Diese WSGI Anwendung ist entweder
+eine Funktion, oder noch besser, eine Klasse mit einer ``__call__`` Methode.
+
+Eine aufrufbare Klasse hat große Vorteile gegenüber einer Funktion.  Zum
+einen kann man Konfigurationsparameter direkt an den Konstruktor übergeben,
+zum anderen können wir WSGI Middlewares innerhalb der WSGI Anwendung
+hinzufügen.  Das ist nützlich für Middlewares, die essenziell für die
+Funktion der Anwendung sind (zB eine session-Middleware).
+
+Hier zunächst einmal der Quellcode für unsere ``shorty/application.py``
+Datei, in die wir die WSGI Anwendung packen::
+
+    from sqlalchemy import create_engine
+    from werkzeug import Request, ClosingIterator
+    from werkzeug.exceptions import HTTPException
+
+    from shorty.utils import session, metadata, local, local_manager, url_map
+    from shorty import views
+    import shorty.models
+
+
+    class Shorty(object):
+
+        def __init__(self, db_uri):
+            local.application = self
+            self.database_engine = create_engine(db_uri, convert_unicode=True)
+
+        def init_database(self):
+            metadata.create_all(self.database_engine)
+
+        def __call__(self, environ, start_response):
+            local.application = self
+            request = Request(environ)
+            local.url_adapter = adapter = url_map.bind_to_environ(environ)
+            try:
+                endpoint, values = adapter.match()
+                handler = getattr(views, endpoint)
+                response = handler(request, **values)
+            except HTTPException, e:
+                response = e
+            return ClosingIterator(response(environ, start_response),
+                                   [session.remove, local_manager.cleanup])
+
+Hui, ziemlich für Code für den Anfang.  Gehen wir mal Schritt für Schritt
+durch den Quellcode.  Zunächst einmal haben wir einige Imports:  Aus dem
+sqlalchemy Paket holen wir uns eine factory-Funktion die eine neue
+Datenbank-Engine für uns erstellt die einen Connectionpool besitzt.  Die
+nächsten paar Imports holen einige Objekte in den Namensraum die uns
+Werkzeug zur Verfügung stellt: ein Request Objekt, ein spezieller
+Iterator der uns hilft am ende eines Requests einige Dinge aufzuräumen,
+eine schließlich die Basisklasse für alle HTTP Exceptions.
+
+Die nächsten fünf Imports funktionieren noch nicht, weil wir das `utils`
+Modul noch nicht erstellt haben.  Aber wir werden trotzdem schon ein wenig
+über diese Objekte sprechen.  Das `session` Objekt ist nicht ein PHP
+ähnliches session array, sondern eine SQLAlchemy Datenbank Session.  Alle
+Datenbank Modelle, die im Kontext eines Requests erstellt werden sind auf
+diesem Objekt zwischengespeichert so dass man Änderungen mit einem Schlag 
+zum Server senden kann.  Im Gegensatz zu Django wird ein instanziertes
+Datenbank Modell automatisch von der Session verwaltet und ist in dieser
+Session ein Singleton.  Es kann also niemals zwei Instanzen des selben
+Datenbankeintrages in einer Session geben.  Das metadata Objekt ist auch
+von SQLAlchemy und speichert die Informationen über die Datenbank Tabellen.
+Es stellt zum Beispiel eine Funktion bereit um alle definierten Tabellen
+zu erstellen.
+
+Das `local` Objekt ist ein Kontext-lokales Objekt erstellt vom utility
+Modul.  Attributzugriffe auf dieses Objekt sind zum aktuellen Request
+gebunden dh, jeder Request bekommt verschiedene Objekte zurück und kann
+verschiedene ablegen ohne Threading Probleme zu bewirken.  Der
+`local_manager` wird genutzt um am Ende des Requests alle auf dem `local`
+Objekt gespeicherten Daten wieder freizugeben.
+
+Der letzte Import von dort ist die URL map die alle URL Routen verwaltet.
+Solltest du bereits mit Django gearbeitet haben ist dies vergleichbar mit
+den Regular expressions im ``urls.py`` Modul.  Solltest du PHP kennen ist
+die URL map ähnlich einem eingebauten mod_rewrite.
+
+Zusätzlich importieren wir hier unser `views` modul, dass die view-Funktionen
+besitzt und dann das `models` Modul, dass unsere Models speichert.  Auch wenn
+es so aussieht, als ob wir diesen Import nicht nutzen ist er wichtig.  Nur
+dadurch werden unsere Tabellen auf dem metadata Objekt registriert.
+
+Schauen wir also auf die Anwendungsklasse.  Der Konstrutor dieser Klasse nimmt
+die Datenbank-URI entgegen die vereinfacht gesagt den Typ der Datenbank und
+die Verbindungsdaten enthält.  Für SQLite das wäre zum Beispiel
+``sqlite:////tmp/shorty.db`` (die vier Slashes sind **kein** Tippfehler).
+
+Im Konstruktor erstellen wir auch gleich eine Datenbank-Engine für diese
+URI und aktivieren das automatische Umwandeln von Bytestrings nach Unicode.
+Das ist nützlich weil sowohl Jinja als auch Werkzeug intern immer Unicode
+verwenden.
+
+Weiters binden wir die Anwendung gleich mal an das `local` Objekt.  Das ist
+eigentlich nicht wirklich nötig aber nützlich wenn wir mit der Anwendung in
+der Python Shell spielen wollen.  Damit werden direkt nach dem instanzieren
+der Anwendung die Datenbank Funktionen funktionieren.  Wenn wir das nicht tun
+wird der Python Interpreter einen Fehler werfen wenn außerhalb eines Requests
+versucht wird eine SQLAlchemy Session zu erstellen.
+
+Die `init_database` Methode können wir später im Management Script verwenden
+um alle Datenbank-Tabellen zu erstellen die wir verwenden.
+
+Und nun zur eigentlichen WSGI Anwendung, der `__call__` Methode.  Dort
+passiert das sogenannte "Request Dispatching" also das weiterleiten von
+eingenden Anfragen zu den richtigen Funktionen.  Als erste Erstellen wir
+dort ein neues Request-Objekt um nicht direkt mit dem environ Arbeiten zu
+müssen und dann binden wir die Anwendung and das local Objekt für den
+aktuellen Kontext.
+
+Dann erstellen wir einen URL Adapter indem wir die URL map an das aktuelle
+WSGI environment binden.  Der Adapter weiß dann wie die aktuelle URL
+aussieht, wo die Anwendung eingebunden ist etc.  Diesen Adapter können wir
+dann nutzen um URLs zu bauen oder eben den aktuellen Request zu matchen.
+Wir binden diesen Adapter auch an das local Objekt damit wir im utils
+Modul auf diesen Zugreifen können.
+
+Danach kommt ein `try`/`except` Konstruct das HTTP Fehler abfängt die
+während dem Matchen oder in einer View Funktion auftreten können.  Wenn
+der Adapter keinen Endpoint für die aktuelle URL findet wird er eine
+`NotFound` Exception werfen, die wir wie ein Response Objekt aufrufen
+können.  Der Endpoint ist in unserem Fall der Name der Funktion im view
+Modul, die wir aufrufen möchten.  Wir suchen uns einfach mit `getattr`
+die Funktion nach dem Namen heraus und rufen sie mit dem Request Objekt
+und den URL Werten auf.
+
+Am Schluss rufen wir dann das gewonnene Response Objekt (oder die Exception)
+als WSGI Anwendung auf und übergeben den Rückgabewert dieser Funktion an
+den `ClosingIterator` zusammen mit zwei Funktionen fürs Aufräumen.  Dies
+schließt die SQLAlchemy Session und leert das local Objekt für diesen
+Request.
+
+Nun müssen wir zwei leere Dateien ``shorty/views.py`` und
+``shorty/models.py`` erstellen, damit die Imports nicht fehlschalgen.
+Den tatsächlichen Code für diese Module werden wir ein wenig später
+erstellen.
+
+
+Teil 2: Die Utilities
+=====================
+
+Nun haben wir die eigentliche WSGI-Applikation fertiggestellt, aber wir
+müssen das Utility-Modul noch um Code ergänzen, damit die Importe klappen.
+Fürs Erste fügen wir nur die Objekte hinzu, die wir brauchen, damit die
+Applikation funktioniert.  Der folgende Code landet in der Datei
+``shorty/utils.py``:
+
+.. sourcecode:: python
+
+    from werkzeug import Local, LocalManager
+    from werkzeug.routing import Map, Rule
+    from sqlalchemy import MetaData
+    from sqlalchemy.orm import create_session, scoped_session
+
+    local = Local()
+    local_manager = LocalManager([local])
+    application = local('application')
+
+    metadata = MetaData()
+    session = scoped_session(lambda: create_session(application.database_engine,
+                             transactional=True), local_manager.get_ident)
+
+    url_map = Map()
+    def expose(rule, **kw):
+        def decorate(f):
+            kw['endpoint'] = f.__name__
+            url_map.add(Rule(rule, **kw))
+            return f
+        return decorate
+
+    def url_for(endpoint, _external=False, **values):
+        return local.url_adapter.build(endpoint, values, force_external=_external)
+
+Als Erstes importieren wir wieder eine Menge, dann erstellen wir das local-Objekt
+Objekte und den Manager dafür, die wir bereits im vorherigen Abschnitt besprochen
+haben.  Neu ist hier dass der Aufruf eines local-Objekts mit einem String ein
+Proxy-Objekt zurückgibt.  Dieses zeigt stets auf die gleichnamigen Attribute des
+local-Objekts. Beispielsweise verweist nun `application` dauerhaft auf
+`local.application`.  Wenn du jedoch darauf zugreifst, aber kein Objekt an
+`local.application` gebunden ist, erhältst du einen `RuntimeError`.
+
+Die folgenden drei Zeilen sind im Prinzip alles um SQLAlchemy 0.4 oder höher in
+eine Werkzeug-Anwendung einzubinden.  Wir erstellen ein Metadaten-Objekt
+für all unsere Tabellen sowie eine "scoped session" über die
+`scoped_session`-Factory-Funktion.  Dadurch wird SQLAlchemy angewiesen, praktisch
+denselben Algorithmus zur Ermittlung des aktuellen Kontextes zu verwenden, wie es
+auch Werkzeug für die local-Objekte tut, und die Datenbank-Engine der aktuellen
+Applikation zu benutzen.
+
+Wenn wir nicht vorhaben, mehrere Instanzen der Applikation in derselben Instanz
+des Python-Interpreters zu unterstützen, können wir den Code einfach halten, indem
+wir nicht über das aktuelle local-Objekt auf die Applikation zugreifen, sondern
+einen anderen Weg nehmen.  Dieser Ansatz wird etwa von Django verfolgt, macht es
+allerdings unmöglich, mehrere solcher Applikationen zu kombinieren.
+
+Der restliche Code des Moduls wird für unsere Views benutzt.  Die Idee besteht
+darin, Dekoratoren zu benutzen, um die URL-Dispatching-Regeln für View-Funktionen
+festzulegen, anstatt ein zentrales Modul ``urls.py`` zu verwenden, wie es Django
+tut, oder über eine ``.htaccess``-Datei URLs umzuschreiben, wie man es in PHP
+machen würde.  Dies ist **eine** Möglichkeit, dies zu tun, und es gibt unzählige
+andere Wege der Handhabung von URL-Regeldefinitionen.
+
+Die Funktion `url_for`, die wir ebenfalls definieren, bietet einen einfachen Weg,
+URLs anhand des Endpunktes zu generieren.  Wir werden sie später in den Views als
+auch unserem Model verwenden.
+
+
+Unterbrechung: Und nun etwas komplett anderes
+=============================================
+
+Da wir nun das Grundgerüst für unsere Anwendung fertig gestellt haben, können
+wir jetzt erst einmal relaxen und uns etwas komplett anderem zuwenden:
+Verwaltungs-Skripte.  Während der Entwicklung erledigt man häufig immer
+wiederkehrende Aufgaben, wie zum Beispiel das Starten eines Entwicklungs-Servers
+(Im Gegensatz zu PHP benötigt Werkzeug keinen Apache Server.  Der in Python
+integrierte wsgiref Server ist völlig ausreichend ist für die Entwicklung
+auf jeden Fall empfehlenswert), das Starten eines Python Interpreters um
+mit den Datenbank Objekten herum zu spielen, oder die Datenbank zu
+initialisieren, etc.
+
+Werkzeug macht es zum Glück unglaublich einfach solche Verwaltungs-Skripts zu
+schreiben. Der folgende Code implemenitert einen voll funktionsfähigen
+Verwaltungs Skript und gehört in die `manage.py` Datei welche du am Anfang
+erstellt hast:
+
+.. sourcecode:: python
+
+    #!/usr/bin/env python
+    from werkzeug import script
+
+    def make_app():
+        from shorty.application import Shorty
+        return Shorty('sqlite:////tmp/shorty.db')
+
+    def make_shell():
+        from shorty import models, utils
+        application = make_app()
+        return locals()
+
+    action_runserver = script.make_runserver(make_app, use_reloader=True)
+    action_shell = script.make_shell(make_shell)
+    action_initdb = lambda: make_app().init_database()
+
+    script.run()
+
+`werkzeug.script` ist genauer in der `Script Dokumentation`_ beschrieben,
+und da der Großteil des Codes sowieso verständlich sein sollte, werden wir
+hier nicht näher darauf eingehen.
+
+Es ist aber wichtig, dass du ``python manage.py shell`` ausführen kannst um
+eine interaktive Python Shell zu starten. Solltest du ein Traceback bekommen,
+kontrolliere bitte die betreffende Code-Zeile und vergleiche sie mit dem
+entsprechenden Code in dieser Anleitung.
+
+Sobald der Skript läuft, können wir mit dem Schreiben der Datenbank Modelle
+beginnen.
+
+.. _Script Dokumentation: script.txt
+
+Teil 3: Datenbank Models
+========================
+
+Jetzt können wir die Models erstellen. Da die Anwendung ziemlich einfach ist,
+haben wir nur ein Model und eine Tabelle:
+
+.. sourcecode:: python
+
+    from datetime import datetime
+    from sqlalchemy import Table, Column, String, Boolean, DateTime
+    from shorty.utils import session, metadata, url_for, get_random_uid
+
+    url_table = Table('urls', metadata,
+        Column('uid', String(140), primary_key=True),
+        Column('target', String(500)),
+        Column('added', DateTime),
+        Column('public', Boolean)
+    )
+
+    class URL(object):
+
+        def __init__(self, target, public=True, uid=None, added=None):
+            self.target = target
+            self.public = public
+            self.added = added or datetime.utcnow()
+            if not uid:
+                while 1:
+                    uid = get_random_uid()
+                    if not URL.query.get(uid):
+                        break
+            self.uid = uid
+
+        @property
+        def short_url(self):
+            return url_for('link', uid=self.uid, _external=True)
+
+        def __repr__(self):
+            return '<URL %r>' % self.uid
+
+    session.mapper(URL, url_table)
+
+
+Dieses Modul ist gut überschaubar.  Wir importieren alles, was wir von
+SQLAlchemy benötigen und erstellen die Tabelle. Dann fügen wir eine Klasse für
+diese Tabelle hinzu und verbinden beide miteinander. Für eine detailiertere
+Erklärung bezüglich SQLAlchemy solltest du dir das `exzellente Tutorial`_
+anschauen.
+
+In dem Konstruktor generieren wir solange eine einzigartige ID bis wir eine
+finden, die noch nicht belegt ist.
+Die `get_random_uid`-Funktion fehlt - wir müssen sie noch in unser utils-Modul
+hinzufügen:
+
+.. sourcecode:: python
+
+    from random import sample, randrange
+    URL_CHARS = 'abcdefghijkmpqrstuvwxyzABCDEFGHIJKLMNPQRST23456789'
+
+    def get_random_uid():
+        return ''.join(sample(URL_CHARS, randrange(3, 9)))
+
+Wenn das getan ist können wir ``python manage.py initdb`` ausführen, um die
+Datenbank zu erstellen und ``python manage.py shell`` um damit herumzuspielen:
+
+.. sourcecode:: pycon
+
+    Interactive Werkzeug Shell
+    >>> from shorty.models import session, URL
+
+Jetzt können wir einige URLs zu der Datenbank hinzufügen:
+
+.. sourcecode:: pycon
+
+    >>> urls = [URL('http://example.org/'), URL('http://localhost:5000/')]
+    >>> URL.query.all()
+    []
+    >>> session.commit()
+>>> URL.query.all()
+[<URL '5cFbsk        [<URL '5cFbsk'>, <URL 'mpugsT'>]
+
+Wie du sehen kannst müssen wir ``sessino.commit()`` aufrufen um die Änderungen
+in der Datenbank zu speichern.
+Lasst uns ein privates Element mit einer eigenen UID erstellen:
+
+.. sourcecode:: pycon
+
+    >>> URL('http://werkzeug.pocoo.org/', False, 'werkzeug-webpage')
+    >>> session.commit()
+
+Und alle abfragen:
+
+.. sourcecode:: pycon
+
+    >>> URL.query.filter_by(public=False).all()
+    [<URL 'werkzeug-webpage'>]
+    >>> URL.query.filter_by(public=True).all()
+    [<URL '5cFbsk'>, <URL 'mpugsT'>]
+    >>> URL.query.get('werkzeug-webpage')
+    <URL 'werkzeug-webpage'>
+
+Jetzt, da wir einige Daten in der Datenbank haben und wir ungefähr wissen auf
+welche Weise SQLAlchemy funktioniert, ist es Zeit unsere Views zu erstellen.
+
+.. _exzellente Tutorial: http://www.sqlalchemy.org/docs/04/ormtutorial.html
+
+
+Teil 4: Die View-Funktionen
+===========================
+
+Nachdem wir mit SQLAlchemy herumgespielt haben, können wir zurück zu Werkzeug
+gehen und damit anfangen unsere Viewfunktionen zu erstellen.  Das Wort
+"Viewfunktion" kommt von Django, welches die Funktionen, die die Templates
+wiedergibt "Viewfunktionen" nennt. Deshalb ist unser Beispiel MTV (Model, View,
+Template) und nicht etwa MVC (Model, View, Controller). Sie meinen dasselbe,
+aber es ist viel einfacher die Djangobenennung zu nutzen.
+
+Zum Anfang erstellen wir einfach eine Viewfunktion für neue URLs und eine
+Funktion, die eine Nachricht über einen neuen Link darstellt.  Das ganze geht
+in unsere jetzt noch leere ``views.py``-Datei:
+
+.. sourcecode:: python
+
+    from werkzeug import redirect
+    from werkzeug.exceptions import NotFound
+    from shorty.utils import session, render_template, expose, validate_url, \
+         url_for
+    from shorty.models import URL
+
+    @expose('/')
+    def new(request):
+        error = url = ''
+        if request.method == 'POST':
+            url = request.form.get('url')
+            alias = request.form.get('alias')
+            if not validate_url(url):
+                error = u"Entschuldigung, aber ich kann die angegebene " \
+                        u"URL nicht kürzen."    
+        elif alias:
+                if len(alias) > 140:
+                    error = 'Dein Alias ist zu lang'
+                elif '/' in alias:
+                    error = 'Dein Alias darf keinen Slash beinhalten'
+                elif URL.query.get(alias):
+                    error = 'Der angegeben Alias existiert bereits'
+            if not error:
+                uid = URL(url, 'private' not in request.form, alias).uid
+                session.commit()
+                return redirect(url_for('display', uid=uid))
+        return render_template('new.html', error=error, url=url)
+
+    @expose('/display/<uid>')
+    def display(request, uid):
+        url = URL.query.get(uid)
+        if not url:
+            raise NotFound()
+        return render_template('display.html', url=url)
+
+    @expose('/u/<uid>')
+    def link(request, uid):
+        url = URL.query.get(uid)
+        if not url:
+            raise NotFound()
+        return redirect(url.target, 301)
+
+    @expose('/list/', defaults={'page': 1})
+    @expose('/list/<int:page>')
+    def list(request, page):
+        pass
+
+Wieder einmal ziemlich viel Code, aber das meiste ist normale Formvalidierung.
+Wir erstellen zwei Funktionen: `new` und `display` und dekorieren sie
+mit unserem `expose`-Dekorator von den Utils.  Dieser Dekorator fügt eine neue
+URL-Rule zur Map hinzu, indem er alle Parameter zum Konstruktor eines
+Ruleobjekts und setzt den Endpunkt zum Name der Funktion.  Damit können wir
+einfach URLs erzeugen zu den Funktionen durch die Nutzung ihrer Funktionsnamen
+als Endpunkt.
+
+Denke daran, dass dieser Code nicht unbedingt eine gute Idee für größere
+Anwendungen ist.  In solchen Fällen ist es besser den vollen Importnamen mit
+einem allgemeinen Prefix oder etwas Ähnliches als Endpunkt zu nutzen.  Sonst
+wird es ziemlich verwirrend.
+
+Die Formvalidierung in der `new`-Methode ist ziemlich simpel.  Wir
+kontrollieren, ob die aktuelle Methode `POST` ist, falls ja nehmen wir die
+Daten von dem Request und validieren sie.  Wenn dort keine Error auftritt
+erstellen wir ein neues `URL`-Objekt, übergeben es der Datenbank und leiten auf
+die Anzeige-Seite um.
+
+Die `display`-Funktion ist nicht viel komplexer. Die URL-Rule erwartet einen
+`uid`-Parameter, welchen die Funktion akzeptiert.  Danach schauen wir in der
+URL-Rule mit der gegebenen UID und geben das Template aus indem wir das
+URL-Objekt ihm übergeben.
+
+Wenn die URL nicht existiert, werfen wir eine `NotFound`-Exception, welche eine
+statische "404 Seite nicht gefunden"-Seite anzeigt. Wir können diese später mit
+einer speziellen Errorseite ersetzen indem wir die Exception auffangen bevor
+die `HTTPException` geworfen wird.
+
+Die `link`-Viewfunktion wird von unseren Models in der `short_url`-Eigenschaft
+genutzt und ist die kurze URL, die wir vermitteln.  Wenn also die URL-UID
+``foobar`` ist, wird die URL unter ``http://localhost:5000/u/foobar`` erreichbar
+sein.
+
+Die `list`-Viewfunktion wurde noch nicht geschrieben, das machen wir später.
+Aber was wichtig ist, ist, dass die Funktion einen optionalen URL-Parameter
+nimmt. Der erste Dekorator sagt Werkzeug, dass wenn einfach nur ``/page/``
+aufgerufen wird, die erste Seite angezeigt wird (da der Parameter `page`
+standardmäßig auf 1 gesetzt wird). 
+Wichtiger ist die Tatsache, dass Werkzeug URLs normalisiert.  Wenn du also
+``/page`` oder ``/page/1`` aufrufst, wirst du zu ``/page/`` in beiden Fällen
+umgeleitet.  Das macht Google glücklich und geschieht automatisch. Wenn du dieses
+Verhalten nicht magst, kannst du es abschalten.
+
+Und wieder einmal müssen wir zwei Objekte aus dem Utils-Modul importieren, die
+jetzt noch nicht existieren.  Eine von diesen soll ein Jinja-Template in ein
+Response-Objekt ausgeben, das andere prüft eine URL.  Lasst uns also diese zu
+``utils.py`` hinzufügen.
+
+.. sourcecode:: python
+
+    for os import path    
+    from urlparse import urlparse
+    from werkzeug import Response
+    from jinja import Environment, FileSystemLoader
+
+    ALLOWED_SCHEMES = frozenset(['http', 'https', 'ftp', 'ftps'])
+    TEMPLATE_PATH = path.join(path.dirname(__file__), 'templates')
+    jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_PATH))
+    jinja_env.globals['url_for'] = url_for
+
+    def render_template(template, **context):
+        return Response(jinja_env.get_template(template).render(**context),
+                        mimetype='text/html')
+
+    def validate_url(url):
+        return urlparse(url)[0] in ALLOWED_SCHEMES
+
+Im Grunde ist es das. Die Validierungsfunktion checkt, ob deine URL wie eine
+HTTP oder FTP URL aussiehen muss.  Wir machen dies, um uns zu versichern,
+dass niemand eingefährliches JavaScript oder ähnliche URLs abschickt.  Die
+`render_template`-Funktion ist auch nicht viel komplizierter, es schaut nach einem
+Template im Dateisystem im `templates`-Ordner und gibt es als Response aus.
+
+Eine andere Sache, die wir hier machen ist, dass wir die `url_for`-Funktion in
+den globalen Templatecontext packen, sodass wir URLs auch in den Templates
+erstellen können.
+
+Da wir jetzt unsere beiden ersten View-Funktionen haben, ist es Zeit, die
+Templates hinzuzufügen.
+
+Teil 5: Die Templates
+=====================
+
+Wir haben beschlossen Jinja-Templates in diesem Beispiel zu nutzen.  Wenn
+du weißt, wie man Django-Templates nutzt, sollte es dir bekannt vorkommen, wenn
+du bis jetzt mit PHP gearbeitet hast, kannst du Jinja-Templates mit Smarty
+vergleichen.  Wenn du bis jetzt PHP als Templatesprache genutzt hast, solltest
+du für dein nächstes Projekt mal in `Mako`_ schauen.
+
+**Sicherheits Warnung**: Wir nutzen hier Jinja, welches ein textbasierendes
+Template-Engine ist. Aufgrund der Tatsache, dass Jinja nicht weiß, womit
+es arbeitet, musst du wenn du HTML-Templates erstellst, *alle* Werte escapen,
+die irgendwann, an irgendeinem Punkt, irgendeinen von den folgenden Zeichen
+enhtalten können: ``>``, ``<`` oder ``&``. Innerhalb von Attributen musst du
+außerdem Anführungszeichen escapen.
+Du kannst Jinjas ``|e``-Filter für normales Escaping benutzen, wenn du `true`
+as Argument übergibst escaped es außerdem Anführungszeichen (``|e(true)``).
+Wie du in den Beispielen unterhalb sehen kannst, escapen wir die  URLs nicht.
+Der Grund dafür ist, dass wir keine Kaufmannsund in der URL haben und deshalb
+ist es sicher, es wegzulassen.
+
+Zwecks Einfachheit werden wir HTML 4 in unseren Templates nutzen.  Wenn du etwas
+Erfahrung mit XHTML hast, kannst du sie in XHTML schreiben.  Aber beachte, dass
+das Beispielstylesheet unten nicht mit XHTML funktioniert.
+
+Eine coole Sache, die Jinja von Django geerbt hat, ist Templatevererbung.
+Templatevererbung bedeutet, dass wir oftgenutzte Elemente in ein Basistemplate
+legen können und es mit Platzhaltern füllen.  Beispielsweise geht der Doctype
+und der HTML-Rahmen in die Datei ``templates/layout.html``:
+
+.. sourcecode:: html+jinja
+
+    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+     "http://www.w3.org/TR/html4/strict.dtd">
+    <html>
+    <head>
+      <title>Shorty</title>
+    </head>
+    <body>
+      <h1><a href="{{ url_for('new') }}">Shorty</a></h1>
+      <div class="body">{% block body %}{% endblock %}</div>
+      <div class="footer">
+        <a href="{{ url_for('new') }}">Neu</a> |
+        <a href="{{ url_for('list') }}">Liste</a> |
+        Benutze Shorty für Gutes, nicht für Böses
+      </div>
+    </body>
+    </html>
+
+Und wir können von diesem Template in unserer ``templates/new.html`` erben:
+
+.. sourcecode:: html+jinja
+
+    {% extends 'layout.html' %}
+    {% block body %}
+      <h2>Erstelle eine Shorty-URL!</h2>
+      {% if error %}<div class="error">{{ error }}</div>{% endif -%}
+      <form action="" method="post">
+        <p>Gebe die URL an, die du kürzen willst</p>
+        <p><input type="text" name="url" id="url" value="{{ url|e(true) }}"></p>
+        <p>Optional kannst du der URL einen merkbaren Namen geben</p>
+        <p><input type="text" id="alias" name="alias">{#
+         #}<input type="submit" id="submit" value="Mach!"></p>
+        <p><input type="checkbox" name="private" id="private">
+           <label for="private">mache diese URL privat, also zeige sie
+             nicht auf der Liste</label></p>
+      </form>
+    {% endblock %}
+
+Wenn du dich über das Kommentar zwischen den Beiden Inputelementen wunderst,
+das ist ein sauberer Trick die Templates sauber zu halten aber keine
+Leerzeichen zwischen beide zu setzen.  Wir haben ein Stylesheet vorbereitet,
+welches dort keine Leerzeichen erwartet.
+
+Und dann ein zweites Template für die Anzeige-Seite
+(``templates/display.html``):
+
+.. sourcecode:: html+jinja
+
+    {% extends 'layout.html' %}
+    {% block body %}
+      <h2>Verkürzte URL</h2>
+      <p>
+        Die URL {{ url.target|urlize(40, true) }}
+        wurde gekürzt zu {{ url.short_url|urlize }}.
+      </p>
+    {% endblock %}
+
+Der `urlize`-Filter ist von Jinja und übersetzt eine URL(s) in einem Text in
+einen klickbaren Link.  Wenn du ihm einen Integer übergibst, wird er die
+Beschreibung dieser Links auf die Nummer der Zeichen setzen, indem man true als
+zweiten Parameter übergibt, fügt man ein `nofollow`-Flag hinzu.
+
+Da wir jetzt unsere ersten beiden Templates haben ist es Zeit den Server zu
+starten und auf den Teil der Anwendung zu schauen, der bereits funktioniert:
+neue URLs hinzufügen und weitergeleitet zu werden.
+
+Zwischenschritt: Das Design hinzufügen
+======================================
+
+Jetzt ist es Zeit etwas anderes zu machen: ein Design hinzufügen.
+Designelemente sind normalerweise in statischen CSS Stylesheets. Also müssen
+wir einige statische Dateien irgendwo hinlegen.  Aber das ist ein wenig
+kompliziert.  Wenn du mit bis jetzt mit PHP gearbeitet hast, wirst du gemerkt
+haben, dass dort nichts ist, das die URL zum Dateisystempfad übersetzt und auf
+die statischen Dateien direkt von der URL zugreifen.  Du musst dem Webserver
+oder unserem Entwicklungsserver explizit sagen, dass es einen Pfad gibt, der
+die statischen Dateien beinhaltet.
+
+Django empfiehlt eine seperate Subdomain und einen eigenen Server für die
+statischen Dateien, was eine schrecklich gute Idee für eine große Ladeumgebung
+ist, aber zu viel des Guten für diese simple Anwendung.
+
+Hier also folgendes Vorgehen: Wir lassen unsere Anwendung die statischen Dateien
+hosten, aber im Produktionsmodus solltest du dem Apachen sagen, dass er diese
+Dateien hosten soll, mit Hilfe der `Alias`-Direktive in der
+Apachekonfiguration:
+
+.. sourcecode:: apache
+
+    Alias /static /path/to/static/files
+
+Das ist um vieles schneller.
+
+Aber wie sagen wir unserer Anwendung, dass wir sie mit dem statischen
+Dateienordner von unserem Anwendungpaket als ``/static`` teilen wollen?
+Glücklicherweise ist das ziemlich einfach, weil Werkzeug dafür eine
+WSGI-Middleware liefert.  Jetzt gibt es zwei Möglichkeiten diese Middleware zu
+integrieren.  Ein Weg ist, die ganze Anwendung in diese Middleware zu wrappen
+(diesen Weg empfehlen wir wirklich nicht) und der andere ist, die
+Ausführungsfunktion zu wrappen (viel besser, weil wir die Referenz zu dem
+Anwendungsobjekt nicht verlieren).  Also gehen wir zurück zu
+``application.py`` und verändern den Code ein wenig.
+
+Als erstes musst du einen neuen Import hinzufügen und den Pfad zu den
+statischen Dateien kalkulieren.
+
+.. sourcecode:: python
+
+    from os import path
+    from werkzeug import SharedDataMiddleware
+
+    STATIC_PATH = path.join(path.dirname(__file__), 'static')
+
+Es wäre besser, die Pfadkalkulation in die ``utils.py``-Datei zu packen, weil
+wir den Templatepfad bereits dort kalkuliert haben.  Aber das interessiert
+nicht wirklich und wegen der Einfachheit können wir es im Anwendungsmodul
+lassen.
+
+Wie können wir also die Ausführungsfunktion wrappen?  Theoretisch müssen wir
+einfach ``self.__call__ = (self.__call__)`` sagen, aber leider klappt das
+nicht in Python.  Es ist aber nicht viel schwieriger.  Benenne einfach
+`__call__` in `dispatch` um und füge eine neue `__call__`-Methode hinzu:
+
+.. sourcecode:: python
+
+        def __call__(self, environ, start_response):
+            return self.dispatch(environ, start_response)
+
+Jetzt können wir in unsere `__init__`-Funktion gehen und die Middleware
+zuschalten indem wir die `dispatch`-Methode wrappen:
+
+.. sourcecode:: python
+
+        self.dispatch = SharedDataMiddleware(self.dispatch, {
+            '/static':  STATIC_PATH
+        })
+
+Das war jetzt nicht schwer.  Mit diesem Weg können wir WSGI-Middlewares in der
+Anwendungsklasse einhaken!
+