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
|