python package builds moved from awips2-builds to awips2-rpm
|
@ -1,17 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<projectDescription>
|
|
||||||
<name>pythonPackages</name>
|
|
||||||
<comment></comment>
|
|
||||||
<projects>
|
|
||||||
</projects>
|
|
||||||
<buildSpec>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.python.pydev.PyDevBuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
</buildSpec>
|
|
||||||
<natures>
|
|
||||||
<nature>org.python.pydev.pythonNature</nature>
|
|
||||||
</natures>
|
|
||||||
</projectDescription>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<?eclipse-pydev version="1.0"?>
|
|
||||||
|
|
||||||
<pydev_project>
|
|
||||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
|
|
||||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
|
||||||
</pydev_project>
|
|
|
@ -1,3 +0,0 @@
|
||||||
#Sun Mar 20 15:17:07 CDT 2011
|
|
||||||
eclipse.preferences.version=1
|
|
||||||
encoding//werkzeug/examples/plnt/__init__.py=utf-8
|
|
|
@ -1,6 +0,0 @@
|
||||||
The python packages dir exists solely for pure python packages.
|
|
||||||
NO JAVA/JEP CODE ALLOWED.
|
|
||||||
|
|
||||||
Each dir under this directory should be able to be copied to an installed python's
|
|
||||||
site-packages dir and function correctly, being able to be imported and used
|
|
||||||
as needed.
|
|
|
@ -1,23 +0,0 @@
|
||||||
This file contains a listing of the versions of every applicable package.
|
|
||||||
This file SHOULD be updated whenever a newer version of a particular package is checked
|
|
||||||
in.
|
|
||||||
|
|
||||||
cherrpy = 3.1.2 [3.2.2]
|
|
||||||
dynamicserialize = RAYTHEON-OWNED (AWIPS II)
|
|
||||||
h5py = 1.3.0 [2.1.0]
|
|
||||||
matplotlib = 0.99.1.1 [1.2.0]
|
|
||||||
nose = 0.11.1 [1.2.1]
|
|
||||||
numpy = 1.5.0b1 [1.6.2]
|
|
||||||
pil = 1.1.6 [1.1.7]
|
|
||||||
pmw = 1.3.2 [2.0.0]
|
|
||||||
pupynere = 1.0.13 [1.0.15]
|
|
||||||
qpid = 0.5 [0.6]
|
|
||||||
scientific = 2.8 [2.8]
|
|
||||||
scipy = 0.8.0 [0.11.0]
|
|
||||||
tables = 2.1.2 [2.4.0]
|
|
||||||
thrift = 20080411p1 [0.9.0]
|
|
||||||
tpg = 3.1.2 [3.2.1]
|
|
||||||
ufpy = RAYTHEON-OWNED (AWIPS II)
|
|
||||||
werkzeug = 0.6.2 [0.8.3]
|
|
||||||
shapely = 1.2.16 [1.2.16]
|
|
||||||
msaslaps = RAYTHEON-OWNED (AWIPS II)
|
|
|
@ -1,60 +0,0 @@
|
||||||
Werkzeug is written and maintained by the Werkzeug Team and various
|
|
||||||
contributors:
|
|
||||||
|
|
||||||
Project Leader / Developer:
|
|
||||||
|
|
||||||
- Armin Ronacher <armin.ronacher@active-4.com>
|
|
||||||
|
|
||||||
- Georg Brandl
|
|
||||||
- Leif K-Brooks <eurleif@gmail.com>
|
|
||||||
- Thomas Johansson
|
|
||||||
- Marian Sigler
|
|
||||||
- Ronny Pfannschmidt
|
|
||||||
- Noah Slater <nslater@tumbolia.org>
|
|
||||||
- Alec Thomas
|
|
||||||
- Shannon Behrens
|
|
||||||
- Christoph Rauch
|
|
||||||
- Clemens Hermann
|
|
||||||
- Jason Kirtland
|
|
||||||
- Ali Afshar
|
|
||||||
- Christopher Grebs <cg@webshox.org>
|
|
||||||
- Sean Cazzell <seancazzell@gmail.com>
|
|
||||||
- Florent Xicluna
|
|
||||||
- Kyle Dawkins
|
|
||||||
- Pedro Algarvio
|
|
||||||
- Zahari Petkov
|
|
||||||
- Ludvig Ericson
|
|
||||||
- Kenneth Reitz
|
|
||||||
- Daniel Neuhäuser
|
|
||||||
- Markus Unterwaditzer
|
|
||||||
- Joe Esposito <joe@joeyespo.com>
|
|
||||||
- Abhinav Upadhyay <er.abhinav.upadhyay@gmail.com>
|
|
||||||
- immerrr <immerrr@gmail.com>
|
|
||||||
- Cédric Krier
|
|
||||||
- Phil Jones
|
|
||||||
- Michael Hunsinger
|
|
||||||
- Lars Holm Nielsen
|
|
||||||
- Joël Charles
|
|
||||||
- Benjamin Dopplinger
|
|
||||||
|
|
||||||
Contributors of code for werkzeug/examples are:
|
|
||||||
|
|
||||||
- Itay Neeman <itay@neeman.net>
|
|
||||||
|
|
||||||
The SSL related parts of the Werkzeug development server are partially
|
|
||||||
taken from Paste. Same thing is true for the range support which comes
|
|
||||||
from WebOb which is a Paste project. The original code is MIT licensed which
|
|
||||||
is largely compatible with the modfied BSD license. The following copyrights
|
|
||||||
apply:
|
|
||||||
|
|
||||||
- (c) 2005 Ian Bicking and contributors
|
|
||||||
- (c) 2005 Clark C. Evans
|
|
||||||
|
|
||||||
The rename() function from the posixemulation was taken almost unmodified
|
|
||||||
from the Trac project's utility module. The original code is BSD licensed
|
|
||||||
with the following copyrights from that module:
|
|
||||||
|
|
||||||
- (c) 2003-2009 Edgewall Software
|
|
||||||
- (c) 2003-2006 Jonas Borgström <jonas@edgewall.com>
|
|
||||||
- (c) 2006 Matthew Good <trac@matt-good.net>
|
|
||||||
- (c) 2005-2006 Christian Boos <cboos@neuf.fr>
|
|
|
@ -1,29 +0,0 @@
|
||||||
Copyright (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* The names of the contributors may not be used to endorse or
|
|
||||||
promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,10 +0,0 @@
|
||||||
include Makefile CHANGES LICENSE AUTHORS
|
|
||||||
recursive-include werkzeug/debug/shared *
|
|
||||||
recursive-include tests *
|
|
||||||
recursive-include docs *
|
|
||||||
recursive-include artwork *
|
|
||||||
recursive-include examples *
|
|
||||||
|
|
||||||
prune docs/_build
|
|
||||||
prune docs/_themes
|
|
||||||
global-exclude *.py[cdo] __pycache__ *.so *.pyd
|
|
|
@ -1,35 +0,0 @@
|
||||||
#
|
|
||||||
# Werkzeug Makefile
|
|
||||||
# ~~~~~~~~~~~~~~~~~
|
|
||||||
#
|
|
||||||
# Shortcuts for various tasks.
|
|
||||||
#
|
|
||||||
# :copyright: (c) 2008 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
# :license: BSD, see LICENSE for more details.
|
|
||||||
#
|
|
||||||
|
|
||||||
documentation:
|
|
||||||
@(cd docs; make html)
|
|
||||||
|
|
||||||
release:
|
|
||||||
python scripts/make-release.py
|
|
||||||
|
|
||||||
test:
|
|
||||||
py.test --tb=native
|
|
||||||
|
|
||||||
tox-test:
|
|
||||||
tox
|
|
||||||
|
|
||||||
coverage:
|
|
||||||
@(coverage run --source=werkzeug --module py.test $(TEST_OPTIONS) $(TESTS))
|
|
||||||
|
|
||||||
doctest:
|
|
||||||
@(cd docs; sphinx-build -b doctest . _build/doctest)
|
|
||||||
|
|
||||||
upload-docs:
|
|
||||||
$(MAKE) -C docs html dirhtml latex
|
|
||||||
$(MAKE) -C docs/_build/latex all-pdf
|
|
||||||
cd docs/_build/; mv html werkzeug-docs; zip -r werkzeug-docs.zip werkzeug-docs; mv werkzeug-docs html
|
|
||||||
rsync -a docs/_build/dirhtml/ flow.srv.pocoo.org:/srv/websites/werkzeug.pocoo.org/docs/
|
|
||||||
rsync -a docs/_build/latex/Werkzeug.pdf flow.srv.pocoo.org:/srv/websites/werkzeug.pocoo.org/docs/
|
|
||||||
rsync -a docs/_build/werkzeug-docs.zip flow.srv.pocoo.org:/srv/websites/werkzeug.pocoo.org/docs/werkzeug-docs.zip
|
|
|
@ -1,78 +0,0 @@
|
||||||
Metadata-Version: 1.1
|
|
||||||
Name: Werkzeug
|
|
||||||
Version: 0.12.1
|
|
||||||
Summary: The Swiss Army knife of Python web development
|
|
||||||
Home-page: http://werkzeug.pocoo.org/
|
|
||||||
Author: Armin Ronacher
|
|
||||||
Author-email: armin.ronacher@active-4.com
|
|
||||||
License: BSD
|
|
||||||
Description:
|
|
||||||
Werkzeug
|
|
||||||
========
|
|
||||||
|
|
||||||
Werkzeug started as simple collection of various utilities for WSGI
|
|
||||||
applications and has become one of the most advanced WSGI utility
|
|
||||||
modules. It includes a powerful debugger, full featured request and
|
|
||||||
response objects, HTTP utilities to handle entity tags, cache control
|
|
||||||
headers, HTTP dates, cookie handling, file uploads, a powerful URL
|
|
||||||
routing system and a bunch of community contributed addon modules.
|
|
||||||
|
|
||||||
Werkzeug is unicode aware and doesn't enforce a specific template
|
|
||||||
engine, database adapter or anything else. It doesn't even enforce
|
|
||||||
a specific way of handling requests and leaves all that up to the
|
|
||||||
developer. It's most useful for end user applications which should work
|
|
||||||
on as many server environments as possible (such as blogs, wikis,
|
|
||||||
bulletin boards, etc.).
|
|
||||||
|
|
||||||
Details and example applications are available on the
|
|
||||||
`Werkzeug website <http://werkzeug.pocoo.org/>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
- unicode awareness
|
|
||||||
|
|
||||||
- request and response objects
|
|
||||||
|
|
||||||
- various utility functions for dealing with HTTP headers such as
|
|
||||||
`Accept` and `Cache-Control` headers.
|
|
||||||
|
|
||||||
- thread local objects with proper cleanup at request end
|
|
||||||
|
|
||||||
- an interactive debugger
|
|
||||||
|
|
||||||
- A simple WSGI server with support for threading and forking
|
|
||||||
with an automatic reloader.
|
|
||||||
|
|
||||||
- a flexible URL routing system with REST support.
|
|
||||||
|
|
||||||
- fully WSGI compatible
|
|
||||||
|
|
||||||
|
|
||||||
Development Version
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The Werkzeug development version can be installed by cloning the git
|
|
||||||
repository from `github`_::
|
|
||||||
|
|
||||||
git clone git@github.com:pallets/werkzeug.git
|
|
||||||
|
|
||||||
.. _github: http://github.com/pallets/werkzeug
|
|
||||||
|
|
||||||
Platform: any
|
|
||||||
Classifier: Development Status :: 5 - Production/Stable
|
|
||||||
Classifier: Environment :: Web Environment
|
|
||||||
Classifier: Intended Audience :: Developers
|
|
||||||
Classifier: License :: OSI Approved :: BSD License
|
|
||||||
Classifier: Operating System :: OS Independent
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Classifier: Programming Language :: Python :: 2
|
|
||||||
Classifier: Programming Language :: Python :: 2.6
|
|
||||||
Classifier: Programming Language :: Python :: 2.7
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3.3
|
|
||||||
Classifier: Programming Language :: Python :: 3.4
|
|
||||||
Classifier: Programming Language :: Python :: 3.5
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
@ -1,40 +0,0 @@
|
||||||
Werkzeug
|
|
||||||
========
|
|
||||||
|
|
||||||
Werkzeug started as simple collection of various utilities for WSGI
|
|
||||||
applications and has become one of the most advanced WSGI utility
|
|
||||||
modules. It includes a powerful debugger, full-featured request and
|
|
||||||
response objects, HTTP utilities to handle entity tags, cache control
|
|
||||||
headers, HTTP dates, cookie handling, file uploads, a powerful URL
|
|
||||||
routing system and a bunch of community-contributed addon modules.
|
|
||||||
|
|
||||||
Werkzeug is unicode aware and doesn't enforce a specific template
|
|
||||||
engine, database adapter or anything else. It doesn't even enforce
|
|
||||||
a specific way of handling requests and leaves all that up to the
|
|
||||||
developer. It's most useful for end user applications which should work
|
|
||||||
on as many server environments as possible (such as blogs, wikis,
|
|
||||||
bulletin boards, etc.).
|
|
||||||
|
|
||||||
Details and example applications are available on the
|
|
||||||
`Werkzeug website <http://werkzeug.pocoo.org/>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Branches
|
|
||||||
--------
|
|
||||||
|
|
||||||
+----------------------+-------------------------------------------------------------------------------+
|
|
||||||
| ``master`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=master |
|
|
||||||
| | :target: https://travis-ci.org/pallets/werkzeug |
|
|
||||||
+----------------------+-------------------------------------------------------------------------------+
|
|
||||||
| ``0.12-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.12-maintenance |
|
|
||||||
| | :target: https://travis-ci.org/pallets/werkzeug |
|
|
||||||
+----------------------+-------------------------------------------------------------------------------+
|
|
||||||
| ``0.11-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.11-maintenance |
|
|
||||||
| | :target: https://travis-ci.org/pallets/werkzeug |
|
|
||||||
+----------------------+-------------------------------------------------------------------------------+
|
|
||||||
| ``0.10-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.10-maintenance |
|
|
||||||
| | :target: https://travis-ci.org/pallets/werkzeug |
|
|
||||||
+----------------------+-------------------------------------------------------------------------------+
|
|
||||||
| ``0.9-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.9-maintenance |
|
|
||||||
| | :target: https://travis-ci.org/pallets/werkzeug |
|
|
||||||
+----------------------+-------------------------------------------------------------------------------+
|
|
|
@ -1,78 +0,0 @@
|
||||||
Metadata-Version: 1.1
|
|
||||||
Name: Werkzeug
|
|
||||||
Version: 0.12.1
|
|
||||||
Summary: The Swiss Army knife of Python web development
|
|
||||||
Home-page: http://werkzeug.pocoo.org/
|
|
||||||
Author: Armin Ronacher
|
|
||||||
Author-email: armin.ronacher@active-4.com
|
|
||||||
License: BSD
|
|
||||||
Description:
|
|
||||||
Werkzeug
|
|
||||||
========
|
|
||||||
|
|
||||||
Werkzeug started as simple collection of various utilities for WSGI
|
|
||||||
applications and has become one of the most advanced WSGI utility
|
|
||||||
modules. It includes a powerful debugger, full featured request and
|
|
||||||
response objects, HTTP utilities to handle entity tags, cache control
|
|
||||||
headers, HTTP dates, cookie handling, file uploads, a powerful URL
|
|
||||||
routing system and a bunch of community contributed addon modules.
|
|
||||||
|
|
||||||
Werkzeug is unicode aware and doesn't enforce a specific template
|
|
||||||
engine, database adapter or anything else. It doesn't even enforce
|
|
||||||
a specific way of handling requests and leaves all that up to the
|
|
||||||
developer. It's most useful for end user applications which should work
|
|
||||||
on as many server environments as possible (such as blogs, wikis,
|
|
||||||
bulletin boards, etc.).
|
|
||||||
|
|
||||||
Details and example applications are available on the
|
|
||||||
`Werkzeug website <http://werkzeug.pocoo.org/>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
- unicode awareness
|
|
||||||
|
|
||||||
- request and response objects
|
|
||||||
|
|
||||||
- various utility functions for dealing with HTTP headers such as
|
|
||||||
`Accept` and `Cache-Control` headers.
|
|
||||||
|
|
||||||
- thread local objects with proper cleanup at request end
|
|
||||||
|
|
||||||
- an interactive debugger
|
|
||||||
|
|
||||||
- A simple WSGI server with support for threading and forking
|
|
||||||
with an automatic reloader.
|
|
||||||
|
|
||||||
- a flexible URL routing system with REST support.
|
|
||||||
|
|
||||||
- fully WSGI compatible
|
|
||||||
|
|
||||||
|
|
||||||
Development Version
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The Werkzeug development version can be installed by cloning the git
|
|
||||||
repository from `github`_::
|
|
||||||
|
|
||||||
git clone git@github.com:pallets/werkzeug.git
|
|
||||||
|
|
||||||
.. _github: http://github.com/pallets/werkzeug
|
|
||||||
|
|
||||||
Platform: any
|
|
||||||
Classifier: Development Status :: 5 - Production/Stable
|
|
||||||
Classifier: Environment :: Web Environment
|
|
||||||
Classifier: Intended Audience :: Developers
|
|
||||||
Classifier: License :: OSI Approved :: BSD License
|
|
||||||
Classifier: Operating System :: OS Independent
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Classifier: Programming Language :: Python :: 2
|
|
||||||
Classifier: Programming Language :: Python :: 2.6
|
|
||||||
Classifier: Programming Language :: Python :: 2.7
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3.3
|
|
||||||
Classifier: Programming Language :: Python :: 3.4
|
|
||||||
Classifier: Programming Language :: Python :: 3.5
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
@ -1,292 +0,0 @@
|
||||||
AUTHORS
|
|
||||||
CHANGES
|
|
||||||
LICENSE
|
|
||||||
MANIFEST.in
|
|
||||||
Makefile
|
|
||||||
README.rst
|
|
||||||
setup.cfg
|
|
||||||
setup.py
|
|
||||||
Werkzeug.egg-info/PKG-INFO
|
|
||||||
Werkzeug.egg-info/SOURCES.txt
|
|
||||||
Werkzeug.egg-info/dependency_links.txt
|
|
||||||
Werkzeug.egg-info/not-zip-safe
|
|
||||||
Werkzeug.egg-info/requires.txt
|
|
||||||
Werkzeug.egg-info/top_level.txt
|
|
||||||
artwork/logo.png
|
|
||||||
artwork/logo.svg
|
|
||||||
docs/Makefile
|
|
||||||
docs/changes.rst
|
|
||||||
docs/conf.py
|
|
||||||
docs/contents.rst.inc
|
|
||||||
docs/datastructures.rst
|
|
||||||
docs/debug.rst
|
|
||||||
docs/exceptions.rst
|
|
||||||
docs/filesystem.rst
|
|
||||||
docs/http.rst
|
|
||||||
docs/index.rst
|
|
||||||
docs/installation.rst
|
|
||||||
docs/latexindex.rst
|
|
||||||
docs/levels.rst
|
|
||||||
docs/local.rst
|
|
||||||
docs/logo.pdf
|
|
||||||
docs/make.bat
|
|
||||||
docs/makearchive.py
|
|
||||||
docs/middlewares.rst
|
|
||||||
docs/python3.rst
|
|
||||||
docs/quickstart.rst
|
|
||||||
docs/request_data.rst
|
|
||||||
docs/routing.rst
|
|
||||||
docs/serving.rst
|
|
||||||
docs/terms.rst
|
|
||||||
docs/test.rst
|
|
||||||
docs/transition.rst
|
|
||||||
docs/tutorial.rst
|
|
||||||
docs/unicode.rst
|
|
||||||
docs/urls.rst
|
|
||||||
docs/utils.rst
|
|
||||||
docs/werkzeugext.py
|
|
||||||
docs/werkzeugstyle.sty
|
|
||||||
docs/wrappers.rst
|
|
||||||
docs/wsgi.rst
|
|
||||||
docs/_static/background.png
|
|
||||||
docs/_static/codebackground.png
|
|
||||||
docs/_static/contents.png
|
|
||||||
docs/_static/debug-screenshot.png
|
|
||||||
docs/_static/favicon.ico
|
|
||||||
docs/_static/header.png
|
|
||||||
docs/_static/navigation.png
|
|
||||||
docs/_static/navigation_active.png
|
|
||||||
docs/_static/shortly.png
|
|
||||||
docs/_static/shorty-screenshot.png
|
|
||||||
docs/_static/style.css
|
|
||||||
docs/_static/werkzeug.js
|
|
||||||
docs/_static/werkzeug.png
|
|
||||||
docs/_templates/sidebarintro.html
|
|
||||||
docs/_templates/sidebarlogo.html
|
|
||||||
docs/contrib/atom.rst
|
|
||||||
docs/contrib/cache.rst
|
|
||||||
docs/contrib/fixers.rst
|
|
||||||
docs/contrib/index.rst
|
|
||||||
docs/contrib/iterio.rst
|
|
||||||
docs/contrib/lint.rst
|
|
||||||
docs/contrib/profiler.rst
|
|
||||||
docs/contrib/securecookie.rst
|
|
||||||
docs/contrib/sessions.rst
|
|
||||||
docs/contrib/wrappers.rst
|
|
||||||
docs/deployment/cgi.rst
|
|
||||||
docs/deployment/fastcgi.rst
|
|
||||||
docs/deployment/index.rst
|
|
||||||
docs/deployment/mod_wsgi.rst
|
|
||||||
docs/deployment/proxying.rst
|
|
||||||
examples/README
|
|
||||||
examples/cookieauth.py
|
|
||||||
examples/httpbasicauth.py
|
|
||||||
examples/manage-coolmagic.py
|
|
||||||
examples/manage-couchy.py
|
|
||||||
examples/manage-cupoftee.py
|
|
||||||
examples/manage-i18nurls.py
|
|
||||||
examples/manage-plnt.py
|
|
||||||
examples/manage-shorty.py
|
|
||||||
examples/manage-simplewiki.py
|
|
||||||
examples/manage-webpylike.py
|
|
||||||
examples/upload.py
|
|
||||||
examples/contrib/README
|
|
||||||
examples/contrib/securecookie.py
|
|
||||||
examples/contrib/sessions.py
|
|
||||||
examples/coolmagic/__init__.py
|
|
||||||
examples/coolmagic/application.py
|
|
||||||
examples/coolmagic/helpers.py
|
|
||||||
examples/coolmagic/utils.py
|
|
||||||
examples/coolmagic/public/style.css
|
|
||||||
examples/coolmagic/templates/layout.html
|
|
||||||
examples/coolmagic/templates/static/about.html
|
|
||||||
examples/coolmagic/templates/static/index.html
|
|
||||||
examples/coolmagic/templates/static/not_found.html
|
|
||||||
examples/coolmagic/views/__init__.py
|
|
||||||
examples/coolmagic/views/static.py
|
|
||||||
examples/couchy/README
|
|
||||||
examples/couchy/__init__.py
|
|
||||||
examples/couchy/application.py
|
|
||||||
examples/couchy/models.py
|
|
||||||
examples/couchy/utils.py
|
|
||||||
examples/couchy/views.py
|
|
||||||
examples/couchy/static/style.css
|
|
||||||
examples/couchy/templates/display.html
|
|
||||||
examples/couchy/templates/layout.html
|
|
||||||
examples/couchy/templates/list.html
|
|
||||||
examples/couchy/templates/new.html
|
|
||||||
examples/couchy/templates/not_found.html
|
|
||||||
examples/cupoftee/__init__.py
|
|
||||||
examples/cupoftee/application.py
|
|
||||||
examples/cupoftee/db.py
|
|
||||||
examples/cupoftee/network.py
|
|
||||||
examples/cupoftee/pages.py
|
|
||||||
examples/cupoftee/utils.py
|
|
||||||
examples/cupoftee/shared/content.png
|
|
||||||
examples/cupoftee/shared/down.png
|
|
||||||
examples/cupoftee/shared/favicon.ico
|
|
||||||
examples/cupoftee/shared/header.png
|
|
||||||
examples/cupoftee/shared/logo.png
|
|
||||||
examples/cupoftee/shared/style.css
|
|
||||||
examples/cupoftee/shared/up.png
|
|
||||||
examples/cupoftee/templates/layout.html
|
|
||||||
examples/cupoftee/templates/missingpage.html
|
|
||||||
examples/cupoftee/templates/search.html
|
|
||||||
examples/cupoftee/templates/server.html
|
|
||||||
examples/cupoftee/templates/serverlist.html
|
|
||||||
examples/i18nurls/__init__.py
|
|
||||||
examples/i18nurls/application.py
|
|
||||||
examples/i18nurls/urls.py
|
|
||||||
examples/i18nurls/views.py
|
|
||||||
examples/i18nurls/templates/about.html
|
|
||||||
examples/i18nurls/templates/blog.html
|
|
||||||
examples/i18nurls/templates/index.html
|
|
||||||
examples/i18nurls/templates/layout.html
|
|
||||||
examples/partial/README
|
|
||||||
examples/partial/complex_routing.py
|
|
||||||
examples/plnt/__init__.py
|
|
||||||
examples/plnt/database.py
|
|
||||||
examples/plnt/sync.py
|
|
||||||
examples/plnt/utils.py
|
|
||||||
examples/plnt/views.py
|
|
||||||
examples/plnt/webapp.py
|
|
||||||
examples/plnt/shared/style.css
|
|
||||||
examples/plnt/templates/about.html
|
|
||||||
examples/plnt/templates/index.html
|
|
||||||
examples/plnt/templates/layout.html
|
|
||||||
examples/shortly/shortly.py
|
|
||||||
examples/shortly/static/style.css
|
|
||||||
examples/shortly/templates/404.html
|
|
||||||
examples/shortly/templates/layout.html
|
|
||||||
examples/shortly/templates/new_url.html
|
|
||||||
examples/shortly/templates/short_link_details.html
|
|
||||||
examples/shorty/__init__.py
|
|
||||||
examples/shorty/application.py
|
|
||||||
examples/shorty/models.py
|
|
||||||
examples/shorty/utils.py
|
|
||||||
examples/shorty/views.py
|
|
||||||
examples/shorty/static/style.css
|
|
||||||
examples/shorty/templates/display.html
|
|
||||||
examples/shorty/templates/layout.html
|
|
||||||
examples/shorty/templates/list.html
|
|
||||||
examples/shorty/templates/new.html
|
|
||||||
examples/shorty/templates/not_found.html
|
|
||||||
examples/simplewiki/__init__.py
|
|
||||||
examples/simplewiki/actions.py
|
|
||||||
examples/simplewiki/application.py
|
|
||||||
examples/simplewiki/database.py
|
|
||||||
examples/simplewiki/specialpages.py
|
|
||||||
examples/simplewiki/utils.py
|
|
||||||
examples/simplewiki/shared/style.css
|
|
||||||
examples/simplewiki/templates/action_diff.html
|
|
||||||
examples/simplewiki/templates/action_edit.html
|
|
||||||
examples/simplewiki/templates/action_log.html
|
|
||||||
examples/simplewiki/templates/action_revert.html
|
|
||||||
examples/simplewiki/templates/action_show.html
|
|
||||||
examples/simplewiki/templates/layout.html
|
|
||||||
examples/simplewiki/templates/macros.xml
|
|
||||||
examples/simplewiki/templates/missing_action.html
|
|
||||||
examples/simplewiki/templates/page_index.html
|
|
||||||
examples/simplewiki/templates/page_missing.html
|
|
||||||
examples/simplewiki/templates/recent_changes.html
|
|
||||||
examples/webpylike/example.py
|
|
||||||
examples/webpylike/webpylike.py
|
|
||||||
tests/__init__.py
|
|
||||||
tests/conftest.py
|
|
||||||
tests/test_compat.py
|
|
||||||
tests/test_datastructures.py
|
|
||||||
tests/test_debug.py
|
|
||||||
tests/test_exceptions.py
|
|
||||||
tests/test_formparser.py
|
|
||||||
tests/test_http.py
|
|
||||||
tests/test_internal.py
|
|
||||||
tests/test_local.py
|
|
||||||
tests/test_routing.py
|
|
||||||
tests/test_security.py
|
|
||||||
tests/test_serving.py
|
|
||||||
tests/test_test.py
|
|
||||||
tests/test_urls.py
|
|
||||||
tests/test_utils.py
|
|
||||||
tests/test_wrappers.py
|
|
||||||
tests/test_wsgi.py
|
|
||||||
tests/contrib/__init__.py
|
|
||||||
tests/contrib/test_atom.py
|
|
||||||
tests/contrib/test_cache.py
|
|
||||||
tests/contrib/test_fixers.py
|
|
||||||
tests/contrib/test_iterio.py
|
|
||||||
tests/contrib/test_securecookie.py
|
|
||||||
tests/contrib/test_sessions.py
|
|
||||||
tests/contrib/test_wrappers.py
|
|
||||||
tests/hypothesis/__init__.py
|
|
||||||
tests/hypothesis/test_urls.py
|
|
||||||
tests/multipart/ie7_full_path_request.txt
|
|
||||||
tests/multipart/test_collect.py
|
|
||||||
tests/multipart/firefox3-2png1txt/file1.png
|
|
||||||
tests/multipart/firefox3-2png1txt/file2.png
|
|
||||||
tests/multipart/firefox3-2png1txt/request.txt
|
|
||||||
tests/multipart/firefox3-2png1txt/text.txt
|
|
||||||
tests/multipart/firefox3-2pnglongtext/file1.png
|
|
||||||
tests/multipart/firefox3-2pnglongtext/file2.png
|
|
||||||
tests/multipart/firefox3-2pnglongtext/request.txt
|
|
||||||
tests/multipart/firefox3-2pnglongtext/text.txt
|
|
||||||
tests/multipart/ie6-2png1txt/file1.png
|
|
||||||
tests/multipart/ie6-2png1txt/file2.png
|
|
||||||
tests/multipart/ie6-2png1txt/request.txt
|
|
||||||
tests/multipart/ie6-2png1txt/text.txt
|
|
||||||
tests/multipart/opera8-2png1txt/file1.png
|
|
||||||
tests/multipart/opera8-2png1txt/file2.png
|
|
||||||
tests/multipart/opera8-2png1txt/request.txt
|
|
||||||
tests/multipart/opera8-2png1txt/text.txt
|
|
||||||
tests/multipart/webkit3-2png1txt/file1.png
|
|
||||||
tests/multipart/webkit3-2png1txt/file2.png
|
|
||||||
tests/multipart/webkit3-2png1txt/request.txt
|
|
||||||
tests/multipart/webkit3-2png1txt/text.txt
|
|
||||||
tests/res/test.txt
|
|
||||||
werkzeug/__init__.py
|
|
||||||
werkzeug/_compat.py
|
|
||||||
werkzeug/_internal.py
|
|
||||||
werkzeug/_reloader.py
|
|
||||||
werkzeug/datastructures.py
|
|
||||||
werkzeug/exceptions.py
|
|
||||||
werkzeug/filesystem.py
|
|
||||||
werkzeug/formparser.py
|
|
||||||
werkzeug/http.py
|
|
||||||
werkzeug/local.py
|
|
||||||
werkzeug/posixemulation.py
|
|
||||||
werkzeug/routing.py
|
|
||||||
werkzeug/script.py
|
|
||||||
werkzeug/security.py
|
|
||||||
werkzeug/serving.py
|
|
||||||
werkzeug/test.py
|
|
||||||
werkzeug/testapp.py
|
|
||||||
werkzeug/urls.py
|
|
||||||
werkzeug/useragents.py
|
|
||||||
werkzeug/utils.py
|
|
||||||
werkzeug/wrappers.py
|
|
||||||
werkzeug/wsgi.py
|
|
||||||
werkzeug/contrib/__init__.py
|
|
||||||
werkzeug/contrib/atom.py
|
|
||||||
werkzeug/contrib/cache.py
|
|
||||||
werkzeug/contrib/fixers.py
|
|
||||||
werkzeug/contrib/iterio.py
|
|
||||||
werkzeug/contrib/jsrouting.py
|
|
||||||
werkzeug/contrib/limiter.py
|
|
||||||
werkzeug/contrib/lint.py
|
|
||||||
werkzeug/contrib/profiler.py
|
|
||||||
werkzeug/contrib/securecookie.py
|
|
||||||
werkzeug/contrib/sessions.py
|
|
||||||
werkzeug/contrib/testtools.py
|
|
||||||
werkzeug/contrib/wrappers.py
|
|
||||||
werkzeug/debug/__init__.py
|
|
||||||
werkzeug/debug/console.py
|
|
||||||
werkzeug/debug/repr.py
|
|
||||||
werkzeug/debug/tbtools.py
|
|
||||||
werkzeug/debug/shared/FONT_LICENSE
|
|
||||||
werkzeug/debug/shared/console.png
|
|
||||||
werkzeug/debug/shared/debugger.js
|
|
||||||
werkzeug/debug/shared/jquery.js
|
|
||||||
werkzeug/debug/shared/less.png
|
|
||||||
werkzeug/debug/shared/more.png
|
|
||||||
werkzeug/debug/shared/source.png
|
|
||||||
werkzeug/debug/shared/style.css
|
|
||||||
werkzeug/debug/shared/ubuntu.ttf
|
|
|
@ -1,6 +0,0 @@
|
||||||
|
|
||||||
[termcolor]
|
|
||||||
termcolor
|
|
||||||
|
|
||||||
[watchdog]
|
|
||||||
watchdog
|
|
|
@ -1 +0,0 @@
|
||||||
werkzeug
|
|
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 16 KiB |
|
@ -1,118 +0,0 @@
|
||||||
# Makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
|
||||||
SPHINXOPTS =
|
|
||||||
SPHINXBUILD = sphinx-build
|
|
||||||
PAPER =
|
|
||||||
BUILDDIR = _build
|
|
||||||
|
|
||||||
# Internal variables.
|
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
|
||||||
PAPEROPT_letter = -D latex_paper_size=letter
|
|
||||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|
||||||
|
|
||||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
|
||||||
@echo " html to make standalone HTML files"
|
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
|
||||||
@echo " singlehtml to make a single large HTML file"
|
|
||||||
@echo " pickle to make pickle files"
|
|
||||||
@echo " json to make JSON files"
|
|
||||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
|
||||||
@echo " qthelp to make HTML files and a qthelp project"
|
|
||||||
@echo " devhelp to make HTML files and a Devhelp project"
|
|
||||||
@echo " epub to make an epub"
|
|
||||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
|
||||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
|
||||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
|
||||||
@echo " linkcheck to check all external links for integrity"
|
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
|
||||||
|
|
||||||
clean:
|
|
||||||
-rm -rf $(BUILDDIR)/*
|
|
||||||
|
|
||||||
html:
|
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
|
||||||
|
|
||||||
dirhtml:
|
|
||||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
|
||||||
|
|
||||||
singlehtml:
|
|
||||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
|
||||||
|
|
||||||
pickle:
|
|
||||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the pickle files."
|
|
||||||
|
|
||||||
json:
|
|
||||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the JSON files."
|
|
||||||
|
|
||||||
htmlhelp:
|
|
||||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
|
||||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
|
||||||
|
|
||||||
qthelp:
|
|
||||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
|
||||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
|
||||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp"
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc"
|
|
||||||
|
|
||||||
devhelp:
|
|
||||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished."
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Flask"
|
|
||||||
@echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask"
|
|
||||||
@echo "# devhelp"
|
|
||||||
|
|
||||||
epub:
|
|
||||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
|
||||||
|
|
||||||
latex:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
|
||||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
|
||||||
"run these through (pdf)latex."
|
|
||||||
|
|
||||||
latexpdf: latex
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
|
|
||||||
@echo "Running LaTeX files through pdflatex..."
|
|
||||||
make -C _build/latex all-pdf
|
|
||||||
@echo "pdflatex finished; the PDF files are in _build/latex."
|
|
||||||
|
|
||||||
changes:
|
|
||||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
|
||||||
@echo
|
|
||||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
|
||||||
|
|
||||||
linkcheck:
|
|
||||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
|
||||||
@echo
|
|
||||||
@echo "Link check complete; look for any errors in the above output " \
|
|
||||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
|
||||||
|
|
||||||
doctest:
|
|
||||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
|
||||||
@echo "Testing of doctests in the sources finished, look at the " \
|
|
||||||
"results in $(BUILDDIR)/doctest/output.txt."
|
|
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 204 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 231 B |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 83 KiB |
|
@ -1,423 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
letter-spacing: -0.01em;
|
|
||||||
line-height: 150%;
|
|
||||||
text-align: center;
|
|
||||||
background: #AFC1C4 url(background.png);
|
|
||||||
color: black;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #CA7900;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #2491CF;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
|
||||||
font-size: 0.85em;
|
|
||||||
letter-spacing: 0.015em;
|
|
||||||
padding: 0.3em 0.7em;
|
|
||||||
border: 1px solid #aaa;
|
|
||||||
border-right-color: #ddd;
|
|
||||||
border-bottom-color: #ddd;
|
|
||||||
background: #f8f8f8 url(codebackground.png);
|
|
||||||
}
|
|
||||||
|
|
||||||
cite, code, tt {
|
|
||||||
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
|
||||||
font-size: 0.95em;
|
|
||||||
letter-spacing: 0.01em;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt {
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.func-signature {
|
|
||||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
|
|
||||||
font-size: 0.85em;
|
|
||||||
background-color: transparent;
|
|
||||||
border-bottom: none;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
|
||||||
margin-top: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd p.first {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd p.last {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
line-height: 150%;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.syntax {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.page {
|
|
||||||
background: white url(contents.png) 0 130px;
|
|
||||||
border: 1px solid #aaa;
|
|
||||||
width: 740px;
|
|
||||||
margin: 20px auto 20px auto;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.header {
|
|
||||||
background-image: url(header.png);
|
|
||||||
height: 100px;
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.header h1 {
|
|
||||||
float: right;
|
|
||||||
position: absolute;
|
|
||||||
margin: -43px 0 0 585px;
|
|
||||||
height: 180px;
|
|
||||||
width: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.header h1 a {
|
|
||||||
display: block;
|
|
||||||
background-image: url(werkzeug.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
height: 180px;
|
|
||||||
width: 180px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: white!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.header span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.header p {
|
|
||||||
background-image: url(header_invert.png);
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px;
|
|
||||||
height: 80px;
|
|
||||||
color: white;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation {
|
|
||||||
background-image: url(navigation.png);
|
|
||||||
height: 2em;
|
|
||||||
list-style: none;
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
height: 2em;
|
|
||||||
line-height: 1.75em;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li a {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 10px 0 10px;
|
|
||||||
color: #EE9816;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li a:hover {
|
|
||||||
color: #3CA8E7;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li.active {
|
|
||||||
background-image: url(navigation_active.png);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li.active a {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li.indexlink a {
|
|
||||||
font-size: 0.9em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #11557C;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.body {
|
|
||||||
margin: 0 20px 0 20px;
|
|
||||||
padding: 0.5em 0 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0.8em 0 0.5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.7em 0 0.3em 0;
|
|
||||||
font-size: 1.5em;
|
|
||||||
color: #11557C;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin: 1.3em 0 0.2em 0;
|
|
||||||
font-size: 1.35em;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 1em 0 -0.3em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 a, h3 a, h4 a, h5 a, h6 a {
|
|
||||||
color: black!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink {
|
|
||||||
color: #B4B4B4!important;
|
|
||||||
font-size: 0.8em;
|
|
||||||
padding: 0 4px 0 4px;
|
|
||||||
text-decoration: none!important;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1:hover > a.headerlink,
|
|
||||||
h2:hover > a.headerlink,
|
|
||||||
h3:hover > a.headerlink,
|
|
||||||
h4:hover > a.headerlink,
|
|
||||||
h5:hover > a.headerlink,
|
|
||||||
h6:hover > a.headerlink,
|
|
||||||
dt:hover > a.headerlink {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink:hover {
|
|
||||||
background-color: #B4B4B4;
|
|
||||||
color: #F0F0F0!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 0 -0.5em 0 -0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td, table th {
|
|
||||||
padding: 0.2em 0.5em 0.2em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer {
|
|
||||||
background-color: #E3EFF1;
|
|
||||||
color: #86989B;
|
|
||||||
padding: 3px 8px 3px 0;
|
|
||||||
clear: both;
|
|
||||||
font-size: 0.8em;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer a {
|
|
||||||
color: #86989B;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc {
|
|
||||||
float: right;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #86989B;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 0 1em 1em;
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc h4 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.9em;
|
|
||||||
padding: 0.1em 0 0.1em 0.6em;
|
|
||||||
margin: 0;
|
|
||||||
color: white;
|
|
||||||
border-bottom: 1px solid #86989B;
|
|
||||||
background-color: #AFC1C4;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc ul {
|
|
||||||
margin: 1em 0 1em 0;
|
|
||||||
padding: 0 0 0 1em;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc ul li {
|
|
||||||
margin: 0.5em 0 0.5em 0;
|
|
||||||
font-size: 0.9em;
|
|
||||||
line-height: 130%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc ul li p {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc ul ul {
|
|
||||||
margin: 0.2em 0 0.2em 0;
|
|
||||||
padding: 0 0 0 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc ul ul li {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition, div.warning, div#toc {
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin: 1em 0 0 0;
|
|
||||||
border: 1px solid #86989B;
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition p, div.warning p, div#toc p {
|
|
||||||
margin: 0.5em 1em 0.5em 1em;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition pre, div.warning pre, div#toc pre {
|
|
||||||
margin: 0.4em 1em 0.4em 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition p.admonition-title,
|
|
||||||
div.warning p.admonition-title,
|
|
||||||
div#toc h3 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.1em 0 0.1em 0.5em;
|
|
||||||
color: white;
|
|
||||||
border-bottom: 1px solid #86989B;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #AFC1C4;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.warning {
|
|
||||||
border: 1px solid #940000;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.warning p.admonition-title {
|
|
||||||
background-color: #CF0000;
|
|
||||||
border-bottom-color: #940000;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition ul, div.admonition ol,
|
|
||||||
div.warning ul, div.warning ol,
|
|
||||||
div#toc ul, div#toc ol {
|
|
||||||
margin: 0.1em 0.5em 0.5em 3em;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#toc div.inner {
|
|
||||||
border-top: 1px solid #86989B;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#toc h3 {
|
|
||||||
border-bottom: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#toc h3:hover {
|
|
||||||
background-color: #86989B;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#toc ul {
|
|
||||||
margin: 2px 0 2px 20px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#toc ul li {
|
|
||||||
line-height: 125%;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.function dt,
|
|
||||||
dl.class dt,
|
|
||||||
dl.exception dt,
|
|
||||||
dl.method dt,
|
|
||||||
dl.attribute dt {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt .descname {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt .descname, dt .descclassname {
|
|
||||||
padding: 0;
|
|
||||||
background: transparent;
|
|
||||||
border-bottom: 1px solid #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt .descclassname {
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl dt big {
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl p + p {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.versionmodified {
|
|
||||||
color: #4B4A49;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.versionadded {
|
|
||||||
color: #30691A;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list td.field-body ul.simple {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0!important;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable td {
|
|
||||||
width: 50%;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable dt {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable dd dt a {
|
|
||||||
color: black!important;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.jumpbox {
|
|
||||||
padding: 1em 0 0.4em 0;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
(function() {
|
|
||||||
Werkzeug = {};
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
$('#toc h3').click(function() {
|
|
||||||
$(this).next().slideToggle();
|
|
||||||
$(this).parent().toggleClass('toc-collapsed');
|
|
||||||
}).next().hide().parent().addClass('toc-collapsed');
|
|
||||||
});
|
|
||||||
})();
|
|
Before Width: | Height: | Size: 19 KiB |
|
@ -1,19 +0,0 @@
|
||||||
<h3>About Werkzeug</h3>
|
|
||||||
<p>
|
|
||||||
Werkzeug is a WSGI utility library. It can serve as the basis for a
|
|
||||||
custom framework.
|
|
||||||
</p>
|
|
||||||
<h3>Other Formats</h3>
|
|
||||||
<p>
|
|
||||||
You can download the documentation in other formats as well:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="http://werkzeug.pocoo.org/docs/werkzeug-docs.pdf">as PDF</a>
|
|
||||||
<li><a href="http://werkzeug.pocoo.org/docs/werkzeug-docs.zip">as zipped HTML</a>
|
|
||||||
</ul>
|
|
||||||
<h3>Useful Links</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="http://werkzeug.pocoo.org/">The Werkzeug Website</a></li>
|
|
||||||
<li><a href="http://pypi.python.org/pypi/Werkzeug">Werkzeug @ PyPI</a></li>
|
|
||||||
<li><a href="http://github.com/pallets/werkzeug">Werkzeug @ github</a></li>
|
|
||||||
</ul>
|
|
|
@ -1,3 +0,0 @@
|
||||||
<p class="logo"><a href="{{ pathto(master_doc) }}">
|
|
||||||
<img class="logo" src="{{ pathto('_static/werkzeug.png', 1) }}" alt="Logo"/>
|
|
||||||
</a></p>
|
|
|
@ -1,109 +0,0 @@
|
||||||
==================
|
|
||||||
Werkzeug Changelog
|
|
||||||
==================
|
|
||||||
|
|
||||||
.. module:: werkzeug
|
|
||||||
|
|
||||||
This file lists all major changes in Werkzeug over the versions.
|
|
||||||
For API breaking changes have a look at :ref:`api-changes`, they
|
|
||||||
are listed there in detail.
|
|
||||||
|
|
||||||
.. include:: ../CHANGES
|
|
||||||
|
|
||||||
.. _api-changes:
|
|
||||||
|
|
||||||
API Changes
|
|
||||||
===========
|
|
||||||
|
|
||||||
`0.9`
|
|
||||||
- Soft-deprecated the :attr:`BaseRequest.data` and
|
|
||||||
:attr:`BaseResponse.data` attributes and introduced new methods
|
|
||||||
to interact with entity data. This will allows in the future to
|
|
||||||
make better APIs to deal with request and response entity
|
|
||||||
bodies. So far there is no deprecation warning but users are
|
|
||||||
strongly encouraged to update.
|
|
||||||
- The :class:`Headers` and :class:`EnvironHeaders` datastructures
|
|
||||||
are now designed to operate on unicode data. This is a backwards
|
|
||||||
incompatible change and was necessary for the Python 3 support.
|
|
||||||
- The :class:`Headers` object no longer supports in-place operations
|
|
||||||
through the old ``linked`` method. This has been removed without
|
|
||||||
replacement due to changes on the encoding model.
|
|
||||||
|
|
||||||
`0.6.2`
|
|
||||||
- renamed the attribute `implicit_seqence_conversion` attribute of
|
|
||||||
the request object to `implicit_sequence_conversion`. Because
|
|
||||||
this is a feature that is typically unused and was only in there
|
|
||||||
for the 0.6 series we consider this a bug that does not require
|
|
||||||
backwards compatibility support which would be impossible to
|
|
||||||
properly implement.
|
|
||||||
|
|
||||||
`0.6`
|
|
||||||
- Old deprecations were removed.
|
|
||||||
- `cached_property.writeable` was deprecated.
|
|
||||||
- :meth:`BaseResponse.get_wsgi_headers` replaces the older
|
|
||||||
`BaseResponse.fix_headers` method. The older method stays
|
|
||||||
around for backwards compatibility reasons until 0.7.
|
|
||||||
- `BaseResponse.header_list` was deprecated. You should not
|
|
||||||
need this function, `get_wsgi_headers` and the `to_list`
|
|
||||||
method on the regular headers should serve as a replacement.
|
|
||||||
- Deprecated `BaseResponse.iter_encoded`'s charset parameter.
|
|
||||||
- :class:`LimitedStream` non-silent usage was deprecated.
|
|
||||||
- the `__repr__` of HTTP exceptions changed. This might break
|
|
||||||
doctests.
|
|
||||||
|
|
||||||
`0.5`
|
|
||||||
- Werkzeug switched away from wsgiref as library for the builtin
|
|
||||||
webserver.
|
|
||||||
- The `encoding` parameter for :class:`Template`\s is now called
|
|
||||||
`charset`. The older one will work for another two versions
|
|
||||||
but warn with a :exc:`DeprecationWarning`.
|
|
||||||
- The :class:`Client` has cookie support now which is enabled
|
|
||||||
by default.
|
|
||||||
- :meth:`BaseResponse._get_file_stream` is now passed more parameters
|
|
||||||
to make the function more useful. In 0.6 the old way to invoke
|
|
||||||
the method will no longer work. To support both newer and older
|
|
||||||
Werkzeug versions you can add all arguments to the signature and
|
|
||||||
provide default values for each of them.
|
|
||||||
- :func:`url_decode` no longer supports both `&` and `;` as
|
|
||||||
separator. This has to be specified explicitly now.
|
|
||||||
- The request object is now enforced to be read-only for all
|
|
||||||
attributes. If your code relies on modifications of some values
|
|
||||||
makes sure to create copies of them using the mutable counterparts!
|
|
||||||
- Some data structures that were only used on request objects are
|
|
||||||
now immutable as well. (:class:`Authorization` / :class:`Accept`
|
|
||||||
and subclasses)
|
|
||||||
- `CacheControl` was split up into :class:`RequestCacheControl`
|
|
||||||
and :class:`ResponseCacheControl`, the former being immutable.
|
|
||||||
The old class will go away in 0.6
|
|
||||||
- undocumented `werkzeug.test.File` was replaced by
|
|
||||||
:class:`FileWrapper`.
|
|
||||||
- it's not longer possible to pass dicts inside the `data` dict
|
|
||||||
in :class:`Client`. Use tuples instead.
|
|
||||||
- It's save to modify the return value of :meth:`MultiDict.getlist`
|
|
||||||
and methods that return lists in the :class:`MultiDict` now. The
|
|
||||||
class creates copies instead of revealing the internal lists.
|
|
||||||
However :class:`MultiDict.setlistdefault` still (and intentionally)
|
|
||||||
returns the internal list for modifications.
|
|
||||||
|
|
||||||
`0.3`
|
|
||||||
- Werkzeug 0.3 will be the last release with Python 2.3 compatibility.
|
|
||||||
- The `environ_property` is now read-only by default. This decision was
|
|
||||||
made because the request in general should be considered read-only.
|
|
||||||
|
|
||||||
`0.2`
|
|
||||||
- The `BaseReporterStream` is now part of the contrib module, the
|
|
||||||
new module is `werkzeug.contrib.reporterstream`. Starting with
|
|
||||||
`0.3`, the old import will not work any longer.
|
|
||||||
- `RequestRedirect` now uses a 301 status code. Previously a 302
|
|
||||||
status code was used incorrectly. If you want to continue using
|
|
||||||
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.
|
|
||||||
- The `response_body` attribute on the response object is now called
|
|
||||||
`data`. With Werkzeug 0.3 the old name will not work any longer.
|
|
||||||
- The file-like methods on the response object are deprecated. If
|
|
||||||
you want to use the response object as file like object use the
|
|
||||||
`Response` class or a subclass of `BaseResponse` and mix the new
|
|
||||||
`ResponseStreamMixin` class and use `response.stream`.
|
|
|
@ -1,214 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Werkzeug documentation build configuration file, created by
|
|
||||||
# sphinx-quickstart on Fri Jan 16 23:10:43 2009.
|
|
||||||
#
|
|
||||||
# This file is execfile()d with the current directory set to its containing dir.
|
|
||||||
#
|
|
||||||
# The contents of this file are pickled, so don't put values in the namespace
|
|
||||||
# that aren't pickleable (module imports are okay, they're removed automatically).
|
|
||||||
#
|
|
||||||
# Note that not all possible configuration values are present in this
|
|
||||||
# autogenerated file.
|
|
||||||
#
|
|
||||||
# All configuration values have a default; values that are commented out
|
|
||||||
# serve to show the default.
|
|
||||||
|
|
||||||
import sys, os
|
|
||||||
|
|
||||||
# If your extensions are in another directory, add it here. If the directory
|
|
||||||
# is relative to the documentation root, use os.path.abspath to make it
|
|
||||||
# absolute, like shown here.
|
|
||||||
sys.path.append(os.path.abspath('.'))
|
|
||||||
sys.path.append(os.path.abspath('_themes'))
|
|
||||||
|
|
||||||
# General configuration
|
|
||||||
# ---------------------
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
|
||||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
|
|
||||||
'sphinx.ext.doctest', 'werkzeugext']
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
|
||||||
templates_path = ['_templates']
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The encoding of source files.
|
|
||||||
#source_encoding = 'utf-8'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = u'Werkzeug'
|
|
||||||
copyright = u'2011, The Werkzeug Team'
|
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
|
||||||
# |version| and |release|, also used in various other places throughout the
|
|
||||||
# built documents.
|
|
||||||
|
|
||||||
import re
|
|
||||||
try:
|
|
||||||
import werkzeug
|
|
||||||
except ImportError:
|
|
||||||
sys.path.append(os.path.abspath('../'))
|
|
||||||
from werkzeug import __version__ as release
|
|
||||||
if 'dev' in release:
|
|
||||||
release = release[:release.find('dev') + 3]
|
|
||||||
if release == 'unknown':
|
|
||||||
version = release
|
|
||||||
else:
|
|
||||||
version = re.match(r'\d+\.\d+(?:\.\d+)?', release).group()
|
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
||||||
# for a list of supported languages.
|
|
||||||
#language = None
|
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
|
||||||
# non-false value, then it is used:
|
|
||||||
#today = ''
|
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
|
||||||
#today_fmt = '%B %d, %Y'
|
|
||||||
|
|
||||||
# List of documents that shouldn't be included in the build.
|
|
||||||
#unused_docs = []
|
|
||||||
|
|
||||||
# List of directories, relative to source directory, that shouldn't be searched
|
|
||||||
# for source files.
|
|
||||||
exclude_trees = ['_build']
|
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
|
||||||
#default_role = None
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
#add_module_names = True
|
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
|
||||||
# output. They are ignored by default.
|
|
||||||
#show_authors = False
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'werkzeug_theme_support.WerkzeugStyle'
|
|
||||||
|
|
||||||
# doctest setup code
|
|
||||||
doctest_global_setup = '''\
|
|
||||||
from werkzeug import *
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
# Options for HTML output
|
|
||||||
# -----------------------
|
|
||||||
|
|
||||||
html_theme = 'werkzeug'
|
|
||||||
html_theme_path = ['_themes']
|
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
|
||||||
# "<project> v<release> documentation".
|
|
||||||
#html_title = None
|
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
|
||||||
#html_short_title = None
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
|
||||||
# of the sidebar.
|
|
||||||
#html_logo = None
|
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
|
||||||
# pixels large.
|
|
||||||
#html_favicon = None
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
||||||
html_static_path = ['_static']
|
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
|
||||||
# using the given strftime format.
|
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
|
||||||
# typographically correct entities.
|
|
||||||
#html_use_smartypants = True
|
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
|
||||||
html_sidebars = {
|
|
||||||
'index': ['sidebarlogo.html', 'sidebarintro.html', 'sourcelink.html',
|
|
||||||
'searchbox.html'],
|
|
||||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
|
||||||
'sourcelink.html', 'searchbox.html']
|
|
||||||
}
|
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
|
||||||
# template names.
|
|
||||||
#html_additional_pages = {}
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#html_use_modindex = True
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
#html_use_index = True
|
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
|
||||||
#html_split_index = False
|
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
|
||||||
#html_show_sourcelink = True
|
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
|
||||||
# base URL from which the finished HTML is served.
|
|
||||||
#html_use_opensearch = ''
|
|
||||||
|
|
||||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
|
||||||
#html_file_suffix = ''
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = 'Werkzeugdoc'
|
|
||||||
|
|
||||||
|
|
||||||
# Options for LaTeX output
|
|
||||||
# ------------------------
|
|
||||||
|
|
||||||
# The paper size ('letter' or 'a4').
|
|
||||||
latex_paper_size = 'a4'
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, document class [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
('latexindex', 'Werkzeug.tex', ur'Werkzeug Documentation',
|
|
||||||
ur'The Werkzeug Team', 'manual'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Additional stuff for LaTeX
|
|
||||||
latex_elements = {
|
|
||||||
'fontpkg': r'\usepackage{mathpazo}',
|
|
||||||
'papersize': 'a4paper',
|
|
||||||
'pointsize': '12pt',
|
|
||||||
'preamble': r'''
|
|
||||||
\usepackage{werkzeugstyle}
|
|
||||||
|
|
||||||
% i hate you latex, here too
|
|
||||||
\DeclareUnicodeCharacter{2603}{\\N\{SNOWMAN\}}
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
|
|
||||||
latex_use_parts = True
|
|
||||||
|
|
||||||
latex_additional_files = ['werkzeugstyle.sty', 'logo.pdf']
|
|
||||||
|
|
||||||
latex_use_modindex = False
|
|
||||||
|
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
|
||||||
intersphinx_mapping = {
|
|
||||||
'http://docs.python.org/dev': None,
|
|
||||||
'http://docs.sqlalchemy.org/en/latest/': None
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
Getting Started
|
|
||||||
---------------
|
|
||||||
|
|
||||||
If you are new to Werkzeug or WSGI development in general you
|
|
||||||
should start here.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
installation
|
|
||||||
transition
|
|
||||||
tutorial
|
|
||||||
levels
|
|
||||||
quickstart
|
|
||||||
python3
|
|
||||||
|
|
||||||
Serving and Testing
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The development server and testing support and management script
|
|
||||||
utilities are covered here:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
serving
|
|
||||||
test
|
|
||||||
debug
|
|
||||||
|
|
||||||
Reference
|
|
||||||
---------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
wrappers
|
|
||||||
routing
|
|
||||||
wsgi
|
|
||||||
filesystem
|
|
||||||
http
|
|
||||||
datastructures
|
|
||||||
utils
|
|
||||||
urls
|
|
||||||
local
|
|
||||||
middlewares
|
|
||||||
exceptions
|
|
||||||
|
|
||||||
Deployment
|
|
||||||
----------
|
|
||||||
|
|
||||||
This section covers running your application in production on a web
|
|
||||||
server such as Apache or lighttpd.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 3
|
|
||||||
|
|
||||||
deployment/index
|
|
||||||
|
|
||||||
Contributed Modules
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
A lot of useful code contributed by the community is shipped with Werkzeug
|
|
||||||
as part of the `contrib` module:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 3
|
|
||||||
|
|
||||||
contrib/index
|
|
||||||
|
|
||||||
Additional Information
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
terms
|
|
||||||
unicode
|
|
||||||
request_data
|
|
||||||
changes
|
|
||||||
|
|
||||||
If you can’t find the information you’re looking for, have a look at the
|
|
||||||
index or try to find it using the search function:
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`search`
|
|
|
@ -1,10 +0,0 @@
|
||||||
================
|
|
||||||
Atom Syndication
|
|
||||||
================
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.atom
|
|
||||||
|
|
||||||
.. autoclass:: AtomFeed
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: FeedEntry
|
|
|
@ -1,36 +0,0 @@
|
||||||
=====
|
|
||||||
Cache
|
|
||||||
=====
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.cache
|
|
||||||
|
|
||||||
|
|
||||||
Cache System API
|
|
||||||
================
|
|
||||||
|
|
||||||
.. autoclass:: BaseCache
|
|
||||||
:members:
|
|
||||||
|
|
||||||
|
|
||||||
Cache Systems
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. autoclass:: NullCache
|
|
||||||
|
|
||||||
.. autoclass:: SimpleCache
|
|
||||||
|
|
||||||
.. autoclass:: MemcachedCache
|
|
||||||
|
|
||||||
.. class:: GAEMemcachedCache
|
|
||||||
|
|
||||||
This class is deprecated in favour of :class:`MemcachedCache` which
|
|
||||||
now supports Google Appengine as well.
|
|
||||||
|
|
||||||
.. versionchanged:: 0.8
|
|
||||||
Deprecated in favour of :class:`MemcachedCache`.
|
|
||||||
|
|
||||||
.. autoclass:: RedisCache
|
|
||||||
|
|
||||||
.. autoclass:: FileSystemCache
|
|
||||||
|
|
||||||
.. autoclass:: UWSGICache
|
|
|
@ -1,16 +0,0 @@
|
||||||
======
|
|
||||||
Fixers
|
|
||||||
======
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.fixers
|
|
||||||
|
|
||||||
.. autoclass:: CGIRootFix
|
|
||||||
|
|
||||||
.. autoclass:: PathInfoFromRequestUriFix
|
|
||||||
|
|
||||||
.. autoclass:: ProxyFix
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: HeaderRewriterFix
|
|
||||||
|
|
||||||
.. autoclass:: InternetExplorerFix
|
|
|
@ -1,19 +0,0 @@
|
||||||
===================
|
|
||||||
Contributed Modules
|
|
||||||
===================
|
|
||||||
|
|
||||||
A lot of useful code contributed by the community is shipped with Werkzeug
|
|
||||||
as part of the `contrib` module:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
atom
|
|
||||||
sessions
|
|
||||||
securecookie
|
|
||||||
cache
|
|
||||||
wrappers
|
|
||||||
iterio
|
|
||||||
fixers
|
|
||||||
profiler
|
|
||||||
lint
|
|
|
@ -1,8 +0,0 @@
|
||||||
=======
|
|
||||||
Iter IO
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.iterio
|
|
||||||
|
|
||||||
.. autoclass:: IterIO
|
|
||||||
:members:
|
|
|
@ -1,9 +0,0 @@
|
||||||
==========================
|
|
||||||
Lint Validation Middleware
|
|
||||||
==========================
|
|
||||||
|
|
||||||
.. currentmodule:: werkzeug.contrib.lint
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.lint
|
|
||||||
|
|
||||||
.. autoclass:: LintMiddleware
|
|
|
@ -1,11 +0,0 @@
|
||||||
=========================
|
|
||||||
WSGI Application Profiler
|
|
||||||
=========================
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.profiler
|
|
||||||
|
|
||||||
.. autoclass:: MergeStream
|
|
||||||
|
|
||||||
.. autoclass:: ProfilerMiddleware
|
|
||||||
|
|
||||||
.. autofunction:: make_action
|
|
|
@ -1,55 +0,0 @@
|
||||||
=============
|
|
||||||
Secure Cookie
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.securecookie
|
|
||||||
|
|
||||||
Security
|
|
||||||
========
|
|
||||||
|
|
||||||
The default implementation uses Pickle as this is the only module that
|
|
||||||
used to be available in the standard library when this module was created.
|
|
||||||
If you have simplejson available it's strongly recommended to create a
|
|
||||||
subclass and replace the serialization method::
|
|
||||||
|
|
||||||
import json
|
|
||||||
from werkzeug.contrib.securecookie import SecureCookie
|
|
||||||
|
|
||||||
class JSONSecureCookie(SecureCookie):
|
|
||||||
serialization_method = json
|
|
||||||
|
|
||||||
The weakness of Pickle is that if someone gains access to the secret key
|
|
||||||
the attacker can not only modify the session but also execute arbitrary
|
|
||||||
code on the server.
|
|
||||||
|
|
||||||
|
|
||||||
Reference
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. autoclass:: SecureCookie
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. attribute:: new
|
|
||||||
|
|
||||||
`True` if the cookie was newly created, otherwise `False`
|
|
||||||
|
|
||||||
.. attribute:: modified
|
|
||||||
|
|
||||||
Whenever an item on the cookie is set, this attribute is set to `True`.
|
|
||||||
However this does not track modifications inside mutable objects
|
|
||||||
in the cookie:
|
|
||||||
|
|
||||||
>>> c = SecureCookie()
|
|
||||||
>>> c["foo"] = [1, 2, 3]
|
|
||||||
>>> c.modified
|
|
||||||
True
|
|
||||||
>>> c.modified = False
|
|
||||||
>>> c["foo"].append(4)
|
|
||||||
>>> c.modified
|
|
||||||
False
|
|
||||||
|
|
||||||
In that situation it has to be set to `modified` by hand so that
|
|
||||||
:attr:`should_save` can pick it up.
|
|
||||||
|
|
||||||
|
|
||||||
.. autoexception:: UnquoteError
|
|
|
@ -1,50 +0,0 @@
|
||||||
========
|
|
||||||
Sessions
|
|
||||||
========
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.sessions
|
|
||||||
|
|
||||||
.. testsetup::
|
|
||||||
|
|
||||||
from werkzeug.contrib.sessions import *
|
|
||||||
|
|
||||||
Reference
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. autoclass:: Session
|
|
||||||
|
|
||||||
.. attribute:: sid
|
|
||||||
|
|
||||||
The session ID as string.
|
|
||||||
|
|
||||||
.. attribute:: new
|
|
||||||
|
|
||||||
`True` is the cookie was newly created, otherwise `False`
|
|
||||||
|
|
||||||
.. attribute:: modified
|
|
||||||
|
|
||||||
Whenever an item on the cookie is set, this attribute is set to `True`.
|
|
||||||
However this does not track modifications inside mutable objects
|
|
||||||
in the session:
|
|
||||||
|
|
||||||
>>> c = Session({}, sid='deadbeefbabe2c00ffee')
|
|
||||||
>>> c["foo"] = [1, 2, 3]
|
|
||||||
>>> c.modified
|
|
||||||
True
|
|
||||||
>>> c.modified = False
|
|
||||||
>>> c["foo"].append(4)
|
|
||||||
>>> c.modified
|
|
||||||
False
|
|
||||||
|
|
||||||
In that situation it has to be set to `modified` by hand so that
|
|
||||||
:attr:`should_save` can pick it up.
|
|
||||||
|
|
||||||
.. autoattribute:: should_save
|
|
||||||
|
|
||||||
.. autoclass:: SessionStore
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: FilesystemSessionStore
|
|
||||||
:members: list
|
|
||||||
|
|
||||||
.. autoclass:: SessionMiddleware
|
|
|
@ -1,23 +0,0 @@
|
||||||
==============
|
|
||||||
Extra Wrappers
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.contrib.wrappers
|
|
||||||
|
|
||||||
.. autoclass:: JSONRequestMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: ProtobufRequestMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: RoutingArgsRequestMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: ReverseSlashBehaviorRequestMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: DynamicCharsetRequestMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: DynamicCharsetResponseMixin
|
|
||||||
:members:
|
|
|
@ -1,137 +0,0 @@
|
||||||
===============
|
|
||||||
Data Structures
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. module:: werkzeug.datastructures
|
|
||||||
|
|
||||||
Werkzeug provides some subclasses of common Python objects to extend them
|
|
||||||
with additional features. Some of them are used to make them immutable, others
|
|
||||||
are used to change some semantics to better work with HTTP.
|
|
||||||
|
|
||||||
General Purpose
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. versionchanged:: 0.6
|
|
||||||
The general purpose classes are now pickleable in each protocol as long
|
|
||||||
as the contained objects are pickleable. This means that the
|
|
||||||
:class:`FileMultiDict` won't be pickleable as soon as it contains a
|
|
||||||
file.
|
|
||||||
|
|
||||||
.. autoclass:: TypeConversionDict
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: ImmutableTypeConversionDict
|
|
||||||
:members: copy
|
|
||||||
|
|
||||||
.. autoclass:: MultiDict
|
|
||||||
:members:
|
|
||||||
:inherited-members:
|
|
||||||
|
|
||||||
.. autoclass:: OrderedMultiDict
|
|
||||||
|
|
||||||
.. autoclass:: ImmutableMultiDict
|
|
||||||
:members: copy
|
|
||||||
|
|
||||||
.. autoclass:: ImmutableOrderedMultiDict
|
|
||||||
:members: copy
|
|
||||||
|
|
||||||
.. autoclass:: CombinedMultiDict
|
|
||||||
|
|
||||||
.. autoclass:: ImmutableDict
|
|
||||||
:members: copy
|
|
||||||
|
|
||||||
.. autoclass:: ImmutableList
|
|
||||||
|
|
||||||
.. autoclass:: FileMultiDict
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. _http-datastructures:
|
|
||||||
|
|
||||||
HTTP Related
|
|
||||||
============
|
|
||||||
|
|
||||||
.. autoclass:: Headers([defaults])
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: EnvironHeaders
|
|
||||||
|
|
||||||
.. autoclass:: HeaderSet
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: Accept
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: MIMEAccept
|
|
||||||
:members: accept_html, accept_xhtml, accept_json
|
|
||||||
|
|
||||||
.. autoclass:: CharsetAccept
|
|
||||||
|
|
||||||
.. autoclass:: LanguageAccept
|
|
||||||
|
|
||||||
.. autoclass:: RequestCacheControl
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoattribute:: no_cache
|
|
||||||
|
|
||||||
.. autoattribute:: no_store
|
|
||||||
|
|
||||||
.. autoattribute:: max_age
|
|
||||||
|
|
||||||
.. autoattribute:: no_transform
|
|
||||||
|
|
||||||
.. autoclass:: ResponseCacheControl
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoattribute:: no_cache
|
|
||||||
|
|
||||||
.. autoattribute:: no_store
|
|
||||||
|
|
||||||
.. autoattribute:: max_age
|
|
||||||
|
|
||||||
.. autoattribute:: no_transform
|
|
||||||
|
|
||||||
.. autoclass:: ETags
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: Authorization
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: WWWAuthenticate
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: IfRange
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: Range
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: ContentRange
|
|
||||||
:members:
|
|
||||||
|
|
||||||
|
|
||||||
Others
|
|
||||||
======
|
|
||||||
|
|
||||||
.. autoclass:: FileStorage
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. attribute:: stream
|
|
||||||
|
|
||||||
The input stream for the uploaded file. This usually points to an
|
|
||||||
open temporary file.
|
|
||||||
|
|
||||||
.. attribute:: filename
|
|
||||||
|
|
||||||
The filename of the file on the client.
|
|
||||||
|
|
||||||
.. attribute:: name
|
|
||||||
|
|
||||||
The name of the form field.
|
|
||||||
|
|
||||||
.. attribute:: headers
|
|
||||||
|
|
||||||
The multipart headers as :class:`Headers` object. This usually contains
|
|
||||||
irrelevant information but in combination with custom multipart requests
|
|
||||||
the raw headers might be interesting.
|
|
||||||
|
|
||||||
.. versionadded:: 0.6
|
|
|
@ -1,89 +0,0 @@
|
||||||
======================
|
|
||||||
Debugging Applications
|
|
||||||
======================
|
|
||||||
|
|
||||||
.. module:: werkzeug.debug
|
|
||||||
|
|
||||||
Depending on the WSGI gateway/server, exceptions are handled differently.
|
|
||||||
But most of the time, exceptions go to stderr or the error log.
|
|
||||||
|
|
||||||
Since this is not the best debugging environment, Werkzeug provides a
|
|
||||||
WSGI middleware that renders nice debugging tracebacks, optionally with an
|
|
||||||
AJAX based debugger (which allows to execute code in the context of the
|
|
||||||
traceback's frames).
|
|
||||||
|
|
||||||
The interactive debugger however does not work in forking environments
|
|
||||||
which makes it nearly impossible to use on production servers. Also the
|
|
||||||
debugger allows the execution of arbitrary code which makes it a major
|
|
||||||
security risk and **must never be used on production machines** because of
|
|
||||||
that. **We cannot stress this enough. Do not enable this in
|
|
||||||
production.**
|
|
||||||
|
|
||||||
Enabling the Debugger
|
|
||||||
=====================
|
|
||||||
|
|
||||||
You can enable the debugger by wrapping the application in a
|
|
||||||
:class:`DebuggedApplication` middleware. Additionally there are
|
|
||||||
parameters to the :func:`run_simple` function to enable it because this
|
|
||||||
is a common task during development.
|
|
||||||
|
|
||||||
.. autoclass:: DebuggedApplication
|
|
||||||
|
|
||||||
Using the Debugger
|
|
||||||
==================
|
|
||||||
|
|
||||||
Once enabled and an error happens during a request you will see a detailed
|
|
||||||
traceback instead of a general "internal server error". If you have the
|
|
||||||
`evalex` feature enabled you can also get a traceback for every frame in
|
|
||||||
the traceback by clicking on the console icon.
|
|
||||||
|
|
||||||
Once clicked a console opens where you can execute Python code in:
|
|
||||||
|
|
||||||
.. image:: _static/debug-screenshot.png
|
|
||||||
:alt: a screenshot of the interactive debugger
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
Inside the interactive consoles you can execute any kind of Python code.
|
|
||||||
Unlike regular Python consoles the output of the object reprs is colored
|
|
||||||
and stripped to a reasonable size by default. If the output is longer
|
|
||||||
than what the console decides to display a small plus sign is added to
|
|
||||||
the repr and a click will expand the repr.
|
|
||||||
|
|
||||||
To display all variables that are defined in the current frame you can
|
|
||||||
use the `dump()` function. You can call it without arguments to get a
|
|
||||||
detailed list of all variables and their values, or with an object as
|
|
||||||
argument to get a detailed list of all the attributes it has.
|
|
||||||
|
|
||||||
Debugger PIN
|
|
||||||
============
|
|
||||||
|
|
||||||
Starting with Werkzeug 0.11 the debugger is additionally protected by a
|
|
||||||
PIN. This is a security helper to make it less likely for the debugger to
|
|
||||||
be exploited in production as it has happened to people to keep the
|
|
||||||
debugger active. The PIN based authentication is enabled by default.
|
|
||||||
|
|
||||||
When the debugger comes up, on first usage it will prompt for a PIN that
|
|
||||||
is printed to the command line. The PIN is generated in a stable way that
|
|
||||||
is specific to the project. In some situations it might be not possible
|
|
||||||
to generate a stable PIN between restarts in which case an explicit PIN
|
|
||||||
can be provided through the environment variable ``WERKZEUG_DEBUG_PIN``.
|
|
||||||
This can be set to a number and will become the PIN. This variable can
|
|
||||||
also be set to the value ``off`` to disable the PIN check entirely.
|
|
||||||
|
|
||||||
If the PIN is entered too many times incorrectly the server needs to be
|
|
||||||
restarted.
|
|
||||||
|
|
||||||
**This feature is not supposed to entirely secure the debugger. It's
|
|
||||||
intended to make it harder for an attacker to exploit the debugger. Never
|
|
||||||
enable the debugger in production.**
|
|
||||||
|
|
||||||
Pasting Errors
|
|
||||||
==============
|
|
||||||
|
|
||||||
If you click on the `Traceback` title, the traceback switches over to a text
|
|
||||||
based one. The text based one can be pasted to `gist.github.com <https://gist.github.com>`_ with one
|
|
||||||
click.
|
|
||||||
|
|
||||||
|
|
||||||
.. _paste.pocoo.org: https://gist.github.com
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
===
|
|
||||||
CGI
|
|
||||||
===
|
|
||||||
|
|
||||||
If all other deployment methods do not work, CGI will work for sure. CGI
|
|
||||||
is supported by all major servers but usually has a less-than-optimal
|
|
||||||
performance.
|
|
||||||
|
|
||||||
This is also the way you can use a Werkzeug application on Google's
|
|
||||||
`AppEngine`_, there however the execution does happen in a CGI-like
|
|
||||||
environment. The application's performance is unaffected because of that.
|
|
||||||
|
|
||||||
.. _AppEngine: http://code.google.com/appengine/
|
|
||||||
|
|
||||||
Creating a `.cgi` file
|
|
||||||
======================
|
|
||||||
|
|
||||||
First you need to create the CGI application file. Let's call it
|
|
||||||
`yourapplication.cgi`::
|
|
||||||
|
|
||||||
#!/usr/bin/python
|
|
||||||
from wsgiref.handlers import CGIHandler
|
|
||||||
from yourapplication import make_app
|
|
||||||
|
|
||||||
application = make_app()
|
|
||||||
CGIHandler().run(application)
|
|
||||||
|
|
||||||
If you're running Python 2.4 you will need the :mod:`wsgiref` package. Python
|
|
||||||
2.5 and higher ship this as part of the standard library.
|
|
||||||
|
|
||||||
Server Setup
|
|
||||||
============
|
|
||||||
|
|
||||||
Usually there are two ways to configure the server. Either just copy the
|
|
||||||
`.cgi` into a `cgi-bin` (and use `mod_rerwite` or something similar to
|
|
||||||
rewrite the URL) or let the server point to the file directly.
|
|
||||||
|
|
||||||
In Apache for example you can put a like like this into the config:
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
ScriptAlias /app /path/to/the/application.cgi
|
|
||||||
|
|
||||||
For more information consult the documentation of your webserver.
|
|
|
@ -1,142 +0,0 @@
|
||||||
=======
|
|
||||||
FastCGI
|
|
||||||
=======
|
|
||||||
|
|
||||||
A very popular deployment setup on servers like `lighttpd`_ and `nginx`_
|
|
||||||
is FastCGI. To use your WSGI application with any of them you will need
|
|
||||||
a FastCGI server first.
|
|
||||||
|
|
||||||
The most popular one is `flup`_ which we will use for this guide. Make
|
|
||||||
sure to have it installed.
|
|
||||||
|
|
||||||
Creating a `.fcgi` file
|
|
||||||
=======================
|
|
||||||
|
|
||||||
First you need to create the FastCGI server file. Let's call it
|
|
||||||
`yourapplication.fcgi`::
|
|
||||||
|
|
||||||
#!/usr/bin/python
|
|
||||||
from flup.server.fcgi import WSGIServer
|
|
||||||
from yourapplication import make_app
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
application = make_app()
|
|
||||||
WSGIServer(application).run()
|
|
||||||
|
|
||||||
This is enough for Apache to work, however ngingx and older versions of
|
|
||||||
lighttpd need a socket to be explicitly passed to communicate with the FastCGI
|
|
||||||
server. For that to work you need to pass the path to the socket to the
|
|
||||||
:class:`~flup.server.fcgi.WSGIServer`::
|
|
||||||
|
|
||||||
WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
|
|
||||||
|
|
||||||
The path has to be the exact same path you define in the server
|
|
||||||
config.
|
|
||||||
|
|
||||||
Save the `yourapplication.fcgi` file somewhere you will find it again.
|
|
||||||
It makes sense to have that in `/var/www/yourapplication` or something
|
|
||||||
similar.
|
|
||||||
|
|
||||||
Make sure to set the executable bit on that file so that the servers
|
|
||||||
can execute it::
|
|
||||||
|
|
||||||
# chmod +x /var/www/yourapplication/yourapplication.fcgi
|
|
||||||
|
|
||||||
Configuring lighttpd
|
|
||||||
====================
|
|
||||||
|
|
||||||
A basic FastCGI configuration for lighttpd looks like this::
|
|
||||||
|
|
||||||
fastcgi.server = ("/yourapplication.fcgi" =>
|
|
||||||
((
|
|
||||||
"socket" => "/tmp/yourapplication-fcgi.sock",
|
|
||||||
"bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
|
|
||||||
"check-local" => "disable",
|
|
||||||
"max-procs" -> 1
|
|
||||||
))
|
|
||||||
)
|
|
||||||
|
|
||||||
alias.url = (
|
|
||||||
"/static/" => "/path/to/your/static"
|
|
||||||
)
|
|
||||||
|
|
||||||
url.rewrite-once = (
|
|
||||||
"^(/static.*)$" => "$1",
|
|
||||||
"^(/.*)$" => "/yourapplication.fcgi$1"
|
|
||||||
|
|
||||||
Remember to enable the FastCGI, alias and rewrite modules. This configuration
|
|
||||||
binds the application to `/yourapplication`. If you want the application to
|
|
||||||
work in the URL root you have to work around a lighttpd bug with the
|
|
||||||
:class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware.
|
|
||||||
|
|
||||||
Make sure to apply it only if you are mounting the application the URL
|
|
||||||
root. Also, see the Lighty docs for more information on `FastCGI and Python
|
|
||||||
<http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModFastCGI>`_ (note that
|
|
||||||
explicitly passing a socket to run() is no longer necessary).
|
|
||||||
|
|
||||||
Configuring nginx
|
|
||||||
=================
|
|
||||||
|
|
||||||
Installing FastCGI applications on nginx is a bit tricky because by default
|
|
||||||
some FastCGI parameters are not properly forwarded.
|
|
||||||
|
|
||||||
A basic FastCGI configuration for nginx looks like this::
|
|
||||||
|
|
||||||
location /yourapplication/ {
|
|
||||||
include fastcgi_params;
|
|
||||||
if ($uri ~ ^/yourapplication/(.*)?) {
|
|
||||||
set $path_url $1;
|
|
||||||
}
|
|
||||||
fastcgi_param PATH_INFO $path_url;
|
|
||||||
fastcgi_param SCRIPT_NAME /yourapplication;
|
|
||||||
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
This configuration binds the application to `/yourapplication`. If you want
|
|
||||||
to have it in the URL root it's a bit easier because you don't have to figure
|
|
||||||
out how to calculate `PATH_INFO` and `SCRIPT_NAME`::
|
|
||||||
|
|
||||||
location /yourapplication/ {
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_param PATH_INFO $fastcgi_script_name;
|
|
||||||
fastcgi_param SCRIPT_NAME "";
|
|
||||||
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
Since Nginx doesn't load FastCGI apps, you have to do it by yourself. You
|
|
||||||
can either write an `init.d` script for that or execute it inside a screen
|
|
||||||
session::
|
|
||||||
|
|
||||||
$ screen
|
|
||||||
$ /var/www/yourapplication/yourapplication.fcgi
|
|
||||||
|
|
||||||
Debugging
|
|
||||||
=========
|
|
||||||
|
|
||||||
FastCGI deployments tend to be hard to debug on most webservers. Very often the
|
|
||||||
only thing the server log tells you is something along the lines of "premature
|
|
||||||
end of headers". In order to debug the application the only thing that can
|
|
||||||
really give you ideas why it breaks is switching to the correct user and
|
|
||||||
executing the application by hand.
|
|
||||||
|
|
||||||
This example assumes your application is called `application.fcgi` and that your
|
|
||||||
webserver user is `www-data`::
|
|
||||||
|
|
||||||
$ su www-data
|
|
||||||
$ cd /var/www/yourapplication
|
|
||||||
$ python application.fcgi
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "yourapplication.fcg", line 4, in <module>
|
|
||||||
ImportError: No module named yourapplication
|
|
||||||
|
|
||||||
In this case the error seems to be "yourapplication" not being on the python
|
|
||||||
path. Common problems are:
|
|
||||||
|
|
||||||
- relative paths being used. Don't rely on the current working directory
|
|
||||||
- the code depending on environment variables that are not set by the
|
|
||||||
web server.
|
|
||||||
- different python interpreters being used.
|
|
||||||
|
|
||||||
.. _lighttpd: http://www.lighttpd.net/
|
|
||||||
.. _nginx: http://nginx.net/
|
|
||||||
.. _flup: http://trac.saddi.com/flup
|
|
|
@ -1,16 +0,0 @@
|
||||||
.. _deployment:
|
|
||||||
|
|
||||||
======================
|
|
||||||
Application Deployment
|
|
||||||
======================
|
|
||||||
|
|
||||||
This section covers running your application in production on a web
|
|
||||||
server such as Apache or lighttpd.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
cgi
|
|
||||||
mod_wsgi
|
|
||||||
fastcgi
|
|
||||||
proxying
|
|
|
@ -1,82 +0,0 @@
|
||||||
===================
|
|
||||||
`mod_wsgi` (Apache)
|
|
||||||
===================
|
|
||||||
|
|
||||||
If you are using the `Apache`_ webserver you should consider using `mod_wsgi`_.
|
|
||||||
|
|
||||||
.. _Apache: http://httpd.apache.org/
|
|
||||||
|
|
||||||
Installing `mod_wsgi`
|
|
||||||
=====================
|
|
||||||
|
|
||||||
If you don't have `mod_wsgi` installed yet you have to either install it using
|
|
||||||
a package manager or compile it yourself.
|
|
||||||
|
|
||||||
The mod_wsgi `installation instructions`_ cover installation instructions for
|
|
||||||
source installations on UNIX systems.
|
|
||||||
|
|
||||||
If you are using ubuntu / debian you can apt-get it and activate it as follows::
|
|
||||||
|
|
||||||
# apt-get install libapache2-mod-wsgi
|
|
||||||
|
|
||||||
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by using
|
|
||||||
pkg_add::
|
|
||||||
|
|
||||||
# pkg_add -r mod_wsgi
|
|
||||||
|
|
||||||
If you are using pkgsrc you can install `mod_wsgi` by compiling the
|
|
||||||
`www/ap2-wsgi` package.
|
|
||||||
|
|
||||||
If you encounter segfaulting child processes after the first apache reload you
|
|
||||||
can safely ignore them. Just restart the server.
|
|
||||||
|
|
||||||
Creating a `.wsgi` file
|
|
||||||
=======================
|
|
||||||
|
|
||||||
To run your application you need a `yourapplication.wsgi` file. This file
|
|
||||||
contains the code `mod_wsgi` is executing on startup to get the application
|
|
||||||
object. The object called `application` in that file is then used as
|
|
||||||
application.
|
|
||||||
|
|
||||||
For most applications the following file should be sufficient::
|
|
||||||
|
|
||||||
from yourapplication import make_app
|
|
||||||
application = make_app()
|
|
||||||
|
|
||||||
If you don't have a factory function for application creation but a singleton
|
|
||||||
instance you can directly import that one as `application`.
|
|
||||||
|
|
||||||
Store that file somewhere where you will find it again (eg:
|
|
||||||
`/var/www/yourapplication`) and make sure that `yourapplication` and all
|
|
||||||
the libraries that are in use are on the python load path. If you don't
|
|
||||||
want to install it system wide consider using a `virtual python`_ instance.
|
|
||||||
|
|
||||||
Configuring Apache
|
|
||||||
==================
|
|
||||||
|
|
||||||
The last thing you have to do is to create an Apache configuration file for
|
|
||||||
your application. In this example we are telling `mod_wsgi` to execute the
|
|
||||||
application under a different user for security reasons:
|
|
||||||
|
|
||||||
.. sourcecode:: apache
|
|
||||||
|
|
||||||
<VirtualHost *>
|
|
||||||
ServerName example.com
|
|
||||||
|
|
||||||
WSGIDaemonProcess yourapplication user=user1 group=group1 processes=2 threads=5
|
|
||||||
WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
|
|
||||||
|
|
||||||
<Directory /var/www/yourapplication>
|
|
||||||
WSGIProcessGroup yourapplication
|
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
|
||||||
Order deny,allow
|
|
||||||
Allow from all
|
|
||||||
</Directory>
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
For more information consult the `mod_wsgi wiki`_.
|
|
||||||
|
|
||||||
.. _mod_wsgi: http://code.google.com/p/modwsgi/
|
|
||||||
.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
|
|
||||||
.. _virtual python: http://pypi.python.org/pypi/virtualenv
|
|
||||||
.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/
|
|
|
@ -1,54 +0,0 @@
|
||||||
=============
|
|
||||||
HTTP Proxying
|
|
||||||
=============
|
|
||||||
|
|
||||||
Many people prefer using a standalone Python HTTP server and proxying that
|
|
||||||
server via nginx, Apache etc.
|
|
||||||
|
|
||||||
A very stable Python server is CherryPy. This part of the documentation
|
|
||||||
shows you how to combine your WSGI application with the CherryPy WSGI
|
|
||||||
server and how to configure the webserver for proxying.
|
|
||||||
|
|
||||||
|
|
||||||
Creating a `.py` server
|
|
||||||
=======================
|
|
||||||
|
|
||||||
To run your application you need a `start-server.py` file that starts up
|
|
||||||
the WSGI Server.
|
|
||||||
|
|
||||||
It looks something along these lines::
|
|
||||||
|
|
||||||
from cherrypy import wsgiserver
|
|
||||||
from yourapplication import make_app
|
|
||||||
server = wsgiserver.CherryPyWSGIServer(('localhost', 8080), make_app())
|
|
||||||
try:
|
|
||||||
server.start()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
server.stop()
|
|
||||||
|
|
||||||
If you now start the file the server will listen on `localhost:8080`. Keep
|
|
||||||
in mind that WSGI applications behave slightly different for proxied setups.
|
|
||||||
If you have not developed your application for proxying in mind, you can
|
|
||||||
apply the :class:`~werkzeug.contrib.fixers.ProxyFix` middleware.
|
|
||||||
|
|
||||||
|
|
||||||
Configuring nginx
|
|
||||||
=================
|
|
||||||
|
|
||||||
As an example we show here how to configure nginx to proxy to the server.
|
|
||||||
|
|
||||||
The basic nginx configuration looks like this::
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_pass http://127.0.0.1:8080;
|
|
||||||
proxy_redirect default;
|
|
||||||
}
|
|
||||||
|
|
||||||
Since Nginx doesn't start your server for you, you have to do it by yourself. You
|
|
||||||
can either write an `init.d` script for that or execute it inside a screen
|
|
||||||
session::
|
|
||||||
|
|
||||||
$ screen
|
|
||||||
$ python start-server.py
|
|
|
@ -1,147 +0,0 @@
|
||||||
===============
|
|
||||||
HTTP Exceptions
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.exceptions
|
|
||||||
|
|
||||||
|
|
||||||
Error Classes
|
|
||||||
=============
|
|
||||||
|
|
||||||
The following error classes exist in Werkzeug:
|
|
||||||
|
|
||||||
.. autoexception:: BadRequest
|
|
||||||
|
|
||||||
.. autoexception:: Unauthorized
|
|
||||||
|
|
||||||
.. autoexception:: Forbidden
|
|
||||||
|
|
||||||
.. autoexception:: NotFound
|
|
||||||
|
|
||||||
.. autoexception:: MethodNotAllowed
|
|
||||||
|
|
||||||
.. autoexception:: NotAcceptable
|
|
||||||
|
|
||||||
.. autoexception:: RequestTimeout
|
|
||||||
|
|
||||||
.. autoexception:: Conflict
|
|
||||||
|
|
||||||
.. autoexception:: Gone
|
|
||||||
|
|
||||||
.. autoexception:: LengthRequired
|
|
||||||
|
|
||||||
.. autoexception:: PreconditionFailed
|
|
||||||
|
|
||||||
.. autoexception:: RequestEntityTooLarge
|
|
||||||
|
|
||||||
.. autoexception:: RequestURITooLarge
|
|
||||||
|
|
||||||
.. autoexception:: UnsupportedMediaType
|
|
||||||
|
|
||||||
.. autoexception:: RequestedRangeNotSatisfiable
|
|
||||||
|
|
||||||
.. autoexception:: ExpectationFailed
|
|
||||||
|
|
||||||
.. autoexception:: ImATeapot
|
|
||||||
|
|
||||||
.. autoexception:: PreconditionRequired
|
|
||||||
|
|
||||||
.. autoexception:: TooManyRequests
|
|
||||||
|
|
||||||
.. autoexception:: RequestHeaderFieldsTooLarge
|
|
||||||
|
|
||||||
.. autoexception:: InternalServerError
|
|
||||||
|
|
||||||
.. autoexception:: NotImplemented
|
|
||||||
|
|
||||||
.. autoexception:: BadGateway
|
|
||||||
|
|
||||||
.. autoexception:: ServiceUnavailable
|
|
||||||
|
|
||||||
.. exception:: HTTPUnicodeError
|
|
||||||
|
|
||||||
This exception is used to signal unicode decode errors of request
|
|
||||||
data. For more information see the :ref:`unicode` chapter.
|
|
||||||
|
|
||||||
.. autoexception:: ClientDisconnected
|
|
||||||
|
|
||||||
.. autoexception:: SecurityError
|
|
||||||
|
|
||||||
|
|
||||||
Baseclass
|
|
||||||
=========
|
|
||||||
|
|
||||||
All the exceptions implement this common interface:
|
|
||||||
|
|
||||||
.. autoexception:: HTTPException
|
|
||||||
:members: get_response, __call__
|
|
||||||
|
|
||||||
|
|
||||||
Special HTTP Exceptions
|
|
||||||
=======================
|
|
||||||
|
|
||||||
Starting with Werkzeug 0.3 some of the builtin classes raise exceptions that
|
|
||||||
look like regular python exceptions (eg :exc:`KeyError`) but are
|
|
||||||
:exc:`BadRequest` HTTP exceptions at the same time. This decision was made
|
|
||||||
to simplify a common pattern where you want to abort if the client tampered
|
|
||||||
with the submitted form data in a way that the application can't recover
|
|
||||||
properly and should abort with ``400 BAD REQUEST``.
|
|
||||||
|
|
||||||
Assuming the application catches all HTTP exceptions and reacts to them
|
|
||||||
properly a view function could do the following safely and doesn't have to
|
|
||||||
check if the keys exist::
|
|
||||||
|
|
||||||
def new_post(request):
|
|
||||||
post = Post(title=request.form['title'], body=request.form['body'])
|
|
||||||
post.save()
|
|
||||||
return redirect(post.url)
|
|
||||||
|
|
||||||
If `title` or `body` are missing in the form, a special key error will be
|
|
||||||
raised which behaves like a :exc:`KeyError` but also a :exc:`BadRequest`
|
|
||||||
exception.
|
|
||||||
|
|
||||||
|
|
||||||
Simple Aborting
|
|
||||||
===============
|
|
||||||
|
|
||||||
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 :func:`abort` function.
|
|
||||||
|
|
||||||
.. autofunction:: abort
|
|
||||||
|
|
||||||
If you want to use this functionality with custom exceptions you can
|
|
||||||
create an instance of the aborter class:
|
|
||||||
|
|
||||||
.. autoclass:: 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 do not
|
|
||||||
represent errors are missing. For redirects you can use the :func:`redirect`
|
|
||||||
function from the utilities.
|
|
||||||
|
|
||||||
If you want to add an error yourself you can subclass :exc:`HTTPException`::
|
|
||||||
|
|
||||||
from werkzeug.exceptions import HTTPException
|
|
||||||
|
|
||||||
class PaymentRequired(HTTPException):
|
|
||||||
code = 402
|
|
||||||
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
|
|
||||||
:meth:`~HTTPException.get_description`, :meth:`~HTTPException.get_body`,
|
|
||||||
:meth:`~HTTPException.get_headers` and :meth:`~HTTPException.get_response`
|
|
||||||
methods. In any case you should have a look at the sourcecode of the
|
|
||||||
exceptions module.
|
|
||||||
|
|
||||||
You can override the default description in the constructor with the
|
|
||||||
`description` parameter (it's the first argument for all exceptions
|
|
||||||
except of the :exc:`MethodNotAllowed` which accepts a list of allowed methods
|
|
||||||
as first argument)::
|
|
||||||
|
|
||||||
raise BadRequest('Request failed because X was not present')
|
|
|
@ -1,11 +0,0 @@
|
||||||
====================
|
|
||||||
Filesystem Utilities
|
|
||||||
====================
|
|
||||||
|
|
||||||
Various utilities for the local filesystem.
|
|
||||||
|
|
||||||
.. module:: werkzeug.filesystem
|
|
||||||
|
|
||||||
.. autoclass:: BrokenFilesystemWarning
|
|
||||||
|
|
||||||
.. autofunction:: get_filesystem_encoding
|
|
|
@ -1,156 +0,0 @@
|
||||||
==============
|
|
||||||
HTTP Utilities
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. module:: werkzeug.http
|
|
||||||
|
|
||||||
Werkzeug provides a couple of functions to parse and generate HTTP headers
|
|
||||||
that are useful when implementing WSGI middlewares or whenever you are
|
|
||||||
operating on a lower level layer. All this functionality is also exposed
|
|
||||||
from request and response objects.
|
|
||||||
|
|
||||||
Date Functions
|
|
||||||
==============
|
|
||||||
|
|
||||||
The following functions simplify working with times in an HTTP context.
|
|
||||||
Werkzeug uses offset-naive :class:`~datetime.datetime` objects internally
|
|
||||||
that store the time in UTC. If you're working with timezones in your
|
|
||||||
application make sure to replace the tzinfo attribute with a UTC timezone
|
|
||||||
information before processing the values.
|
|
||||||
|
|
||||||
.. autofunction:: cookie_date
|
|
||||||
|
|
||||||
.. autofunction:: http_date
|
|
||||||
|
|
||||||
.. autofunction:: parse_date
|
|
||||||
|
|
||||||
Header Parsing
|
|
||||||
==============
|
|
||||||
|
|
||||||
The following functions can be used to parse incoming HTTP headers.
|
|
||||||
Because Python does not provide data structures with the semantics required
|
|
||||||
by :rfc:`2616`, Werkzeug implements some custom data structures that are
|
|
||||||
:ref:`documented separately <http-datastructures>`.
|
|
||||||
|
|
||||||
.. autofunction:: parse_options_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_set_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_list_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_dict_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_accept_header(value, [class])
|
|
||||||
|
|
||||||
.. autofunction:: parse_cache_control_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_authorization_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_www_authenticate_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_if_range_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_range_header
|
|
||||||
|
|
||||||
.. autofunction:: parse_content_range_header
|
|
||||||
|
|
||||||
Header Utilities
|
|
||||||
================
|
|
||||||
|
|
||||||
The following utilities operate on HTTP headers well but do not parse
|
|
||||||
them. They are useful if you're dealing with conditional responses or if
|
|
||||||
you want to proxy arbitrary requests but want to remove WSGI-unsupported
|
|
||||||
hop-by-hop headers. Also there is a function to create HTTP header
|
|
||||||
strings from the parsed data.
|
|
||||||
|
|
||||||
.. autofunction:: is_entity_header
|
|
||||||
|
|
||||||
.. autofunction:: is_hop_by_hop_header
|
|
||||||
|
|
||||||
.. autofunction:: remove_entity_headers
|
|
||||||
|
|
||||||
.. autofunction:: remove_hop_by_hop_headers
|
|
||||||
|
|
||||||
.. autofunction:: is_byte_range_valid
|
|
||||||
|
|
||||||
.. autofunction:: quote_header_value
|
|
||||||
|
|
||||||
.. autofunction:: unquote_header_value
|
|
||||||
|
|
||||||
.. autofunction:: dump_header
|
|
||||||
|
|
||||||
|
|
||||||
Cookies
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. autofunction:: parse_cookie
|
|
||||||
|
|
||||||
.. autofunction:: dump_cookie
|
|
||||||
|
|
||||||
|
|
||||||
Conditional Response Helpers
|
|
||||||
============================
|
|
||||||
|
|
||||||
For conditional responses the following functions might be useful:
|
|
||||||
|
|
||||||
.. autofunction:: parse_etags
|
|
||||||
|
|
||||||
.. autofunction:: quote_etag
|
|
||||||
|
|
||||||
.. autofunction:: unquote_etag
|
|
||||||
|
|
||||||
.. autofunction:: generate_etag
|
|
||||||
|
|
||||||
.. autofunction:: is_resource_modified
|
|
||||||
|
|
||||||
Constants
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. data:: HTTP_STATUS_CODES
|
|
||||||
|
|
||||||
A dict of status code -> default status message pairs. This is used
|
|
||||||
by the wrappers and other places where an integer status code is expanded
|
|
||||||
to a string throughout Werkzeug.
|
|
||||||
|
|
||||||
Form Data Parsing
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. module:: werkzeug.formparser
|
|
||||||
|
|
||||||
Werkzeug provides the form parsing functions separately from the request
|
|
||||||
object so that you can access form data from a plain WSGI environment.
|
|
||||||
|
|
||||||
The following formats are currently supported by the form data parser:
|
|
||||||
|
|
||||||
- `application/x-www-form-urlencoded`
|
|
||||||
- `multipart/form-data`
|
|
||||||
|
|
||||||
Nested multipart is not currently supported (Werkzeug 0.9), but it isn't used
|
|
||||||
by any of the modern web browsers.
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
|
|
||||||
>>> from cStringIO import StringIO
|
|
||||||
>>> data = '--foo\r\nContent-Disposition: form-data; name="test"\r\n' \
|
|
||||||
... '\r\nHello World!\r\n--foo--'
|
|
||||||
>>> environ = {'wsgi.input': StringIO(data), 'CONTENT_LENGTH': str(len(data)),
|
|
||||||
... 'CONTENT_TYPE': 'multipart/form-data; boundary=foo',
|
|
||||||
... 'REQUEST_METHOD': 'POST'}
|
|
||||||
>>> stream, form, files = parse_form_data(environ)
|
|
||||||
>>> stream.read()
|
|
||||||
''
|
|
||||||
>>> form['test']
|
|
||||||
u'Hello World!'
|
|
||||||
>>> not files
|
|
||||||
True
|
|
||||||
|
|
||||||
Normally the WSGI environment is provided by the WSGI gateway with the
|
|
||||||
incoming data as part of it. If you want to generate such fake-WSGI
|
|
||||||
environments for unittesting you might want to use the
|
|
||||||
:func:`create_environ` function or the :class:`EnvironBuilder` instead.
|
|
||||||
|
|
||||||
.. autoclass:: FormDataParser
|
|
||||||
|
|
||||||
.. autofunction:: parse_form_data
|
|
||||||
|
|
||||||
.. autofunction:: parse_multipart_headers
|
|
|
@ -1,7 +0,0 @@
|
||||||
======================
|
|
||||||
Documentation Overview
|
|
||||||
======================
|
|
||||||
|
|
||||||
Welcome to the Werkzeug |version| documentation.
|
|
||||||
|
|
||||||
.. include:: contents.rst.inc
|
|
|
@ -1,133 +0,0 @@
|
||||||
============
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
Werkzeug requires at least Python 2.6 to work correctly. If you do need
|
|
||||||
to support an older version you can download an older version of Werkzeug
|
|
||||||
though we strongly recommend against that. Werkzeug currently has
|
|
||||||
experimental support for Python 3. For more information about the
|
|
||||||
Python 3 support see :ref:`python3`.
|
|
||||||
|
|
||||||
|
|
||||||
Installing a released version
|
|
||||||
=============================
|
|
||||||
|
|
||||||
As a Python egg (via easy_install or pip)
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
You can install the most recent Werkzeug version using `easy_install`_::
|
|
||||||
|
|
||||||
easy_install Werkzeug
|
|
||||||
|
|
||||||
Alternatively you can also use pip::
|
|
||||||
|
|
||||||
pip install Werkzeug
|
|
||||||
|
|
||||||
Either way we strongly recommend using these tools in combination with
|
|
||||||
:ref:`virtualenv`.
|
|
||||||
|
|
||||||
This will install a Werkzeug egg in your Python installation's `site-packages`
|
|
||||||
directory.
|
|
||||||
|
|
||||||
From the tarball release
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
1. Download the most recent tarball from the `download page`_.
|
|
||||||
2. Unpack the tarball.
|
|
||||||
3. ``python setup.py install``
|
|
||||||
|
|
||||||
Note that the last command will automatically download and install
|
|
||||||
`setuptools`_ if you don't already have it installed. This requires a working
|
|
||||||
Internet connection.
|
|
||||||
|
|
||||||
This will install Werkzeug into your Python installation's `site-packages`
|
|
||||||
directory.
|
|
||||||
|
|
||||||
|
|
||||||
Installing the development version
|
|
||||||
==================================
|
|
||||||
|
|
||||||
1. Install `Git`_
|
|
||||||
2. ``git clone git://github.com/pallets/werkzeug.git``
|
|
||||||
3. ``cd werkzeug``
|
|
||||||
4. ``pip install --editable .``
|
|
||||||
|
|
||||||
.. _virtualenv:
|
|
||||||
|
|
||||||
virtualenv
|
|
||||||
==========
|
|
||||||
|
|
||||||
Virtualenv is probably what you want to use during development, and in
|
|
||||||
production too if you have shell access there.
|
|
||||||
|
|
||||||
What problem does virtualenv solve? If you like Python as I do,
|
|
||||||
chances are you want to use it for other projects besides Werkzeug-based
|
|
||||||
web applications. But the more projects you have, the more likely it is
|
|
||||||
that you will be working with different versions of Python itself, or at
|
|
||||||
least different versions of Python libraries. Let's face it; quite often
|
|
||||||
libraries break backwards compatibility, and it's unlikely that any serious
|
|
||||||
application will have zero dependencies. So what do you do if two or more
|
|
||||||
of your projects have conflicting dependencies?
|
|
||||||
|
|
||||||
Virtualenv to the rescue! It basically enables multiple side-by-side
|
|
||||||
installations of Python, one for each project. It doesn't actually
|
|
||||||
install separate copies of Python, but it does provide a clever way
|
|
||||||
to keep different project environments isolated.
|
|
||||||
|
|
||||||
So let's see how virtualenv works!
|
|
||||||
|
|
||||||
If you are on Mac OS X or Linux, chances are that one of the following two
|
|
||||||
commands will work for you::
|
|
||||||
|
|
||||||
$ sudo easy_install virtualenv
|
|
||||||
|
|
||||||
or even better::
|
|
||||||
|
|
||||||
$ sudo pip install virtualenv
|
|
||||||
|
|
||||||
One of these will probably install virtualenv on your system. Maybe it's
|
|
||||||
even in your package manager. If you use Ubuntu, try::
|
|
||||||
|
|
||||||
$ sudo apt-get install python-virtualenv
|
|
||||||
|
|
||||||
If you are on Windows and don't have the `easy_install` command, you must
|
|
||||||
install it first. Once you have it installed, run the same commands as
|
|
||||||
above, but without the `sudo` prefix.
|
|
||||||
|
|
||||||
Once you have virtualenv installed, just fire up a shell and create
|
|
||||||
your own environment. I usually create a project folder and an `env`
|
|
||||||
folder within::
|
|
||||||
|
|
||||||
$ mkdir myproject
|
|
||||||
$ cd myproject
|
|
||||||
$ virtualenv env
|
|
||||||
New python executable in env/bin/python
|
|
||||||
Installing setuptools............done.
|
|
||||||
|
|
||||||
Now, whenever you want to work on a project, you only have to activate
|
|
||||||
the corresponding environment. On OS X and Linux, do the following::
|
|
||||||
|
|
||||||
$ . env/bin/activate
|
|
||||||
|
|
||||||
(Note the space between the dot and the script name. The dot means that
|
|
||||||
this script should run in the context of the current shell. If this command
|
|
||||||
does not work in your shell, try replacing the dot with ``source``)
|
|
||||||
|
|
||||||
If you are a Windows user, the following command is for you::
|
|
||||||
|
|
||||||
$ env\scripts\activate
|
|
||||||
|
|
||||||
Either way, you should now be using your virtualenv (see how the prompt of
|
|
||||||
your shell has changed to show the virtualenv).
|
|
||||||
|
|
||||||
Now you can just enter the following command to get Werkzeug activated in
|
|
||||||
your virtualenv::
|
|
||||||
|
|
||||||
$ pip install Werkzeug
|
|
||||||
|
|
||||||
A few seconds later you are good to go.
|
|
||||||
|
|
||||||
.. _download page: https://pypi.python.org/pypi/Werkzeug
|
|
||||||
.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
|
|
||||||
.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
|
|
||||||
.. _Git: http://git-scm.org/
|
|
|
@ -1,6 +0,0 @@
|
||||||
:orphan:
|
|
||||||
|
|
||||||
Werkzeug Documentation
|
|
||||||
======================
|
|
||||||
|
|
||||||
.. include:: contents.rst.inc
|
|
|
@ -1,70 +0,0 @@
|
||||||
==========
|
|
||||||
API Levels
|
|
||||||
==========
|
|
||||||
|
|
||||||
.. module:: werkzeug
|
|
||||||
|
|
||||||
Werkzeug is intended to be a utility rather than a framework. Because of that
|
|
||||||
the user-friendly API is separated from the lower-level API so that Werkzeug
|
|
||||||
can easily be used to extend another system.
|
|
||||||
|
|
||||||
All the functionality the :class:`Request` and :class:`Response` objects (aka
|
|
||||||
the "wrappers") provide is also available in small utility functions.
|
|
||||||
|
|
||||||
Example
|
|
||||||
=======
|
|
||||||
|
|
||||||
This example implements a small `Hello World` application that greets the
|
|
||||||
user with the name entered::
|
|
||||||
|
|
||||||
from werkzeug.utils import escape
|
|
||||||
from werkzeug.wrappers import Request, Response
|
|
||||||
|
|
||||||
@Request.application
|
|
||||||
def hello_world(request):
|
|
||||||
result = ['<title>Greeter</title>']
|
|
||||||
if request.method == 'POST':
|
|
||||||
result.append('<h1>Hello %s!</h1>' % escape(request.form['name']))
|
|
||||||
result.append('''
|
|
||||||
<form action="" method="post">
|
|
||||||
<p>Name: <input type="text" name="name" size="20">
|
|
||||||
<input type="submit" value="Greet me">
|
|
||||||
</form>
|
|
||||||
''')
|
|
||||||
return Response(''.join(result), mimetype='text/html')
|
|
||||||
|
|
||||||
Alternatively the same application could be used without request and response
|
|
||||||
objects but by taking advantage of the parsing functions werkzeug provides::
|
|
||||||
|
|
||||||
from werkzeug.formparser import parse_form_data
|
|
||||||
from werkzeug.utils import escape
|
|
||||||
|
|
||||||
def hello_world(environ, start_response):
|
|
||||||
result = ['<title>Greeter</title>']
|
|
||||||
if environ['REQUEST_METHOD'] == 'POST':
|
|
||||||
form = parse_form_data(environ)[1]
|
|
||||||
result.append('<h1>Hello %s!</h1>' % escape(form['name']))
|
|
||||||
result.append('''
|
|
||||||
<form action="" method="post">
|
|
||||||
<p>Name: <input type="text" name="name" size="20">
|
|
||||||
<input type="submit" value="Greet me">
|
|
||||||
</form>
|
|
||||||
''')
|
|
||||||
start_response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
|
|
||||||
return [''.join(result)]
|
|
||||||
|
|
||||||
High or Low?
|
|
||||||
============
|
|
||||||
|
|
||||||
Usually you want to use the high-level layer (the request and response
|
|
||||||
objects). But there are situations where this might not be what you want.
|
|
||||||
|
|
||||||
For example you might be maintaining code for an application written in
|
|
||||||
Django or another framework and you have to parse HTTP headers. You can
|
|
||||||
utilize Werkzeug for that by accessing the lower-level HTTP header parsing
|
|
||||||
functions.
|
|
||||||
|
|
||||||
Another situation where the low level parsing functions can be useful are
|
|
||||||
custom WSGI frameworks, unit-testing or modernizing an old CGI/mod_python
|
|
||||||
application to WSGI as well as WSGI middlewares where you want to keep the
|
|
||||||
overhead low.
|
|
|
@ -1,94 +0,0 @@
|
||||||
==============
|
|
||||||
Context Locals
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. module:: werkzeug.local
|
|
||||||
|
|
||||||
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 isn't possible in WSGI applications without a
|
|
||||||
major drawback: As soon as you operate on the global namespace your
|
|
||||||
application isn't thread-safe any longer.
|
|
||||||
|
|
||||||
The Python standard library has a concept called "thread locals" (or thread-local
|
|
||||||
data). A thread local is a global object in which you can put stuff in and get back
|
|
||||||
later in a thread-safe and thread-specific way. That means that whenever you set
|
|
||||||
or get a value on a thread local object, the thread local object checks in which
|
|
||||||
thread you are and retrieves the value corresponding to your thread (if one exists).
|
|
||||||
So, you won't accidentally get another thread's data.
|
|
||||||
|
|
||||||
This approach, however, has a few disadvantages. For example, besides threads,
|
|
||||||
there are other types of concurrency in Python. A very popular one
|
|
||||||
is greenlets. Also, whether every request gets its own thread is not
|
|
||||||
guaranteed in WSGI. It could be that a request is reusing a thread from
|
|
||||||
a previous request, and hence data is left over in the thread local object.
|
|
||||||
|
|
||||||
Werkzeug provides its own implementation of local data storage called `werkzeug.local`.
|
|
||||||
This approach provides a similar functionality to thread locals but also works with
|
|
||||||
greenlets.
|
|
||||||
|
|
||||||
Here's a simple example of how one could use werkzeug.local::
|
|
||||||
|
|
||||||
from werkzeug.local import Local, LocalManager
|
|
||||||
|
|
||||||
local = Local()
|
|
||||||
local_manager = LocalManager([local])
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
local.request = request = Request(environ)
|
|
||||||
...
|
|
||||||
|
|
||||||
application = local_manager.make_middleware(application)
|
|
||||||
|
|
||||||
This binds the 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 all references to the local objects are cleared 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::
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Note that local objects cannot manage themselves, for that you need a local
|
|
||||||
manager. You can pass a local manager multiple locals or add additionals
|
|
||||||
later by appending them to `manager.locals` and every time the manager
|
|
||||||
cleans up it will clean up all the data left in the locals for this
|
|
||||||
context.
|
|
||||||
|
|
||||||
.. autofunction:: release_local
|
|
||||||
|
|
||||||
.. autoclass:: LocalManager
|
|
||||||
:members: cleanup, make_middleware, middleware, get_ident
|
|
||||||
|
|
||||||
.. autoclass:: LocalStack
|
|
||||||
:members: push, pop, top
|
|
||||||
|
|
||||||
.. autoclass:: LocalProxy
|
|
||||||
:members: _get_current_object
|
|
||||||
|
|
||||||
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.local import LocalProxy
|
|
||||||
>>> isinstance(request, LocalProxy)
|
|
||||||
True
|
|
||||||
|
|
||||||
You can also create proxy objects by hand:
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
from werkzeug.local import Local, LocalProxy
|
|
||||||
local = Local()
|
|
||||||
request = LocalProxy(local, 'request')
|
|
|
@ -1,95 +0,0 @@
|
||||||
@ECHO OFF
|
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
|
||||||
|
|
||||||
set SPHINXBUILD=sphinx-build
|
|
||||||
set ALLSPHINXOPTS=-d _build/doctrees %SPHINXOPTS% .
|
|
||||||
if NOT "%PAPER%" == "" (
|
|
||||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
|
||||||
|
|
||||||
if "%1" == "help" (
|
|
||||||
:help
|
|
||||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
|
||||||
echo. html to make standalone HTML files
|
|
||||||
echo. pickle to make pickle files
|
|
||||||
echo. json to make JSON files
|
|
||||||
echo. htmlhelp to make HTML files and a HTML help project
|
|
||||||
echo. qthelp to make HTML files and a qthelp project
|
|
||||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
|
||||||
echo. changes to make an overview over all changed/added/deprecated items
|
|
||||||
echo. linkcheck to check all external links for integrity
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "clean" (
|
|
||||||
for /d %%i in (_build\*) do rmdir /q /s %%i
|
|
||||||
del /q /s _build\*
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "html" (
|
|
||||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% _build/html
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in _build/html.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "pickle" (
|
|
||||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% _build/pickle
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the pickle files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "json" (
|
|
||||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% _build/json
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the JSON files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "htmlhelp" (
|
|
||||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% _build/htmlhelp
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
|
||||||
.hhp project file in _build/htmlhelp.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "qthelp" (
|
|
||||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% _build/qthelp
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
|
||||||
.qhcp project file in _build/qthelp, like this:
|
|
||||||
echo.^> qcollectiongenerator _build\qthelp\Werkzeug.qhcp
|
|
||||||
echo.To view the help file:
|
|
||||||
echo.^> assistant -collectionFile _build\qthelp\Werkzeug.ghc
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "latex" (
|
|
||||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% _build/latex
|
|
||||||
echo.
|
|
||||||
echo.Build finished; the LaTeX files are in _build/latex.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "changes" (
|
|
||||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% _build/changes
|
|
||||||
echo.
|
|
||||||
echo.The overview file is in _build/changes.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "linkcheck" (
|
|
||||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% _build/linkcheck
|
|
||||||
echo.
|
|
||||||
echo.Link check complete; look for any errors in the above output ^
|
|
||||||
or in _build/linkcheck/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
:end
|
|
|
@ -1,7 +0,0 @@
|
||||||
import os
|
|
||||||
import conf
|
|
||||||
name = "werkzeug-docs-" + conf.version
|
|
||||||
os.chdir("_build")
|
|
||||||
os.rename("html", name)
|
|
||||||
os.system("tar czf %s.tar.gz %s" % (name, name))
|
|
||||||
os.rename(name, "html")
|
|
|
@ -1,19 +0,0 @@
|
||||||
===========
|
|
||||||
Middlewares
|
|
||||||
===========
|
|
||||||
|
|
||||||
.. module:: werkzeug.wsgi
|
|
||||||
|
|
||||||
Middlewares wrap applications to dispatch between them or provide
|
|
||||||
additional request handling. Additionally to the middlewares documented
|
|
||||||
here, there is also the :class:`DebuggedApplication` class that is
|
|
||||||
implemented as a WSGI middleware.
|
|
||||||
|
|
||||||
.. autoclass:: SharedDataMiddleware
|
|
||||||
:members: is_allowed
|
|
||||||
|
|
||||||
.. autoclass:: DispatcherMiddleware
|
|
||||||
|
|
||||||
Also there's the …
|
|
||||||
|
|
||||||
.. autofunction:: werkzeug._internal._easteregg
|
|
|
@ -1,73 +0,0 @@
|
||||||
.. _python3:
|
|
||||||
|
|
||||||
==============
|
|
||||||
Python 3 Notes
|
|
||||||
==============
|
|
||||||
|
|
||||||
Since version 0.9, Werkzeug supports Python 3.3+ in addition to versions 2.6
|
|
||||||
and 2.7. Older Python 3 versions such as 3.2 or 3.1 are not supported.
|
|
||||||
|
|
||||||
This part of the documentation outlines special information required to
|
|
||||||
use Werkzeug and WSGI on Python 3.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Python 3 support in Werkzeug is currently highly experimental. Please
|
|
||||||
give feedback on it and help us improve it.
|
|
||||||
|
|
||||||
|
|
||||||
WSGI Environment
|
|
||||||
================
|
|
||||||
|
|
||||||
The WSGI environment on Python 3 works slightly different than it does on
|
|
||||||
Python 2. For the most part Werkzeug hides the differences from you if
|
|
||||||
you work on the higher level APIs. The main difference between Python 2
|
|
||||||
and Python 3 is that on Python 2 the WSGI environment contains bytes
|
|
||||||
whereas the environment on Python 3 contains a range of differently
|
|
||||||
encoded strings.
|
|
||||||
|
|
||||||
There are two different kinds of strings in the WSGI environ on Python 3:
|
|
||||||
|
|
||||||
- unicode strings restricted to latin1 values. These are used for
|
|
||||||
HTTP headers and a few other things.
|
|
||||||
- unicode strings carrying binary payload, roundtripped through latin1
|
|
||||||
values. This is usually referred as “WSGI encoding dance” throughout
|
|
||||||
Werkzeug.
|
|
||||||
|
|
||||||
Werkzeug provides you with functionality to deal with these automatically
|
|
||||||
so that you don't need to be aware of the inner workings. The following
|
|
||||||
functions and classes should be used to read information out of the
|
|
||||||
WSGI environment:
|
|
||||||
|
|
||||||
- :func:`~werkzeug.wsgi.get_current_url`
|
|
||||||
- :func:`~werkzeug.wsgi.get_host`
|
|
||||||
- :func:`~werkzeug.wsgi.get_script_name`
|
|
||||||
- :func:`~werkzeug.wsgi.get_path_info`
|
|
||||||
- :func:`~werkzeug.wsgi.get_query_string`
|
|
||||||
- :func:`~werkzeug.datastructures.EnvironHeaders`
|
|
||||||
|
|
||||||
Applications are strongly discouraged to create and modify a WSGI
|
|
||||||
environment themselves on Python 3 unless they take care of the proper
|
|
||||||
decoding step. All high level interfaces in Werkzeug will apply the
|
|
||||||
correct encoding and decoding steps as necessary.
|
|
||||||
|
|
||||||
URLs
|
|
||||||
====
|
|
||||||
|
|
||||||
URLs in Werkzeug attempt to represent themselves as unicode strings on
|
|
||||||
Python 3. All the parsing functions generally also provide functionality
|
|
||||||
that allow operations on bytes. In some cases functions that deal with
|
|
||||||
URLs allow passing in `None` as charset to change the return value to byte
|
|
||||||
objects. Internally Werkzeug will now unify URIs and IRIs as much as
|
|
||||||
possible.
|
|
||||||
|
|
||||||
Request Cleanup
|
|
||||||
===============
|
|
||||||
|
|
||||||
Request objects on Python 3 and PyPy require explicit closing when file
|
|
||||||
uploads are involved. This is required to properly close temporary file
|
|
||||||
objects created by the multipart parser. For that purpose the ``close()``
|
|
||||||
method was introduced.
|
|
||||||
|
|
||||||
In addition to that request objects now also act as context managers that
|
|
||||||
automatically close.
|
|
|
@ -1,323 +0,0 @@
|
||||||
==========
|
|
||||||
Quickstart
|
|
||||||
==========
|
|
||||||
|
|
||||||
.. module:: werkzeug
|
|
||||||
|
|
||||||
This part of the documentation shows how to use the most important parts of
|
|
||||||
Werkzeug. It's intended as a starting point for developers with basic
|
|
||||||
understanding of :pep:`333` (WSGI) and :rfc:`2616` (HTTP).
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Make sure to import all objects from the places the documentation
|
|
||||||
suggests. It is theoretically possible in some situations to import
|
|
||||||
objects from different locations but this is not supported.
|
|
||||||
|
|
||||||
For example :class:`MultiDict` is a member of the `werkzeug` module
|
|
||||||
but internally implemented in a different one.
|
|
||||||
|
|
||||||
|
|
||||||
WSGI Environment
|
|
||||||
================
|
|
||||||
|
|
||||||
The WSGI environment contains all the information the user request transmits
|
|
||||||
to the application. It is passed to the WSGI application but you can also
|
|
||||||
create a WSGI environ dict using the :func:`create_environ` helper:
|
|
||||||
|
|
||||||
>>> from werkzeug.test import create_environ
|
|
||||||
>>> environ = create_environ('/foo', 'http://localhost:8080/')
|
|
||||||
|
|
||||||
Now we have an environment to play around:
|
|
||||||
|
|
||||||
>>> environ['PATH_INFO']
|
|
||||||
'/foo'
|
|
||||||
>>> environ['SCRIPT_NAME']
|
|
||||||
''
|
|
||||||
>>> environ['SERVER_NAME']
|
|
||||||
'localhost'
|
|
||||||
|
|
||||||
Usually nobody wants to work with the environ directly because it is limited
|
|
||||||
to bytestrings and does not provide any way to access the form data besides
|
|
||||||
parsing that data by hand.
|
|
||||||
|
|
||||||
|
|
||||||
Enter Request
|
|
||||||
=============
|
|
||||||
|
|
||||||
For access to the request data the :class:`Request` object is much more fun.
|
|
||||||
It wraps the `environ` and provides a read-only access to the data from
|
|
||||||
there:
|
|
||||||
|
|
||||||
>>> from werkzeug.wrappers import Request
|
|
||||||
>>> request = Request(environ)
|
|
||||||
|
|
||||||
Now you can access the important variables and Werkzeug will parse them
|
|
||||||
for you and decode them where it makes sense. The default charset for
|
|
||||||
requests is set to `utf-8` but you can change that by subclassing
|
|
||||||
:class:`Request`.
|
|
||||||
|
|
||||||
>>> request.path
|
|
||||||
u'/foo'
|
|
||||||
>>> request.script_root
|
|
||||||
u''
|
|
||||||
>>> request.host
|
|
||||||
'localhost:8080'
|
|
||||||
>>> request.url
|
|
||||||
'http://localhost:8080/foo'
|
|
||||||
|
|
||||||
We can also find out which HTTP method was used for the request:
|
|
||||||
|
|
||||||
>>> request.method
|
|
||||||
'GET'
|
|
||||||
|
|
||||||
This way we can also access URL arguments (the query string) and data that
|
|
||||||
was transmitted in a POST/PUT request.
|
|
||||||
|
|
||||||
For testing purposes we can create a request object from supplied data
|
|
||||||
using the :meth:`~BaseRequest.from_values` method:
|
|
||||||
|
|
||||||
>>> from cStringIO import StringIO
|
|
||||||
>>> data = "name=this+is+encoded+form+data&another_key=another+one"
|
|
||||||
>>> request = Request.from_values(query_string='foo=bar&blah=blafasel',
|
|
||||||
... content_length=len(data), input_stream=StringIO(data),
|
|
||||||
... content_type='application/x-www-form-urlencoded',
|
|
||||||
... method='POST')
|
|
||||||
...
|
|
||||||
>>> request.method
|
|
||||||
'POST'
|
|
||||||
|
|
||||||
Now we can access the URL parameters easily:
|
|
||||||
|
|
||||||
>>> request.args.keys()
|
|
||||||
['blah', 'foo']
|
|
||||||
>>> request.args['blah']
|
|
||||||
u'blafasel'
|
|
||||||
|
|
||||||
Same for the supplied form data:
|
|
||||||
|
|
||||||
>>> request.form['name']
|
|
||||||
u'this is encoded form data'
|
|
||||||
|
|
||||||
Handling for uploaded files is not much harder as you can see from this
|
|
||||||
example::
|
|
||||||
|
|
||||||
def store_file(request):
|
|
||||||
file = request.files.get('my_file')
|
|
||||||
if file:
|
|
||||||
file.save('/where/to/store/the/file.txt')
|
|
||||||
else:
|
|
||||||
handle_the_error()
|
|
||||||
|
|
||||||
The files are represented as :class:`FileStorage` objects which provide
|
|
||||||
some common operations to work with them.
|
|
||||||
|
|
||||||
Request headers can be accessed by using the :class:`~BaseRequest.headers`
|
|
||||||
attribute:
|
|
||||||
|
|
||||||
>>> request.headers['Content-Length']
|
|
||||||
'54'
|
|
||||||
>>> request.headers['Content-Type']
|
|
||||||
'application/x-www-form-urlencoded'
|
|
||||||
|
|
||||||
The keys for the headers are of course case insensitive.
|
|
||||||
|
|
||||||
|
|
||||||
Header Parsing
|
|
||||||
==============
|
|
||||||
|
|
||||||
There is more. Werkzeug provides convenient access to often used HTTP headers
|
|
||||||
and other request data.
|
|
||||||
|
|
||||||
Let's create a request object with all the data a typical web browser transmits
|
|
||||||
so that we can play with it:
|
|
||||||
|
|
||||||
>>> environ = create_environ()
|
|
||||||
>>> environ.update(
|
|
||||||
... HTTP_USER_AGENT='Mozilla/5.0 (Macintosh; U; Mac OS X 10.5; en-US; ) Firefox/3.1',
|
|
||||||
... HTTP_ACCEPT='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
||||||
... HTTP_ACCEPT_LANGUAGE='de-at,en-us;q=0.8,en;q=0.5',
|
|
||||||
... HTTP_ACCEPT_ENCODING='gzip,deflate',
|
|
||||||
... HTTP_ACCEPT_CHARSET='ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
|
||||||
... HTTP_IF_MODIFIED_SINCE='Fri, 20 Feb 2009 10:10:25 GMT',
|
|
||||||
... HTTP_IF_NONE_MATCH='"e51c9-1e5d-46356dc86c640"',
|
|
||||||
... HTTP_CACHE_CONTROL='max-age=0'
|
|
||||||
... )
|
|
||||||
...
|
|
||||||
>>> request = Request(environ)
|
|
||||||
|
|
||||||
Let's start with the most useless header: the user agent:
|
|
||||||
|
|
||||||
>>> request.user_agent.browser
|
|
||||||
'firefox'
|
|
||||||
>>> request.user_agent.platform
|
|
||||||
'macos'
|
|
||||||
>>> request.user_agent.version
|
|
||||||
'3.1'
|
|
||||||
>>> request.user_agent.language
|
|
||||||
'en-US'
|
|
||||||
|
|
||||||
A more useful header is the accept header. With this header the browser
|
|
||||||
informs the web application what mimetypes it can handle and how well. All
|
|
||||||
accept headers are sorted by the quality, the best item being the first:
|
|
||||||
|
|
||||||
>>> request.accept_mimetypes.best
|
|
||||||
'text/html'
|
|
||||||
>>> 'application/xhtml+xml' in request.accept_mimetypes
|
|
||||||
True
|
|
||||||
>>> print request.accept_mimetypes["application/json"]
|
|
||||||
0.8
|
|
||||||
|
|
||||||
The same works for languages:
|
|
||||||
|
|
||||||
>>> request.accept_languages.best
|
|
||||||
'de-at'
|
|
||||||
>>> request.accept_languages.values()
|
|
||||||
['de-at', 'en-us', 'en']
|
|
||||||
|
|
||||||
And of course encodings and charsets:
|
|
||||||
|
|
||||||
>>> 'gzip' in request.accept_encodings
|
|
||||||
True
|
|
||||||
>>> request.accept_charsets.best
|
|
||||||
'ISO-8859-1'
|
|
||||||
>>> 'utf-8' in request.accept_charsets
|
|
||||||
True
|
|
||||||
|
|
||||||
Normalization is available, so you can safely use alternative forms to perform
|
|
||||||
containment checking:
|
|
||||||
|
|
||||||
>>> 'UTF8' in request.accept_charsets
|
|
||||||
True
|
|
||||||
>>> 'de_AT' in request.accept_languages
|
|
||||||
True
|
|
||||||
|
|
||||||
E-tags and other conditional headers are available in parsed form as well:
|
|
||||||
|
|
||||||
>>> request.if_modified_since
|
|
||||||
datetime.datetime(2009, 2, 20, 10, 10, 25)
|
|
||||||
>>> request.if_none_match
|
|
||||||
<ETags '"e51c9-1e5d-46356dc86c640"'>
|
|
||||||
>>> request.cache_control
|
|
||||||
<RequestCacheControl 'max-age=0'>
|
|
||||||
>>> request.cache_control.max_age
|
|
||||||
0
|
|
||||||
>>> 'e51c9-1e5d-46356dc86c640' in request.if_none_match
|
|
||||||
True
|
|
||||||
|
|
||||||
|
|
||||||
Responses
|
|
||||||
=========
|
|
||||||
|
|
||||||
Response objects are the opposite of request objects. They are used to send
|
|
||||||
data back to the client. In reality, response objects are nothing more than
|
|
||||||
glorified WSGI applications.
|
|
||||||
|
|
||||||
So what you are doing is not *returning* the response objects from your WSGI
|
|
||||||
application but *calling* it as WSGI application inside your WSGI application
|
|
||||||
and returning the return value of that call.
|
|
||||||
|
|
||||||
So imagine your standard WSGI "Hello World" application::
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
start_response('200 OK', [('Content-Type', 'text/plain')])
|
|
||||||
return ['Hello World!']
|
|
||||||
|
|
||||||
With response objects it would look like this::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import Response
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
response = Response('Hello World!')
|
|
||||||
return response(environ, start_response)
|
|
||||||
|
|
||||||
Also, unlike request objects, response objects are designed to be modified.
|
|
||||||
So here is what you can do with them:
|
|
||||||
|
|
||||||
>>> from werkzeug.wrappers import Response
|
|
||||||
>>> response = Response("Hello World!")
|
|
||||||
>>> response.headers['content-type']
|
|
||||||
'text/plain; charset=utf-8'
|
|
||||||
>>> response.data
|
|
||||||
'Hello World!'
|
|
||||||
>>> response.headers['content-length'] = len(response.data)
|
|
||||||
|
|
||||||
You can modify the status of the response in the same way. Either just the
|
|
||||||
code or provide a message as well:
|
|
||||||
|
|
||||||
>>> response.status
|
|
||||||
'200 OK'
|
|
||||||
>>> response.status = '404 Not Found'
|
|
||||||
>>> response.status_code
|
|
||||||
404
|
|
||||||
>>> response.status_code = 400
|
|
||||||
>>> response.status
|
|
||||||
'400 BAD REQUEST'
|
|
||||||
|
|
||||||
As you can see attributes work in both directions. So you can set both
|
|
||||||
:attr:`~BaseResponse.status` and :attr:`~BaseResponse.status_code` and the
|
|
||||||
change will be reflected to the other.
|
|
||||||
|
|
||||||
Also common headers are exposed as attributes or with methods to set /
|
|
||||||
retrieve them:
|
|
||||||
|
|
||||||
>>> response.content_length
|
|
||||||
12
|
|
||||||
>>> from datetime import datetime
|
|
||||||
>>> response.date = datetime(2009, 2, 20, 17, 42, 51)
|
|
||||||
>>> response.headers['Date']
|
|
||||||
'Fri, 20 Feb 2009 17:42:51 GMT'
|
|
||||||
|
|
||||||
Because etags can be weak or strong there are methods to set them:
|
|
||||||
|
|
||||||
>>> response.set_etag("12345-abcd")
|
|
||||||
>>> response.headers['etag']
|
|
||||||
'"12345-abcd"'
|
|
||||||
>>> response.get_etag()
|
|
||||||
('12345-abcd', False)
|
|
||||||
>>> response.set_etag("12345-abcd", weak=True)
|
|
||||||
>>> response.get_etag()
|
|
||||||
('12345-abcd', True)
|
|
||||||
|
|
||||||
Some headers are available as mutable structures. For example most
|
|
||||||
of the `Content-` headers are sets of values:
|
|
||||||
|
|
||||||
>>> response.content_language.add('en-us')
|
|
||||||
>>> response.content_language.add('en')
|
|
||||||
>>> response.headers['Content-Language']
|
|
||||||
'en-us, en'
|
|
||||||
|
|
||||||
Also here this works in both directions:
|
|
||||||
|
|
||||||
>>> response.headers['Content-Language'] = 'de-AT, de'
|
|
||||||
>>> response.content_language
|
|
||||||
HeaderSet(['de-AT', 'de'])
|
|
||||||
|
|
||||||
Authentication headers can be set that way as well:
|
|
||||||
|
|
||||||
>>> response.www_authenticate.set_basic("My protected resource")
|
|
||||||
>>> response.headers['www-authenticate']
|
|
||||||
'Basic realm="My protected resource"'
|
|
||||||
|
|
||||||
Cookies can be set as well:
|
|
||||||
|
|
||||||
>>> response.set_cookie('name', 'value')
|
|
||||||
>>> response.headers['Set-Cookie']
|
|
||||||
'name=value; Path=/'
|
|
||||||
>>> response.set_cookie('name2', 'value2')
|
|
||||||
|
|
||||||
If headers appear multiple times you can use the :meth:`~Headers.getlist`
|
|
||||||
method to get all values for a header:
|
|
||||||
|
|
||||||
>>> response.headers.getlist('Set-Cookie')
|
|
||||||
['name=value; Path=/', 'name2=value2; Path=/']
|
|
||||||
|
|
||||||
Finally if you have set all the conditional values, you can make the
|
|
||||||
response conditional against a request. Which means that if the request
|
|
||||||
can assure that it has the information already, no data besides the headers
|
|
||||||
is sent over the network which saves traffic. For that you should set at
|
|
||||||
least an etag (which is used for comparison) and the date header and then
|
|
||||||
call :class:`~BaseRequest.make_conditional` with the request object.
|
|
||||||
|
|
||||||
The response is modified accordingly (status code changed, response body
|
|
||||||
removed, entity headers removed etc.)
|
|
|
@ -1,116 +0,0 @@
|
||||||
.. _dealing-with-request-data:
|
|
||||||
|
|
||||||
Dealing with Request Data
|
|
||||||
=========================
|
|
||||||
|
|
||||||
.. module:: werkzeug
|
|
||||||
|
|
||||||
The most important rule about web development is "Do not trust the user".
|
|
||||||
This is especially true for incoming request data on the input stream.
|
|
||||||
With WSGI this is actually a bit harder than you would expect. Because
|
|
||||||
of that Werkzeug wraps the request stream for you to save you from the
|
|
||||||
most prominent problems with it.
|
|
||||||
|
|
||||||
|
|
||||||
Missing EOF Marker on Input Stream
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
The input stream has no end-of-file marker. If you would call the
|
|
||||||
:meth:`~file.read` method on the `wsgi.input` stream you would cause your
|
|
||||||
application to hang on conforming servers. This is actually intentional
|
|
||||||
however painful. Werkzeug solves that problem by wrapping the input
|
|
||||||
stream in a special :class:`LimitedStream`. The input stream is exposed
|
|
||||||
on the request objects as :attr:`~BaseRequest.stream`. This one is either
|
|
||||||
an empty stream (if the form data was parsed) or a limited stream with
|
|
||||||
the contents of the input stream.
|
|
||||||
|
|
||||||
|
|
||||||
When does Werkzeug Parse?
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Werkzeug parses the incoming data under the following situations:
|
|
||||||
|
|
||||||
- you access either :attr:`~BaseRequest.form`, :attr:`~BaseRequest.files`,
|
|
||||||
or :attr:`~BaseRequest.stream` and the request method was
|
|
||||||
`POST` or `PUT`.
|
|
||||||
- if you call :func:`parse_form_data`.
|
|
||||||
|
|
||||||
These calls are not interchangeable. If you invoke :func:`parse_form_data`
|
|
||||||
you must not use the request object or at least not the attributes that
|
|
||||||
trigger the parsing process.
|
|
||||||
|
|
||||||
This is also true if you read from the `wsgi.input` stream before the
|
|
||||||
parsing.
|
|
||||||
|
|
||||||
**General rule:** Leave the WSGI input stream alone. Especially in
|
|
||||||
WSGI middlewares. Use either the parsing functions or the request
|
|
||||||
object. Do not mix multiple WSGI utility libraries for form data
|
|
||||||
parsing or anything else that works on the input stream.
|
|
||||||
|
|
||||||
|
|
||||||
How does it Parse?
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The standard Werkzeug parsing behavior handles three cases:
|
|
||||||
|
|
||||||
- input content type was `multipart/form-data`. In this situation the
|
|
||||||
:class:`~BaseRequest.stream` will be empty and
|
|
||||||
:class:`~BaseRequest.form` will contain the regular `POST` / `PUT`
|
|
||||||
data, :class:`~BaseRequest.files` will contain the uploaded
|
|
||||||
files as :class:`FileStorage` objects.
|
|
||||||
- input content type was `application/x-www-form-urlencoded`. Then the
|
|
||||||
:class:`~BaseRequest.stream` will be empty and
|
|
||||||
:class:`~BaseRequest.form` will contain the regular `POST` / `PUT`
|
|
||||||
data and :class:`~BaseRequest.files` will be empty.
|
|
||||||
- the input content type was neither of them, :class:`~BaseRequest.stream`
|
|
||||||
points to a :class:`LimitedStream` with the input data for further
|
|
||||||
processing.
|
|
||||||
|
|
||||||
Special note on the :attr:`~BaseRequest.get_data` method: Calling this
|
|
||||||
loads the full request data into memory. This is only safe to do if the
|
|
||||||
:attr:`~BaseRequest.max_content_length` is set. Also you can *either*
|
|
||||||
read the stream *or* call :meth:`~BaseRequest.get_data`.
|
|
||||||
|
|
||||||
|
|
||||||
Limiting Request Data
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
To avoid being the victim of a DDOS attack you can set the maximum
|
|
||||||
accepted content length and request field sizes. The :class:`BaseRequest`
|
|
||||||
class has two attributes for that: :attr:`~BaseRequest.max_content_length`
|
|
||||||
and :attr:`~BaseRequest.max_form_memory_size`.
|
|
||||||
|
|
||||||
The first one can be used to limit the total content length. For example
|
|
||||||
by setting it to ``1024 * 1024 * 16`` the request won't accept more than
|
|
||||||
16MB of transmitted data.
|
|
||||||
|
|
||||||
Because certain data can't be moved to the hard disk (regular post data)
|
|
||||||
whereas temporary files can, there is a second limit you can set. The
|
|
||||||
:attr:`~BaseRequest.max_form_memory_size` limits the size of `POST`
|
|
||||||
transmitted form data. By setting it to ``1024 * 1024 * 2`` you can make
|
|
||||||
sure that all in memory-stored fields are not more than 2MB in size.
|
|
||||||
|
|
||||||
This however does *not* affect in-memory stored files if the
|
|
||||||
`stream_factory` used returns a in-memory file.
|
|
||||||
|
|
||||||
|
|
||||||
How to extend Parsing?
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Modern web applications transmit a lot more than multipart form data or
|
|
||||||
url encoded data. Extending the parsing capabilities by subclassing
|
|
||||||
the :class:`BaseRequest` is simple. The following example implements
|
|
||||||
parsing for incoming JSON data::
|
|
||||||
|
|
||||||
from werkzeug.utils import cached_property
|
|
||||||
from werkzeug.wrappers import Request
|
|
||||||
from simplejson import loads
|
|
||||||
|
|
||||||
class JSONRequest(Request):
|
|
||||||
# accept up to 4MB of transmitted data.
|
|
||||||
max_content_length = 1024 * 1024 * 4
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def json(self):
|
|
||||||
if self.headers.get('content-type') == 'application/json':
|
|
||||||
return loads(self.data)
|
|
|
@ -1,205 +0,0 @@
|
||||||
.. _routing:
|
|
||||||
|
|
||||||
===========
|
|
||||||
URL Routing
|
|
||||||
===========
|
|
||||||
|
|
||||||
.. module:: werkzeug.routing
|
|
||||||
|
|
||||||
.. testsetup::
|
|
||||||
|
|
||||||
from werkzeug.routing import *
|
|
||||||
|
|
||||||
When it comes to combining multiple controller or view functions (however
|
|
||||||
you want to call them), you need a dispatcher. A simple way would be
|
|
||||||
applying regular expression tests on ``PATH_INFO`` and call registered
|
|
||||||
callback functions that return the value.
|
|
||||||
|
|
||||||
Werkzeug provides a much more powerful system, similar to `Routes`_. All the
|
|
||||||
objects mentioned on this page must be imported from :mod:`werkzeug.routing`, not
|
|
||||||
from :mod:`werkzeug`!
|
|
||||||
|
|
||||||
.. _Routes: http://routes.groovie.org/
|
|
||||||
|
|
||||||
|
|
||||||
Quickstart
|
|
||||||
==========
|
|
||||||
|
|
||||||
Here is a simple example which could be the URL definition for a blog::
|
|
||||||
|
|
||||||
from werkzeug.routing import Map, Rule, NotFound, RequestRedirect
|
|
||||||
|
|
||||||
url_map = Map([
|
|
||||||
Rule('/', endpoint='blog/index'),
|
|
||||||
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('/about', endpoint='blog/about_me'),
|
|
||||||
Rule('/feeds/', endpoint='blog/feeds'),
|
|
||||||
Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
|
|
||||||
])
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
urls = url_map.bind_to_environ(environ)
|
|
||||||
try:
|
|
||||||
endpoint, args = urls.match()
|
|
||||||
except HTTPException, e:
|
|
||||||
return e(environ, start_response)
|
|
||||||
start_response('200 OK', [('Content-Type', 'text/plain')])
|
|
||||||
return ['Rule points to %r with arguments %r' % (endpoint, args)]
|
|
||||||
|
|
||||||
So what does that do? First of all we create a new :class:`Map` which stores
|
|
||||||
a bunch of URL rules. Then we pass it a list of :class:`Rule` objects.
|
|
||||||
|
|
||||||
Each :class:`Rule` object is instantiated with a string that represents a rule
|
|
||||||
and an endpoint which will be the alias for what view the rule represents.
|
|
||||||
Multiple rules can have the same endpoint, but should have different arguments
|
|
||||||
to allow URL construction.
|
|
||||||
|
|
||||||
The format for the URL rules is straightforward, but explained in detail below.
|
|
||||||
|
|
||||||
Inside the WSGI application we bind the url_map to the current request which will
|
|
||||||
return a new :class:`MapAdapter`. This url_map adapter can then be used to match
|
|
||||||
or build domains for the current request.
|
|
||||||
|
|
||||||
The :meth:`MapAdapter.match` method can then either return a tuple in the form
|
|
||||||
``(endpoint, args)`` or raise one of the three exceptions
|
|
||||||
:exc:`~werkzeug.exceptions.NotFound`, :exc:`~werkzeug.exceptions.MethodNotAllowed`,
|
|
||||||
or :exc:`~werkzeug.exceptions.RequestRedirect`. For more details about those
|
|
||||||
exceptions have a look at the documentation of the :meth:`MapAdapter.match` method.
|
|
||||||
|
|
||||||
|
|
||||||
Rule Format
|
|
||||||
===========
|
|
||||||
|
|
||||||
Rule strings basically are just normal URL paths with placeholders in the
|
|
||||||
format ``<converter(arguments):name>``, where converter and the arguments
|
|
||||||
are optional. If no converter is defined, the `default` converter is used
|
|
||||||
(which means `string` in the normal configuration).
|
|
||||||
|
|
||||||
URL rules that end with a slash are branch URLs, others are leaves. If you
|
|
||||||
have `strict_slashes` enabled (which is the default), all branch URLs that are
|
|
||||||
visited without a trailing slash will trigger a redirect to the same URL with
|
|
||||||
that slash appended.
|
|
||||||
|
|
||||||
The list of converters can be extended, the default converters are explained
|
|
||||||
below.
|
|
||||||
|
|
||||||
|
|
||||||
Builtin Converters
|
|
||||||
==================
|
|
||||||
|
|
||||||
Here a list of converters that come with Werkzeug:
|
|
||||||
|
|
||||||
.. autoclass:: UnicodeConverter
|
|
||||||
|
|
||||||
.. autoclass:: PathConverter
|
|
||||||
|
|
||||||
.. autoclass:: AnyConverter
|
|
||||||
|
|
||||||
.. autoclass:: IntegerConverter
|
|
||||||
|
|
||||||
.. autoclass:: FloatConverter
|
|
||||||
|
|
||||||
.. autoclass:: UUIDConverter
|
|
||||||
|
|
||||||
|
|
||||||
Maps, Rules and Adapters
|
|
||||||
========================
|
|
||||||
|
|
||||||
.. autoclass:: Map
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. attribute:: converters
|
|
||||||
|
|
||||||
The dictionary of converters. This can be modified after the class
|
|
||||||
was created, but will only affect rules added after the
|
|
||||||
modification. If the rules are defined with the list passed to the
|
|
||||||
class, the `converters` parameter to the constructor has to be used
|
|
||||||
instead.
|
|
||||||
|
|
||||||
.. autoclass:: MapAdapter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: Rule
|
|
||||||
:members: empty
|
|
||||||
|
|
||||||
|
|
||||||
Rule Factories
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. autoclass:: RuleFactory
|
|
||||||
:members: get_rules
|
|
||||||
|
|
||||||
.. autoclass:: Subdomain
|
|
||||||
|
|
||||||
.. autoclass:: Submount
|
|
||||||
|
|
||||||
.. autoclass:: EndpointPrefix
|
|
||||||
|
|
||||||
|
|
||||||
Rule Templates
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. autoclass:: RuleTemplate
|
|
||||||
|
|
||||||
|
|
||||||
Custom Converters
|
|
||||||
=================
|
|
||||||
|
|
||||||
You can easily add custom converters. The only thing you have to do is to
|
|
||||||
subclass :class:`BaseConverter` and pass that new converter to the url_map.
|
|
||||||
A converter has to provide two public methods: `to_python` and `to_url`,
|
|
||||||
as well as a member that represents a regular expression. Here is a small
|
|
||||||
example::
|
|
||||||
|
|
||||||
from random import randrange
|
|
||||||
from werkzeug.routing import Rule, Map, BaseConverter, ValidationError
|
|
||||||
|
|
||||||
class BooleanConverter(BaseConverter):
|
|
||||||
|
|
||||||
def __init__(self, url_map, randomify=False):
|
|
||||||
super(BooleanConverter, self).__init__(url_map)
|
|
||||||
self.randomify = randomify
|
|
||||||
self.regex = '(?:yes|no|maybe)'
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
if value == 'maybe':
|
|
||||||
if self.randomify:
|
|
||||||
return not randrange(2)
|
|
||||||
raise ValidationError()
|
|
||||||
return value == 'yes'
|
|
||||||
|
|
||||||
def to_url(self, value):
|
|
||||||
return value and 'yes' or 'no'
|
|
||||||
|
|
||||||
url_map = Map([
|
|
||||||
Rule('/vote/<bool:werkzeug_rocks>', endpoint='vote'),
|
|
||||||
Rule('/vote/<bool(randomify=True):foo>', endpoint='foo')
|
|
||||||
], converters={'bool': BooleanConverter})
|
|
||||||
|
|
||||||
If you want that converter to be the default converter, name it ``'default'``.
|
|
||||||
|
|
||||||
Host Matching
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
|
||||||
|
|
||||||
Starting with Werkzeug 0.7 it's also possible to do matching on the whole
|
|
||||||
host names instead of just the subdomain. To enable this feature you need
|
|
||||||
to pass ``host_matching=True`` to the :class:`Map` constructor and provide
|
|
||||||
the `host` argument to all routes::
|
|
||||||
|
|
||||||
url_map = Map([
|
|
||||||
Rule('/', endpoint='www_index', host='www.example.com'),
|
|
||||||
Rule('/', endpoint='help_index', host='help.example.com')
|
|
||||||
], host_matching=True)
|
|
||||||
|
|
||||||
Variable parts are of course also possible in the host section::
|
|
||||||
|
|
||||||
url_map = Map([
|
|
||||||
Rule('/', endpoint='www_index', host='www.example.com'),
|
|
||||||
Rule('/', endpoint='user_index', host='<user>.example.com')
|
|
||||||
], host_matching=True)
|
|
|
@ -1,227 +0,0 @@
|
||||||
=========================
|
|
||||||
Serving WSGI Applications
|
|
||||||
=========================
|
|
||||||
|
|
||||||
.. module:: werkzeug.serving
|
|
||||||
|
|
||||||
There are many ways to serve a WSGI application. While you're developing it,
|
|
||||||
you usually don't want to have a full-blown webserver like Apache up and
|
|
||||||
running, but instead a simple standalone one. Because of that Werkzeug comes
|
|
||||||
with a builtin development server.
|
|
||||||
|
|
||||||
The easiest way is creating a small ``start-myproject.py`` file that runs the
|
|
||||||
application using the builtin server::
|
|
||||||
|
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from werkzeug.serving import run_simple
|
|
||||||
from myproject import make_app
|
|
||||||
|
|
||||||
app = make_app(...)
|
|
||||||
run_simple('localhost', 8080, app, use_reloader=True)
|
|
||||||
|
|
||||||
You can also pass it the `extra_files` keyword argument with a list of
|
|
||||||
additional files (like configuration files) you want to observe.
|
|
||||||
|
|
||||||
.. autofunction:: run_simple
|
|
||||||
|
|
||||||
.. autofunction:: is_running_from_reloader
|
|
||||||
|
|
||||||
.. autofunction:: make_ssl_devcert
|
|
||||||
|
|
||||||
.. admonition:: Information
|
|
||||||
|
|
||||||
The development server is not intended to be used on production systems.
|
|
||||||
It was designed especially for development purposes and performs poorly
|
|
||||||
under high load. For deployment setups have a look at the
|
|
||||||
:ref:`deployment` pages.
|
|
||||||
|
|
||||||
.. _reloader:
|
|
||||||
|
|
||||||
Reloader
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. versionchanged:: 0.10
|
|
||||||
|
|
||||||
The Werkzeug reloader constantly monitors modules and paths of your web
|
|
||||||
application, and restarts the server if any of the observed files change.
|
|
||||||
|
|
||||||
Since version 0.10, there are two backends the reloader supports: ``stat`` and
|
|
||||||
``watchdog``.
|
|
||||||
|
|
||||||
- The default ``stat`` backend simply checks the ``mtime`` of all files in a
|
|
||||||
regular interval. This is sufficient for most cases, however, it is known to
|
|
||||||
drain a laptop's battery.
|
|
||||||
|
|
||||||
- The ``watchdog`` backend uses filesystem events, and is much faster than
|
|
||||||
``stat``. It requires the `watchdog <https://pypi.python.org/pypi/watchdog>`_
|
|
||||||
module to be installed. The recommended way to achieve this is to add
|
|
||||||
``Werkzeug[watchdog]`` to your requirements file.
|
|
||||||
|
|
||||||
If ``watchdog`` is installed and available it will automatically be used
|
|
||||||
instead of the builtin ``stat`` reloader.
|
|
||||||
|
|
||||||
To switch between the backends you can use the `reloader_type` parameter of the
|
|
||||||
:func:`run_simple` function. ``'stat'`` sets it to the default stat based
|
|
||||||
polling and ``'watchdog'`` forces it to the watchdog backend.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Some edge cases, like modules that failed to import correctly, are not
|
|
||||||
handled by the stat reloader for performance reasons. The watchdog reloader
|
|
||||||
monitors such files too.
|
|
||||||
|
|
||||||
Colored Logging
|
|
||||||
---------------
|
|
||||||
Werkzeug is able to color the output of request logs when ran from a terminal, just install the `termcolor
|
|
||||||
<https://pypi.python.org/pypi/termcolor>`_ package. Windows users need to install `colorama
|
|
||||||
<https://pypi.python.org/pypi/colorama>`_ in addition to termcolor for this to work.
|
|
||||||
|
|
||||||
Virtual Hosts
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Many web applications utilize multiple subdomains. This can be a bit tricky
|
|
||||||
to simulate locally. Fortunately there is the `hosts file`_ that can be used
|
|
||||||
to assign the local computer multiple names.
|
|
||||||
|
|
||||||
This allows you to call your local computer `yourapplication.local` and
|
|
||||||
`api.yourapplication.local` (or anything else) in addition to `localhost`.
|
|
||||||
|
|
||||||
You can find the hosts file on the following location:
|
|
||||||
|
|
||||||
=============== ==============================================
|
|
||||||
Windows ``%SystemRoot%\system32\drivers\etc\hosts``
|
|
||||||
Linux / OS X ``/etc/hosts``
|
|
||||||
=============== ==============================================
|
|
||||||
|
|
||||||
You can open the file with your favorite text editor and add a new name after
|
|
||||||
`localhost`::
|
|
||||||
|
|
||||||
127.0.0.1 localhost yourapplication.local api.yourapplication.local
|
|
||||||
|
|
||||||
Save the changes and after a while you should be able to access the
|
|
||||||
development server on these host names as well. You can use the
|
|
||||||
:ref:`routing` system to dispatch between different hosts or parse
|
|
||||||
:attr:`request.host` yourself.
|
|
||||||
|
|
||||||
Shutting Down The Server
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
|
||||||
|
|
||||||
Starting with Werkzeug 0.7 the development server provides a way to shut
|
|
||||||
down the server after a request. This currently only works with Python
|
|
||||||
2.6 and later and will only work with the development server. To initiate
|
|
||||||
the shutdown you have to call a function named
|
|
||||||
``'werkzeug.server.shutdown'`` in the WSGI environment::
|
|
||||||
|
|
||||||
def shutdown_server(environ):
|
|
||||||
if not 'werkzeug.server.shutdown' in environ:
|
|
||||||
raise RuntimeError('Not running the development server')
|
|
||||||
environ['werkzeug.server.shutdown']()
|
|
||||||
|
|
||||||
Troubleshooting
|
|
||||||
---------------
|
|
||||||
|
|
||||||
On operating systems that support ipv6 and have it configured such as modern
|
|
||||||
Linux systems, OS X 10.4 or higher as well as Windows Vista some browsers can
|
|
||||||
be painfully slow if accessing your local server. The reason for this is that
|
|
||||||
sometimes "localhost" is configured to be available on both ipv4 and ipv6 sockets
|
|
||||||
and some browsers will try to access ipv6 first and then ipv4.
|
|
||||||
|
|
||||||
At the current time the integrated webserver does not support ipv6 and ipv4 at
|
|
||||||
the same time and for better portability ipv4 is the default.
|
|
||||||
|
|
||||||
If you notice that the web browser takes ages to load the page there are two ways
|
|
||||||
around this issue. If you don't need ipv6 support you can disable the ipv6 entry
|
|
||||||
in the `hosts file`_ by removing this line::
|
|
||||||
|
|
||||||
::1 localhost
|
|
||||||
|
|
||||||
Alternatively you can also disable ipv6 support in your browser. For example
|
|
||||||
if Firefox shows this behavior you can disable it by going to ``about:config``
|
|
||||||
and disabling the `network.dns.disableIPv6` key. This however is not
|
|
||||||
recommended as of Werkzeug 0.6.1!
|
|
||||||
|
|
||||||
Starting with Werkzeug 0.6.1, the server will now switch between ipv4 and
|
|
||||||
ipv6 based on your operating system's configuration. This means if that
|
|
||||||
you disabled ipv6 support in your browser but your operating system is
|
|
||||||
preferring ipv6, you will be unable to connect to your server. In that
|
|
||||||
situation, you can either remove the localhost entry for ``::1`` or
|
|
||||||
explicitly bind the hostname to an ipv4 address (`127.0.0.1`)
|
|
||||||
|
|
||||||
.. _hosts file: http://en.wikipedia.org/wiki/Hosts_file
|
|
||||||
|
|
||||||
SSL
|
|
||||||
---
|
|
||||||
|
|
||||||
.. versionadded:: 0.6
|
|
||||||
|
|
||||||
The builtin server supports SSL for testing purposes. If an SSL context is
|
|
||||||
provided it will be used. That means a server can either run in HTTP or HTTPS
|
|
||||||
mode, but not both.
|
|
||||||
|
|
||||||
Quickstart
|
|
||||||
``````````
|
|
||||||
|
|
||||||
The easiest way to do SSL based development with Werkzeug is by using it
|
|
||||||
to generate an SSL certificate and private key and storing that somewhere
|
|
||||||
and to then put it there. For the certificate you need to provide the
|
|
||||||
name of your server on generation or a `CN`.
|
|
||||||
|
|
||||||
1. Generate an SSL key and store it somewhere:
|
|
||||||
|
|
||||||
>>> from werkzeug.serving import make_ssl_devcert
|
|
||||||
>>> make_ssl_devcert('/path/to/the/key', host='localhost')
|
|
||||||
('/path/to/the/key.crt', '/path/to/the/key.key')
|
|
||||||
|
|
||||||
2. Now this tuple can be passed as ``ssl_context`` to the
|
|
||||||
:func:`run_simple` method::
|
|
||||||
|
|
||||||
run_simple('localhost', 4000, application,
|
|
||||||
ssl_context=('/path/to/the/key.crt',
|
|
||||||
'/path/to/the/key.key'))
|
|
||||||
|
|
||||||
You will have to acknowledge the certificate in your browser once then.
|
|
||||||
|
|
||||||
Loading Contexts by Hand
|
|
||||||
````````````````````````
|
|
||||||
|
|
||||||
In Python 2.7.9 and 3+ you also have the option to use a ``ssl.SSLContext``
|
|
||||||
object instead of a simple tuple. This way you have better control over the SSL
|
|
||||||
behavior of Werkzeug's builtin server::
|
|
||||||
|
|
||||||
import ssl
|
|
||||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
|
||||||
ctx.load_cert_chain('ssl.cert', 'ssl.key')
|
|
||||||
run_simple('localhost', 4000, application, ssl_context=ctx)
|
|
||||||
|
|
||||||
|
|
||||||
.. versionchanged 0.10:: ``OpenSSL`` contexts are not supported anymore.
|
|
||||||
|
|
||||||
Generating Certificates
|
|
||||||
```````````````````````
|
|
||||||
|
|
||||||
A key and certificate can be created in advance using the openssl tool
|
|
||||||
instead of the :func:`make_ssl_devcert`. This requires that you have
|
|
||||||
the `openssl` command installed on your system::
|
|
||||||
|
|
||||||
$ openssl genrsa 1024 > ssl.key
|
|
||||||
$ openssl req -new -x509 -nodes -sha1 -days 365 -key ssl.key > ssl.cert
|
|
||||||
|
|
||||||
Adhoc Certificates
|
|
||||||
``````````````````
|
|
||||||
|
|
||||||
The easiest way to enable SSL is to start the server in adhoc-mode. In
|
|
||||||
that case Werkzeug will generate an SSL certificate for you::
|
|
||||||
|
|
||||||
run_simple('localhost', 4000, application,
|
|
||||||
ssl_context='adhoc')
|
|
||||||
|
|
||||||
The downside of this of course is that you will have to acknowledge the
|
|
||||||
certificate each time the server is reloaded. Adhoc certificates are
|
|
||||||
discouraged because modern browsers do a bad job at supporting them for
|
|
||||||
security reasons.
|
|
||||||
|
|
||||||
This feature requires the pyOpenSSL library to be installed.
|
|
|
@ -1,44 +0,0 @@
|
||||||
===============
|
|
||||||
Important Terms
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. module:: werkzeug
|
|
||||||
|
|
||||||
This page covers important terms used in the documentation and Werkzeug
|
|
||||||
itself.
|
|
||||||
|
|
||||||
|
|
||||||
WSGI
|
|
||||||
----
|
|
||||||
|
|
||||||
WSGI a specification for Python web applications Werkzeug follows. It was
|
|
||||||
specified in the :pep:`333` and is widely supported. Unlike previous solutions
|
|
||||||
it guarantees that web applications, servers and utilities can work together.
|
|
||||||
|
|
||||||
Response Object
|
|
||||||
---------------
|
|
||||||
|
|
||||||
For Werkzeug, a response object is an object that works like a WSGI
|
|
||||||
application but does not do any request processing. Usually you have a view
|
|
||||||
function or controller method that processes the request and assembles a
|
|
||||||
response object.
|
|
||||||
|
|
||||||
A response object is *not* necessarily the :class:`BaseResponse` object or a
|
|
||||||
subclass thereof.
|
|
||||||
|
|
||||||
For example Pylons/webob provide a very similar response class that can
|
|
||||||
be used as well (:class:`webob.Response`).
|
|
||||||
|
|
||||||
View Function
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Often people speak of MVC (Model, View, Controller) when developing web
|
|
||||||
applications. However, the Django framework coined MTV (Model, Template,
|
|
||||||
View) which basically means the same but reduces the concept to the data
|
|
||||||
model, a function that processes data from the request and the database and
|
|
||||||
renders a template.
|
|
||||||
|
|
||||||
Werkzeug itself does not tell you how you should develop applications, but the
|
|
||||||
documentation often speaks of view functions that work roughly the same. The
|
|
||||||
idea of a view function is that it's called with a request object (and
|
|
||||||
optionally some parameters from an URL rule) and returns a response object.
|
|
|
@ -1,172 +0,0 @@
|
||||||
==============
|
|
||||||
Test Utilities
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. module:: werkzeug.test
|
|
||||||
|
|
||||||
Quite often you want to unittest your application or just check the output
|
|
||||||
from an interactive python session. In theory that is pretty simple because
|
|
||||||
you can fake a WSGI environment and call the application with a dummy
|
|
||||||
`start_response` and iterate over the application iterator but there are
|
|
||||||
argumentably better ways to interact with an application.
|
|
||||||
|
|
||||||
|
|
||||||
Diving In
|
|
||||||
=========
|
|
||||||
|
|
||||||
Werkzeug provides a `Client` object which you can pass a WSGI application (and
|
|
||||||
optionally a response wrapper) which you can use to send virtual requests to
|
|
||||||
the application.
|
|
||||||
|
|
||||||
A response wrapper is a callable that takes three arguments: the application
|
|
||||||
iterator, the status and finally a list of headers. The default response
|
|
||||||
wrapper returns a tuple. Because response objects have the same signature,
|
|
||||||
you can use them as response wrapper, ideally by subclassing them and hooking
|
|
||||||
in test functionality.
|
|
||||||
|
|
||||||
>>> from werkzeug.test import Client
|
|
||||||
>>> from werkzeug.testapp import test_app
|
|
||||||
>>> from werkzeug.wrappers import BaseResponse
|
|
||||||
>>> c = Client(test_app, BaseResponse)
|
|
||||||
>>> resp = c.get('/')
|
|
||||||
>>> resp.status_code
|
|
||||||
200
|
|
||||||
>>> resp.headers
|
|
||||||
Headers([('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '8339')])
|
|
||||||
>>> resp.data.splitlines()[0]
|
|
||||||
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"'
|
|
||||||
|
|
||||||
Or without a wrapper defined:
|
|
||||||
|
|
||||||
>>> c = Client(test_app)
|
|
||||||
>>> app_iter, status, headers = c.get('/')
|
|
||||||
>>> status
|
|
||||||
'200 OK'
|
|
||||||
>>> headers
|
|
||||||
[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '8339')]
|
|
||||||
>>> ''.join(app_iter).splitlines()[0]
|
|
||||||
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"'
|
|
||||||
|
|
||||||
|
|
||||||
Environment Building
|
|
||||||
====================
|
|
||||||
|
|
||||||
.. versionadded:: 0.5
|
|
||||||
|
|
||||||
The easiest way to interactively test applications is using the
|
|
||||||
:class:`EnvironBuilder`. It can create both standard WSGI environments
|
|
||||||
and request objects.
|
|
||||||
|
|
||||||
The following example creates a WSGI environment with one uploaded file
|
|
||||||
and a form field:
|
|
||||||
|
|
||||||
>>> from werkzeug.test import EnvironBuilder
|
|
||||||
>>> from StringIO import StringIO
|
|
||||||
>>> builder = EnvironBuilder(method='POST', data={'foo': 'this is some text',
|
|
||||||
... 'file': (StringIO('my file contents'), 'test.txt')})
|
|
||||||
>>> env = builder.get_environ()
|
|
||||||
|
|
||||||
The resulting environment is a regular WSGI environment that can be used for
|
|
||||||
further processing:
|
|
||||||
|
|
||||||
>>> from werkzeug.wrappers import Request
|
|
||||||
>>> req = Request(env)
|
|
||||||
>>> req.form['foo']
|
|
||||||
u'this is some text'
|
|
||||||
>>> req.files['file']
|
|
||||||
<FileStorage: u'test.txt' ('text/plain')>
|
|
||||||
>>> req.files['file'].read()
|
|
||||||
'my file contents'
|
|
||||||
|
|
||||||
The :class:`EnvironBuilder` figures out the content type automatically if you
|
|
||||||
pass a dict to the constructor as `data`. If you provide a string or an
|
|
||||||
input stream you have to do that yourself.
|
|
||||||
|
|
||||||
By default it will try to use ``application/x-www-form-urlencoded`` and only
|
|
||||||
use ``multipart/form-data`` if files are uploaded:
|
|
||||||
|
|
||||||
>>> builder = EnvironBuilder(method='POST', data={'foo': 'bar'})
|
|
||||||
>>> builder.content_type
|
|
||||||
'application/x-www-form-urlencoded'
|
|
||||||
>>> builder.files['foo'] = StringIO('contents')
|
|
||||||
>>> builder.content_type
|
|
||||||
'multipart/form-data'
|
|
||||||
|
|
||||||
If a string is provided as data (or an input stream) you have to specify
|
|
||||||
the content type yourself:
|
|
||||||
|
|
||||||
>>> builder = EnvironBuilder(method='POST', data='{"json": "this is"}')
|
|
||||||
>>> builder.content_type
|
|
||||||
>>> builder.content_type = 'application/json'
|
|
||||||
|
|
||||||
|
|
||||||
Testing API
|
|
||||||
===========
|
|
||||||
|
|
||||||
.. autoclass:: EnvironBuilder
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. attribute:: path
|
|
||||||
|
|
||||||
The path of the application. (aka `PATH_INFO`)
|
|
||||||
|
|
||||||
.. attribute:: charset
|
|
||||||
|
|
||||||
The charset used to encode unicode data.
|
|
||||||
|
|
||||||
.. attribute:: headers
|
|
||||||
|
|
||||||
A :class:`Headers` object with the request headers.
|
|
||||||
|
|
||||||
.. attribute:: errors_stream
|
|
||||||
|
|
||||||
The error stream used for the `wsgi.errors` stream.
|
|
||||||
|
|
||||||
.. attribute:: multithread
|
|
||||||
|
|
||||||
The value of `wsgi.multithread`
|
|
||||||
|
|
||||||
.. attribute:: multiprocess
|
|
||||||
|
|
||||||
The value of `wsgi.multiprocess`
|
|
||||||
|
|
||||||
.. attribute:: environ_base
|
|
||||||
|
|
||||||
The dict used as base for the newly create environ.
|
|
||||||
|
|
||||||
.. attribute:: environ_overrides
|
|
||||||
|
|
||||||
A dict with values that are used to override the generated environ.
|
|
||||||
|
|
||||||
.. attribute:: input_stream
|
|
||||||
|
|
||||||
The optional input stream. This and :attr:`form` / :attr:`files`
|
|
||||||
is mutually exclusive. Also do not provide this stream if the
|
|
||||||
request method is not `POST` / `PUT` or something comparable.
|
|
||||||
|
|
||||||
.. autoclass:: Client
|
|
||||||
|
|
||||||
.. automethod:: open
|
|
||||||
|
|
||||||
Shortcut methods are available for many HTTP methods:
|
|
||||||
|
|
||||||
.. automethod:: get
|
|
||||||
|
|
||||||
.. automethod:: patch
|
|
||||||
|
|
||||||
.. automethod:: post
|
|
||||||
|
|
||||||
.. automethod:: head
|
|
||||||
|
|
||||||
.. automethod:: put
|
|
||||||
|
|
||||||
.. automethod:: delete
|
|
||||||
|
|
||||||
.. automethod:: options
|
|
||||||
|
|
||||||
.. automethod:: trace
|
|
||||||
|
|
||||||
|
|
||||||
.. autofunction:: create_environ([options])
|
|
||||||
|
|
||||||
.. autofunction:: run_wsgi_app
|
|
|
@ -1,73 +0,0 @@
|
||||||
Transition to Werkzeug 1.0
|
|
||||||
==========================
|
|
||||||
|
|
||||||
Werkzeug originally had a magical import system hook that enabled
|
|
||||||
everything to be imported from one module and still loading the actual
|
|
||||||
implementations lazily as necessary. Unfortunately this turned out to be
|
|
||||||
slow and also unreliable on alternative Python implementations and
|
|
||||||
Google's App Engine.
|
|
||||||
|
|
||||||
Starting with 0.7 we recommend against the short imports and strongly
|
|
||||||
encourage starting importing from the actual implementation module.
|
|
||||||
Werkzeug 1.0 will disable the magical import hook completely.
|
|
||||||
|
|
||||||
Because finding out where the actual functions are imported and rewriting
|
|
||||||
them by hand is a painful and boring process we wrote a tool that aids in
|
|
||||||
making this transition.
|
|
||||||
|
|
||||||
Automatically Rewriting Imports
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
For instance, with Werkzeug < 0.7 the recommended way to use the escape function
|
|
||||||
was this::
|
|
||||||
|
|
||||||
from werkzeug import escape
|
|
||||||
|
|
||||||
With Werkzeug 0.7, the recommended way to import this function is
|
|
||||||
directly from the utils module (and with 1.0 this will become mandatory).
|
|
||||||
To automatically rewrite all imports one can use the
|
|
||||||
`werkzeug-import-rewrite <http://bit.ly/import-rewrite>`_ script.
|
|
||||||
|
|
||||||
You can use it by executing it with Python and with a list of folders with
|
|
||||||
Werkzeug based code. It will then spit out a hg/git compatible patch
|
|
||||||
file. Example patch file creation::
|
|
||||||
|
|
||||||
$ python werkzeug-import-rewrite.py . > new-imports.udiff
|
|
||||||
|
|
||||||
To apply the patch one of the following methods work:
|
|
||||||
|
|
||||||
hg:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
hg import new-imports.udiff
|
|
||||||
|
|
||||||
git:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
git apply new-imports.udiff
|
|
||||||
|
|
||||||
patch:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
patch -p1 < new-imports.udiff
|
|
||||||
|
|
||||||
Stop Using Deprecated Things
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
A few things in Werkzeug will stop being supported and for others, we're
|
|
||||||
suggesting alternatives even if they will stick around for a longer time.
|
|
||||||
|
|
||||||
Do not use:
|
|
||||||
|
|
||||||
- `werkzeug.script`, replace it with custom scripts written with
|
|
||||||
`argparse` or something similar.
|
|
||||||
- `werkzeug.template`, replace with a proper template engine.
|
|
||||||
- `werkzeug.contrib.jsrouting`, stop using URL generation for
|
|
||||||
JavaScript, it does not scale well with many public routing.
|
|
||||||
- `werkzeug.contrib.kickstart`, replace with hand written code, the
|
|
||||||
Werkzeug API became better in general that this is no longer
|
|
||||||
necessary.
|
|
||||||
- `werkzeug.contrib.testtools`, not useful really.
|
|
|
@ -1,475 +0,0 @@
|
||||||
=================
|
|
||||||
Werkzeug Tutorial
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. module:: werkzeug
|
|
||||||
|
|
||||||
Welcome to the Werkzeug tutorial in which we will create a `TinyURL`_ clone
|
|
||||||
that stores URLs in a redis instance. The libraries we will use for this
|
|
||||||
applications are `Jinja`_ 2 for the templates, `redis`_ for the database
|
|
||||||
layer and, of course, Werkzeug for the WSGI layer.
|
|
||||||
|
|
||||||
You can use `pip` to install the required libraries::
|
|
||||||
|
|
||||||
pip install Jinja2 redis Werkzeug
|
|
||||||
|
|
||||||
Also make sure to have a redis server running on your local machine. If
|
|
||||||
you are on OS X, you can use `brew` to install it::
|
|
||||||
|
|
||||||
brew install redis
|
|
||||||
|
|
||||||
If you are on Ubuntu or Debian, you can use apt-get::
|
|
||||||
|
|
||||||
sudo apt-get install redis-server
|
|
||||||
|
|
||||||
Redis was developed for UNIX systems and was never really designed to
|
|
||||||
work on Windows. For development purposes, the unofficial ports however
|
|
||||||
work well enough. You can get them from `github
|
|
||||||
<https://github.com/dmajkic/redis/downloads>`_.
|
|
||||||
|
|
||||||
Introducing Shortly
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
In this tutorial, we will together create a simple URL shortener service
|
|
||||||
with Werkzeug. Please keep in mind that Werkzeug is not a framework, it's
|
|
||||||
a library with utilities to create your own framework or application and
|
|
||||||
as such is very flexible. The approach we use here is just one of many you
|
|
||||||
can use.
|
|
||||||
|
|
||||||
As data store, we will use `redis`_ here instead of a relational database
|
|
||||||
to keep this simple and because that's the kind of job that `redis`_
|
|
||||||
excels at.
|
|
||||||
|
|
||||||
The final result will look something like this:
|
|
||||||
|
|
||||||
.. image:: _static/shortly.png
|
|
||||||
:alt: a screenshot of shortly
|
|
||||||
|
|
||||||
.. _TinyURL: http://tinyurl.com/
|
|
||||||
.. _Jinja: http://jinja.pocoo.org/
|
|
||||||
.. _redis: http://redis.io/
|
|
||||||
|
|
||||||
Step 0: A Basic WSGI Introduction
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Werkzeug is a utility library for WSGI. WSGI itself is a protocol or
|
|
||||||
convention that ensures that your web application can speak with the
|
|
||||||
webserver and more importantly that web applications work nicely together.
|
|
||||||
|
|
||||||
A basic “Hello World” application in WSGI without the help of Werkzeug
|
|
||||||
looks like this::
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
start_response('200 OK', [('Content-Type', 'text/plain')])
|
|
||||||
return ['Hello World!']
|
|
||||||
|
|
||||||
A WSGI application is something you can call and pass an environ dict
|
|
||||||
and a ``start_response`` callable. The environ contains all incoming
|
|
||||||
information, the ``start_response`` function can be used to indicate the
|
|
||||||
start of the response. With Werkzeug you don't have to deal directly with
|
|
||||||
either as request and response objects are provided to work with them.
|
|
||||||
|
|
||||||
The request data takes the environ object and allows you to access the
|
|
||||||
data from that environ in a nice manner. The response object is a WSGI
|
|
||||||
application in itself and provides a much nicer way to create responses.
|
|
||||||
|
|
||||||
Here is how you would write that application with response objects::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import Response
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
response = Response('Hello World!', mimetype='text/plain')
|
|
||||||
return response(environ, start_response)
|
|
||||||
|
|
||||||
And here an expanded version that looks at the query string in the URL
|
|
||||||
(more importantly at the `name` parameter in the URL to substitute “World”
|
|
||||||
against another word)::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import Request, Response
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
request = Request(environ)
|
|
||||||
text = 'Hello %s!' % request.args.get('name', 'World')
|
|
||||||
response = Response(text, mimetype='text/plain')
|
|
||||||
return response(environ, start_response)
|
|
||||||
|
|
||||||
And that's all you need to know about WSGI.
|
|
||||||
|
|
||||||
|
|
||||||
Step 1: Creating the Folders
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Before we get started, let’s create the folders needed for this application::
|
|
||||||
|
|
||||||
/shortly
|
|
||||||
/static
|
|
||||||
/templates
|
|
||||||
|
|
||||||
The shortly folder is not a python package, but just something where we
|
|
||||||
drop our files. Directly into this folder we will then put our main
|
|
||||||
module in the following steps. The files inside the static folder are
|
|
||||||
available to users of the application via HTTP. This is the place where
|
|
||||||
CSS and JavaScript files go. Inside the templates folder we will make
|
|
||||||
Jinja2 look for templates. The templates you create later in the tutorial
|
|
||||||
will go in this directory.
|
|
||||||
|
|
||||||
Step 2: The Base Structure
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Now let's get right into it and create a module for our application. Let's
|
|
||||||
create a file called `shortly.py` in the `shortly` folder. At first we
|
|
||||||
will need a bunch of imports. I will pull in all the imports here, even
|
|
||||||
if they are not used right away, to keep it from being confusing::
|
|
||||||
|
|
||||||
import os
|
|
||||||
import redis
|
|
||||||
import urlparse
|
|
||||||
from werkzeug.wrappers import Request, Response
|
|
||||||
from werkzeug.routing import Map, Rule
|
|
||||||
from werkzeug.exceptions import HTTPException, NotFound
|
|
||||||
from werkzeug.wsgi import SharedDataMiddleware
|
|
||||||
from werkzeug.utils import redirect
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
|
|
||||||
Then we can create the basic structure for our application and a function
|
|
||||||
to create a new instance of it, optionally with a piece of WSGI middleware
|
|
||||||
that exports all the files on the `static` folder on the web::
|
|
||||||
|
|
||||||
class Shortly(object):
|
|
||||||
|
|
||||||
def __init__(self, config):
|
|
||||||
self.redis = redis.Redis(config['redis_host'], config['redis_port'])
|
|
||||||
|
|
||||||
def dispatch_request(self, request):
|
|
||||||
return Response('Hello World!')
|
|
||||||
|
|
||||||
def wsgi_app(self, environ, start_response):
|
|
||||||
request = Request(environ)
|
|
||||||
response = self.dispatch_request(request)
|
|
||||||
return response(environ, start_response)
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
return self.wsgi_app(environ, start_response)
|
|
||||||
|
|
||||||
|
|
||||||
def create_app(redis_host='localhost', redis_port=6379, with_static=True):
|
|
||||||
app = Shortly({
|
|
||||||
'redis_host': redis_host,
|
|
||||||
'redis_port': redis_port
|
|
||||||
})
|
|
||||||
if with_static:
|
|
||||||
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
|
|
||||||
'/static': os.path.join(os.path.dirname(__file__), 'static')
|
|
||||||
})
|
|
||||||
return app
|
|
||||||
|
|
||||||
Lastly we can add a piece of code that will start a local development
|
|
||||||
server with automatic code reloading and a debugger::
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from werkzeug.serving import run_simple
|
|
||||||
app = create_app()
|
|
||||||
run_simple('127.0.0.1', 5000, app, use_debugger=True, use_reloader=True)
|
|
||||||
|
|
||||||
The basic idea here is that our ``Shortly`` class is an actual WSGI
|
|
||||||
application. The ``__call__`` method directly dispatches to ``wsgi_app``.
|
|
||||||
This is done so that we can wrap ``wsgi_app`` to apply middlewares like we
|
|
||||||
do in the ``create_app`` function. The actual ``wsgi_app`` method then
|
|
||||||
creates a :class:`Request` object and calls the ``dispatch_request``
|
|
||||||
method which then has to return a :class:`Response` object which is then
|
|
||||||
evaluated as WSGI application again. As you can see: turtles all the way
|
|
||||||
down. Both the ``Shortly`` class we create, as well as any request object
|
|
||||||
in Werkzeug implements the WSGI interface. As a result of that you could
|
|
||||||
even return another WSGI application from the ``dispatch_request`` method.
|
|
||||||
|
|
||||||
The ``create_app`` factory function can be used to create a new instance
|
|
||||||
of our application. Not only will it pass some parameters as
|
|
||||||
configuration to the application but also optionally add a WSGI middleware
|
|
||||||
that exports static files. This way we have access to the files from the
|
|
||||||
static folder even when we are not configuring our server to provide them
|
|
||||||
which is very helpful for development.
|
|
||||||
|
|
||||||
Intermezzo: Running the Application
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Now you should be able to execute the file with `python` and see a server
|
|
||||||
on your local machine::
|
|
||||||
|
|
||||||
$ python shortly.py
|
|
||||||
* Running on http://127.0.0.1:5000/
|
|
||||||
* Restarting with reloader: stat() polling
|
|
||||||
|
|
||||||
It also tells you that the reloader is active. It will use various
|
|
||||||
techniques to figure out if any file changed on the disk and then
|
|
||||||
automatically restart.
|
|
||||||
|
|
||||||
Just go to the URL and you should see “Hello World!”.
|
|
||||||
|
|
||||||
Step 3: The Environment
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Now that we have the basic application class, we can make the constructor
|
|
||||||
do something useful and provide a few helpers on there that can come in
|
|
||||||
handy. We will need to be able to render templates and connect to redis,
|
|
||||||
so let's extend the class a bit::
|
|
||||||
|
|
||||||
def __init__(self, config):
|
|
||||||
self.redis = redis.Redis(config['redis_host'], config['redis_port'])
|
|
||||||
template_path = os.path.join(os.path.dirname(__file__), 'templates')
|
|
||||||
self.jinja_env = Environment(loader=FileSystemLoader(template_path),
|
|
||||||
autoescape=True)
|
|
||||||
|
|
||||||
def render_template(self, template_name, **context):
|
|
||||||
t = self.jinja_env.get_template(template_name)
|
|
||||||
return Response(t.render(context), mimetype='text/html')
|
|
||||||
|
|
||||||
Step 4: The Routing
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Next up is routing. Routing is the process of matching and parsing the URL to
|
|
||||||
something we can use. Werkzeug provides a flexible integrated routing
|
|
||||||
system which we can use for that. The way it works is that you create a
|
|
||||||
:class:`~werkzeug.routing.Map` instance and add a bunch of
|
|
||||||
:class:`~werkzeug.routing.Rule` objects. Each rule has a pattern it will
|
|
||||||
try to match the URL against and an “endpoint”. The endpoint is typically
|
|
||||||
a string and can be used to uniquely identify the URL. We could also use
|
|
||||||
this to automatically reverse the URL, but that's not what we will do in this
|
|
||||||
tutorial.
|
|
||||||
|
|
||||||
Just put this into the constructor::
|
|
||||||
|
|
||||||
self.url_map = Map([
|
|
||||||
Rule('/', endpoint='new_url'),
|
|
||||||
Rule('/<short_id>', endpoint='follow_short_link'),
|
|
||||||
Rule('/<short_id>+', endpoint='short_link_details')
|
|
||||||
])
|
|
||||||
|
|
||||||
Here we create a URL map with three rules. ``/`` for the root of the URL
|
|
||||||
space where we will just dispatch to a function that implements the logic
|
|
||||||
to create a new URL. And then one that follows the short link to the
|
|
||||||
target URL and another one with the same rule but a plus (``+``) at the
|
|
||||||
end to show the link details.
|
|
||||||
|
|
||||||
So how do we find our way from the endpoint to a function? That's up to you.
|
|
||||||
The way we will do it in this tutorial is by calling the method ``on_``
|
|
||||||
+ endpoint on the class itself. Here is how this works::
|
|
||||||
|
|
||||||
def dispatch_request(self, request):
|
|
||||||
adapter = self.url_map.bind_to_environ(request.environ)
|
|
||||||
try:
|
|
||||||
endpoint, values = adapter.match()
|
|
||||||
return getattr(self, 'on_' + endpoint)(request, **values)
|
|
||||||
except HTTPException, e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
We bind the URL map to the current environment and get back a
|
|
||||||
:class:`~werkzeug.routing.URLAdapter`. The adapter can be used to match
|
|
||||||
the request but also to reverse URLs. The match method will return the
|
|
||||||
endpoint and a dictionary of values in the URL. For instance the rule for
|
|
||||||
``follow_short_link`` has a variable part called ``short_id``. When we go
|
|
||||||
to ``http://localhost:5000/foo`` we will get the following values back::
|
|
||||||
|
|
||||||
endpoint = 'follow_short_link'
|
|
||||||
values = {'short_id': u'foo'}
|
|
||||||
|
|
||||||
If it does not match anything, it will raise a
|
|
||||||
:exc:`~werkzeug.exceptions.NotFound` exception, which is an
|
|
||||||
:exc:`~werkzeug.exceptions.HTTPException`. All HTTP exceptions are also
|
|
||||||
WSGI applications by themselves which render a default error page. So we
|
|
||||||
just catch all of them down and return the error itself.
|
|
||||||
|
|
||||||
If all works well, we call the function ``on_`` + endpoint and pass it the
|
|
||||||
request as argument as well as all the URL arguments as keyword arguments
|
|
||||||
and return the response object that method returns.
|
|
||||||
|
|
||||||
Step 5: The First View
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Let's start with the first view: the one for new URLs::
|
|
||||||
|
|
||||||
def on_new_url(self, request):
|
|
||||||
error = None
|
|
||||||
url = ''
|
|
||||||
if request.method == 'POST':
|
|
||||||
url = request.form['url']
|
|
||||||
if not is_valid_url(url):
|
|
||||||
error = 'Please enter a valid URL'
|
|
||||||
else:
|
|
||||||
short_id = self.insert_url(url)
|
|
||||||
return redirect('/%s+' % short_id)
|
|
||||||
return self.render_template('new_url.html', error=error, url=url)
|
|
||||||
|
|
||||||
This logic should be easy to understand. Basically we are checking that
|
|
||||||
the request method is POST, in which case we validate the URL and add a
|
|
||||||
new entry to the database, then redirect to the detail page. This means
|
|
||||||
we need to write a function and a helper method. For URL validation this
|
|
||||||
is good enough::
|
|
||||||
|
|
||||||
def is_valid_url(url):
|
|
||||||
parts = urlparse.urlparse(url)
|
|
||||||
return parts.scheme in ('http', 'https')
|
|
||||||
|
|
||||||
For inserting the URL, all we need is this little method on our class::
|
|
||||||
|
|
||||||
def insert_url(self, url):
|
|
||||||
short_id = self.redis.get('reverse-url:' + url)
|
|
||||||
if short_id is not None:
|
|
||||||
return short_id
|
|
||||||
url_num = self.redis.incr('last-url-id')
|
|
||||||
short_id = base36_encode(url_num)
|
|
||||||
self.redis.set('url-target:' + short_id, url)
|
|
||||||
self.redis.set('reverse-url:' + url, short_id)
|
|
||||||
return short_id
|
|
||||||
|
|
||||||
``reverse-url:`` + the URL will store the short id. If the URL was
|
|
||||||
already submitted this won't be None and we can just return that value
|
|
||||||
which will be the short ID. Otherwise we increment the ``last-url-id``
|
|
||||||
key and convert it to base36. Then we store the link and the reverse
|
|
||||||
entry in redis. And here the function to convert to base 36::
|
|
||||||
|
|
||||||
def base36_encode(number):
|
|
||||||
assert number >= 0, 'positive integer required'
|
|
||||||
if number == 0:
|
|
||||||
return '0'
|
|
||||||
base36 = []
|
|
||||||
while number != 0:
|
|
||||||
number, i = divmod(number, 36)
|
|
||||||
base36.append('0123456789abcdefghijklmnopqrstuvwxyz'[i])
|
|
||||||
return ''.join(reversed(base36))
|
|
||||||
|
|
||||||
So what is missing for this view to work is the template. We will create
|
|
||||||
this later, let's first also write the other views and then do the
|
|
||||||
templates in one go.
|
|
||||||
|
|
||||||
Step 6: Redirect View
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
The redirect view is easy. All it has to do is to look for the link in
|
|
||||||
redis and redirect to it. Additionally we will also increment a counter
|
|
||||||
so that we know how often a link was clicked::
|
|
||||||
|
|
||||||
def on_follow_short_link(self, request, short_id):
|
|
||||||
link_target = self.redis.get('url-target:' + short_id)
|
|
||||||
if link_target is None:
|
|
||||||
raise NotFound()
|
|
||||||
self.redis.incr('click-count:' + short_id)
|
|
||||||
return redirect(link_target)
|
|
||||||
|
|
||||||
In this case we will raise a :exc:`~werkzeug.exceptions.NotFound` exception
|
|
||||||
by hand if the URL does not exist, which will bubble up to the
|
|
||||||
``dispatch_request`` function and be converted into a default 404
|
|
||||||
response.
|
|
||||||
|
|
||||||
Step 7: Detail View
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The link detail view is very similar, we just render a template
|
|
||||||
again. In addition to looking up the target, we also ask redis for the
|
|
||||||
number of times the link was clicked and let it default to zero if such
|
|
||||||
a key does not yet exist::
|
|
||||||
|
|
||||||
def on_short_link_details(self, request, short_id):
|
|
||||||
link_target = self.redis.get('url-target:' + short_id)
|
|
||||||
if link_target is None:
|
|
||||||
raise NotFound()
|
|
||||||
click_count = int(self.redis.get('click-count:' + short_id) or 0)
|
|
||||||
return self.render_template('short_link_details.html',
|
|
||||||
link_target=link_target,
|
|
||||||
short_id=short_id,
|
|
||||||
click_count=click_count
|
|
||||||
)
|
|
||||||
|
|
||||||
Please be aware that redis always works with strings, so you have to convert
|
|
||||||
the click count to :class:`int` by hand.
|
|
||||||
|
|
||||||
Step 8: Templates
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
And here are all the templates. Just drop them into the `templates`
|
|
||||||
folder. Jinja2 supports template inheritance, so the first thing we will
|
|
||||||
do is create a layout template with blocks that act as placeholders. We
|
|
||||||
also set up Jinja2 so that it automatically escapes strings with HTML
|
|
||||||
rules, so we don't have to spend time on that ourselves. This prevents
|
|
||||||
XSS attacks and rendering errors.
|
|
||||||
|
|
||||||
*layout.html*:
|
|
||||||
|
|
||||||
.. sourcecode:: html+jinja
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<title>{% block title %}{% endblock %} | shortly</title>
|
|
||||||
<link rel=stylesheet href=/static/style.css type=text/css>
|
|
||||||
<div class=box>
|
|
||||||
<h1><a href=/>shortly</a></h1>
|
|
||||||
<p class=tagline>Shortly is a URL shortener written with Werkzeug
|
|
||||||
{% block body %}{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
*new_url.html*:
|
|
||||||
|
|
||||||
.. sourcecode:: html+jinja
|
|
||||||
|
|
||||||
{% extends "layout.html" %}
|
|
||||||
{% block title %}Create New Short URL{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
<h2>Submit URL</h2>
|
|
||||||
<form action="" method=post>
|
|
||||||
{% if error %}
|
|
||||||
<p class=error><strong>Error:</strong> {{ error }}
|
|
||||||
{% endif %}
|
|
||||||
<p>URL:
|
|
||||||
<input type=text name=url value="{{ url }}" class=urlinput>
|
|
||||||
<input type=submit value="Shorten">
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
*short_link_details.html*:
|
|
||||||
|
|
||||||
.. sourcecode:: html+jinja
|
|
||||||
|
|
||||||
{% extends "layout.html" %}
|
|
||||||
{% block title %}Details about /{{ short_id }}{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
<h2><a href="/{{ short_id }}">/{{ short_id }}</a></h2>
|
|
||||||
<dl>
|
|
||||||
<dt>Full link
|
|
||||||
<dd class=link><div>{{ link_target }}</div>
|
|
||||||
<dt>Click count:
|
|
||||||
<dd>{{ click_count }}
|
|
||||||
</dl>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
Step 9: The Style
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
For this to look better than ugly black and white, here a simple
|
|
||||||
stylesheet that goes along:
|
|
||||||
|
|
||||||
.. sourcecode:: css
|
|
||||||
|
|
||||||
body { background: #E8EFF0; margin: 0; padding: 0; }
|
|
||||||
body, input { font-family: 'Helvetica Neue', Arial,
|
|
||||||
sans-serif; font-weight: 300; font-size: 18px; }
|
|
||||||
.box { width: 500px; margin: 60px auto; padding: 20px;
|
|
||||||
background: white; box-shadow: 0 1px 4px #BED1D4;
|
|
||||||
border-radius: 2px; }
|
|
||||||
a { color: #11557C; }
|
|
||||||
h1, h2 { margin: 0; color: #11557C; }
|
|
||||||
h1 a { text-decoration: none; }
|
|
||||||
h2 { font-weight: normal; font-size: 24px; }
|
|
||||||
.tagline { color: #888; font-style: italic; margin: 0 0 20px 0; }
|
|
||||||
.link div { overflow: auto; font-size: 0.8em; white-space: pre;
|
|
||||||
padding: 4px 10px; margin: 5px 0; background: #E5EAF1; }
|
|
||||||
dt { font-weight: normal; }
|
|
||||||
.error { background: #E8EFF0; padding: 3px 8px; color: #11557C;
|
|
||||||
font-size: 0.9em; border-radius: 2px; }
|
|
||||||
.urlinput { width: 300px; }
|
|
||||||
|
|
||||||
Bonus: Refinements
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Look at the implementation in the example dictionary in the Werkzeug
|
|
||||||
repository to see a version of this tutorial with some small refinements
|
|
||||||
such as a custom 404 page.
|
|
||||||
|
|
||||||
- `shortly in the example folder <https://github.com/pallets/werkzeug/blob/master/examples/shortly>`_
|
|
|
@ -1,158 +0,0 @@
|
||||||
.. _unicode:
|
|
||||||
|
|
||||||
=======
|
|
||||||
Unicode
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. module:: werkzeug
|
|
||||||
|
|
||||||
Since early Python 2 days unicode was part of all default Python builds. It
|
|
||||||
allows developers to write applications that deal with non-ASCII characters
|
|
||||||
in a straightforward way. But working with unicode requires a basic knowledge
|
|
||||||
about that matter, especially when working with libraries that do not support
|
|
||||||
it.
|
|
||||||
|
|
||||||
Werkzeug uses unicode internally everywhere text data is assumed, even if the
|
|
||||||
HTTP standard is not unicode aware as it. Basically all incoming data is
|
|
||||||
decoded from the charset specified (per default `utf-8`) so that you don't
|
|
||||||
operate on bytestrings any more. Outgoing unicode data is then encoded into
|
|
||||||
the target charset again.
|
|
||||||
|
|
||||||
Unicode in Python
|
|
||||||
=================
|
|
||||||
|
|
||||||
In Python 2 there are two basic string types: `str` and `unicode`. `str` may
|
|
||||||
carry encoded unicode data but it's always represented in bytes whereas the
|
|
||||||
`unicode` type does not contain bytes but charpoints. What does this mean?
|
|
||||||
Imagine you have the German Umlaut `ö`. In ASCII you cannot represent that
|
|
||||||
character, but in the `latin-1` and `utf-8` character sets you can represent
|
|
||||||
it, but they look differently when encoded:
|
|
||||||
|
|
||||||
>>> u'ö'.encode('latin1')
|
|
||||||
'\xf6'
|
|
||||||
>>> u'ö'.encode('utf-8')
|
|
||||||
'\xc3\xb6'
|
|
||||||
|
|
||||||
So an `ö` might look totally different depending on the encoding which makes
|
|
||||||
it hard to work with it. The solution is using the `unicode` type (as we did
|
|
||||||
above, note the `u` prefix before the string). The unicode type does not
|
|
||||||
store the bytes for `ö` but the information, that this is a
|
|
||||||
``LATIN SMALL LETTER O WITH DIAERESIS``.
|
|
||||||
|
|
||||||
Doing ``len(u'ö')`` will always give us the expected "1" but ``len('ö')``
|
|
||||||
might give different results depending on the encoding of ``'ö'``.
|
|
||||||
|
|
||||||
Unicode in HTTP
|
|
||||||
===============
|
|
||||||
|
|
||||||
The problem with unicode is that HTTP does not know what unicode is. HTTP
|
|
||||||
is limited to bytes but this is not a big problem as Werkzeug decodes and
|
|
||||||
encodes for us automatically all incoming and outgoing data. Basically what
|
|
||||||
this means is that data sent from the browser to the web application is per
|
|
||||||
default decoded from an utf-8 bytestring into a `unicode` string. Data sent
|
|
||||||
from the application back to the browser that is not yet a bytestring is then
|
|
||||||
encoded back to utf-8.
|
|
||||||
|
|
||||||
Usually this "just works" and we don't have to worry about it, but there are
|
|
||||||
situations where this behavior is problematic. For example the Python 2 IO
|
|
||||||
layer is not unicode aware. This means that whenever you work with data from
|
|
||||||
the file system you have to properly decode it. The correct way to load
|
|
||||||
a text file from the file system looks like this::
|
|
||||||
|
|
||||||
f = file('/path/to/the_file.txt', 'r')
|
|
||||||
try:
|
|
||||||
text = f.decode('utf-8') # assuming the file is utf-8 encoded
|
|
||||||
finally:
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
There is also the codecs module which provides an open function that decodes
|
|
||||||
automatically from the given encoding.
|
|
||||||
|
|
||||||
Error Handling
|
|
||||||
==============
|
|
||||||
|
|
||||||
With Werkzeug 0.3 onwards you can further control the way Werkzeug works with
|
|
||||||
unicode. In the past Werkzeug ignored encoding errors silently on incoming
|
|
||||||
data. This decision was made to avoid internal server errors if the user
|
|
||||||
tampered with the submitted data. However there are situations where you
|
|
||||||
want to abort with a `400 BAD REQUEST` instead of silently ignoring the error.
|
|
||||||
|
|
||||||
All the functions that do internal decoding now accept an `errors` keyword
|
|
||||||
argument that behaves like the `errors` parameter of the builtin string method
|
|
||||||
`decode`. The following values are possible:
|
|
||||||
|
|
||||||
`ignore`
|
|
||||||
This is the default behavior and tells the codec to ignore characters that
|
|
||||||
it doesn't understand silently.
|
|
||||||
|
|
||||||
`replace`
|
|
||||||
The codec will replace unknown characters with a replacement character
|
|
||||||
(`U+FFFD` ``REPLACEMENT CHARACTER``)
|
|
||||||
|
|
||||||
`strict`
|
|
||||||
Raise an exception if decoding fails.
|
|
||||||
|
|
||||||
Unlike the regular python decoding Werkzeug does not raise an
|
|
||||||
:exc:`UnicodeDecodeError` if the decoding failed but an
|
|
||||||
:exc:`~exceptions.HTTPUnicodeError` which
|
|
||||||
is a direct subclass of `UnicodeError` and the `BadRequest` HTTP exception.
|
|
||||||
The reason is that if this exception is not caught by the application but
|
|
||||||
a catch-all for HTTP exceptions exists a default `400 BAD REQUEST` error
|
|
||||||
page is displayed.
|
|
||||||
|
|
||||||
There is additional error handling available which is a Werkzeug extension
|
|
||||||
to the regular codec error handling which is called `fallback`. Often you
|
|
||||||
want to use utf-8 but support latin1 as legacy encoding too if decoding
|
|
||||||
failed. For this case you can use the `fallback` error handling. For
|
|
||||||
example you can specify ``'fallback:iso-8859-15'`` to tell Werkzeug it should
|
|
||||||
try with `iso-8859-15` if `utf-8` failed. If this decoding fails too (which
|
|
||||||
should not happen for most legacy charsets such as `iso-8859-15`) the error
|
|
||||||
is silently ignored as if the error handling was `ignore`.
|
|
||||||
|
|
||||||
Further details are available as part of the API documentation of the concrete
|
|
||||||
implementations of the functions or classes working with unicode.
|
|
||||||
|
|
||||||
Request and Response Objects
|
|
||||||
============================
|
|
||||||
|
|
||||||
As request and response objects usually are the central entities of Werkzeug
|
|
||||||
powered applications you can change the default encoding Werkzeug operates on
|
|
||||||
by subclassing these two classes. For example you can easily set the
|
|
||||||
application to utf-7 and strict error handling::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import BaseRequest, BaseResponse
|
|
||||||
|
|
||||||
class Request(BaseRequest):
|
|
||||||
charset = 'utf-7'
|
|
||||||
encoding_errors = 'strict'
|
|
||||||
|
|
||||||
class Response(BaseResponse):
|
|
||||||
charset = 'utf-7'
|
|
||||||
|
|
||||||
Keep in mind that the error handling is only customizable for all decoding
|
|
||||||
but not encoding. If Werkzeug encounters an encoding error it will raise a
|
|
||||||
:exc:`UnicodeEncodeError`. It's your responsibility to not create data that is
|
|
||||||
not present in the target charset (a non issue with all unicode encodings
|
|
||||||
such as utf-8).
|
|
||||||
|
|
||||||
.. _filesystem-encoding:
|
|
||||||
|
|
||||||
The Filesystem
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. versionchanged:: 0.11
|
|
||||||
|
|
||||||
Up until version 0.11, Werkzeug used Python's stdlib functionality to detect
|
|
||||||
the filesystem encoding. However, several bug reports against Werkzeug have
|
|
||||||
shown that the value of :py:func:`sys.getfilesystemencoding` cannot be
|
|
||||||
trusted under traditional UNIX systems. The usual problems come from
|
|
||||||
misconfigured systems, where ``LANG`` and similar environment variables are not
|
|
||||||
set. In such cases, Python would default to ASCII as filesystem encoding, a
|
|
||||||
very conservative default that is usually wrong and causes more problems than
|
|
||||||
it avoids.
|
|
||||||
|
|
||||||
Therefore Werkzeug will force the filesystem encoding to ``UTF-8`` and issue a
|
|
||||||
warning whenever it detects that it is running under BSD or Linux, and
|
|
||||||
:py:func:`sys.getfilesystemencoding` is returning an ASCII encoding.
|
|
||||||
|
|
||||||
See also :py:mod:`werkzeug.filesystem`.
|
|
|
@ -1,6 +0,0 @@
|
||||||
===========
|
|
||||||
URL Helpers
|
|
||||||
===========
|
|
||||||
|
|
||||||
.. automodule:: werkzeug.urls
|
|
||||||
:members:
|
|
|
@ -1,80 +0,0 @@
|
||||||
=========
|
|
||||||
Utilities
|
|
||||||
=========
|
|
||||||
|
|
||||||
Various utility functions shipped with Werkzeug.
|
|
||||||
|
|
||||||
|
|
||||||
HTML Helpers
|
|
||||||
============
|
|
||||||
|
|
||||||
.. module:: werkzeug.utils
|
|
||||||
|
|
||||||
.. autoclass:: HTMLBuilder
|
|
||||||
|
|
||||||
.. autofunction:: escape
|
|
||||||
|
|
||||||
.. autofunction:: unescape
|
|
||||||
|
|
||||||
|
|
||||||
General Helpers
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. autoclass:: cached_property
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: environ_property
|
|
||||||
|
|
||||||
.. autoclass:: header_property
|
|
||||||
|
|
||||||
.. autofunction:: parse_cookie
|
|
||||||
|
|
||||||
.. autofunction:: dump_cookie
|
|
||||||
|
|
||||||
.. autofunction:: redirect
|
|
||||||
|
|
||||||
.. autofunction:: append_slash_redirect
|
|
||||||
|
|
||||||
.. autofunction:: import_string
|
|
||||||
|
|
||||||
.. autofunction:: find_modules
|
|
||||||
|
|
||||||
.. autofunction:: validate_arguments
|
|
||||||
|
|
||||||
.. autofunction:: secure_filename
|
|
||||||
|
|
||||||
.. autofunction:: bind_arguments
|
|
||||||
|
|
||||||
|
|
||||||
URL Helpers
|
|
||||||
===========
|
|
||||||
|
|
||||||
Please refer to :doc:`urls`.
|
|
||||||
|
|
||||||
UserAgent Parsing
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. module:: werkzeug.useragents
|
|
||||||
|
|
||||||
.. autoclass:: UserAgent
|
|
||||||
:members:
|
|
||||||
|
|
||||||
|
|
||||||
Security Helpers
|
|
||||||
================
|
|
||||||
|
|
||||||
.. module:: werkzeug.security
|
|
||||||
|
|
||||||
.. versionadded:: 0.6.1
|
|
||||||
|
|
||||||
.. autofunction:: generate_password_hash
|
|
||||||
|
|
||||||
.. autofunction:: check_password_hash
|
|
||||||
|
|
||||||
.. autofunction:: safe_str_cmp
|
|
||||||
|
|
||||||
.. autofunction:: safe_join
|
|
||||||
|
|
||||||
.. autofunction:: pbkdf2_hex
|
|
||||||
|
|
||||||
.. autofunction:: pbkdf2_bin
|
|
|
@ -1,15 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Werkzeug Sphinx Extensions
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Provides some more helpers for the werkzeug docs.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
from sphinx.ext.autodoc import cut_lines
|
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
|
||||||
app.connect('autodoc-process-docstring', cut_lines(3, 3, what=['module']))
|
|
|
@ -1,119 +0,0 @@
|
||||||
\definecolor{TitleColor}{rgb}{0,0,0}
|
|
||||||
\definecolor{InnerLinkColor}{rgb}{0,0,0}
|
|
||||||
\definecolor{OuterLinkColor}{rgb}{1.0,0.5,0.0}
|
|
||||||
|
|
||||||
\renewcommand{\maketitle}{%
|
|
||||||
\begin{titlepage}%
|
|
||||||
\let\footnotesize\small
|
|
||||||
\let\footnoterule\relax
|
|
||||||
\ifsphinxpdfoutput
|
|
||||||
\begingroup
|
|
||||||
% This \def is required to deal with multi-line authors; it
|
|
||||||
% changes \\ to ', ' (comma-space), making it pass muster for
|
|
||||||
% generating document info in the PDF file.
|
|
||||||
\def\\{, }
|
|
||||||
\pdfinfo{
|
|
||||||
/Author (\@author)
|
|
||||||
/Title (\@title)
|
|
||||||
}
|
|
||||||
\endgroup
|
|
||||||
\fi
|
|
||||||
\begin{flushright}%
|
|
||||||
%\sphinxlogo%
|
|
||||||
{\center
|
|
||||||
\vspace*{3cm}
|
|
||||||
\includegraphics{logo.pdf}
|
|
||||||
\vspace{3cm}
|
|
||||||
\par
|
|
||||||
{\rm\Huge \@title \par}%
|
|
||||||
{\em\LARGE \py@release\releaseinfo \par}
|
|
||||||
{\large
|
|
||||||
\@date \par
|
|
||||||
\py@authoraddress \par
|
|
||||||
}}%
|
|
||||||
\end{flushright}%\par
|
|
||||||
\@thanks
|
|
||||||
\end{titlepage}%
|
|
||||||
\cleardoublepage%
|
|
||||||
\setcounter{footnote}{0}%
|
|
||||||
\let\thanks\relax\let\maketitle\relax
|
|
||||||
%\gdef\@thanks{}\gdef\@author{}\gdef\@title{}
|
|
||||||
}
|
|
||||||
|
|
||||||
\fancypagestyle{normal}{
|
|
||||||
\fancyhf{}
|
|
||||||
\fancyfoot[LE,RO]{{\thepage}}
|
|
||||||
\fancyfoot[LO]{{\nouppercase{\rightmark}}}
|
|
||||||
\fancyfoot[RE]{{\nouppercase{\leftmark}}}
|
|
||||||
\fancyhead[LE,RO]{{ \@title, \py@release}}
|
|
||||||
\renewcommand{\headrulewidth}{0.4pt}
|
|
||||||
\renewcommand{\footrulewidth}{0.4pt}
|
|
||||||
}
|
|
||||||
|
|
||||||
\fancypagestyle{plain}{
|
|
||||||
\fancyhf{}
|
|
||||||
\fancyfoot[LE,RO]{{\thepage}}
|
|
||||||
\renewcommand{\headrulewidth}{0pt}
|
|
||||||
\renewcommand{\footrulewidth}{0.4pt}
|
|
||||||
}
|
|
||||||
|
|
||||||
\titleformat{\section}{\Large}%
|
|
||||||
{\py@TitleColor\thesection}{0.5em}{\py@TitleColor}{\py@NormalColor}
|
|
||||||
\titleformat{\subsection}{\large}%
|
|
||||||
{\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
|
|
||||||
\titleformat{\subsubsection}{}%
|
|
||||||
{\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
|
|
||||||
\titleformat{\paragraph}{\large}%
|
|
||||||
{\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor}
|
|
||||||
|
|
||||||
\ChNameVar{\raggedleft\normalsize}
|
|
||||||
\ChNumVar{\raggedleft \bfseries\Large}
|
|
||||||
\ChTitleVar{\raggedleft \rm\Huge}
|
|
||||||
|
|
||||||
\renewcommand\thepart{\@Roman\c@part}
|
|
||||||
\renewcommand\part{%
|
|
||||||
\pagestyle{plain}
|
|
||||||
\if@noskipsec \leavevmode \fi
|
|
||||||
\cleardoublepage
|
|
||||||
\vspace*{6cm}%
|
|
||||||
\@afterindentfalse
|
|
||||||
\secdef\@part\@spart}
|
|
||||||
|
|
||||||
\def\@part[#1]#2{%
|
|
||||||
\ifnum \c@secnumdepth >\m@ne
|
|
||||||
\refstepcounter{part}%
|
|
||||||
\addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
|
|
||||||
\else
|
|
||||||
\addcontentsline{toc}{part}{#1}%
|
|
||||||
\fi
|
|
||||||
{\parindent \z@ %\center
|
|
||||||
\interlinepenalty \@M
|
|
||||||
\normalfont
|
|
||||||
\ifnum \c@secnumdepth >\m@ne
|
|
||||||
\rm\Large \partname~\thepart
|
|
||||||
\par\nobreak
|
|
||||||
\fi
|
|
||||||
\MakeUppercase{\rm\Huge #2}%
|
|
||||||
\markboth{}{}\par}%
|
|
||||||
\nobreak
|
|
||||||
\vskip 8ex
|
|
||||||
\@afterheading}
|
|
||||||
\def\@spart#1{%
|
|
||||||
{\parindent \z@ %\center
|
|
||||||
\interlinepenalty \@M
|
|
||||||
\normalfont
|
|
||||||
\huge \bfseries #1\par}%
|
|
||||||
\nobreak
|
|
||||||
\vskip 3ex
|
|
||||||
\@afterheading}
|
|
||||||
|
|
||||||
% use inconsolata font
|
|
||||||
\usepackage{inconsolata}
|
|
||||||
|
|
||||||
% fix single quotes, for inconsolata. (does not work)
|
|
||||||
%%\usepackage{textcomp}
|
|
||||||
%%\begingroup
|
|
||||||
%% \catcode`'=\active
|
|
||||||
%% \g@addto@macro\@noligs{\let'\textsinglequote}
|
|
||||||
%% \endgroup
|
|
||||||
%%\endinput
|
|
|
@ -1,178 +0,0 @@
|
||||||
.. _wrappers:
|
|
||||||
|
|
||||||
==========================
|
|
||||||
Request / Response Objects
|
|
||||||
==========================
|
|
||||||
|
|
||||||
.. module:: werkzeug.wrappers
|
|
||||||
|
|
||||||
The request and response objects wrap the WSGI environment or the return
|
|
||||||
value from a WSGI application so that it is another WSGI application
|
|
||||||
(wraps a whole application).
|
|
||||||
|
|
||||||
How they Work
|
|
||||||
=============
|
|
||||||
|
|
||||||
Your WSGI application is always passed two arguments. The WSGI "environment"
|
|
||||||
and the WSGI `start_response` function that is used to start the response
|
|
||||||
phase. The :class:`Request` class wraps the `environ` for easier access to
|
|
||||||
request variables (form data, request headers etc.).
|
|
||||||
|
|
||||||
The :class:`Response` on the other hand is a standard WSGI application that
|
|
||||||
you can create. The simple hello world in Werkzeug looks like this::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import Response
|
|
||||||
application = Response('Hello World!')
|
|
||||||
|
|
||||||
To make it more useful you can replace it with a function and do some
|
|
||||||
processing::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import Request, Response
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
request = Request(environ)
|
|
||||||
response = Response("Hello %s!" % request.args.get('name', 'World!'))
|
|
||||||
return response(environ, start_response)
|
|
||||||
|
|
||||||
Because this is a very common task the :class:`~Request` object provides
|
|
||||||
a helper for that. The above code can be rewritten like this::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import Request, Response
|
|
||||||
|
|
||||||
@Request.application
|
|
||||||
def application(request):
|
|
||||||
return Response("Hello %s!" % request.args.get('name', 'World!'))
|
|
||||||
|
|
||||||
The `application` is still a valid WSGI application that accepts the
|
|
||||||
environment and `start_response` callable.
|
|
||||||
|
|
||||||
|
|
||||||
Mutability and Reusability of Wrappers
|
|
||||||
======================================
|
|
||||||
|
|
||||||
The implementation of the Werkzeug request and response objects are trying
|
|
||||||
to guard you from common pitfalls by disallowing certain things as much as
|
|
||||||
possible. This serves two purposes: high performance and avoiding of
|
|
||||||
pitfalls.
|
|
||||||
|
|
||||||
For the request object the following rules apply:
|
|
||||||
|
|
||||||
1. The request object is immutable. Modifications are not supported by
|
|
||||||
default, you may however replace the immutable attributes with mutable
|
|
||||||
attributes if you need to modify it.
|
|
||||||
2. The request object may be shared in the same thread, but is not thread
|
|
||||||
safe itself. If you need to access it from multiple threads, use
|
|
||||||
locks around calls.
|
|
||||||
3. It's not possible to pickle the request object.
|
|
||||||
|
|
||||||
For the response object the following rules apply:
|
|
||||||
|
|
||||||
1. The response object is mutable
|
|
||||||
2. The response object can be pickled or copied after `freeze()` was
|
|
||||||
called.
|
|
||||||
3. Since Werkzeug 0.6 it's safe to use the same response object for
|
|
||||||
multiple WSGI responses.
|
|
||||||
4. It's possible to create copies using `copy.deepcopy`.
|
|
||||||
|
|
||||||
|
|
||||||
Base Wrappers
|
|
||||||
=============
|
|
||||||
|
|
||||||
These objects implement a common set of operations. They are missing fancy
|
|
||||||
addon functionality like user agent parsing or etag handling. These features
|
|
||||||
are available by mixing in various mixin classes or using :class:`Request` and
|
|
||||||
:class:`Response`.
|
|
||||||
|
|
||||||
.. autoclass:: BaseRequest
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. attribute:: environ
|
|
||||||
|
|
||||||
The WSGI environment that the request object uses for data retrival.
|
|
||||||
|
|
||||||
.. attribute:: shallow
|
|
||||||
|
|
||||||
`True` if this request object is shallow (does not modify :attr:`environ`),
|
|
||||||
`False` otherwise.
|
|
||||||
|
|
||||||
.. automethod:: _get_file_stream
|
|
||||||
|
|
||||||
|
|
||||||
.. autoclass:: BaseResponse
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. attribute:: response
|
|
||||||
|
|
||||||
The application iterator. If constructed from a string this will be a
|
|
||||||
list, otherwise the object provided as application iterator. (The first
|
|
||||||
argument passed to :class:`BaseResponse`)
|
|
||||||
|
|
||||||
.. attribute:: headers
|
|
||||||
|
|
||||||
A :class:`Headers` object representing the response headers.
|
|
||||||
|
|
||||||
.. attribute:: status_code
|
|
||||||
|
|
||||||
The response status as integer.
|
|
||||||
|
|
||||||
.. attribute:: direct_passthrough
|
|
||||||
|
|
||||||
If ``direct_passthrough=True`` was passed to the response object or if
|
|
||||||
this attribute was set to `True` before using the response object as
|
|
||||||
WSGI application, the wrapped iterator is returned unchanged. This
|
|
||||||
makes it possible to pass a special `wsgi.file_wrapper` to the response
|
|
||||||
object. See :func:`wrap_file` for more details.
|
|
||||||
|
|
||||||
.. automethod:: __call__
|
|
||||||
|
|
||||||
.. automethod:: _ensure_sequence
|
|
||||||
|
|
||||||
|
|
||||||
Mixin Classes
|
|
||||||
=============
|
|
||||||
|
|
||||||
Werkzeug also provides helper mixins for various HTTP related functionality
|
|
||||||
such as etags, cache control, user agents etc. When subclassing you can
|
|
||||||
mix those classes in to extend the functionality of the :class:`BaseRequest`
|
|
||||||
or :class:`BaseResponse` object. Here a small example for a request object
|
|
||||||
that parses accept headers::
|
|
||||||
|
|
||||||
from werkzeug.wrappers import AcceptMixin, BaseRequest
|
|
||||||
|
|
||||||
class Request(BaseRequest, AcceptMixin):
|
|
||||||
pass
|
|
||||||
|
|
||||||
The :class:`Request` and :class:`Response` classes subclass the :class:`BaseRequest`
|
|
||||||
and :class:`BaseResponse` classes and implement all the mixins Werkzeug provides:
|
|
||||||
|
|
||||||
|
|
||||||
.. autoclass:: Request
|
|
||||||
|
|
||||||
.. autoclass:: Response
|
|
||||||
|
|
||||||
.. autoclass:: AcceptMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: AuthorizationMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: ETagRequestMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: ETagResponseMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: ResponseStreamMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: CommonRequestDescriptorsMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: CommonResponseDescriptorsMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: WWWAuthenticateMixin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: UserAgentMixin
|
|
||||||
:members:
|
|
|
@ -1,66 +0,0 @@
|
||||||
============
|
|
||||||
WSGI Helpers
|
|
||||||
============
|
|
||||||
|
|
||||||
.. module:: werkzeug.wsgi
|
|
||||||
|
|
||||||
The following classes and functions are designed to make working with
|
|
||||||
the WSGI specification easier or operate on the WSGI layer. All the
|
|
||||||
functionality from this module is available on the high-level
|
|
||||||
:ref:`Request/Response classes <wrappers>`.
|
|
||||||
|
|
||||||
|
|
||||||
Iterator / Stream Helpers
|
|
||||||
=========================
|
|
||||||
|
|
||||||
These classes and functions simplify working with the WSGI application
|
|
||||||
iterator and the input stream.
|
|
||||||
|
|
||||||
.. autoclass:: ClosingIterator
|
|
||||||
|
|
||||||
.. autoclass:: FileWrapper
|
|
||||||
|
|
||||||
.. autoclass:: LimitedStream
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autofunction:: make_line_iter
|
|
||||||
|
|
||||||
.. autofunction:: make_chunk_iter
|
|
||||||
|
|
||||||
.. autofunction:: wrap_file
|
|
||||||
|
|
||||||
|
|
||||||
Environ Helpers
|
|
||||||
===============
|
|
||||||
|
|
||||||
These functions operate on the WSGI environment. They extract useful
|
|
||||||
information or perform common manipulations:
|
|
||||||
|
|
||||||
.. autofunction:: get_host
|
|
||||||
|
|
||||||
.. autofunction:: get_content_length
|
|
||||||
|
|
||||||
.. autofunction:: get_input_stream
|
|
||||||
|
|
||||||
.. autofunction:: get_current_url
|
|
||||||
|
|
||||||
.. autofunction:: get_query_string
|
|
||||||
|
|
||||||
.. autofunction:: get_script_name
|
|
||||||
|
|
||||||
.. autofunction:: get_path_info
|
|
||||||
|
|
||||||
.. autofunction:: pop_path_info
|
|
||||||
|
|
||||||
.. autofunction:: peek_path_info
|
|
||||||
|
|
||||||
.. autofunction:: extract_path_info
|
|
||||||
|
|
||||||
.. autofunction:: host_is_trusted
|
|
||||||
|
|
||||||
Convenience Helpers
|
|
||||||
===================
|
|
||||||
|
|
||||||
.. autofunction:: responder
|
|
||||||
|
|
||||||
.. autofunction:: werkzeug.testapp.test_app
|
|
|
@ -1,103 +0,0 @@
|
||||||
=================
|
|
||||||
Werkzeug Examples
|
|
||||||
=================
|
|
||||||
|
|
||||||
This directory contains various example applications and example code of
|
|
||||||
Werkzeug powered applications.
|
|
||||||
|
|
||||||
Beside the proof of concept applications and code snippets in the partial
|
|
||||||
folder they all have external depencencies for template engines or database
|
|
||||||
adapters (SQLAlchemy only so far).
|
|
||||||
|
|
||||||
|
|
||||||
Full Example Applications
|
|
||||||
=========================
|
|
||||||
|
|
||||||
The following example applications are application types you would actually
|
|
||||||
find in real life :-)
|
|
||||||
|
|
||||||
|
|
||||||
`simplewiki`
|
|
||||||
A simple Wiki implementation.
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
- SQLAlchemy
|
|
||||||
- Creoleparser >= 0.7
|
|
||||||
- genshi
|
|
||||||
|
|
||||||
You can obtain all packages in the Cheeseshop via easy_install. You have
|
|
||||||
to have at least version 0.7 of Creoleparser.
|
|
||||||
|
|
||||||
Usage::
|
|
||||||
|
|
||||||
./manage-simplewiki.py initdb
|
|
||||||
./manage-simplewiki.py runserver
|
|
||||||
|
|
||||||
Or of course you can just use the application object
|
|
||||||
(`simplewiki.SimpleWiki`) and hook that into your favourite WSGI gateway.
|
|
||||||
The constructor of the application object takes a single argument which is
|
|
||||||
the SQLAlchemy URI for the database.
|
|
||||||
|
|
||||||
The management script for the devserver looks up the an environment var
|
|
||||||
called `SIMPLEWIKI_DATABASE_URI` and uses that for the database URI. If
|
|
||||||
no such variable is provided "sqlite:////tmp/simplewiki.db" is assumed.
|
|
||||||
|
|
||||||
`plnt`
|
|
||||||
A planet called plnt, pronounce plant.
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
- SQLAlchemy
|
|
||||||
- Jinja
|
|
||||||
- feedparser
|
|
||||||
|
|
||||||
You can obtain all packages in the Cheeseshop via easy_install.
|
|
||||||
|
|
||||||
Usage::
|
|
||||||
|
|
||||||
./manage-plnt.py initdb
|
|
||||||
./manage-plnt.py sync
|
|
||||||
./manage-plnt.py runserver
|
|
||||||
|
|
||||||
The WSGI application is called `plnt.Plnt` which, like the simple wiki,
|
|
||||||
accepts a database URI as first argument. The environment variable for
|
|
||||||
the database key is called `PLNT_DATABASE_URI` and the default is
|
|
||||||
"sqlite:////tmp/plnt.db".
|
|
||||||
|
|
||||||
Per default a few python related blogs are added to the database, you
|
|
||||||
can add more in a python shell by playing with the `Blog` model.
|
|
||||||
|
|
||||||
`shorty`
|
|
||||||
A tinyurl clone for the Werkzeug tutorial.
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
- SQLAlchemy
|
|
||||||
- Jinja2
|
|
||||||
|
|
||||||
You can obtain all packages in the Cheeseshop via easy_install.
|
|
||||||
|
|
||||||
Usage::
|
|
||||||
|
|
||||||
./manage-shorty.py initdb
|
|
||||||
./manage-shorty.py runserver
|
|
||||||
|
|
||||||
The WSGI application is called `shorty.application.Shorty` which, like the
|
|
||||||
simple wiki, accepts a database URI as first argument.
|
|
||||||
|
|
||||||
The source code of the application is explained in detail in the Werkzeug
|
|
||||||
tutorial.
|
|
||||||
|
|
||||||
`couchy`
|
|
||||||
Like shorty, but implemented using CouchDB.
|
|
||||||
|
|
||||||
Requirements :
|
|
||||||
- werkzeug : http://werkzeug.pocoo.org
|
|
||||||
- jinja : http://jinja.pocoo.org
|
|
||||||
- couchdb 0.72 & above : http://www.couchdb.org
|
|
||||||
|
|
||||||
`cupoftee`
|
|
||||||
A `Teeworlds <http://www.teeworlds.com/>`_ server browser. This application
|
|
||||||
works best in a non forking environment and won't work for CGI.
|
|
||||||
|
|
||||||
Usage::
|
|
||||||
|
|
||||||
./manage-cupoftee.py runserver
|
|
|
@ -1 +0,0 @@
|
||||||
This folder includes example applications for werkzeug.contrib
|
|
|
@ -1,50 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Secure Cookie Example
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Stores session on the client.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD.
|
|
||||||
"""
|
|
||||||
from time import asctime
|
|
||||||
from werkzeug.serving import run_simple
|
|
||||||
from werkzeug.wrappers import BaseRequest, BaseResponse
|
|
||||||
from werkzeug.contrib.securecookie import SecureCookie
|
|
||||||
|
|
||||||
SECRET_KEY = 'V\x8a$m\xda\xe9\xc3\x0f|f\x88\xbccj>\x8bI^3+'
|
|
||||||
|
|
||||||
|
|
||||||
class Request(BaseRequest):
|
|
||||||
|
|
||||||
def __init__(self, environ):
|
|
||||||
BaseRequest.__init__(self, environ)
|
|
||||||
self.session = SecureCookie.load_cookie(self, secret_key=SECRET_KEY)
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
|
||||||
return '<a href="set">Set the Time</a> or <a href="get">Get the time</a>'
|
|
||||||
|
|
||||||
|
|
||||||
def get_time(request):
|
|
||||||
return 'Time: %s' % request.session.get('time', 'not set')
|
|
||||||
|
|
||||||
|
|
||||||
def set_time(request):
|
|
||||||
request.session['time'] = time = asctime()
|
|
||||||
return 'Time set to %s' % time
|
|
||||||
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
request = Request(environ)
|
|
||||||
response = BaseResponse({
|
|
||||||
'get': get_time,
|
|
||||||
'set': set_time
|
|
||||||
}.get(request.path.strip('/'), index)(request), mimetype='text/html')
|
|
||||||
request.session.save_cookie(response)
|
|
||||||
return response(environ, start_response)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_simple('localhost', 5000, application)
|
|
|
@ -1,43 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from werkzeug.serving import run_simple
|
|
||||||
from werkzeug.contrib.sessions import SessionStore, SessionMiddleware
|
|
||||||
|
|
||||||
|
|
||||||
class MemorySessionStore(SessionStore):
|
|
||||||
|
|
||||||
def __init__(self, session_class=None):
|
|
||||||
SessionStore.__init__(self, session_class=None)
|
|
||||||
self.sessions = {}
|
|
||||||
|
|
||||||
def save(self, session):
|
|
||||||
self.sessions[session.sid] = session
|
|
||||||
|
|
||||||
def delete(self, session):
|
|
||||||
self.sessions.pop(session.id, None)
|
|
||||||
|
|
||||||
def get(self, sid):
|
|
||||||
if not self.is_valid_key(sid) or sid not in self.sessions:
|
|
||||||
return self.new()
|
|
||||||
return self.session_class(self.sessions[sid], sid, False)
|
|
||||||
|
|
||||||
|
|
||||||
def application(environ, start_response):
|
|
||||||
session = environ['werkzeug.session']
|
|
||||||
session['visit_count'] = session.get('visit_count', 0) + 1
|
|
||||||
|
|
||||||
start_response('200 OK', [('Content-Type', 'text/html')])
|
|
||||||
return ['''
|
|
||||||
<!doctype html>
|
|
||||||
<title>Session Example</title>
|
|
||||||
<h1>Session Example</h1>
|
|
||||||
<p>You visited this page %d times.</p>
|
|
||||||
''' % session['visit_count']]
|
|
||||||
|
|
||||||
|
|
||||||
def make_app():
|
|
||||||
return SessionMiddleware(application, MemorySessionStore())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_simple('localhost', 5000, make_app())
|
|
|
@ -1,108 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Cookie Based Auth
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This is a very simple application that uses a secure cookie to do the
|
|
||||||
user authentification.
|
|
||||||
|
|
||||||
:copyright: Copyright 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
from werkzeug.serving import run_simple
|
|
||||||
from werkzeug.utils import cached_property, escape, redirect
|
|
||||||
from werkzeug.wrappers import Request, Response
|
|
||||||
from werkzeug.contrib.securecookie import SecureCookie
|
|
||||||
|
|
||||||
|
|
||||||
# don't use this key but a different one; you could just use
|
|
||||||
# os.unrandom(20) to get something random. Changing this key
|
|
||||||
# invalidates all sessions at once.
|
|
||||||
SECRET_KEY = '\xfa\xdd\xb8z\xae\xe0}4\x8b\xea'
|
|
||||||
|
|
||||||
# the cookie name for the session
|
|
||||||
COOKIE_NAME = 'session'
|
|
||||||
|
|
||||||
# the users that may access
|
|
||||||
USERS = {
|
|
||||||
'admin': 'default',
|
|
||||||
'user1': 'default'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AppRequest(Request):
|
|
||||||
"""A request with a secure cookie session."""
|
|
||||||
|
|
||||||
def logout(self):
|
|
||||||
"""Log the user out."""
|
|
||||||
self.session.pop('username', None)
|
|
||||||
|
|
||||||
def login(self, username):
|
|
||||||
"""Log the user in."""
|
|
||||||
self.session['username'] = username
|
|
||||||
|
|
||||||
@property
|
|
||||||
def logged_in(self):
|
|
||||||
"""Is the user logged in?"""
|
|
||||||
return self.user is not None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def user(self):
|
|
||||||
"""The user that is logged in."""
|
|
||||||
return self.session.get('username')
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def session(self):
|
|
||||||
data = self.cookies.get(COOKIE_NAME)
|
|
||||||
if not data:
|
|
||||||
return SecureCookie(secret_key=SECRET_KEY)
|
|
||||||
return SecureCookie.unserialize(data, SECRET_KEY)
|
|
||||||
|
|
||||||
|
|
||||||
def login_form(request):
|
|
||||||
error = ''
|
|
||||||
if request.method == 'POST':
|
|
||||||
username = request.form.get('username')
|
|
||||||
password = request.form.get('password')
|
|
||||||
if password and USERS.get(username) == password:
|
|
||||||
request.login(username)
|
|
||||||
return redirect('')
|
|
||||||
error = '<p>Invalid credentials'
|
|
||||||
return Response('''
|
|
||||||
<title>Login</title><h1>Login</h1>
|
|
||||||
<p>Not logged in.
|
|
||||||
%s
|
|
||||||
<form action="" method="post">
|
|
||||||
<p>
|
|
||||||
<input type="hidden" name="do" action="login">
|
|
||||||
<input type="text" name="username" size=20>
|
|
||||||
<input type="password" name="password", size=20>
|
|
||||||
<input type="submit" value="Login">
|
|
||||||
</form>''' % error, mimetype='text/html')
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
|
||||||
return Response('''
|
|
||||||
<title>Logged in</title>
|
|
||||||
<h1>Logged in</h1>
|
|
||||||
<p>Logged in as %s
|
|
||||||
<p><a href="/?do=logout">Logout</a>
|
|
||||||
''' % escape(request.user), mimetype='text/html')
|
|
||||||
|
|
||||||
|
|
||||||
@AppRequest.application
|
|
||||||
def application(request):
|
|
||||||
if request.args.get('do') == 'logout':
|
|
||||||
request.logout()
|
|
||||||
response = redirect('.')
|
|
||||||
elif request.logged_in:
|
|
||||||
response = index(request)
|
|
||||||
else:
|
|
||||||
response = login_form(request)
|
|
||||||
request.session.save_cookie(response)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_simple('localhost', 4000, application)
|
|
|
@ -1,11 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
coolmagic
|
|
||||||
~~~~~~~~~
|
|
||||||
|
|
||||||
Package description goes here.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
from coolmagic.application import make_app
|
|
|
@ -1,78 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
coolmagic.application
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module provides the WSGI application.
|
|
||||||
|
|
||||||
The WSGI middlewares are applied in the `make_app` factory function
|
|
||||||
that automatically wraps the application within the require
|
|
||||||
middlewares. Per default only the `SharedDataMiddleware` is applied.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
from os import path, listdir
|
|
||||||
from coolmagic.utils import Request, local_manager, redirect
|
|
||||||
from werkzeug.routing import Map, Rule, RequestRedirect
|
|
||||||
from werkzeug.exceptions import HTTPException, NotFound
|
|
||||||
|
|
||||||
|
|
||||||
class CoolMagicApplication(object):
|
|
||||||
"""
|
|
||||||
The application class. It's passed a directory with configuration values.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
for fn in listdir(path.join(path.dirname(__file__), 'views')):
|
|
||||||
if fn.endswith('.py') and fn != '__init__.py':
|
|
||||||
__import__('coolmagic.views.' + fn[:-3])
|
|
||||||
|
|
||||||
from coolmagic.utils import exported_views
|
|
||||||
rules = [
|
|
||||||
# url for shared data. this will always be unmatched
|
|
||||||
# because either the middleware or the webserver
|
|
||||||
# handles that request first.
|
|
||||||
Rule('/public/<path:file>',
|
|
||||||
endpoint='shared_data')
|
|
||||||
]
|
|
||||||
self.views = {}
|
|
||||||
for endpoint, (func, rule, extra) in exported_views.iteritems():
|
|
||||||
if rule is not None:
|
|
||||||
rules.append(Rule(rule, endpoint=endpoint, **extra))
|
|
||||||
self.views[endpoint] = func
|
|
||||||
self.url_map = Map(rules)
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
urls = self.url_map.bind_to_environ(environ)
|
|
||||||
req = Request(environ, urls)
|
|
||||||
try:
|
|
||||||
endpoint, args = urls.match(req.path)
|
|
||||||
resp = self.views[endpoint](**args)
|
|
||||||
except NotFound, e:
|
|
||||||
resp = self.views['static.not_found']()
|
|
||||||
except (HTTPException, RequestRedirect), e:
|
|
||||||
resp = e
|
|
||||||
return resp(environ, start_response)
|
|
||||||
|
|
||||||
|
|
||||||
def make_app(config=None):
|
|
||||||
"""
|
|
||||||
Factory function that creates a new `CoolmagicApplication`
|
|
||||||
object. Optional WSGI middlewares should be applied here.
|
|
||||||
"""
|
|
||||||
config = config or {}
|
|
||||||
app = CoolMagicApplication(config)
|
|
||||||
|
|
||||||
# static stuff
|
|
||||||
from werkzeug.utils import SharedDataMiddleware
|
|
||||||
app = SharedDataMiddleware(app, {
|
|
||||||
'/public': path.join(path.dirname(__file__), 'public')
|
|
||||||
})
|
|
||||||
|
|
||||||
# clean up locals
|
|
||||||
app = local_manager.make_middleware(app)
|
|
||||||
|
|
||||||
return app
|
|
|
@ -1,18 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
coolmagic.helpers
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The star-import module for all views.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
from coolmagic.utils import Response, TemplateResponse, ThreadedRequest, \
|
|
||||||
export, url_for, redirect
|
|
||||||
from werkzeug.utils import escape
|
|
||||||
|
|
||||||
|
|
||||||
#: a thread local proxy request object
|
|
||||||
request = ThreadedRequest()
|
|
||||||
del ThreadedRequest
|
|
|
@ -1,10 +0,0 @@
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, a {
|
|
||||||
color: #a00;
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/html4/loose.dtd">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{{ page_title }} — Cool Magic!</title>
|
|
||||||
<link rel="stylesheet" href="{{ h.url_for('shared_data', file='style.css') }}" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Cool Magic</h1>
|
|
||||||
<h2>{{ page_title }}</h2>
|
|
||||||
{% block page_body %}{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,10 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
{% set page_title = 'About the Magic' %}
|
|
||||||
{% block page_body %}
|
|
||||||
<p>
|
|
||||||
Nothing to see. It's just magic.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="{{ h.url_for('static.index') }}">back to the index</a>
|
|
||||||
</p>
|
|
||||||
{% endblock %}
|
|
|
@ -1,13 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
{% set page_title = 'Welcome to the Magic' %}
|
|
||||||
{% block page_body %}
|
|
||||||
<p>
|
|
||||||
Welcome to the magic! This is a bigger example for the
|
|
||||||
Werkzeug toolkit. And it contains a lot of magic.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="{{ h.url_for('static.about') }}">about the implementation</a> or
|
|
||||||
click here if you want to see a <a href="{{ h.url_for('static.broken')
|
|
||||||
}}">broken view</a>.
|
|
||||||
</p>
|
|
||||||
{% endblock %}
|
|
|
@ -1,8 +0,0 @@
|
||||||
{% extends "layout.html" %}
|
|
||||||
{% set page_title = 'Missing Magic' %}
|
|
||||||
{% block page_body %}
|
|
||||||
<p>
|
|
||||||
The requested magic really does not exist. Maybe you want
|
|
||||||
to look for it on the <a href="{{ h.url_for('static.index') }}">index</a>.
|
|
||||||
</p>
|
|
||||||
{% endblock %}
|
|
|
@ -1,107 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
coolmagic.utils
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module contains the subclasses of the base request and response
|
|
||||||
objects provided by werkzeug. The subclasses know about their charset
|
|
||||||
and implement some additional functionallity like the ability to link
|
|
||||||
to view functions.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
from os.path import dirname, join
|
|
||||||
from jinja import Environment, FileSystemLoader
|
|
||||||
from werkzeug.local import Local, LocalManager
|
|
||||||
from werkzeug.utils import redirect
|
|
||||||
from werkzeug.wrappers import BaseRequest, BaseResponse
|
|
||||||
|
|
||||||
|
|
||||||
local = Local()
|
|
||||||
local_manager = LocalManager([local])
|
|
||||||
template_env = Environment(
|
|
||||||
loader=FileSystemLoader(join(dirname(__file__), 'templates'),
|
|
||||||
use_memcache=False)
|
|
||||||
)
|
|
||||||
exported_views = {}
|
|
||||||
|
|
||||||
|
|
||||||
def export(string, template=None, **extra):
|
|
||||||
"""
|
|
||||||
Decorator for registering view functions and adding
|
|
||||||
templates to it.
|
|
||||||
"""
|
|
||||||
def wrapped(f):
|
|
||||||
endpoint = (f.__module__ + '.' + f.__name__)[16:]
|
|
||||||
if template is not None:
|
|
||||||
old_f = f
|
|
||||||
def f(**kwargs):
|
|
||||||
rv = old_f(**kwargs)
|
|
||||||
if not isinstance(rv, Response):
|
|
||||||
rv = TemplateResponse(template, **(rv or {}))
|
|
||||||
return rv
|
|
||||||
f.__name__ = old_f.__name__
|
|
||||||
f.__doc__ = old_f.__doc__
|
|
||||||
exported_views[endpoint] = (f, string, extra)
|
|
||||||
return f
|
|
||||||
return wrapped
|
|
||||||
|
|
||||||
|
|
||||||
def url_for(endpoint, **values):
|
|
||||||
"""
|
|
||||||
Build a URL
|
|
||||||
"""
|
|
||||||
return local.request.url_adapter.build(endpoint, values)
|
|
||||||
|
|
||||||
|
|
||||||
class Request(BaseRequest):
|
|
||||||
"""
|
|
||||||
The concrete request object used in the WSGI application.
|
|
||||||
It has some helper functions that can be used to build URLs.
|
|
||||||
"""
|
|
||||||
charset = 'utf-8'
|
|
||||||
|
|
||||||
def __init__(self, environ, url_adapter):
|
|
||||||
BaseRequest.__init__(self, environ)
|
|
||||||
self.url_adapter = url_adapter
|
|
||||||
local.request = self
|
|
||||||
|
|
||||||
|
|
||||||
class ThreadedRequest(object):
|
|
||||||
"""
|
|
||||||
A pseudo request object that always poins to the current
|
|
||||||
context active request.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name == '__members__':
|
|
||||||
return [x for x in dir(local.request) if not
|
|
||||||
x.startswith('_')]
|
|
||||||
return getattr(local.request, name)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
return setattr(local.request, name, value)
|
|
||||||
|
|
||||||
|
|
||||||
class Response(BaseResponse):
|
|
||||||
"""
|
|
||||||
The concrete response object for the WSGI application.
|
|
||||||
"""
|
|
||||||
charset = 'utf-8'
|
|
||||||
default_mimetype = 'text/html'
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateResponse(Response):
|
|
||||||
"""
|
|
||||||
Render a template to a response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, template_name, **values):
|
|
||||||
from coolmagic import helpers
|
|
||||||
values.update(
|
|
||||||
request=local.request,
|
|
||||||
h=helpers
|
|
||||||
)
|
|
||||||
template = template_env.get_template(template_name)
|
|
||||||
Response.__init__(self, template.render(values))
|
|
|
@ -1,10 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
coolmagic.views
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module collects and assambles the urls.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
|
@ -1,36 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
coolmagic.views.static
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Some static views.
|
|
||||||
|
|
||||||
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
from coolmagic.helpers import *
|
|
||||||
|
|
||||||
|
|
||||||
@export('/', template='static/index.html')
|
|
||||||
def index():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@export('/about', template='static/about.html')
|
|
||||||
def about():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@export('/broken')
|
|
||||||
def broken():
|
|
||||||
foo = request.args.get('foo', 42)
|
|
||||||
raise RuntimeError('that\'s really broken')
|
|
||||||
|
|
||||||
|
|
||||||
@export(None, template='static/not_found.html')
|
|
||||||
def not_found():
|
|
||||||
"""
|
|
||||||
This function is always executed if an url does not
|
|
||||||
match or a `NotFound` exception is raised.
|
|
||||||
"""
|
|
||||||
pass
|
|