├── .devcontainer
├── devcontainer.json
└── on-create-command.sh
├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug-report.md
│ ├── config.yml
│ └── feature-request.md
├── pull_request_template.md
└── workflows
│ ├── lock.yaml
│ ├── pre-commit.yaml
│ ├── publish.yaml
│ └── tests.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGES.rst
├── LICENSE.txt
├── README.md
├── docs
├── Makefile
├── _static
│ ├── debug-screenshot.png
│ ├── shortcut-icon.png
│ ├── shortly.png
│ ├── werkzeug-horizontal.png
│ └── werkzeug-vertical.png
├── changes.rst
├── conf.py
├── datastructures.rst
├── debug.rst
├── deployment
│ ├── apache-httpd.rst
│ ├── eventlet.rst
│ ├── gevent.rst
│ ├── gunicorn.rst
│ ├── index.rst
│ ├── mod_wsgi.rst
│ ├── nginx.rst
│ ├── proxy_fix.rst
│ ├── uwsgi.rst
│ └── waitress.rst
├── exceptions.rst
├── http.rst
├── index.rst
├── installation.rst
├── levels.rst
├── license.rst
├── local.rst
├── make.bat
├── middleware
│ ├── dispatcher.rst
│ ├── http_proxy.rst
│ ├── index.rst
│ ├── lint.rst
│ ├── profiler.rst
│ ├── proxy_fix.rst
│ └── shared_data.rst
├── quickstart.rst
├── request_data.rst
├── routing.rst
├── serving.rst
├── terms.rst
├── test.rst
├── tutorial.rst
├── urls.rst
├── utils.rst
├── wrappers.rst
└── wsgi.rst
├── examples
├── README.rst
├── coolmagic
│ ├── __init__.py
│ ├── application.py
│ ├── helpers.py
│ ├── public
│ │ └── style.css
│ ├── templates
│ │ ├── layout.html
│ │ └── static
│ │ │ ├── about.html
│ │ │ ├── index.html
│ │ │ └── not_found.html
│ ├── utils.py
│ └── views
│ │ ├── __init__.py
│ │ └── static.py
├── couchy
│ ├── README
│ ├── __init__.py
│ ├── application.py
│ ├── models.py
│ ├── static
│ │ └── style.css
│ ├── templates
│ │ ├── display.html
│ │ ├── layout.html
│ │ ├── list.html
│ │ ├── new.html
│ │ └── not_found.html
│ ├── utils.py
│ └── views.py
├── cupoftee
│ ├── __init__.py
│ ├── application.py
│ ├── db.py
│ ├── network.py
│ ├── pages.py
│ ├── shared
│ │ ├── content.png
│ │ ├── down.png
│ │ ├── favicon.ico
│ │ ├── header.png
│ │ ├── logo.png
│ │ ├── style.css
│ │ └── up.png
│ ├── templates
│ │ ├── layout.html
│ │ ├── missingpage.html
│ │ ├── search.html
│ │ ├── server.html
│ │ └── serverlist.html
│ └── utils.py
├── httpbasicauth.py
├── i18nurls
│ ├── __init__.py
│ ├── application.py
│ ├── templates
│ │ ├── about.html
│ │ ├── blog.html
│ │ ├── index.html
│ │ └── layout.html
│ ├── urls.py
│ └── views.py
├── manage-coolmagic.py
├── manage-couchy.py
├── manage-cupoftee.py
├── manage-i18nurls.py
├── manage-plnt.py
├── manage-shorty.py
├── manage-simplewiki.py
├── manage-webpylike.py
├── partial
│ ├── README
│ └── complex_routing.py
├── plnt
│ ├── __init__.py
│ ├── database.py
│ ├── shared
│ │ └── style.css
│ ├── sync.py
│ ├── templates
│ │ ├── about.html
│ │ ├── index.html
│ │ └── layout.html
│ ├── utils.py
│ ├── views.py
│ └── webapp.py
├── shortly
│ ├── shortly.py
│ ├── static
│ │ └── style.css
│ └── templates
│ │ ├── 404.html
│ │ ├── layout.html
│ │ ├── new_url.html
│ │ └── short_link_details.html
├── shorty
│ ├── __init__.py
│ ├── application.py
│ ├── models.py
│ ├── static
│ │ └── style.css
│ ├── templates
│ │ ├── display.html
│ │ ├── layout.html
│ │ ├── list.html
│ │ ├── new.html
│ │ └── not_found.html
│ ├── utils.py
│ └── views.py
├── simplewiki
│ ├── __init__.py
│ ├── actions.py
│ ├── application.py
│ ├── database.py
│ ├── shared
│ │ └── style.css
│ ├── specialpages.py
│ ├── templates
│ │ ├── action_diff.html
│ │ ├── action_edit.html
│ │ ├── action_log.html
│ │ ├── action_revert.html
│ │ ├── action_show.html
│ │ ├── layout.html
│ │ ├── macros.xml
│ │ ├── missing_action.html
│ │ ├── page_index.html
│ │ ├── page_missing.html
│ │ └── recent_changes.html
│ └── utils.py
├── upload.py
├── webpylike
│ ├── example.py
│ └── webpylike.py
└── wsecho.py
├── pyproject.toml
├── src
└── werkzeug
│ ├── __init__.py
│ ├── _internal.py
│ ├── _reloader.py
│ ├── datastructures
│ ├── __init__.py
│ ├── accept.py
│ ├── auth.py
│ ├── cache_control.py
│ ├── csp.py
│ ├── etag.py
│ ├── file_storage.py
│ ├── headers.py
│ ├── mixins.py
│ ├── range.py
│ └── structures.py
│ ├── debug
│ ├── __init__.py
│ ├── console.py
│ ├── repr.py
│ ├── shared
│ │ ├── ICON_LICENSE.md
│ │ ├── console.png
│ │ ├── debugger.js
│ │ ├── less.png
│ │ ├── more.png
│ │ └── style.css
│ └── tbtools.py
│ ├── exceptions.py
│ ├── formparser.py
│ ├── http.py
│ ├── local.py
│ ├── middleware
│ ├── __init__.py
│ ├── dispatcher.py
│ ├── http_proxy.py
│ ├── lint.py
│ ├── profiler.py
│ ├── proxy_fix.py
│ └── shared_data.py
│ ├── py.typed
│ ├── routing
│ ├── __init__.py
│ ├── converters.py
│ ├── exceptions.py
│ ├── map.py
│ ├── matcher.py
│ └── rules.py
│ ├── sansio
│ ├── __init__.py
│ ├── http.py
│ ├── multipart.py
│ ├── request.py
│ ├── response.py
│ └── utils.py
│ ├── security.py
│ ├── serving.py
│ ├── test.py
│ ├── testapp.py
│ ├── urls.py
│ ├── user_agent.py
│ ├── utils.py
│ ├── wrappers
│ ├── __init__.py
│ ├── request.py
│ └── response.py
│ └── wsgi.py
├── tests
├── conftest.py
├── live_apps
│ ├── data_app.py
│ ├── reloader_app.py
│ ├── run.py
│ ├── standard_app.py
│ └── streaming_app.py
├── middleware
│ ├── test_dispatcher.py
│ ├── test_http_proxy.py
│ ├── test_lint.py
│ ├── test_profiler.py
│ ├── test_proxy_fix.py
│ └── test_shared_data.py
├── multipart
│ ├── firefox3-2png1txt
│ │ ├── file1.png
│ │ ├── file2.png
│ │ ├── request.http
│ │ └── text.txt
│ ├── firefox3-2pnglongtext
│ │ ├── file1.png
│ │ ├── file2.png
│ │ ├── request.http
│ │ └── text.txt
│ ├── ie6-2png1txt
│ │ ├── file1.png
│ │ ├── file2.png
│ │ ├── request.http
│ │ └── text.txt
│ ├── ie7_full_path_request.http
│ ├── opera8-2png1txt
│ │ ├── file1.png
│ │ ├── file2.png
│ │ ├── request.http
│ │ └── text.txt
│ └── webkit3-2png1txt
│ │ ├── file1.png
│ │ ├── file2.png
│ │ ├── request.http
│ │ └── text.txt
├── res
│ ├── index.html
│ └── test.txt
├── sansio
│ ├── __init__.py
│ ├── test_multipart.py
│ ├── test_request.py
│ └── test_utils.py
├── test_datastructures.py
├── test_debug.py
├── test_exceptions.py
├── test_formparser.py
├── test_http.py
├── test_internal.py
├── test_local.py
├── test_routing.py
├── test_security.py
├── test_send_file.py
├── test_serving.py
├── test_test.py
├── test_urls.py
├── test_utils.py
├── test_wrappers.py
└── test_wsgi.py
└── uv.lock
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pallets/werkzeug",
3 | "image": "mcr.microsoft.com/devcontainers/python:3",
4 | "customizations": {
5 | "vscode": {
6 | "settings": {
7 | "python.defaultInterpreterPath": "${workspaceFolder}/.venv",
8 | "python.terminal.activateEnvInCurrentTerminal": true,
9 | "python.terminal.launchArgs": [
10 | "-X",
11 | "dev"
12 | ]
13 | }
14 | }
15 | },
16 | "onCreateCommand": ".devcontainer/on-create-command.sh"
17 | }
18 |
--------------------------------------------------------------------------------
/.devcontainer/on-create-command.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | python3 -m venv --upgrade-deps .venv
4 | . .venv/bin/activate
5 | pip install -r requirements/dev.txt
6 | pip install -e .
7 | pre-commit install --install-hooks
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 | end_of_line = lf
9 | charset = utf-8
10 | max_line_length = 88
11 |
12 | [*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}]
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Normalize CRLF to LF for all text files
2 | * text=auto
3 |
4 | # Declare binary file types so they won't be normalized
5 | *.png binary
6 | *.jpg binary
7 | tests/**/*.http binary
8 | tests/res/test.txt binary
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Report a bug in Werkzeug (not other projects which depend on Werkzeug)
4 | ---
5 |
6 |
12 |
13 |
19 |
20 |
23 |
24 | Environment:
25 |
26 | - Python version:
27 | - Werkzeug version:
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Questions on Discussions
4 | url: https://github.com/pallets/werkzeug/discussions/
5 | about: Ask questions about your own code on the Discussions tab.
6 | - name: Questions on Chat
7 | url: https://discord.gg/pallets
8 | about: Ask questions about your own code on our Discord chat.
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest a new feature for Werkzeug
4 | ---
5 |
6 |
10 |
11 |
16 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
9 |
10 |
16 |
17 |
26 |
--------------------------------------------------------------------------------
/.github/workflows/lock.yaml:
--------------------------------------------------------------------------------
1 | name: Lock inactive closed issues
2 | # Lock closed issues that have not received any further activity for two weeks.
3 | # This does not close open issues, only humans may do that. It is easier to
4 | # respond to new issues with fresh examples rather than continuing discussions
5 | # on old issues.
6 |
7 | on:
8 | schedule:
9 | - cron: '0 0 * * *'
10 | permissions:
11 | issues: write
12 | pull-requests: write
13 | discussions: write
14 | concurrency:
15 | group: lock
16 | jobs:
17 | lock:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
21 | with:
22 | issue-inactive-days: 14
23 | pr-inactive-days: 14
24 | discussion-inactive-days: 14
25 |
--------------------------------------------------------------------------------
/.github/workflows/pre-commit.yaml:
--------------------------------------------------------------------------------
1 | name: pre-commit
2 | on:
3 | pull_request:
4 | push:
5 | branches: [main, stable]
6 | jobs:
7 | main:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
11 | - uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
12 | with:
13 | enable-cache: true
14 | prune-cache: false
15 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
16 | id: setup-python
17 | with:
18 | python-version-file: pyproject.toml
19 | - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
20 | with:
21 | path: ~/.cache/pre-commit
22 | key: pre-commit|${{ hashFiles('pyproject.toml', '.pre-commit-config.yaml') }}
23 | - run: uv run --locked --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files
24 | - uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
25 | if: ${{ !cancelled() }}
26 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yaml:
--------------------------------------------------------------------------------
1 | name: Publish
2 | on:
3 | push:
4 | tags: ['*']
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | outputs:
9 | hash: ${{ steps.hash.outputs.hash }}
10 | steps:
11 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
12 | - uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
13 | with:
14 | enable-cache: true
15 | prune-cache: false
16 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
17 | with:
18 | python-version-file: pyproject.toml
19 | - run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
20 | - run: uv build
21 | - name: generate hash
22 | id: hash
23 | run: cd dist && echo "hash=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT
24 | - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
25 | with:
26 | path: ./dist
27 | provenance:
28 | needs: [build]
29 | permissions:
30 | actions: read
31 | id-token: write
32 | contents: write
33 | # Can't pin with hash due to how this workflow works.
34 | uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
35 | with:
36 | base64-subjects: ${{ needs.build.outputs.hash }}
37 | create-release:
38 | needs: [provenance]
39 | runs-on: ubuntu-latest
40 | permissions:
41 | contents: write
42 | steps:
43 | - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
44 | - name: create release
45 | run: >
46 | gh release create --draft --repo ${{ github.repository }}
47 | ${{ github.ref_name }}
48 | *.intoto.jsonl/* artifact/*
49 | env:
50 | GH_TOKEN: ${{ github.token }}
51 | publish-pypi:
52 | needs: [provenance]
53 | environment:
54 | name: publish
55 | url: https://pypi.org/project/Werkzeug/${{ github.ref_name }}
56 | runs-on: ubuntu-latest
57 | permissions:
58 | id-token: write
59 | steps:
60 | - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
61 | - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
62 | with:
63 | packages-dir: artifact/
64 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on:
3 | pull_request:
4 | paths-ignore: ['docs/**', 'README.md']
5 | push:
6 | branches: [main, stable]
7 | paths-ignore: ['docs/**', 'README.md']
8 | jobs:
9 | tests:
10 | name: ${{ matrix.name || matrix.python }}
11 | runs-on: ${{ matrix.os || 'ubuntu-latest' }}
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | include:
16 | - {python: '3.13'}
17 | - {name: Windows, python: '3.13', os: windows-latest}
18 | - {name: Mac, python: '3.13', os: macos-latest}
19 | - {python: '3.12'}
20 | - {python: '3.11'}
21 | - {python: '3.10'}
22 | - {python: '3.9'}
23 | - {name: PyPy, python: 'pypy-3.11', tox: pypy3.11}
24 | steps:
25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
26 | - uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
27 | with:
28 | enable-cache: true
29 | prune-cache: false
30 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
31 | with:
32 | python-version: ${{ matrix.python }}
33 | - run: uv run --locked tox run -e ${{ matrix.tox || format('py{0}', matrix.python) }}
34 | typing:
35 | runs-on: ubuntu-latest
36 | steps:
37 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
38 | - uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
39 | with:
40 | enable-cache: true
41 | prune-cache: false
42 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
43 | with:
44 | python-version-file: pyproject.toml
45 | - name: cache mypy
46 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
47 | with:
48 | path: ./.mypy_cache
49 | key: mypy|${{ hashFiles('pyproject.toml') }}
50 | - run: uv run --locked tox run -e typing
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vscode/
3 | __pycache__/
4 | dist/
5 | .coverage*
6 | htmlcov/
7 | .tox/
8 | docs/_build/
9 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/astral-sh/ruff-pre-commit
3 | rev: 24e02b24b8ab2b7c76225602d13fa60e12d114e6 # frozen: v0.11.9
4 | hooks:
5 | - id: ruff
6 | - id: ruff-format
7 | - repo: https://github.com/astral-sh/uv-pre-commit
8 | rev: 14ac15b122e538e407d036ff45e3895b7cf4a2bf # frozen: 0.7.3
9 | hooks:
10 | - id: uv-lock
11 | - repo: https://github.com/pre-commit/pre-commit-hooks
12 | rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0
13 | hooks:
14 | - id: check-merge-conflict
15 | - id: debug-statements
16 | - id: fix-byte-order-marker
17 | - id: trailing-whitespace
18 | - id: end-of-file-fixer
19 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 | build:
3 | os: ubuntu-24.04
4 | tools:
5 | python: '3.13'
6 | commands:
7 | - asdf plugin add uv
8 | - asdf install uv latest
9 | - asdf global uv latest
10 | - uv run --group docs sphinx-build -W -b dirhtml docs $READTHEDOCS_OUTPUT/html
11 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2007 Pallets
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 |
14 | 3. Neither the name of the copyright holder nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Werkzeug
2 |
3 | *werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff")
4 |
5 | Werkzeug is a comprehensive [WSGI][] web application library. It began as
6 | a simple collection of various utilities for WSGI applications and has
7 | become one of the most advanced WSGI utility libraries.
8 |
9 | It includes:
10 |
11 | - An interactive debugger that allows inspecting stack traces and
12 | source code in the browser with an interactive interpreter for any
13 | frame in the stack.
14 | - A full-featured request object with objects to interact with
15 | headers, query args, form data, files, and cookies.
16 | - A response object that can wrap other WSGI applications and handle
17 | streaming data.
18 | - A routing system for matching URLs to endpoints and generating URLs
19 | for endpoints, with an extensible system for capturing variables
20 | from URLs.
21 | - HTTP utilities to handle entity tags, cache control, dates, user
22 | agents, cookies, files, and more.
23 | - A threaded WSGI server for use while developing applications
24 | locally.
25 | - A test client for simulating HTTP requests during testing without
26 | requiring running a server.
27 |
28 | Werkzeug doesn't enforce any dependencies. It is up to the developer to
29 | choose a template engine, database adapter, and even how to handle
30 | requests. It can be used to build all sorts of end user applications
31 | such as blogs, wikis, or bulletin boards.
32 |
33 | [Flask][] wraps Werkzeug, using it to handle the details of WSGI while
34 | providing more structure and patterns for defining powerful
35 | applications.
36 |
37 | [WSGI]: https://wsgi.readthedocs.io/en/latest/
38 | [Flask]: https://www.palletsprojects.com/p/flask/
39 |
40 |
41 | ## A Simple Example
42 |
43 | ```python
44 | # save this as app.py
45 | from werkzeug.wrappers import Request, Response
46 |
47 | @Request.application
48 | def application(request: Request) -> Response:
49 | return Response("Hello, World!")
50 |
51 | if __name__ == "__main__":
52 | from werkzeug.serving import run_simple
53 | run_simple("127.0.0.1", 5000, application)
54 | ```
55 |
56 | ```
57 | $ python -m app
58 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
59 | ```
60 |
61 |
62 | ## Donate
63 |
64 | The Pallets organization develops and supports Werkzeug and other
65 | popular packages. In order to grow the community of contributors and
66 | users, and allow the maintainers to devote more time to the projects,
67 | [please donate today][].
68 |
69 | [please donate today]: https://palletsprojects.com/donate
70 |
71 | ## Contributing
72 |
73 | See our [detailed contributing documentation][contrib] for many ways to
74 | contribute, including reporting issues, requesting features, asking or answering
75 | questions, and making PRs.
76 |
77 | [contrib]: https://palletsprojects.com/contributing/
78 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SOURCEDIR = .
8 | BUILDDIR = _build
9 |
10 | # Put it first so that "make" without argument is like "make help".
11 | help:
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13 |
14 | .PHONY: help Makefile
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | %: Makefile
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
20 |
--------------------------------------------------------------------------------
/docs/_static/debug-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pallets/werkzeug/99fc293bc7dccd6a7dc96b96012d4aa849794ba4/docs/_static/debug-screenshot.png
--------------------------------------------------------------------------------
/docs/_static/shortcut-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pallets/werkzeug/99fc293bc7dccd6a7dc96b96012d4aa849794ba4/docs/_static/shortcut-icon.png
--------------------------------------------------------------------------------
/docs/_static/shortly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pallets/werkzeug/99fc293bc7dccd6a7dc96b96012d4aa849794ba4/docs/_static/shortly.png
--------------------------------------------------------------------------------
/docs/_static/werkzeug-horizontal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pallets/werkzeug/99fc293bc7dccd6a7dc96b96012d4aa849794ba4/docs/_static/werkzeug-horizontal.png
--------------------------------------------------------------------------------
/docs/_static/werkzeug-vertical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pallets/werkzeug/99fc293bc7dccd6a7dc96b96012d4aa849794ba4/docs/_static/werkzeug-vertical.png
--------------------------------------------------------------------------------
/docs/changes.rst:
--------------------------------------------------------------------------------
1 | Changes
2 | =======
3 |
4 | .. include:: ../CHANGES.rst
5 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | from pallets_sphinx_themes import get_version
2 | from pallets_sphinx_themes import ProjectLink
3 |
4 | # Project --------------------------------------------------------------
5 |
6 | project = "Werkzeug"
7 | copyright = "2007 Pallets"
8 | author = "Pallets"
9 | release, version = get_version("Werkzeug")
10 |
11 | # General --------------------------------------------------------------
12 |
13 | default_role = "code"
14 | extensions = [
15 | "sphinx.ext.autodoc",
16 | "sphinx.ext.extlinks",
17 | "sphinx.ext.intersphinx",
18 | "sphinxcontrib.log_cabinet",
19 | "pallets_sphinx_themes",
20 | ]
21 | autoclass_content = "both"
22 | autodoc_member_order = "bysource"
23 | autodoc_typehints = "description"
24 | autodoc_preserve_defaults = True
25 | extlinks = {
26 | "issue": ("https://github.com/pallets/werkzeug/issues/%s", "#%s"),
27 | "pr": ("https://github.com/pallets/werkzeug/pull/%s", "#%s"),
28 | "ghsa": ("https://github.com/advisories/GHSA-%s", "GHSA-%s"),
29 | }
30 | intersphinx_mapping = {
31 | "python": ("https://docs.python.org/3/", None),
32 | }
33 |
34 | # HTML -----------------------------------------------------------------
35 |
36 | html_theme = "werkzeug"
37 | html_theme_options = {"index_sidebar_logo": False}
38 | html_context = {
39 | "project_links": [
40 | ProjectLink("Donate", "https://palletsprojects.com/donate"),
41 | ProjectLink("PyPI Releases", "https://pypi.org/project/Werkzeug/"),
42 | ProjectLink("Source Code", "https://github.com/pallets/werkzeug/"),
43 | ProjectLink("Issue Tracker", "https://github.com/pallets/werkzeug/issues/"),
44 | ProjectLink("Chat", "https://discord.gg/pallets"),
45 | ]
46 | }
47 | html_sidebars = {
48 | "index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
49 | "**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
50 | }
51 | singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
52 | html_static_path = ["_static"]
53 | html_favicon = "_static/shortcut-icon.png"
54 | html_logo = "_static/werkzeug-vertical.png"
55 | html_title = f"Werkzeug Documentation ({version})"
56 | html_show_sourcelink = False
57 |
--------------------------------------------------------------------------------
/docs/deployment/apache-httpd.rst:
--------------------------------------------------------------------------------
1 | Apache httpd
2 | ============
3 |
4 | `Apache httpd`_ is a fast, production level HTTP server. When serving
5 | your application with one of the WSGI servers listed in :doc:`index`, it
6 | is often good or necessary to put a dedicated HTTP server in front of
7 | it. This "reverse proxy" can handle incoming requests, TLS, and other
8 | security and performance concerns better than the WSGI server.
9 |
10 | httpd can be installed using your system package manager, or a pre-built
11 | executable for Windows. Installing and running httpd itself is outside
12 | the scope of this doc. This page outlines the basics of configuring
13 | httpd to proxy your application. Be sure to read its documentation to
14 | understand what features are available.
15 |
16 | .. _Apache httpd: https://httpd.apache.org/
17 |
18 |
19 | Domain Name
20 | -----------
21 |
22 | Acquiring and configuring a domain name is outside the scope of this
23 | doc. In general, you will buy a domain name from a registrar, pay for
24 | server space with a hosting provider, and then point your registrar
25 | at the hosting provider's name servers.
26 |
27 | To simulate this, you can also edit your ``hosts`` file, located at
28 | ``/etc/hosts`` on Linux. Add a line that associates a name with the
29 | local IP.
30 |
31 | Modern Linux systems may be configured to treat any domain name that
32 | ends with ``.localhost`` like this without adding it to the ``hosts``
33 | file.
34 |
35 | .. code-block:: python
36 | :caption: ``/etc/hosts``
37 |
38 | 127.0.0.1 hello.localhost
39 |
40 |
41 | Configuration
42 | -------------
43 |
44 | The httpd configuration is located at ``/etc/httpd/conf/httpd.conf`` on
45 | Linux. It may be different depending on your operating system. Check the
46 | docs and look for ``httpd.conf``.
47 |
48 | Remove or comment out any existing ``DocumentRoot`` directive. Add the
49 | config lines below. We'll assume the WSGI server is listening locally at
50 | ``http://127.0.0.1:8000``.
51 |
52 | .. code-block:: apache
53 | :caption: ``/etc/httpd/conf/httpd.conf``
54 |
55 | LoadModule proxy_module modules/mod_proxy.so
56 | LoadModule proxy_http_module modules/mod_proxy_http.so
57 | ProxyPass / http://127.0.0.1:8000/
58 | RequestHeader set X-Forwarded-Proto http
59 | RequestHeader set X-Forwarded-Prefix /
60 |
61 | The ``LoadModule`` lines might already exist. If so, make sure they are
62 | uncommented instead of adding them manually.
63 |
64 | Then :doc:`proxy_fix` so that your application uses the ``X-Forwarded``
65 | headers. ``X-Forwarded-For`` and ``X-Forwarded-Host`` are automatically
66 | set by ``ProxyPass``.
67 |
68 |
69 | Static Files
70 | ------------
71 |
72 | If your application has static files such as JavaScript, CSS, and
73 | images, it will be more efficient to let Nginx serve them directly
74 | rather than going through the Python application.
75 |
76 | Assuming the static files are expected to be available under the
77 | ``/static/`` URL, and are stored at ``/home/project/static/``, add the
78 | following to the config above.
79 |
80 | .. code-block:: apache
81 |
82 | Alias /static/ /home/project/static/
83 |
--------------------------------------------------------------------------------
/docs/deployment/eventlet.rst:
--------------------------------------------------------------------------------
1 | eventlet
2 | ========
3 |
4 | Prefer using :doc:`gunicorn` with eventlet workers rather than using
5 | `eventlet`_ directly. Gunicorn provides a much more configurable and
6 | production-tested server.
7 |
8 | `eventlet`_ allows writing asynchronous, coroutine-based code that looks
9 | like standard synchronous Python. It uses `greenlet`_ to enable task
10 | switching without writing ``async/await`` or using ``asyncio``.
11 |
12 | :doc:`gevent` is another library that does the same thing. Certain
13 | dependencies you have, or other considerations, may affect which of the
14 | two you choose to use.
15 |
16 | eventlet provides a WSGI server that can handle many connections at once
17 | instead of one per worker process. You must actually use eventlet in
18 | your own code to see any benefit to using the server.
19 |
20 | .. _eventlet: https://eventlet.net/
21 | .. _greenlet: https://greenlet.readthedocs.io/en/latest/
22 |
23 |
24 | Installing
25 | ----------
26 |
27 | When using eventlet, greenlet>=1.0 is required, otherwise context locals
28 | such as ``request`` will not work as expected. When using PyPy,
29 | PyPy>=7.3.7 is required.
30 |
31 | Create a virtualenv, install your application, then install
32 | ``eventlet``.
33 |
34 | .. code-block:: text
35 |
36 | $ cd hello-app
37 | $ python -m venv venv
38 | $ . venv/bin/activate
39 | $ pip install . # install your application
40 | $ pip install eventlet
41 |
42 |
43 | Running
44 | -------
45 |
46 | To use eventlet to serve your application, write a script that imports
47 | its ``wsgi.server``, as well as your app or app factory.
48 |
49 | .. code-block:: python
50 | :caption: ``wsgi.py``
51 |
52 | import eventlet
53 | from eventlet import wsgi
54 | from hello import create_app
55 |
56 | app = create_app()
57 | wsgi.server(eventlet.listen(("127.0.0.1", 8000), app)
58 |
59 | .. code-block:: text
60 |
61 | $ python wsgi.py
62 | (x) wsgi starting up on http://127.0.0.1:8000
63 |
64 |
65 | Binding Externally
66 | ------------------
67 |
68 | eventlet should not be run as root because it would cause your
69 | application code to run as root, which is not secure. However, this
70 | means it will not be possible to bind to port 80 or 443. Instead, a
71 | reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
72 | in front of eventlet.
73 |
74 | You can bind to all external IPs on a non-privileged port by using
75 | ``0.0.0.0`` in the server arguments shown in the previous section.
76 | Don't do this when using a reverse proxy setup, otherwise it will be
77 | possible to bypass the proxy.
78 |
79 | ``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
80 | IP address in your browser.
81 |
--------------------------------------------------------------------------------
/docs/deployment/gevent.rst:
--------------------------------------------------------------------------------
1 | gevent
2 | ======
3 |
4 | Prefer using :doc:`gunicorn` or :doc:`uwsgi` with gevent workers rather
5 | than using `gevent`_ directly. Gunicorn and uWSGI provide much more
6 | configurable and production-tested servers.
7 |
8 | `gevent`_ allows writing asynchronous, coroutine-based code that looks
9 | like standard synchronous Python. It uses `greenlet`_ to enable task
10 | switching without writing ``async/await`` or using ``asyncio``.
11 |
12 | :doc:`eventlet` is another library that does the same thing. Certain
13 | dependencies you have, or other considerations, may affect which of the
14 | two you choose to use.
15 |
16 | gevent provides a WSGI server that can handle many connections at once
17 | instead of one per worker process. You must actually use gevent in your
18 | own code to see any benefit to using the server.
19 |
20 | .. _gevent: https://www.gevent.org/
21 | .. _greenlet: https://greenlet.readthedocs.io/en/latest/
22 |
23 |
24 | Installing
25 | ----------
26 |
27 | When using gevent, greenlet>=1.0 is required, otherwise context locals
28 | such as ``request`` will not work as expected. When using PyPy,
29 | PyPy>=7.3.7 is required.
30 |
31 | Create a virtualenv, install your application, then install ``gevent``.
32 |
33 | .. code-block:: text
34 |
35 | $ cd hello-app
36 | $ python -m venv venv
37 | $ . venv/bin/activate
38 | $ pip install . # install your application
39 | $ pip install gevent
40 |
41 |
42 | Running
43 | -------
44 |
45 | To use gevent to serve your application, write a script that imports its
46 | ``WSGIServer``, as well as your app or app factory.
47 |
48 | .. code-block:: python
49 | :caption: ``wsgi.py``
50 |
51 | from gevent.pywsgi import WSGIServer
52 | from hello import create_app
53 |
54 | app = create_app()
55 | http_server = WSGIServer(("127.0.0.1", 8000), app)
56 | http_server.serve_forever()
57 |
58 | .. code-block:: text
59 |
60 | $ python wsgi.py
61 |
62 | No output is shown when the server starts.
63 |
64 |
65 | Binding Externally
66 | ------------------
67 |
68 | gevent should not be run as root because it would cause your
69 | application code to run as root, which is not secure. However, this
70 | means it will not be possible to bind to port 80 or 443. Instead, a
71 | reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
72 | in front of gevent.
73 |
74 | You can bind to all external IPs on a non-privileged port by using
75 | ``0.0.0.0`` in the server arguments shown in the previous section. Don't
76 | do this when using a reverse proxy setup, otherwise it will be possible
77 | to bypass the proxy.
78 |
79 | ``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
80 | IP address in your browser.
81 |
--------------------------------------------------------------------------------
/docs/deployment/index.rst:
--------------------------------------------------------------------------------
1 | Deploying to Production
2 | =======================
3 |
4 | After developing your application, you'll want to make it available
5 | publicly to other users. When you're developing locally, you're probably
6 | using the built-in development server, debugger, and reloader. These
7 | should not be used in production. Instead, you should use a dedicated
8 | WSGI server or hosting platform, some of which will be described here.
9 |
10 | "Production" means "not development", which applies whether you're
11 | serving your application publicly to millions of users or privately /
12 | locally to a single user. **Do not use the development server when
13 | deploying to production. It is intended for use only during local
14 | development. It is not designed to be particularly secure, stable, or
15 | efficient.**
16 |
17 | Self-Hosted Options
18 | -------------------
19 |
20 | Werkzeug is a WSGI *application*. A WSGI *server* is used to run the
21 | application, converting incoming HTTP requests to the standard WSGI
22 | environ, and converting outgoing WSGI responses to HTTP responses.
23 |
24 | The primary goal of these docs is to familiarize you with the concepts
25 | involved in running a WSGI application using a production WSGI server
26 | and HTTP server. There are many WSGI servers and HTTP servers, with many
27 | configuration possibilities. The pages below discuss the most common
28 | servers, and show the basics of running each one. The next section
29 | discusses platforms that can manage this for you.
30 |
31 | .. toctree::
32 | :maxdepth: 1
33 |
34 | gunicorn
35 | waitress
36 | mod_wsgi
37 | uwsgi
38 | gevent
39 | eventlet
40 |
41 | WSGI servers have HTTP servers built-in. However, a dedicated HTTP
42 | server may be safer, more efficient, or more capable. Putting an HTTP
43 | server in front of the WSGI server is called a "reverse proxy."
44 |
45 | .. toctree::
46 | :maxdepth: 1
47 |
48 | proxy_fix
49 | nginx
50 | apache-httpd
51 |
52 | This list is not exhaustive, and you should evaluate these and other
53 | servers based on your application's needs. Different servers will have
54 | different capabilities, configuration, and support.
55 |
56 |
57 | Hosting Platforms
58 | -----------------
59 |
60 | There are many services available for hosting web applications without
61 | needing to maintain your own server, networking, domain, etc. Some
62 | services may have a free tier up to a certain time or bandwidth. Many of
63 | these services use one of the WSGI servers described above, or a similar
64 | interface.
65 |
66 | You should evaluate services based on your application's needs.
67 | Different services will have different capabilities, configuration,
68 | pricing, and support.
69 |
70 | You'll probably need to :doc:`proxy_fix` when using most hosting
71 | platforms.
72 |
--------------------------------------------------------------------------------
/docs/deployment/mod_wsgi.rst:
--------------------------------------------------------------------------------
1 | mod_wsgi
2 | ========
3 |
4 | `mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server.
5 | The modern `mod_wsgi-express`_ command makes it easy to configure and
6 | start the server without needing to write Apache httpd configuration.
7 |
8 | * Tightly integrated with Apache httpd.
9 | * Supports Windows directly.
10 | * Requires a compiler and the Apache development headers to install.
11 | * Does not require a reverse proxy setup.
12 |
13 | This page outlines the basics of running mod_wsgi-express, not the more
14 | complex installation and configuration with httpd. Be sure to read the
15 | `mod_wsgi-express`_, `mod_wsgi`_, and `Apache httpd`_ documentation to
16 | understand what features are available.
17 |
18 | .. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/
19 | .. _mod_wsgi: https://modwsgi.readthedocs.io/
20 | .. _Apache httpd: https://httpd.apache.org/
21 |
22 |
23 | Installing
24 | ----------
25 |
26 | Installing mod_wsgi requires a compiler and the Apache server and
27 | development headers installed. You will get an error if they are not.
28 | How to install them depends on the OS and package manager that you use.
29 |
30 | Create a virtualenv, install your application, then install
31 | ``mod_wsgi``.
32 |
33 | .. code-block:: text
34 |
35 | $ cd hello-app
36 | $ python -m venv venv
37 | $ . venv/bin/activate
38 | $ pip install . # install your application
39 | $ pip install mod_wsgi
40 |
41 |
42 | Running
43 | -------
44 |
45 | The only argument to ``mod_wsgi-express`` specifies a script containing
46 | your application, which must be called ``application``. You can
47 | write a small script to import your app with this name, or to create it
48 | if using the app factory pattern.
49 |
50 | .. code-block:: python
51 | :caption: ``wsgi.py``
52 |
53 | from hello import app
54 |
55 | application = app
56 |
57 | .. code-block:: python
58 | :caption: ``wsgi.py``
59 |
60 | from hello import create_app
61 |
62 | application = create_app()
63 |
64 | Now run the ``mod_wsgi-express start-server`` command.
65 |
66 | .. code-block:: text
67 |
68 | $ mod_wsgi-express start-server wsgi.py --processes 4
69 |
70 | The ``--processes`` option specifies the number of worker processes to
71 | run; a starting value could be ``CPU * 2``.
72 |
73 | Logs for each request aren't show in the terminal. If an error occurs,
74 | its information is written to the error log file shown when starting the
75 | server.
76 |
77 |
78 | Binding Externally
79 | ------------------
80 |
81 | Unlike the other WSGI servers in these docs, mod_wsgi can be run as
82 | root to bind to privileged ports like 80 and 443. However, it must be
83 | configured to drop permissions to a different user and group for the
84 | worker processes.
85 |
86 | For example, if you created a ``hello`` user and group, you should
87 | install your virtualenv and application as that user, then tell
88 | mod_wsgi to drop to that user after starting.
89 |
90 | .. code-block:: text
91 |
92 | $ sudo /home/hello/venv/bin/mod_wsgi-express start-server \
93 | /home/hello/wsgi.py \
94 | --user hello --group hello --port 80 --processes 4
95 |
--------------------------------------------------------------------------------
/docs/deployment/nginx.rst:
--------------------------------------------------------------------------------
1 | nginx
2 | =====
3 |
4 | `nginx`_ is a fast, production level HTTP server. When serving your
5 | application with one of the WSGI servers listed in :doc:`index`, it is
6 | often good or necessary to put a dedicated HTTP server in front of it.
7 | This "reverse proxy" can handle incoming requests, TLS, and other
8 | security and performance concerns better than the WSGI server.
9 |
10 | Nginx can be installed using your system package manager, or a pre-built
11 | executable for Windows. Installing and running Nginx itself is outside
12 | the scope of this doc. This page outlines the basics of configuring
13 | Nginx to proxy your application. Be sure to read its documentation to
14 | understand what features are available.
15 |
16 | .. _nginx: https://nginx.org/
17 |
18 |
19 | Domain Name
20 | -----------
21 |
22 | Acquiring and configuring a domain name is outside the scope of this
23 | doc. In general, you will buy a domain name from a registrar, pay for
24 | server space with a hosting provider, and then point your registrar
25 | at the hosting provider's name servers.
26 |
27 | To simulate this, you can also edit your ``hosts`` file, located at
28 | ``/etc/hosts`` on Linux. Add a line that associates a name with the
29 | local IP.
30 |
31 | Modern Linux systems may be configured to treat any domain name that
32 | ends with ``.localhost`` like this without adding it to the ``hosts``
33 | file.
34 |
35 | .. code-block:: python
36 | :caption: ``/etc/hosts``
37 |
38 | 127.0.0.1 hello.localhost
39 |
40 |
41 | Configuration
42 | -------------
43 |
44 | The nginx configuration is located at ``/etc/nginx/nginx.conf`` on
45 | Linux. It may be different depending on your operating system. Check the
46 | docs and look for ``nginx.conf``.
47 |
48 | Remove or comment out any existing ``server`` section. Add a ``server``
49 | section and use the ``proxy_pass`` directive to point to the address the
50 | WSGI server is listening on. We'll assume the WSGI server is listening
51 | locally at ``http://127.0.0.1:8000``.
52 |
53 | .. code-block:: nginx
54 | :caption: ``/etc/nginx.conf``
55 |
56 | server {
57 | listen 80;
58 | server_name _;
59 |
60 | location / {
61 | proxy_pass http://127.0.0.1:8000/;
62 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
63 | proxy_set_header X-Forwarded-Proto $scheme;
64 | proxy_set_header X-Forwarded-Host $host;
65 | proxy_set_header X-Forwarded-Prefix /;
66 | }
67 | }
68 |
69 | Then :doc:`proxy_fix` so that your application uses these headers.
70 |
71 |
72 | Static Files
73 | ------------
74 |
75 | If your application has static files such as JavaScript, CSS, and
76 | images, it will be more efficient to let Nginx serve them directly
77 | rather than going through the Python application.
78 |
79 | Assuming the static files are expected to be available under the
80 | ``/static/`` URL, and are stored at ``/home/project/static/``, add the
81 | following to the ``server`` block above.
82 |
83 | .. code-block:: nginx
84 |
85 | location /static {
86 | alias /home/project/static;
87 | }
88 |
--------------------------------------------------------------------------------
/docs/deployment/proxy_fix.rst:
--------------------------------------------------------------------------------
1 | Tell Werkzeug it is Behind a Proxy
2 | ==================================
3 |
4 | When using a reverse proxy, or many Python hosting platforms, the proxy
5 | will intercept and forward all external requests to the local WSGI
6 | server.
7 |
8 | From the WSGI server and application's perspectives, requests are now
9 | coming from the HTTP server to the local address, rather than from
10 | the remote address to the external server address.
11 |
12 | HTTP servers should set ``X-Forwarded-`` headers to pass on the real
13 | values to the application. The application can then be told to trust and
14 | use those values by wrapping it with the
15 | :doc:`../middleware/proxy_fix` middleware provided by Werkzeug.
16 |
17 | This middleware should only be used if the application is actually
18 | behind a proxy, and should be configured with the number of proxies that
19 | are chained in front of it. Not all proxies set all the headers. Since
20 | incoming headers can be faked, you must set how many proxies are setting
21 | each header so the middleware knows what to trust.
22 |
23 | .. code-block:: python
24 |
25 | from werkzeug.middleware.proxy_fix import ProxyFix
26 |
27 | app.wsgi_app = ProxyFix(
28 | app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
29 | )
30 |
31 | Remember, only apply this middleware if you are behind a proxy, and set
32 | the correct number of proxies that set each header. It can be a security
33 | issue if you get this configuration wrong.
34 |
--------------------------------------------------------------------------------
/docs/deployment/waitress.rst:
--------------------------------------------------------------------------------
1 | Waitress
2 | ========
3 |
4 | `Waitress`_ is a pure Python WSGI server.
5 |
6 | * It is easy to configure.
7 | * It supports Windows directly.
8 | * It is easy to install as it does not require additional dependencies
9 | or compilation.
10 | * It does not support streaming requests, full request data is always
11 | buffered.
12 | * It uses a single process with multiple thread workers.
13 |
14 | This page outlines the basics of running Waitress. Be sure to read its
15 | documentation and ``waitress-serve --help`` to understand what features
16 | are available.
17 |
18 | .. _Waitress: https://docs.pylonsproject.org/projects/waitress/
19 |
20 |
21 | Installing
22 | ----------
23 |
24 | Create a virtualenv, install your application, then install
25 | ``waitress``.
26 |
27 | .. code-block:: text
28 |
29 | $ cd hello-app
30 | $ python -m venv venv
31 | $ . venv/bin/activate
32 | $ pip install . # install your application
33 | $ pip install waitress
34 |
35 |
36 | Running
37 | -------
38 |
39 | The only required argument to ``waitress-serve`` tells it how to load
40 | your application. The syntax is ``{module}:{app}``. ``module`` is
41 | the dotted import name to the module with your application. ``app`` is
42 | the variable with the application. If you're using the app factory
43 | pattern, use ``--call {module}:{factory}`` instead.
44 |
45 | .. code-block:: text
46 |
47 | # equivalent to 'from hello import app'
48 | $ waitress-serve hello:app --host 127.0.0.1
49 |
50 | # equivalent to 'from hello import create_app; create_app()'
51 | $ waitress-serve --call hello:create_app --host 127.0.0.1
52 |
53 | Serving on http://127.0.0.1:8080
54 |
55 | The ``--host`` option binds the server to local ``127.0.0.1`` only.
56 |
57 | Logs for each request aren't shown, only errors are shown. Logging can
58 | be configured through the Python interface instead of the command line.
59 |
60 |
61 | Binding Externally
62 | ------------------
63 |
64 | Waitress should not be run as root because it would cause your
65 | application code to run as root, which is not secure. However, this
66 | means it will not be possible to bind to port 80 or 443. Instead, a
67 | reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
68 | in front of Waitress.
69 |
70 | You can bind to all external IPs on a non-privileged port by not
71 | specifying the ``--host`` option. Don't do this when using a revers
72 | proxy setup, otherwise it will be possible to bypass the proxy.
73 |
74 | ``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
75 | IP address in your browser.
76 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. rst-class:: hide-header
2 |
3 | Werkzeug
4 | ========
5 |
6 | .. image:: _static/werkzeug-horizontal.png
7 | :align: center
8 | :target: https://werkzeug.palletsprojects.com
9 |
10 | *werkzeug* German noun: "tool".
11 | Etymology: *werk* ("work"), *zeug* ("stuff")
12 |
13 | Werkzeug is a comprehensive `WSGI`_ web application library. It began as
14 | a simple collection of various utilities for WSGI applications and has
15 | become one of the most advanced WSGI utility libraries.
16 |
17 | Werkzeug doesn't enforce any dependencies. It is up to the developer to
18 | choose a template engine, database adapter, and even how to handle
19 | requests.
20 |
21 | .. _WSGI: https://wsgi.readthedocs.io/en/latest/
22 |
23 |
24 | Getting Started
25 | ---------------
26 |
27 | .. toctree::
28 | :maxdepth: 2
29 |
30 | installation
31 | tutorial
32 | levels
33 | quickstart
34 |
35 |
36 | Serving and Testing
37 | -------------------
38 |
39 | .. toctree::
40 | :maxdepth: 2
41 |
42 | serving
43 | test
44 | debug
45 |
46 |
47 | Reference
48 | ---------
49 |
50 | .. toctree::
51 | :maxdepth: 2
52 |
53 | wrappers
54 | routing
55 | wsgi
56 | http
57 | datastructures
58 | utils
59 | urls
60 | local
61 | middleware/index
62 | exceptions
63 |
64 |
65 | Deployment
66 | ----------
67 |
68 | .. toctree::
69 | :maxdepth: 3
70 |
71 | deployment/index
72 |
73 |
74 | Additional Information
75 | ----------------------
76 |
77 | .. toctree::
78 | :maxdepth: 2
79 |
80 | terms
81 | request_data
82 | license
83 | changes
84 |
--------------------------------------------------------------------------------
/docs/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 |
5 | Python Version
6 | --------------
7 |
8 | We recommend using the latest version of Python. Werkzeug supports
9 | Python 3.9 and newer.
10 |
11 |
12 | Optional dependencies
13 | ~~~~~~~~~~~~~~~~~~~~~
14 |
15 | These distributions will not be installed automatically. Werkzeug will
16 | detect and use them if you install them.
17 |
18 | * `Colorama`_ provides request log highlighting when using the
19 | development server on Windows. This works automatically on other
20 | systems.
21 | * `Watchdog`_ provides a faster, more efficient reloader for the
22 | development server.
23 |
24 | .. _Colorama: https://pypi.org/project/colorama/
25 | .. _Watchdog: https://pypi.org/project/watchdog/
26 |
27 |
28 | greenlet
29 | ~~~~~~~~
30 |
31 | You may choose to use gevent or eventlet with your application. In this
32 | case, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
33 | required.
34 |
35 | These are not minimum supported versions, they only indicate the first
36 | versions that added necessary features. You should use the latest
37 | versions of each.
38 |
39 |
40 | Virtual environments
41 | --------------------
42 |
43 | Use a virtual environment to manage the dependencies for your project,
44 | both in development and in production.
45 |
46 | What problem does a virtual environment solve? The more Python
47 | projects you have, the more likely it is that you need to work with
48 | different versions of Python libraries, or even Python itself. Newer
49 | versions of libraries for one project can break compatibility in
50 | another project.
51 |
52 | Virtual environments are independent groups of Python libraries, one for
53 | each project. Packages installed for one project will not affect other
54 | projects or the operating system's packages.
55 |
56 | Python comes bundled with the :mod:`venv` module to create virtual
57 | environments.
58 |
59 |
60 | Create an environment
61 | ~~~~~~~~~~~~~~~~~~~~~
62 |
63 | Create a project folder and a :file:`venv` folder within:
64 |
65 | .. code-block:: sh
66 |
67 | mkdir myproject
68 | cd myproject
69 | python3 -m venv venv
70 |
71 | On Windows:
72 |
73 | .. code-block:: bat
74 |
75 | py -3 -m venv venv
76 |
77 |
78 | Activate the environment
79 | ~~~~~~~~~~~~~~~~~~~~~~~~
80 |
81 | Before you work on your project, activate the corresponding environment:
82 |
83 | .. code-block:: sh
84 |
85 | . venv/bin/activate
86 |
87 | On Windows:
88 |
89 | .. code-block:: bat
90 |
91 | venv\Scripts\activate
92 |
93 | Your shell prompt will change to show the name of the activated
94 | environment.
95 |
96 |
97 | Install Werkzeug
98 | ----------------
99 |
100 | Within the activated environment, use the following command to install
101 | Werkzeug:
102 |
103 | .. code-block:: sh
104 |
105 | pip install Werkzeug
106 |
--------------------------------------------------------------------------------
/docs/levels.rst:
--------------------------------------------------------------------------------
1 | ==========
2 | API Levels
3 | ==========
4 |
5 | .. currentmodule:: werkzeug
6 |
7 | Werkzeug is intended to be a utility rather than a framework. Because of that
8 | the user-friendly API is separated from the lower-level API so that Werkzeug
9 | can easily be used to extend another system.
10 |
11 | All the functionality the :class:`Request` and :class:`Response` objects (aka
12 | the "wrappers") provide is also available in small utility functions.
13 |
14 | Example
15 | =======
16 |
17 | This example implements a small `Hello World` application that greets the
18 | user with the name entered.
19 |
20 | .. code-block:: python
21 |
22 | from markupsafe import escape
23 | from werkzeug.wrappers import Request, Response
24 |
25 | @Request.application
26 | def hello_world(request):
27 | result = ['
Greeter']
28 | if request.method == 'POST':
29 | result.append(f"
Hello {escape(request.form['name'])}!
")
30 | result.append('''
31 |
35 | ''')
36 | return Response(''.join(result), mimetype='text/html')
37 |
38 | Alternatively the same application could be used without request and response
39 | objects but by taking advantage of the parsing functions werkzeug provides::
40 |
41 | from markupsafe import escape
42 | from werkzeug.formparser import parse_form_data
43 |
44 | def hello_world(environ, start_response):
45 | result = ['Greeter']
46 | if environ['REQUEST_METHOD'] == 'POST':
47 | form = parse_form_data(environ)[1]
48 | result.append(f"
Hello {escape(form['name'])}!
")
49 | result.append('''
50 |
54 | ''')
55 | start_response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
56 | return [''.join(result).encode('utf-8')]
57 |
58 | High or Low?
59 | ============
60 |
61 | Usually you want to use the high-level layer (the request and response
62 | objects). But there are situations where this might not be what you want.
63 |
64 | For example you might be maintaining code for an application written in
65 | Django or another framework and you have to parse HTTP headers. You can
66 | utilize Werkzeug for that by accessing the lower-level HTTP header parsing
67 | functions.
68 |
69 | Another situation where the low level parsing functions can be useful are
70 | custom WSGI frameworks, unit-testing or modernizing an old CGI/mod_python
71 | application to WSGI as well as WSGI middlewares where you want to keep the
72 | overhead low.
73 |
--------------------------------------------------------------------------------
/docs/license.rst:
--------------------------------------------------------------------------------
1 | BSD-3-Clause License
2 | ====================
3 |
4 | .. literalinclude:: ../LICENSE.txt
5 | :language: text
6 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/middleware/dispatcher.rst:
--------------------------------------------------------------------------------
1 | .. automodule:: werkzeug.middleware.dispatcher
2 |
--------------------------------------------------------------------------------
/docs/middleware/http_proxy.rst:
--------------------------------------------------------------------------------
1 | .. automodule:: werkzeug.middleware.http_proxy
2 |
--------------------------------------------------------------------------------
/docs/middleware/index.rst:
--------------------------------------------------------------------------------
1 | Middleware
2 | ==========
3 |
4 | A WSGI middleware is a WSGI application that wraps another application
5 | in order to observe or change its behavior. Werkzeug provides some
6 | middleware for common use cases.
7 |
8 | .. toctree::
9 | :maxdepth: 1
10 |
11 | proxy_fix
12 | shared_data
13 | dispatcher
14 | http_proxy
15 | lint
16 | profiler
17 |
18 | The :doc:`interactive debugger ` is also a middleware that can
19 | be applied manually, although it is typically used automatically with
20 | the :doc:`development server `.
21 |
--------------------------------------------------------------------------------
/docs/middleware/lint.rst:
--------------------------------------------------------------------------------
1 | .. automodule:: werkzeug.middleware.lint
2 |
--------------------------------------------------------------------------------
/docs/middleware/profiler.rst:
--------------------------------------------------------------------------------
1 | .. automodule:: werkzeug.middleware.profiler
2 |
--------------------------------------------------------------------------------
/docs/middleware/proxy_fix.rst:
--------------------------------------------------------------------------------
1 | .. automodule:: werkzeug.middleware.proxy_fix
2 |
--------------------------------------------------------------------------------
/docs/middleware/shared_data.rst:
--------------------------------------------------------------------------------
1 | .. automodule:: werkzeug.middleware.shared_data
2 |
--------------------------------------------------------------------------------
/docs/terms.rst:
--------------------------------------------------------------------------------
1 | ===============
2 | Important Terms
3 | ===============
4 |
5 | .. currentmodule:: werkzeug
6 |
7 | This page covers important terms used in the documentation and Werkzeug
8 | itself.
9 |
10 |
11 | WSGI
12 | ----
13 |
14 | WSGI a specification for Python web applications Werkzeug follows. It was
15 | specified in the :pep:`3333` and is widely supported. Unlike previous solutions
16 | it guarantees that web applications, servers and utilities can work together.
17 |
18 | Response Object
19 | ---------------
20 |
21 | For Werkzeug, a response object is an object that works like a WSGI
22 | application but does not do any request processing. Usually you have a view
23 | function or controller method that processes the request and assembles a
24 | response object.
25 |
26 | A response object is *not* necessarily the :class:`Response` class or a
27 | subclass thereof.
28 |
29 | For example Pylons/webob provide a very similar response class that can
30 | be used as well (:class:`webob.Response`).
31 |
32 | View Function
33 | -------------
34 |
35 | Often people speak of MVC (Model, View, Controller) when developing web
36 | applications. However, the Django framework coined MTV (Model, Template,
37 | View) which basically means the same but reduces the concept to the data
38 | model, a function that processes data from the request and the database and
39 | renders a template.
40 |
41 | Werkzeug itself does not tell you how you should develop applications, but the
42 | documentation often speaks of view functions that work roughly the same. The
43 | idea of a view function is that it's called with a request object (and
44 | optionally some parameters from an URL rule) and returns a response object.
45 |
--------------------------------------------------------------------------------
/docs/test.rst:
--------------------------------------------------------------------------------
1 | .. module:: werkzeug.test
2 |
3 | Testing WSGI Applications
4 | =========================
5 |
6 |
7 | Test Client
8 | -----------
9 |
10 | Werkzeug provides a :class:`Client` to simulate requests to a WSGI
11 | application without starting a server. The client has methods for making
12 | different types of requests, as well as managing cookies across
13 | requests.
14 |
15 | >>> from werkzeug.test import Client
16 | >>> from werkzeug.testapp import test_app
17 | >>> c = Client(test_app)
18 | >>> response = c.get("/")
19 | >>> response.status_code
20 | 200
21 | >>> response.headers
22 | Headers([('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '5211')])
23 | >>> response.get_data(as_text=True)
24 | '...'
25 |
26 | The client's request methods return instances of :class:`TestResponse`.
27 | This provides extra attributes and methods on top of
28 | :class:`~werkzeug.wrappers.Response` that are useful for testing.
29 |
30 |
31 | Request Body
32 | ------------
33 |
34 | By passing a dict to ``data``, the client will construct a request body
35 | with file and form data. It will set the content type to
36 | ``application/x-www-form-urlencoded`` if there are no files, or
37 | ``multipart/form-data`` there are.
38 |
39 | .. code-block:: python
40 |
41 | import io
42 |
43 | response = client.post(data={
44 | "name": "test",
45 | "file": (BytesIO("file contents".encode("utf8")), "test.txt")
46 | })
47 |
48 | Pass a string, bytes, or file-like object to ``data`` to use that as the
49 | raw request body. In that case, you should set the content type
50 | appropriately. For example, to post YAML:
51 |
52 | .. code-block:: python
53 |
54 | response = client.post(
55 | data="a: value\nb: 1\n", content_type="application/yaml"
56 | )
57 |
58 | A shortcut when testing JSON APIs is to pass a dict to ``json`` instead
59 | of using ``data``. This will automatically call ``json.dumps()`` and
60 | set the content type to ``application/json``. Additionally, if the
61 | app returns JSON, ``response.json`` will automatically call
62 | ``json.loads()``.
63 |
64 | .. code-block:: python
65 |
66 | response = client.post("/api", json={"a": "value", "b": 1})
67 | obj = response.json()
68 |
69 |
70 | Environment Builder
71 | -------------------
72 |
73 | :class:`EnvironBuilder` is used to construct a WSGI environ dict. The
74 | test client uses this internally to prepare its requests. The arguments
75 | passed to the client request methods are the same as the builder.
76 |
77 | Sometimes, it can be useful to construct a WSGI environment manually.
78 | An environ builder or dict can be passed to the test client request
79 | methods in place of other arguments to use a custom environ.
80 |
81 | .. code-block:: Python
82 |
83 | from werkzeug.test import EnvironBuilder
84 | builder = EnvironBuilder(...)
85 | # build an environ dict
86 | environ = builder.get_environ()
87 | # build an environ dict wrapped in a request
88 | request = builder.get_request()
89 |
90 | The test client responses make this available through
91 | :attr:`TestResponse.request` and ``response.request.environ``.
92 |
93 |
94 | API
95 | ---
96 |
97 | .. autoclass:: Client
98 | :members:
99 | :member-order: bysource
100 |
101 | .. autoclass:: TestResponse
102 | :members:
103 | :member-order: bysource
104 |
105 | .. autoclass:: Cookie
106 | :members:
107 | :member-order: bysource
108 |
109 | .. autoclass:: EnvironBuilder
110 | :members:
111 | :member-order: bysource
112 |
113 | .. autofunction:: create_environ
114 |
115 | .. autofunction:: run_wsgi_app
116 |
--------------------------------------------------------------------------------
/docs/urls.rst:
--------------------------------------------------------------------------------
1 | ===========
2 | URL Helpers
3 | ===========
4 |
5 | .. automodule:: werkzeug.urls
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/utils.rst:
--------------------------------------------------------------------------------
1 | =========
2 | Utilities
3 | =========
4 |
5 | Various utility functions shipped with Werkzeug.
6 |
7 | .. module:: werkzeug.utils
8 |
9 |
10 | General Helpers
11 | ===============
12 |
13 | .. autoclass:: cached_property
14 | :members:
15 |
16 | .. autoclass:: environ_property
17 |
18 | .. autoclass:: header_property
19 |
20 | .. autofunction:: redirect
21 |
22 | .. autofunction:: append_slash_redirect
23 |
24 | .. autofunction:: send_file
25 |
26 | .. autofunction:: send_from_directory
27 |
28 | .. autofunction:: import_string
29 |
30 | .. autofunction:: find_modules
31 |
32 | .. autofunction:: secure_filename
33 |
34 |
35 | URL Helpers
36 | ===========
37 |
38 | Please refer to :doc:`urls`.
39 |
40 |
41 | User Agent API
42 | ==============
43 |
44 | .. module:: werkzeug.user_agent
45 |
46 | .. autoclass:: UserAgent
47 | :members:
48 | :member-order: bysource
49 |
50 |
51 | Security Helpers
52 | ================
53 |
54 | .. module:: werkzeug.security
55 |
56 | .. autofunction:: generate_password_hash
57 |
58 | .. autofunction:: check_password_hash
59 |
60 | .. autofunction:: safe_join
61 |
62 |
63 | Logging
64 | =======
65 |
66 | Werkzeug uses standard Python :mod:`logging`. The logger is named
67 | ``"werkzeug"``.
68 |
69 | .. code-block:: python
70 |
71 | import logging
72 | logger = logging.getLogger("werkzeug")
73 |
74 | If the logger level is not set, it will be set to :data:`~logging.INFO`
75 | on first use. If there is no handler for that level, a
76 | :class:`~logging.StreamHandler` is added.
77 |
--------------------------------------------------------------------------------
/docs/wrappers.rst:
--------------------------------------------------------------------------------
1 | ==========================
2 | Request / Response Objects
3 | ==========================
4 |
5 | .. module:: werkzeug.wrappers
6 |
7 | The request and response objects wrap the WSGI environment or the return
8 | value from a WSGI application so that it is another WSGI application
9 | (wraps a whole application).
10 |
11 | How they Work
12 | =============
13 |
14 | Your WSGI application is always passed two arguments. The WSGI "environment"
15 | and the WSGI `start_response` function that is used to start the response
16 | phase. The :class:`Request` class wraps the `environ` for easier access to
17 | request variables (form data, request headers etc.).
18 |
19 | The :class:`Response` on the other hand is a standard WSGI application that
20 | you can create. The simple hello world in Werkzeug looks like this::
21 |
22 | from werkzeug.wrappers import Response
23 | application = Response('Hello World!')
24 |
25 | To make it more useful you can replace it with a function and do some
26 | processing::
27 |
28 | from werkzeug.wrappers import Request, Response
29 |
30 | def application(environ, start_response):
31 | request = Request(environ)
32 | response = Response(f"Hello {request.args.get('name', 'World!')}!")
33 | return response(environ, start_response)
34 |
35 | Because this is a very common task the :class:`~Request` object provides
36 | a helper for that. The above code can be rewritten like this::
37 |
38 | from werkzeug.wrappers import Request, Response
39 |
40 | @Request.application
41 | def application(request):
42 | return Response(f"Hello {request.args.get('name', 'World!')}!")
43 |
44 | The `application` is still a valid WSGI application that accepts the
45 | environment and `start_response` callable.
46 |
47 |
48 | Mutability and Reusability of Wrappers
49 | ======================================
50 |
51 | The implementation of the Werkzeug request and response objects are trying
52 | to guard you from common pitfalls by disallowing certain things as much as
53 | possible. This serves two purposes: high performance and avoiding of
54 | pitfalls.
55 |
56 | For the request object the following rules apply:
57 |
58 | 1. The request object is immutable. Modifications are not supported by
59 | default, you may however replace the immutable attributes with mutable
60 | attributes if you need to modify it.
61 | 2. The request object may be shared in the same thread, but is not thread
62 | safe itself. If you need to access it from multiple threads, use
63 | locks around calls.
64 | 3. It's not possible to pickle the request object.
65 |
66 | For the response object the following rules apply:
67 |
68 | 1. The response object is mutable
69 | 2. The response object can be pickled or copied after `freeze()` was
70 | called.
71 | 3. Since Werkzeug 0.6 it's safe to use the same response object for
72 | multiple WSGI responses.
73 | 4. It's possible to create copies using `copy.deepcopy`.
74 |
75 |
76 | Wrapper Classes
77 | ===============
78 |
79 | .. autoclass:: Request
80 | :members:
81 | :inherited-members:
82 |
83 | .. automethod:: _get_file_stream
84 |
85 |
86 | .. autoclass:: Response
87 | :members:
88 | :inherited-members:
89 |
90 | .. automethod:: __call__
91 |
92 | .. automethod:: _ensure_sequence
93 |
--------------------------------------------------------------------------------
/examples/README.rst:
--------------------------------------------------------------------------------
1 | =================
2 | Werkzeug Examples
3 | =================
4 |
5 | This directory contains various example applications and example code of
6 | Werkzeug powered applications.
7 |
8 | Beside the proof of concept applications and code snippets in the partial
9 | folder they all have external dependencies for template engines or database
10 | adapters (SQLAlchemy only so far). Also, every application has click as
11 | external dependency, used to create the command line interface.
12 |
13 |
14 | Full Example Applications
15 | =========================
16 |
17 | The following example applications are application types you would actually
18 | find in real life :-)
19 |
20 |
21 | `simplewiki`
22 |
23 | A simple Wiki implementation.
24 |
25 | Requirements:
26 |
27 | - SQLAlchemy
28 | - Creoleparser >= 0.7
29 | - genshi
30 |
31 | You can obtain all packages in the Cheeseshop via easy_install. You have
32 | to have at least version 0.7 of Creoleparser.
33 |
34 | Usage::
35 |
36 | ./manage-simplewiki.py initdb
37 | ./manage-simplewiki.py runserver
38 |
39 | Or of course you can just use the application object
40 | (`simplewiki.SimpleWiki`) and hook that into your favourite WSGI gateway.
41 | The constructor of the application object takes a single argument which is
42 | the SQLAlchemy URI for the database.
43 |
44 | The management script for the devserver looks up the an environment var
45 | called `SIMPLEWIKI_DATABASE_URI` and uses that for the database URI. If
46 | no such variable is provided "sqlite:////tmp/simplewiki.db" is assumed.
47 |
48 | `plnt`
49 |
50 | A planet called plnt, pronounce plant.
51 |
52 | Requirements:
53 |
54 | - SQLAlchemy
55 | - Jinja2
56 | - feedparser
57 |
58 | You can obtain all packages in the Cheeseshop via easy_install.
59 |
60 | Usage::
61 |
62 | ./manage-plnt.py initdb
63 | ./manage-plnt.py sync
64 | ./manage-plnt.py runserver
65 |
66 | The WSGI application is called `plnt.Plnt` which, like the simple wiki,
67 | accepts a database URI as first argument. The environment variable for
68 | the database key is called `PLNT_DATABASE_URI` and the default is
69 | "sqlite:////tmp/plnt.db".
70 |
71 | Per default a few python related blogs are added to the database, you
72 | can add more in a python shell by playing with the `Blog` model.
73 |
74 | `shorty`
75 |
76 | A tinyurl clone for the Werkzeug tutorial.
77 |
78 | Requirements:
79 |
80 | - SQLAlchemy
81 | - Jinja2
82 |
83 | You can obtain all packages in the Cheeseshop via easy_install.
84 |
85 | Usage::
86 |
87 | ./manage-shorty.py initdb
88 | ./manage-shorty.py runserver
89 |
90 | The WSGI application is called `shorty.application.Shorty` which, like the
91 | simple wiki, accepts a database URI as first argument.
92 |
93 | The source code of the application is explained in detail in the Werkzeug
94 | tutorial.
95 |
96 | `couchy`
97 |
98 | Like shorty, but implemented using CouchDB.
99 |
100 | Requirements :
101 |
102 | - werkzeug : http://werkzeug.pocoo.org
103 | - jinja : http://jinja.pocoo.org
104 | - couchdb 0.72 & above : https://couchdb.apache.org/
105 |
106 | `cupoftee`
107 |
108 | A `Teeworlds `_ server browser. This application
109 | works best in a non forking environment and won't work for CGI.
110 |
111 | Usage::
112 |
113 | ./manage-cupoftee.py runserver
114 |
--------------------------------------------------------------------------------
/examples/coolmagic/__init__.py:
--------------------------------------------------------------------------------
1 | from .application import make_app
2 |
--------------------------------------------------------------------------------
/examples/coolmagic/application.py:
--------------------------------------------------------------------------------
1 | """This module provides the WSGI application.
2 |
3 | The WSGI middlewares are applied in the `make_app` factory function that
4 | automatically wraps the application within the require middlewares. Per
5 | default only the `SharedDataMiddleware` is applied.
6 | """
7 | from os import listdir
8 | from os import path
9 |
10 | from werkzeug.exceptions import HTTPException
11 | from werkzeug.exceptions import NotFound
12 | from werkzeug.middleware.shared_data import SharedDataMiddleware
13 | from werkzeug.routing import Map
14 | from werkzeug.routing import RequestRedirect
15 | from werkzeug.routing import Rule
16 |
17 | from .utils import local_manager
18 | from .utils import Request
19 |
20 |
21 | class CoolMagicApplication:
22 | """
23 | The application class. It's passed a directory with configuration values.
24 | """
25 |
26 | def __init__(self, config):
27 | self.config = config
28 |
29 | for fn in listdir(path.join(path.dirname(__file__), "views")):
30 | if fn.endswith(".py") and fn != "__init__.py":
31 | __import__(f"coolmagic.views.{fn[:-3]}")
32 |
33 | from coolmagic.utils import exported_views
34 |
35 | rules = [
36 | # url for shared data. this will always be unmatched
37 | # because either the middleware or the webserver
38 | # handles that request first.
39 | Rule("/public/", endpoint="shared_data")
40 | ]
41 | self.views = {}
42 | for endpoint, (func, rule, extra) in exported_views.items():
43 | if rule is not None:
44 | rules.append(Rule(rule, endpoint=endpoint, **extra))
45 | self.views[endpoint] = func
46 | self.url_map = Map(rules)
47 |
48 | def __call__(self, environ, start_response):
49 | urls = self.url_map.bind_to_environ(environ)
50 | req = Request(environ, urls)
51 | try:
52 | endpoint, args = urls.match(req.path)
53 | resp = self.views[endpoint](**args)
54 | except NotFound:
55 | resp = self.views["static.not_found"]()
56 | except (HTTPException, RequestRedirect) as e:
57 | resp = e
58 | return resp(environ, start_response)
59 |
60 |
61 | def make_app(config=None):
62 | """
63 | Factory function that creates a new `CoolmagicApplication`
64 | object. Optional WSGI middlewares should be applied here.
65 | """
66 | config = config or {}
67 | app = CoolMagicApplication(config)
68 |
69 | # static stuff
70 | app = SharedDataMiddleware(
71 | app, {"/public": path.join(path.dirname(__file__), "public")}
72 | )
73 |
74 | # clean up locals
75 | app = local_manager.make_middleware(app)
76 |
77 | return app
78 |
--------------------------------------------------------------------------------
/examples/coolmagic/helpers.py:
--------------------------------------------------------------------------------
1 | from .utils import ThreadedRequest
2 |
3 | #: a thread local proxy request object
4 | request = ThreadedRequest()
5 | del ThreadedRequest
6 |
--------------------------------------------------------------------------------
/examples/coolmagic/public/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 20px;
4 | font-family: sans-serif;
5 | font-size: 15px;
6 | }
7 |
8 | h1, a {
9 | color: #a00;
10 | }
11 |
--------------------------------------------------------------------------------
/examples/coolmagic/templates/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ page_title }} — Cool Magic!
5 |
6 |
7 |
8 |
5 | The requested page does not exist on this server. If you expect something
6 | here (for example a server) it probably went away after the last update.
7 |
5 | Currently {{ len(players) }} players are playing on
6 | {{ len(servers) }} servers.
7 | {% if cup.server_browser.last_sync %}
8 | This list was last synced on
9 | {{ cup.server_browser.last_sync.strftime('%d %B %Y at %H:%M UTC') }}.
10 | {% else %}
11 | Synchronization with main server in progress. Reload the page in a minute
12 | or two, to see the server list.
13 | {% endif %}
14 |
39 | The following map represents the users playing currently. The bigger their name
40 | the higher their score in the current game. Clicking on the name takes you to
41 | the detail page of the server for some more information.
42 |
43 |
44 | {% for player in players %}
45 | {{ player.name }}
47 | {% endfor %}
48 |
49 |
Find User
50 |
51 | Find a user by username. The result page contains a link you can bookmark to
52 | find your buddy easily. Because currently there is no central user database
53 | users can appear on multiple servers for too generic usernames (like the
54 | default "nameless tee" user).
55 |
6 | Plnt is a small example application written using the
7 | Werkzeug WSGI toolkit,
8 | the Jinja template language,
9 | the SQLAlchemy database abstraction
10 | layer and ORM and last but not least the awesome
11 | feedparser library.
12 |
13 |
14 | It's one of the example applications developed to show some of the
15 | features werkzeug provides and could be the base of a real planet
16 | software.
17 |
18 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/examples/plnt/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block body %}
3 | {% for day in days %}
4 |
I am sorry, but no such page was found here.
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/examples/shortly/templates/layout.html:
--------------------------------------------------------------------------------
1 |
2 |