     Flask 2.1.2, Jun 11, 2022

     Pallets

     Copyright © 2010 Pallets

   Generated by Sphinx 4.5.0.

Flask
*****

     Flask 2.1.2, Jun 11, 2022

     Pallets

     Copyright © 2010 Pallets

Welcome to Flask’s documentation.  Get started with Installation
and then get an overview with the Quickstart.  There is also
a more detailed Tutorial that shows how to create a small but
complete application with Flask.  Common patterns are described in the
Patterns for Flask section.  The rest of the docs describe
each component of Flask in detail, with a full reference in the
API section.

Flask depends on the Jinja(1) template engine and the Werkzeug(2) WSGI
toolkit.  The documentation for these libraries can be found at:

   - Jinja documentation(3)

   - Werkzeug documentation(4)

 — The Detailed Node Listing —

User’s Guide

Foreword

Foreword for Experienced Programmers

Installation

Dependencies

Virtual environments

Quickstart

What to do if the Server does not Start

Routing

Accessing Request Data

About Responses

Tutorial

Application Setup

Define and Access the Database

Blueprints and Views

Templates

Blog Blueprint

Make the Project Installable

Test Coverage

Deploy to Production

Templates

Testing Flask Applications

Sending Requests with the Test Client

Handling Application Errors

Error Handlers

Custom Error Pages

Debugging Application Errors

Logging

Basic Configuration

Other Libraries

Configuration Handling

Signals

Pluggable Views

The Application Context

The Request Context

Callbacks and Errors

Modular Applications with Blueprints

Blueprint Resources

Extensions

Command Line Interface

Environments

Environment Variables From dotenv

Custom Commands

Development Server

Command Line

Working with the Shell

Patterns for Flask

Large Applications as Packages

Application Factories

Application Dispatching

Using URL Processors

Deploying with Setuptools

Deploying with Fabric

Using SQLite 3 with Flask

SQLAlchemy in Flask

Uploading Files

View Decorators

Form Validation with WTForms

Template Inheritance

Message Flashing

AJAX with jQuery

Lazily Loading Views

MongoDB with MongoEngine

Adding a favicon

Streaming Contents

Celery Background Tasks

Deployment Options

Self-hosted options

Standalone WSGI Servers

Gunicorn

uWSGI

uWSGI

mod_wsgi (Apache)

FastCGI

CGI

Becoming Big

Using async and await

API Reference

API

JSON Support

Additional Notes

Design Decisions in Flask

HTML/XHTML FAQ

Security Considerations

Security Headers

Flask Extension Development

“Hello Flaskext!”

How to contribute to Flask

Submitting patches

License

Changes

   ---------- Footnotes ----------

   (1) https://www.palletsprojects.com/p/jinja/

   (2) https://www.palletsprojects.com/p/werkzeug/

   (3) https://jinja.palletsprojects.com/

   (4) https://werkzeug.palletsprojects.com/

1 User’s Guide
**************

This part of the documentation, which is mostly prose, begins with some
background information about Flask, then focuses on step-by-step
instructions for web development with Flask.

1.1 Foreword
============

Read this before you get started with Flask.  This hopefully answers
some questions about the purpose and goals of the project, and when you
should or should not be using it.

1.1.1 What does “micro” mean?
-----------------------------

“Micro” does not mean that your whole web application has to fit into a
single Python file (although it certainly can), nor does it mean that
Flask is lacking in functionality.  The “micro” in microframework means
Flask aims to keep the core simple but extensible.  Flask won’t make
many decisions for you, such as what database to use.  Those decisions
that it does make, such as what templating engine to use, are easy to
change.  Everything else is up to you, so that Flask can be everything
you need and nothing you don’t.

By default, Flask does not include a database abstraction layer, form
validation or anything else where different libraries already exist that
can handle that.  Instead, Flask supports extensions to add such
functionality to your application as if it was implemented in Flask
itself.  Numerous extensions provide database integration, form
validation, upload handling, various open authentication technologies,
and more.  Flask may be “micro”, but it’s ready for production use on a
variety of needs.

1.1.2 Configuration and Conventions
-----------------------------------

Flask has many configuration values, with sensible defaults, and a few
conventions when getting started.  By convention, templates and static
files are stored in subdirectories within the application’s Python
source tree, with the names ‘templates’ and ‘static’ respectively.
While this can be changed, you usually don’t have to, especially when
getting started.

1.1.3 Growing with Flask
------------------------

Once you have Flask up and running, you’ll find a variety of extensions
available in the community to integrate your project for production.

As your codebase grows, you are free to make the design decisions
appropriate for your project.  Flask will continue to provide a very
simple glue layer to the best that Python has to offer.  You can
implement advanced patterns in SQLAlchemy or another database tool,
introduce non-relational data persistence as appropriate, and take
advantage of framework-agnostic tools built for WSGI, the Python web
interface.

Flask includes many hooks to customize its behavior.  Should you need
more customization, the Flask class is built for subclassing.  If you
are interested in that, check out the Becoming Big chapter.
If you are curious about the Flask design principles, head over to the
section about Design Decisions in Flask.

1.2 Foreword for Experienced Programmers
========================================

1.2.1 Thread-Locals in Flask
----------------------------

One of the design decisions in Flask was that simple tasks should be
simple; they should not take a lot of code and yet they should not limit
you.  Because of that, Flask has a few design choices that some people
might find surprising or unorthodox.  For example, Flask uses
thread-local objects internally so that you don’t have to pass objects
around from function to function within a request in order to stay
threadsafe.  This approach is convenient, but requires a valid request
context for dependency injection or when attempting to reuse code which
uses a value pegged to the request.  The Flask project is honest about
thread-locals, does not hide them, and calls out in the code and
documentation where they are used.

1.2.2 Develop for the Web with Caution
--------------------------------------

Always keep security in mind when building web applications.

If you write a web application, you are probably allowing users to
register and leave their data on your server.  The users are entrusting
you with data.  And even if you are the only user that might leave data
in your application, you still want that data to be stored securely.

Unfortunately, there are many ways the security of a web application can
be compromised.  Flask protects you against one of the most common
security problems of modern web applications: cross-site scripting
(XSS). Unless you deliberately mark insecure HTML as secure, Flask and
the underlying Jinja2 template engine have you covered.  But there are
many more ways to cause security problems.

The documentation will warn you about aspects of web development that
require attention to security.  Some of these security concerns are far
more complex than one might think, and we all sometimes underestimate
the likelihood that a vulnerability will be exploited - until a clever
attacker figures out a way to exploit our applications.  And don’t think
that your application is not important enough to attract an attacker.
Depending on the kind of attack, chances are that automated bots are
probing for ways to fill your database with spam, links to malicious
software, and the like.

Flask is no different from any other framework in that you the developer
must build with caution, watching for exploits when building to your
requirements.

1.3 Installation
================

1.3.1 Python Version
--------------------

We recommend using the latest version of Python.  Flask supports Python
3.7 and newer.

1.3.2 Dependencies
------------------

These distributions will be installed automatically when installing
Flask.

   * Werkzeug(1) implements WSGI, the standard Python interface between
     applications and servers.

   * Jinja(2) is a template language that renders the pages your
     application serves.

   * MarkupSafe(3) comes with Jinja.  It escapes untrusted input when
     rendering templates to avoid injection attacks.

   * ItsDangerous(4) securely signs data to ensure its integrity.  This
     is used to protect Flask’s session cookie.

   * Click(5) is a framework for writing command line applications.  It
     provides the ‘flask’ command and allows adding custom management
     commands.

   ---------- Footnotes ----------

   (1) https://palletsprojects.com/p/werkzeug/

   (2) https://palletsprojects.com/p/jinja/

   (3) https://palletsprojects.com/p/markupsafe/

   (4) https://palletsprojects.com/p/itsdangerous/

   (5) https://palletsprojects.com/p/click/

1.3.2.1 Optional dependencies
.............................

These distributions will not be installed automatically.  Flask will
detect and use them if you install them.

   * Blinker(1) provides support for Signals.

   * python-dotenv(2) enables support for Environment Variables From dotenv
     when running ‘flask’ commands.

   * Watchdog(3) provides a faster, more efficient reloader for the
     development server.

   ---------- Footnotes ----------

   (1) https://pythonhosted.org/blinker/

   (2) https://github.com/theskumar/python-dotenv#readme

   (3) https://pythonhosted.org/watchdog/

1.3.2.2 greenlet
................

You may choose to use gevent or eventlet with your application.  In this
case, greenlet>=1.0 is required.  When using PyPy, PyPy>=7.3.7 is
required.

These are not minimum supported versions, they only indicate the first
versions that added necessary features.  You should use the latest
versions of each.

1.3.3 Virtual environments
--------------------------

Use a virtual environment to manage the dependencies for your project,
both in development and in production.

What problem does a virtual environment solve?  The more Python projects
you have, the more likely it is that you need to work with different
versions of Python libraries, or even Python itself.  Newer versions of
libraries for one project can break compatibility in another project.

Virtual environments are independent groups of Python libraries, one for
each project.  Packages installed for one project will not affect other
projects or the operating system’s packages.

Python comes bundled with the venv(1) module to create virtual
environments.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/venv.html#module-venv

1.3.3.1 Create an environment
.............................

Create a project folder and a ‘venv’ folder within:

macOS/Linux

Windows

     $ mkdir myproject
     $ cd myproject
     $ python3 -m venv venv

     > mkdir myproject
     > cd myproject
     > py -3 -m venv venv

1.3.3.2 Activate the environment
................................

Before you work on your project, activate the corresponding environment:

macOS/Linux

Windows

     $ . venv/bin/activate

     > venv\Scripts\activate

Your shell prompt will change to show the name of the activated
environment.

1.3.4 Install Flask
-------------------

Within the activated environment, use the following command to install
Flask:

     $ pip install Flask

Flask is now installed.  Check out the Quickstart or go to the
Documentation Overview.

1.4 Quickstart
==============

Eager to get started?  This page gives a good introduction to Flask.
Follow Installation to set up a project and install Flask
first.

1.4.1 A Minimal Application
---------------------------

A minimal Flask application looks something like this:

     from flask import Flask

     app = Flask(__name__)

     @app.route("/")
     def hello_world():
         return "<p>Hello, World!</p>"

So what did that code do?

  1. First we imported the Flask class.  An instance of this
     class will be our WSGI application.

  2. Next we create an instance of this class.  The first argument is
     the name of the application’s module or package.  ‘__name__’ is a
     convenient shortcut for this that is appropriate for most cases.
     This is needed so that Flask knows where to look for resources such
     as templates and static files.

  3. We then use the route() decorator to tell Flask what URL
     should trigger our function.

  4. The function returns the message we want to display in the user’s
     browser.  The default content type is HTML, so HTML in the string
     will be rendered by the browser.

Save it as ‘hello.py’ or something similar.  Make sure to not call your
application ‘flask.py’ because this would conflict with Flask itself.

To run the application, use the ‘flask’ command or ‘python -m flask’.
Before you can do that you need to tell your terminal the application to
work with by exporting the ‘FLASK_APP’ environment variable:

Bash

Fish

CMD

Powershell

     $ export FLASK_APP=hello
     $ flask run
      * Running on http://127.0.0.1:5000/

     $ set -x FLASK_APP hello
     $ flask run
      * Running on http://127.0.0.1:5000/

     > set FLASK_APP=hello
     > flask run
      * Running on http://127.0.0.1:5000/

     > $env:FLASK_APP = "hello"
     > flask run
      * Running on http://127.0.0.1:5000/

     Application Discovery Behavior: As a shortcut, if the file is named
     ‘app.py’ or ‘wsgi.py’, you don’t have to set the ‘FLASK_APP’
     environment variable.  See Command Line Interface for
     more details.

This launches a very simple builtin server, which is good enough for
testing but probably not what you want to use in production.  For
deployment options see Deployment Options.

Now head over to ‘http://127.0.0.1:5000/’, and you should see your hello
world greeting.

If another program is already using port 5000, you’ll see ‘OSError:
[Errno 98]’ or ‘OSError: [WinError 10013]’ when the server tries to
start.  See Address already in use for how to handle that.
     Externally Visible Server: If you run the server you will notice
     that the server is only accessible from your own computer, not from
     any other in the network.  This is the default because in debugging
     mode a user of the application can execute arbitrary Python code on
     your computer.

     If you have the debugger disabled or trust the users on your
     network, you can make the server publicly available simply by
     adding ‘--host=0.0.0.0’ to the command line:

          $ flask run --host=0.0.0.0

     This tells your operating system to listen on all public IPs.

1.4.2 What to do if the Server does not Start
---------------------------------------------

In case the ‘python -m flask’ fails or ‘flask’ does not exist, there are
multiple reasons this might be the case.  First of all you need to look
at the error message.

1.4.2.1 Old Version of Flask
............................

Versions of Flask older than 0.11 used to have different ways to start
the application.  In short, the ‘flask’ command did not exist, and
neither did ‘python -m flask’.  In that case you have two options:
either upgrade to newer Flask versions or have a look at
Development Server to see the alternative method for running a
server.

1.4.2.2 Invalid Import Name
...........................

The ‘FLASK_APP’ environment variable is the name of the module to import
at ‘flask run’.  In case that module is incorrectly named you will get
an import error upon start (or if debug is enabled when you navigate to
the application).  It will tell you what it tried to import and why it
failed.

The most common reason is a typo or because you did not actually create
an ‘app’ object.

1.4.3 Debug Mode
----------------

The ‘flask run’ command can do more than just start the development
server.  By enabling debug mode, the server will automatically reload if
code changes, and will show an interactive debugger in the browser if an
error occurs during a request.

     Warning: The debugger allows executing arbitrary Python code from
     the browser.  It is protected by a pin, but still represents a
     major security risk.  Do not run the development server or debugger
     in a production environment.

To enable all development features, set the ‘FLASK_ENV’ environment
variable to ‘development’ before calling ‘flask run’.

Bash

Fish

CMD

Powershell

     $ export FLASK_ENV=development
     $ flask run

     $ set -x FLASK_ENV development
     $ flask run

     > set FLASK_ENV=development
     > flask run

     > $env:FLASK_ENV = "development"
     > flask run

See also:

   - Development Server and Command Line Interface.
     for information about running in development mode.

   - Debugging Application Errors for information about using
     the built-in debugger and other debuggers.

   - Logging and Handling Application Errors to
     log errors and display nice error pages.

1.4.4 HTML Escaping
-------------------

When returning HTML (the default response type in Flask), any
user-provided values rendered in the output must be escaped to protect
from injection attacks.  HTML templates rendered with Jinja, introduced
later, will do this automatically.

‘escape()’, shown here, can be used manually.  It is omitted in most
examples for brevity, but you should always be aware of how you’re using
untrusted data.

     from markupsafe import escape

     @app.route("/<name>")
     def hello(name):
         return f"Hello, {escape(name)}!"

If a user managed to submit the name ‘<script>alert("bad")</script>’,
escaping causes it to be rendered as text, rather than running the
script in the user’s browser.

‘<name>’ in the route captures a value from the URL and passes it to the
view function.  These variable rules are explained below.

1.4.5 Routing
-------------

Modern web applications use meaningful URLs to help users.  Users are
more likely to like a page and come back if the page uses a meaningful
URL they can remember and use to directly visit a page.

Use the route() decorator to bind a function to a URL.

     @app.route('/')
     def index():
         return 'Index Page'

     @app.route('/hello')
     def hello():
         return 'Hello, World'

You can do more!  You can make parts of the URL dynamic and attach
multiple rules to a function.

1.4.5.1 Variable Rules
......................

You can add variable sections to a URL by marking sections with
‘<variable_name>’.  Your function then receives the ‘<variable_name>’ as
a keyword argument.  Optionally, you can use a converter to specify the
type of the argument like ‘<converter:variable_name>’.

     from markupsafe import escape

     @app.route('/user/<username>')
     def show_user_profile(username):
         # show the user profile for that user
         return f'User {escape(username)}'

     @app.route('/post/<int:post_id>')
     def show_post(post_id):
         # show the post with the given id, the id is an integer
         return f'Post {post_id}'

     @app.route('/path/<path:subpath>')
     def show_subpath(subpath):
         # show the subpath after /path/
         return f'Subpath {escape(subpath)}'

Converter types:

‘string’       (default) accepts any text without a slash

‘int’          accepts positive integers

‘float’        accepts positive floating point values

‘path’         like ‘string’ but also accepts slashes

‘uuid’         accepts UUID strings

1.4.5.2 Unique URLs / Redirection Behavior
..........................................

The following two rules differ in their use of a trailing slash.

     @app.route('/projects/')
     def projects():
         return 'The project page'

     @app.route('/about')
     def about():
         return 'The about page'

The canonical URL for the ‘projects’ endpoint has a trailing slash.
It’s similar to a folder in a file system.  If you access the URL
without a trailing slash (‘/projects’), Flask redirects you to the
canonical URL with the trailing slash (‘/projects/’).

The canonical URL for the ‘about’ endpoint does not have a trailing
slash.  It’s similar to the pathname of a file.  Accessing the URL with
a trailing slash (‘/about/’) produces a 404 “Not Found” error.  This
helps keep URLs unique for these resources, which helps search engines
avoid indexing the same page twice.

1.4.5.3 URL Building
....................

To build a URL to a specific function, use the url_for().
function.  It accepts the name of the function as its first argument and
any number of keyword arguments, each corresponding to a variable part
of the URL rule.  Unknown variable parts are appended to the URL as
query parameters.

Why would you want to build URLs using the URL reversing function
url_for() instead of hard-coding them into your templates?

  1. Reversing is often more descriptive than hard-coding the URLs.

  2. You can change your URLs in one go instead of needing to remember
     to manually change hard-coded URLs.

  3. URL building handles escaping of special characters transparently.

  4. The generated paths are always absolute, avoiding unexpected
     behavior of relative paths in browsers.

  5. If your application is placed outside the URL root, for example, in
     ‘/myapplication’ instead of ‘/’, see url_for() properly
     handles that for you.

For example, here we use the test_request_context() method to
try out url_for().  test_request_context() tells
Flask to behave as though it’s handling a request even while we use a
Python shell.  See Context Locals.

     from flask import url_for

     @app.route('/')
     def index():
         return 'index'

     @app.route('/login')
     def login():
         return 'login'

     @app.route('/user/<username>')
     def profile(username):
         return f'{username}\'s profile'

     with app.test_request_context():
         print(url_for('index'))
         print(url_for('login'))
         print(url_for('login', next='/'))
         print(url_for('profile', username='John Doe'))

     /
     /login
     /login?next=/
     /user/John%20Doe

1.4.5.4 HTTP Methods
....................

Web applications use different HTTP methods when accessing URLs.  You
should familiarize yourself with the HTTP methods as you work with
Flask.  By default, a route only answers to ‘GET’ requests.  You can use
the ‘methods’ argument of the route() decorator to handle
different HTTP methods.

     from flask import request

     @app.route('/login', methods=['GET', 'POST'])
     def login():
         if request.method == 'POST':
             return do_the_login()
         else:
             return show_the_login_form()

If ‘GET’ is present, Flask automatically adds support for the ‘HEAD’
method and handles ‘HEAD’ requests according to the HTTP RFC(1).
Likewise, ‘OPTIONS’ is automatically implemented for you.

   ---------- Footnotes ----------

   (1) https://www.ietf.org/rfc/rfc2068.txt

1.4.6 Static Files
------------------

Dynamic web applications also need static files.  That’s usually where
the CSS and JavaScript files are coming from.  Ideally your web server
is configured to serve them for you, but during development Flask can do
that as well.  Just create a folder called ‘static’ in your package or
next to your module and it will be available at ‘/static’ on the
application.

To generate URLs for static files, use the special ‘'static'’ endpoint
name:

     url_for('static', filename='style.css')

The file has to be stored on the filesystem as ‘static/style.css’.

1.4.7 Rendering Templates
-------------------------

Generating HTML from within Python is not fun, and actually pretty
cumbersome because you have to do the HTML escaping on your own to keep
the application secure.  Because of that Flask configures the Jinja2(1)
template engine for you automatically.

To render a template you can use the render_template().
method.  All you have to do is provide the name of the template and the
variables you want to pass to the template engine as keyword arguments.
Here’s a simple example of how to render a template:

     from flask import render_template

     @app.route('/hello/')
     @app.route('/hello/<name>')
     def hello(name=None):
         return render_template('hello.html', name=name)

Flask will look for templates in the ‘templates’ folder.  So if your
application is a module, this folder is next to that module, if it’s a
package it’s actually inside your package:

`Case 1': a module:

     /application.py
     /templates
         /hello.html

`Case 2': a package:

     /application
         /__init__.py
         /templates
             /hello.html

For templates you can use the full power of Jinja2 templates.  Head over
to the official Jinja2 Template Documentation(2) for more information.

Here is an example template:

     <!doctype html>
     <title>Hello from Flask</title>
     {% if name %}
       <h1>Hello {{ name }}!</h1>
     {% else %}
       <h1>Hello, World!</h1>
     {% endif %}

Inside templates you also have access to the config,
request, session and g. (3) objects as well as
the url_for() and get_flashed_messages().
functions.

Templates are especially useful if inheritance is used.  If you want to
know how that works, see Template Inheritance.  Basically
template inheritance makes it possible to keep certain elements on each
page (like header, navigation and footer).

Automatic escaping is enabled, so if ‘name’ contains HTML it will be
escaped automatically.  If you can trust a variable and you know that it
will be safe HTML (for example because it came from a module that
converts wiki markup to HTML) you can mark it as safe by using the
Markup class or by using the ‘|safe’ filter in the template.  Head
over to the Jinja 2 documentation for more examples.

Here is a basic introduction to how the Markup class works:

     >>> from markupsafe import Markup
     >>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
     Markup('<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
     >>> Markup.escape('<blink>hacker</blink>')
     Markup('&lt;blink&gt;hacker&lt;/blink&gt;')
     >>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
     'Marked up » HTML'

Changed in version 0.5: Autoescaping is no longer enabled for all
templates.  The following extensions for templates trigger autoescaping:
‘.html’, ‘.htm’, ‘.xml’, ‘.xhtml’.  Templates loaded from a string will
have autoescaping disabled.

   ---------- Footnotes ----------

   (1) https://palletsprojects.com/p/jinja/

   (2) https://jinja.palletsprojects.com/templates/

   (3) Unsure what that g object is?  It’s something in which
you can store information for your own needs.  See the documentation for
flask.g and Using SQLite 3 with Flask.

1.4.8 Accessing Request Data
----------------------------

For web applications it’s crucial to react to the data a client sends to
the server.  In Flask this information is provided by the global
request object.  If you have some experience with Python you might
be wondering how that object can be global and how Flask manages to
still be threadsafe.  The answer is context locals:

1.4.8.1 Context Locals
......................

     Insider Information: If you want to understand how that works and
     how you can implement tests with context locals, read this section,
     otherwise just skip it.

Certain objects in Flask are global objects, but not of the usual kind.
These objects are actually proxies to objects that are local to a
specific context.  What a mouthful.  But that is actually quite easy to
understand.

Imagine the context being the handling thread.  A request comes in and
the web server decides to spawn a new thread (or something else, the
underlying object is capable of dealing with concurrency systems other
than threads).  When Flask starts its internal request handling it
figures out that the current thread is the active context and binds the
current application and the WSGI environments to that context (thread).
It does that in an intelligent way so that one application can invoke
another application without breaking.

So what does this mean to you?  Basically you can completely ignore that
this is the case unless you are doing something like unit testing.  You
will notice that code which depends on a request object will suddenly
break because there is no request object.  The solution is creating a
request object yourself and binding it to the context.  The easiest
solution for unit testing is to use the test_request_context()
context manager.  In combination with the ‘with’ statement it will
bind a test request so that you can interact with it.  Here is an
example:

     from flask import request

     with app.test_request_context('/hello', method='POST'):
         # now you can do something with the request until the
         # end of the with block, such as basic assertions:
         assert request.path == '/hello'
         assert request.method == 'POST'

The other possibility is passing a whole WSGI environment to the
request_context() method:

     with app.request_context(environ):
         assert request.method == 'POST'

1.4.8.2 The Request Object
..........................

The request object is documented in the API section and we will not
cover it here in detail (see Request).  Here is a broad
overview of some of the most common operations.  First of all you have
to import it from the ‘flask’ module:

     from flask import request

The current request method is available by using the method.
attribute.  To access form data (data transmitted in a ‘POST’ or ‘PUT’
request) you can use the form attribute.  Here is a full
example of the two attributes mentioned above:

     @app.route('/login', methods=['POST', 'GET'])
     def login():
         error = None
         if request.method == 'POST':
             if valid_login(request.form['username'],
                            request.form['password']):
                 return log_the_user_in(request.form['username'])
             else:
                 error = 'Invalid username/password'
         # the code below is executed if the request method
         # was GET or the credentials were invalid
         return render_template('login.html', error=error)

What happens if the key does not exist in the ‘form’ attribute?  In that
case a special KeyError(1) is raised.  You can catch it like a standard
KeyError(2) but if you don’t do that, a HTTP 400 Bad Request error page
is shown instead.  So for many situations you don’t have to deal with
that problem.

To access parameters submitted in the URL (‘?key=value’) you can use the
args attribute:

     searchword = request.args.get('key', '')

We recommend accessing URL parameters with ‘get’ or by catching the
KeyError(3) because users might change the URL and presenting them a 400
bad request page in that case is not user friendly.

For a full list of methods and attributes of the request object, head
over to the Request documentation.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/exceptions.html#KeyError

   (2) https://docs.python.org/3/library/exceptions.html#KeyError

   (3) https://docs.python.org/3/library/exceptions.html#KeyError

1.4.8.3 File Uploads
....................

You can handle uploaded files with Flask easily.  Just make sure not to
forget to set the ‘enctype="multipart/form-data"’ attribute on your HTML
form, otherwise the browser will not transmit your files at all.

Uploaded files are stored in memory or at a temporary location on the
filesystem.  You can access those files by looking at the ‘files’
attribute on the request object.  Each uploaded file is stored in that
dictionary.  It behaves just like a standard Python ‘file’ object, but
it also has a save()(1) method that allows you to store that file on the
filesystem of the server.  Here is a simple example showing how that
works:

     from flask import request

     @app.route('/upload', methods=['GET', 'POST'])
     def upload_file():
         if request.method == 'POST':
             f = request.files['the_file']
             f.save('/var/www/uploads/uploaded_file.txt')
         ...

If you want to know how the file was named on the client before it was
uploaded to your application, you can access the filename(2) attribute.
However please keep in mind that this value can be forged so never ever
trust that value.  If you want to use the filename of the client to
store the file on the server, pass it through the secure_filename()(3)
function that Werkzeug provides for you:

     from werkzeug.utils import secure_filename

     @app.route('/upload', methods=['GET', 'POST'])
     def upload_file():
         if request.method == 'POST':
             file = request.files['the_file']
             file.save(f"/var/www/uploads/{secure_filename(file.filename)}")
         ...

For some better examples, see Uploading Files.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.FileStorage.save

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.FileStorage.filename

   (3)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.utils.secure_filename

1.4.8.4 Cookies
...............

To access cookies you can use the cookies attribute.  To set
cookies you can use the set_cookie method of response
objects.  The cookies attribute of request objects is a
dictionary with all the cookies the client transmits.  If you want to
use sessions, do not use the cookies directly but instead use the
Sessions in Flask that add some security on top of cookies for you.

Reading cookies:

     from flask import request

     @app.route('/')
     def index():
         username = request.cookies.get('username')
         # use cookies.get(key) instead of cookies[key] to not get a
         # KeyError if the cookie is missing.

Storing cookies:

     from flask import make_response

     @app.route('/')
     def index():
         resp = make_response(render_template(...))
         resp.set_cookie('username', 'the username')
         return resp

Note that cookies are set on response objects.  Since you normally just
return strings from the view functions Flask will convert them into
response objects for you.  If you explicitly want to do that you can use
the make_response() function and then modify it.

Sometimes you might want to set a cookie at a point where the response
object does not exist yet.  This is possible by utilizing the
Deferred Request Callbacks pattern.

For this also see About Responses.

1.4.9 Redirects and Errors
--------------------------

To redirect a user to another endpoint, use the redirect().
function; to abort a request early with an error code, use the
abort() function:

     from flask import abort, redirect, url_for

     @app.route('/')
     def index():
         return redirect(url_for('login'))

     @app.route('/login')
     def login():
         abort(401)
         this_is_never_executed()

This is a rather pointless example because a user will be redirected
from the index to a page they cannot access (401 means access denied)
but it shows how that works.

By default a black and white error page is shown for each error code.
If you want to customize the error page, you can use the
errorhandler() decorator:

     from flask import render_template

     @app.errorhandler(404)
     def page_not_found(error):
         return render_template('page_not_found.html'), 404

Note the ‘404’ after the render_template() call.  This tells
Flask that the status code of that page should be 404 which means not
found.  By default 200 is assumed which translates to: all went well.

See Handling Application Errors for more details.

1.4.10 About Responses
----------------------

The return value from a view function is automatically converted into a
response object for you.  If the return value is a string it’s converted
into a response object with the string as response body, a ‘200 OK’
status code and a ‘text/html’ mimetype.  If the return value is a dict,
‘jsonify()’ is called to produce a response.  The logic that Flask
applies to converting return values into response objects is as follows:

  1. If a response object of the correct type is returned it’s directly
     returned from the view.

  2. If it’s a string, a response object is created with that data and
     the default parameters.

  3. If it’s a dict, a response object is created using ‘jsonify’.

  4. If a tuple is returned the items in the tuple can provide extra
     information.  Such tuples have to be in the form ‘(response,
     status)’, ‘(response, headers)’, or ‘(response, status, headers)’.
     The ‘status’ value will override the status code and ‘headers’ can
     be a list or dictionary of additional header values.

  5. If none of that works, Flask will assume the return value is a
     valid WSGI application and convert that into a response object.

If you want to get hold of the resulting response object inside the view
you can use the make_response() function.

Imagine you have a view like this:

     from flask import render_template

     @app.errorhandler(404)
     def not_found(error):
         return render_template('error.html'), 404

You just need to wrap the return expression with make_response()
and get the response object to modify it, then return it:

     from flask import make_response

     @app.errorhandler(404)
     def not_found(error):
         resp = make_response(render_template('error.html'), 404)
         resp.headers['X-Something'] = 'A value'
         return resp

1.4.10.1 APIs with JSON
.......................

A common response format when writing an API is JSON. It’s easy to get
started writing such an API with Flask.  If you return a ‘dict’ from a
view, it will be converted to a JSON response.

     @app.route("/me")
     def me_api():
         user = get_current_user()
         return {
             "username": user.username,
             "theme": user.theme,
             "image": url_for("user_image", filename=user.image),
         }

Depending on your API design, you may want to create JSON responses for
types other than ‘dict’.  In that case, use the jsonify().
function, which will serialize any supported JSON data type.  Or look
into Flask community extensions that support more complex applications.

     from flask import jsonify

     @app.route("/users")
     def users_api():
         users = get_all_users()
         return jsonify([user.to_json() for user in users])

1.4.11 Sessions
---------------

In addition to the request object there is also a second object called
session which allows you to store information specific to a
user from one request to the next.  This is implemented on top of
cookies for you and signs the cookies cryptographically.  What this
means is that the user could look at the contents of your cookie but not
modify it, unless they know the secret key used for signing.

In order to use sessions you have to set a secret key.  Here is how
sessions work:

     from flask import session

     # Set the secret key to some random bytes. Keep this really secret!
     app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

     @app.route('/')
     def index():
         if 'username' in session:
             return f'Logged in as {session["username"]}'
         return 'You are not logged in'

     @app.route('/login', methods=['GET', 'POST'])
     def login():
         if request.method == 'POST':
             session['username'] = request.form['username']
             return redirect(url_for('index'))
         return '''
             <form method="post">
                 <p><input type=text name=username>
                 <p><input type=submit value=Login>
             </form>
         '''

     @app.route('/logout')
     def logout():
         # remove the username from the session if it's there
         session.pop('username', None)
         return redirect(url_for('index'))

     How to generate good secret keys: A secret key should be as random
     as possible.  Your operating system has ways to generate pretty
     random data based on a cryptographic random generator.  Use the
     following command to quickly generate a value for
     ‘Flask.secret_key’ (or SECRET_KEY):

          $ python -c 'import secrets; print(secrets.token_hex())'
          '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

A note on cookie-based sessions: Flask will take the values you put into
the session object and serialize them into a cookie.  If you are finding
some values do not persist across requests, cookies are indeed enabled,
and you are not getting a clear error message, check the size of the
cookie in your page responses compared to the size supported by web
browsers.

Besides the default client-side based sessions, if you want to handle
sessions on the server-side instead, there are several Flask extensions
that support this.

1.4.12 Message Flashing
-----------------------

Good applications and user interfaces are all about feedback.  If the
user does not get enough feedback they will probably end up hating the
application.  Flask provides a really simple way to give feedback to a
user with the flashing system.  The flashing system basically makes it
possible to record a message at the end of a request and access it on
the next (and only the next) request.  This is usually combined with a
layout template to expose the message.

To flash a message use the flash() method, to get hold of the
messages you can use get_flashed_messages() which is also
available in the templates.  See Message Flashing for a full
example.

1.4.13 Logging
--------------

New in version 0.3.

Sometimes you might be in a situation where you deal with data that
should be correct, but actually is not.  For example you may have some
client-side code that sends an HTTP request to the server but it’s
obviously malformed.  This might be caused by a user tampering with the
data, or the client code failing.  Most of the time it’s okay to reply
with ‘400 Bad Request’ in that situation, but sometimes that won’t do
and the code has to continue working.

You may still want to log that something fishy happened.  This is where
loggers come in handy.  As of Flask 0.3 a logger is preconfigured for
you to use.

Here are some example log calls:

     app.logger.debug('A value for debugging')
     app.logger.warning('A warning occurred (%d apples)', 42)
     app.logger.error('An error occurred')

The attached logger is a standard logging Logger(1), so head
over to the official logging(2) docs for more information.

See Handling Application Errors.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/logging.html#logging.Logger

   (2) https://docs.python.org/3/library/logging.html#module-logging

1.4.14 Hooking in WSGI Middleware
---------------------------------

To add WSGI middleware to your Flask application, wrap the application’s
‘wsgi_app’ attribute.  For example, to apply Werkzeug’s ProxyFix(1)
middleware for running behind Nginx:

     from werkzeug.middleware.proxy_fix import ProxyFix
     app.wsgi_app = ProxyFix(app.wsgi_app)

Wrapping ‘app.wsgi_app’ instead of ‘app’ means that ‘app’ still points
at your Flask application, not at the middleware, so you can continue to
use and configure ‘app’ directly.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/middleware/proxy_fix/#werkzeug.middleware.proxy_fix.ProxyFix

1.4.15 Using Flask Extensions
-----------------------------

Extensions are packages that help you accomplish common tasks.  For
example, Flask-SQLAlchemy provides SQLAlchemy support that makes it
simple and easy to use with Flask.

For more on Flask extensions, see Extensions.

1.4.16 Deploying to a Web Server
--------------------------------

Ready to deploy your new Flask app?  See Deployment Options.

1.5 Tutorial
============

1.5.1 Project Layout
--------------------

Create a project directory and enter it:

     $ mkdir flask-tutorial
     $ cd flask-tutorial

Then follow the installation instructions to set up a Python
virtual environment and install Flask for your project.

The tutorial will assume you’re working from the ‘flask-tutorial’
directory from now on.  The file names at the top of each code block are
relative to this directory.

__________________________________________________________________

A Flask application can be as simple as a single file.

     from flask import Flask

     app = Flask(__name__)

     @app.route('/')
     def hello():
         return 'Hello, World!'

LiteralBlock: ‘hello.py’

However, as a project gets bigger, it becomes overwhelming to keep all
the code in one file.  Python projects use `packages' to organize code
into multiple modules that can be imported where needed, and the
tutorial will do this as well.

The project directory will contain:

   * ‘flaskr/’, a Python package containing your application code and
     files.

   * ‘tests/’, a directory containing test modules.

   * ‘venv/’, a Python virtual environment where Flask and other
     dependencies are installed.

   * Installation files telling Python how to install your project.

   * Version control config, such as git(1).  You should make a habit of
     using some type of version control for all your projects, no matter
     the size.

   * Any other project files you might add in the future.

By the end, your project layout will look like this:

     /home/user/Projects/flask-tutorial
     ├── flaskr/
     │   ├── __init__.py
     │   ├── db.py
     │   ├── schema.sql
     │   ├── auth.py
     │   ├── blog.py
     │   ├── templates/
     │   │   ├── base.html
     │   │   ├── auth/
     │   │   │   ├── login.html
     │   │   │   └── register.html
     │   │   └── blog/
     │   │       ├── create.html
     │   │       ├── index.html
     │   │       └── update.html
     │   └── static/
     │       └── style.css
     ├── tests/
     │   ├── conftest.py
     │   ├── data.sql
     │   ├── test_factory.py
     │   ├── test_db.py
     │   ├── test_auth.py
     │   └── test_blog.py
     ├── venv/
     ├── setup.py
     └── MANIFEST.in

If you’re using version control, the following files that are generated
while running your project should be ignored.  There may be other files
based on the editor you use.  In general, ignore files that you didn’t
write.  For example, with git:

     venv/

     *.pyc
     __pycache__/

     instance/

     .pytest_cache/
     .coverage
     htmlcov/

     dist/
     build/
     *.egg-info/

LiteralBlock: ‘.gitignore’

Continue to Application Setup.

   ---------- Footnotes ----------

   (1) https://git-scm.com/

1.5.2 Application Setup
-----------------------

A Flask application is an instance of the Flask class.
Everything about the application, such as configuration and URLs, will
be registered with this class.

The most straightforward way to create a Flask application is to create
a global Flask instance directly at the top of your code,
like how the “Hello, World!” example did on the previous page.  While
this is simple and useful in some cases, it can cause some tricky issues
as the project grows.

Instead of creating a Flask instance globally, you will
create it inside a function.  This function is known as the `application
factory'.  Any configuration, registration, and other setup the
application needs will happen inside the function, then the application
will be returned.

1.5.2.1 The Application Factory
...............................

It’s time to start coding!  Create the ‘flaskr’ directory and add the
‘__init__.py’ file.  The ‘__init__.py’ serves double duty: it will
contain the application factory, and it tells Python that the ‘flaskr’
directory should be treated as a package.

     $ mkdir flaskr

     import os

     from flask import Flask

     def create_app(test_config=None):
         # create and configure the app
         app = Flask(__name__, instance_relative_config=True)
         app.config.from_mapping(
             SECRET_KEY='dev',
             DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
         )

         if test_config is None:
             # load the instance config, if it exists, when not testing
             app.config.from_pyfile('config.py', silent=True)
         else:
             # load the test config if passed in
             app.config.from_mapping(test_config)

         # ensure the instance folder exists
         try:
             os.makedirs(app.instance_path)
         except OSError:
             pass

         # a simple page that says hello
         @app.route('/hello')
         def hello():
             return 'Hello, World!'

         return app

LiteralBlock: ‘flaskr/__init__.py’

‘create_app’ is the application factory function.  You’ll add to it
later in the tutorial, but it already does a lot.

  1. ‘app = Flask(__name__, instance_relative_config=True)’ creates the
     Flask instance.

        * ‘__name__’ is the name of the current Python module.  The app
          needs to know where it’s located to set up some paths, and
          ‘__name__’ is a convenient way to tell it that.

        * ‘instance_relative_config=True’ tells the app that
          configuration files are relative to the instance folder.
          The instance folder is located outside the ‘flaskr’
          package and can hold local data that shouldn’t be committed to
          version control, such as configuration secrets and the
          database file.

  2. app.config.from_mapping() sets some default
     configuration that the app will use:

        * SECRET_KEY is used by Flask and extensions to keep
          data safe.  It’s set to ‘'dev'’ to provide a convenient value
          during development, but it should be overridden with a random
          value when deploying.

        * ‘DATABASE’ is the path where the SQLite database file will be
          saved.  It’s under app.instance_path, which is the
          path that Flask has chosen for the instance folder.  You’ll
          learn more about the database in the next section.

  3. app.config.from_pyfile() overrides the default
     configuration with values taken from the ‘config.py’ file in the
     instance folder if it exists.  For example, when deploying, this
     can be used to set a real ‘SECRET_KEY’.

        * ‘test_config’ can also be passed to the factory, and will be
          used instead of the instance configuration.  This is so the
          tests you’ll write later in the tutorial can be configured
          independently of any development values you have configured.

  4. os.makedirs()(1) ensures that app.instance_path exists.
     Flask doesn’t create the instance folder automatically, but it
     needs to be created because your project will create the SQLite
     database file there.

  5. @app.route() creates a simple route so you can see the
     application working before getting into the rest of the tutorial.
     It creates a connection between the URL ‘/hello’ and a function
     that returns a response, the string ‘'Hello, World!'’ in this case.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/os.html#os.makedirs

1.5.2.2 Run The Application
...........................

Now you can run your application using the ‘flask’ command.  From the
terminal, tell Flask where to find your application, then run it in
development mode.  Remember, you should still be in the top-level
‘flask-tutorial’ directory, not the ‘flaskr’ package.

Development mode shows an interactive debugger whenever a page raises an
exception, and restarts the server whenever you make changes to the
code.  You can leave it running and just reload the browser page as you
follow the tutorial.

Bash

Fish

CMD

Powershell

     $ export FLASK_APP=flaskr
     $ export FLASK_ENV=development
     $ flask run

     $ set -x FLASK_APP flaskr
     $ set -x FLASK_ENV development
     $ flask run

     > set FLASK_APP=flaskr
     > set FLASK_ENV=development
     > flask run

     > $env:FLASK_APP = "flaskr"
     > $env:FLASK_ENV = "development"
     > flask run

You’ll see output similar to this:

     * Serving Flask app "flaskr"
     * Environment: development
     * Debug mode: on
     * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 855-212-761

Visit ‘http://127.0.0.1:5000/hello’ in a browser and you should see the
“Hello, World!” message.  Congratulations, you’re now running your Flask
web application!

If another program is already using port 5000, you’ll see ‘OSError:
[Errno 98]’ or ‘OSError: [WinError 10013]’ when the server tries to
start.  See Address already in use for how to handle that.

Continue to Define and Access the Database.

1.5.3 Define and Access the Database
------------------------------------

The application will use a SQLite(1) database to store users and posts.
Python comes with built-in support for SQLite in the sqlite3(2) module.

SQLite is convenient because it doesn’t require setting up a separate
database server and is built-in to Python.  However, if concurrent
requests try to write to the database at the same time, they will slow
down as each write happens sequentially.  Small applications won’t
notice this.  Once you become big, you may want to switch to a different
database.

The tutorial doesn’t go into detail about SQL. If you are not familiar
with it, the SQLite docs describe the language(3).

   ---------- Footnotes ----------

   (1) https://sqlite.org/about.html

   (2) https://docs.python.org/3/library/sqlite3.html#module-sqlite3

   (3) https://sqlite.org/lang.html

1.5.3.1 Connect to the Database
...............................

The first thing to do when working with a SQLite database (and most
other Python database libraries) is to create a connection to it.  Any
queries and operations are performed using the connection, which is
closed after the work is finished.

In web applications this connection is typically tied to the request.
It is created at some point when handling a request, and closed before
the response is sent.

     import sqlite3

     import click
     from flask import current_app, g
     from flask.cli import with_appcontext

     def get_db():
         if 'db' not in g:
             g.db = sqlite3.connect(
                 current_app.config['DATABASE'],
                 detect_types=sqlite3.PARSE_DECLTYPES
             )
             g.db.row_factory = sqlite3.Row

         return g.db

     def close_db(e=None):
         db = g.pop('db', None)

         if db is not None:
             db.close()

LiteralBlock: ‘flaskr/db.py’

g is a special object that is unique for each request.  It is
used to store data that might be accessed by multiple functions during
the request.  The connection is stored and reused instead of creating a
new connection if ‘get_db’ is called a second time in the same request.

current_app is another special object that points to the
Flask application handling the request.  Since you used an application
factory, there is no application object when writing the rest of your
code.  ‘get_db’ will be called when the application has been created and
is handling a request, so current_app can be used.

sqlite3.connect()(1) establishes a connection to the file pointed at by
the ‘DATABASE’ configuration key.  This file doesn’t have to exist yet,
and won’t until you initialize the database later.

sqlite3.Row(2) tells the connection to return rows that behave like
dicts.  This allows accessing the columns by name.

‘close_db’ checks if a connection was created by checking if ‘g.db’ was
set.  If the connection exists, it is closed.  Further down you will
tell your application about the ‘close_db’ function in the application
factory so that it is called after each request.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/sqlite3.html#sqlite3.connect

   (2) https://docs.python.org/3/library/sqlite3.html#sqlite3.Row

1.5.3.2 Create the Tables
.........................

In SQLite, data is stored in `tables' and `columns'.  These need to be
created before you can store and retrieve data.  Flaskr will store users
in the ‘user’ table, and posts in the ‘post’ table.  Create a file with
the SQL commands needed to create empty tables:

     DROP TABLE IF EXISTS user;
     DROP TABLE IF EXISTS post;

     CREATE TABLE user (
       id INTEGER PRIMARY KEY AUTOINCREMENT,
       username TEXT UNIQUE NOT NULL,
       password TEXT NOT NULL
     );

     CREATE TABLE post (
       id INTEGER PRIMARY KEY AUTOINCREMENT,
       author_id INTEGER NOT NULL,
       created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
       title TEXT NOT NULL,
       body TEXT NOT NULL,
       FOREIGN KEY (author_id) REFERENCES user (id)
     );

LiteralBlock: ‘flaskr/schema.sql’

Add the Python functions that will run these SQL commands to the ‘db.py’
file:

     def init_db():
         db = get_db()

         with current_app.open_resource('schema.sql') as f:
             db.executescript(f.read().decode('utf8'))

     @click.command('init-db')
     @with_appcontext
     def init_db_command():
         """Clear the existing data and create new tables."""
         init_db()
         click.echo('Initialized the database.')

LiteralBlock: ‘flaskr/db.py’

open_resource() opens a file relative to the ‘flaskr’
package, which is useful since you won’t necessarily know where that
location is when deploying the application later.  ‘get_db’ returns a
database connection, which is used to execute the commands read from the
file.

click.command()(1) defines a command line command called ‘init-db’ that
calls the ‘init_db’ function and shows a success message to the user.
You can read Command Line Interface to learn more about
writing commands.

   ---------- Footnotes ----------

   (1) https://click.palletsprojects.com/en/8.1.x/api/#click.command

1.5.3.3 Register with the Application
.....................................

The ‘close_db’ and ‘init_db_command’ functions need to be registered
with the application instance; otherwise, they won’t be used by the
application.  However, since you’re using a factory function, that
instance isn’t available when writing the functions.  Instead, write a
function that takes an application and does the registration.

     def init_app(app):
         app.teardown_appcontext(close_db)
         app.cli.add_command(init_db_command)

LiteralBlock: ‘flaskr/db.py’

app.teardown_appcontext() tells Flask to call that function
when cleaning up after returning the response.

app.cli.add_command()(1) adds a new command that can be called with the
‘flask’ command.

Import and call this function from the factory.  Place the new code at
the end of the factory function before returning the app.

     def create_app():
         app = ...
         # existing code omitted

         from . import db
         db.init_app(app)

         return app

LiteralBlock: ‘flaskr/__init__.py’

   ---------- Footnotes ----------

   (1)
https://click.palletsprojects.com/en/8.1.x/api/#click.Group.add_command

1.5.3.4 Initialize the Database File
....................................

Now that ‘init-db’ has been registered with the app, it can be called
using the ‘flask’ command, similar to the ‘run’ command from the
previous page.

     Note: If you’re still running the server from the previous page,
     you can either stop the server, or run this command in a new
     terminal.  If you use a new terminal, remember to change to your
     project directory and activate the env as described in
     Installation.  You’ll also need to set ‘FLASK_APP’ and
     ‘FLASK_ENV’ as shown on the previous page.

Run the ‘init-db’ command:

     $ flask init-db
     Initialized the database.

There will now be a ‘flaskr.sqlite’ file in the ‘instance’ folder in
your project.

Continue to Blueprints and Views.

1.5.4 Blueprints and Views
--------------------------

A view function is the code you write to respond to requests to your
application.  Flask uses patterns to match the incoming request URL to
the view that should handle it.  The view returns data that Flask turns
into an outgoing response.  Flask can also go the other direction and
generate a URL to a view based on its name and arguments.

1.5.4.1 Create a Blueprint
..........................

A Blueprint is a way to organize a group of related views and
other code.  Rather than registering views and other code directly with
an application, they are registered with a blueprint.  Then the
blueprint is registered with the application when it is available in the
factory function.

Flaskr will have two blueprints, one for authentication functions and
one for the blog posts functions.  The code for each blueprint will go
in a separate module.  Since the blog needs to know about
authentication, you’ll write the authentication one first.

     import functools

     from flask import (
         Blueprint, flash, g, redirect, render_template, request, session, url_for
     )
     from werkzeug.security import check_password_hash, generate_password_hash

     from flaskr.db import get_db

     bp = Blueprint('auth', __name__, url_prefix='/auth')

LiteralBlock: ‘flaskr/auth.py’

This creates a Blueprint named ‘'auth'’.  Like the
application object, the blueprint needs to know where it’s defined, so
‘__name__’ is passed as the second argument.  The ‘url_prefix’ will be
prepended to all the URLs associated with the blueprint.

Import and register the blueprint from the factory using
app.register_blueprint().  Place the new code at the end of the
factory function before returning the app.

     def create_app():
         app = ...
         # existing code omitted

         from . import auth
         app.register_blueprint(auth.bp)

         return app

LiteralBlock: ‘flaskr/__init__.py’

The authentication blueprint will have views to register new users and
to log in and log out.

1.5.4.2 The First View: Register
................................

When the user visits the ‘/auth/register’ URL, the ‘register’ view will
return HTML(1) with a form for them to fill out.  When they submit the
form, it will validate their input and either show the form again with
an error message or create the new user and go to the login page.

For now you will just write the view code.  On the next page, you’ll
write templates to generate the HTML form.

     @bp.route('/register', methods=('GET', 'POST'))
     def register():
         if request.method == 'POST':
             username = request.form['username']
             password = request.form['password']
             db = get_db()
             error = None

             if not username:
                 error = 'Username is required.'
             elif not password:
                 error = 'Password is required.'

             if error is None:
                 try:
                     db.execute(
                         "INSERT INTO user (username, password) VALUES (?, ?)",
                         (username, generate_password_hash(password)),
                     )
                     db.commit()
                 except db.IntegrityError:
                     error = f"User {username} is already registered."
                 else:
                     return redirect(url_for("auth.login"))

             flash(error)

         return render_template('auth/register.html')

LiteralBlock: ‘flaskr/auth.py’

Here’s what the ‘register’ view function is doing:

  1. @bp.route associates the URL ‘/register’ with the
     ‘register’ view function.  When Flask receives a request to
     ‘/auth/register’, it will call the ‘register’ view and use the
     return value as the response.

  2. If the user submitted the form, see request.method will be
     ‘'POST'’.  In this case, start validating the input.

  3. request.form is a special type of dict(2) mapping
     submitted form keys and values.  The user will input their
     ‘username’ and ‘password’.

  4. Validate that ‘username’ and ‘password’ are not empty.

  5. If validation succeeds, insert the new user data into the database.

        - db.execute(3) takes a SQL query with ‘?’ placeholders for any
          user input, and a tuple of values to replace the placeholders
          with.  The database library will take care of escaping the
          values so you are not vulnerable to a `SQL injection attack'.

        - For security, passwords should never be stored in the database
          directly.  Instead, generate_password_hash()(4) is used to
          securely hash the password, and that hash is stored.  Since
          this query modifies data, db.commit()(5) needs to be called
          afterwards to save the changes.

        - An sqlite3.IntegrityError(6) will occur if the username
          already exists, which should be shown to the user as another
          validation error.

  6. After storing the user, they are redirected to the login page.
     url_for() generates the URL for the login view based on
     its name.  This is preferable to writing the URL directly as it
     allows you to change the URL later without changing all code that
     links to it.  redirect() generates a redirect response
     to the generated URL.

  7. If validation fails, the error is shown to the user.
     flash() stores messages that can be retrieved when rendering
     the template.

  8. When the user initially navigates to ‘auth/register’, or there was
     a validation error, an HTML page with the registration form should
     be shown.  render_template() will render a template
     containing the HTML, which you’ll write in the next step of the
     tutorial.

   ---------- Footnotes ----------

   (1) https://developer.mozilla.org/docs/Web/HTML

   (2) https://docs.python.org/3/library/stdtypes.html#dict

   (3)
https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.execute

   (4)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.security.generate_password_hash

   (5)
https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.commit

   (6)
https://docs.python.org/3/library/sqlite3.html#sqlite3.IntegrityError

1.5.4.3 Login
.............

This view follows the same pattern as the ‘register’ view above.

     @bp.route('/login', methods=('GET', 'POST'))
     def login():
         if request.method == 'POST':
             username = request.form['username']
             password = request.form['password']
             db = get_db()
             error = None
             user = db.execute(
                 'SELECT * FROM user WHERE username = ?', (username,)
             ).fetchone()

             if user is None:
                 error = 'Incorrect username.'
             elif not check_password_hash(user['password'], password):
                 error = 'Incorrect password.'

             if error is None:
                 session.clear()
                 session['user_id'] = user['id']
                 return redirect(url_for('index'))

             flash(error)

         return render_template('auth/login.html')

LiteralBlock: ‘flaskr/auth.py’

There are a few differences from the ‘register’ view:

  1. The user is queried first and stored in a variable for later use.

     fetchone()(1) returns one row from the query.  If the query
     returned no results, it returns ‘None’.  Later, fetchall()(2) will
     be used, which returns a list of all results.

  2. check_password_hash()(3) hashes the submitted password in the same
     way as the stored hash and securely compares them.  If they match,
     the password is valid.

  3. session is a dict(4) that stores data across requests.
     When validation succeeds, the user’s ‘id’ is stored in a new
     session.  The data is stored in a `cookie' that is sent to the
     browser, and the browser then sends it back with subsequent
     requests.  Flask securely `signs' the data so that it can’t be
     tampered with.

Now that the user’s ‘id’ is stored in the session, it will be
available on subsequent requests.  At the beginning of each request, if
a user is logged in their information should be loaded and made
available to other views.

     @bp.before_app_request
     def load_logged_in_user():
         user_id = session.get('user_id')

         if user_id is None:
             g.user = None
         else:
             g.user = get_db().execute(
                 'SELECT * FROM user WHERE id = ?', (user_id,)
             ).fetchone()

LiteralBlock: ‘flaskr/auth.py’

bp.before_app_request() registers a function that runs before
the view function, no matter what URL is requested.
‘load_logged_in_user’ checks if a user id is stored in the
session and gets that user’s data from the database, storing it on
g.user, which lasts for the length of the request.  If there
is no user id, or if the id doesn’t exist, ‘g.user’ will be ‘None’.

   ---------- Footnotes ----------

   (1)
https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.fetchone

   (2)
https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.fetchall

   (3)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.security.check_password_hash

   (4) https://docs.python.org/3/library/stdtypes.html#dict

1.5.4.4 Logout
..............

To log out, you need to remove the user id from the session.
Then ‘load_logged_in_user’ won’t load a user on subsequent requests.

     @bp.route('/logout')
     def logout():
         session.clear()
         return redirect(url_for('index'))

LiteralBlock: ‘flaskr/auth.py’

1.5.4.5 Require Authentication in Other Views
.............................................

Creating, editing, and deleting blog posts will require a user to be
logged in.  A `decorator' can be used to check this for each view it’s
applied to.

     def login_required(view):
         @functools.wraps(view)
         def wrapped_view(**kwargs):
             if g.user is None:
                 return redirect(url_for('auth.login'))

             return view(**kwargs)

         return wrapped_view

LiteralBlock: ‘flaskr/auth.py’

This decorator returns a new view function that wraps the original view
it’s applied to.  The new function checks if a user is loaded and
redirects to the login page otherwise.  If a user is loaded the original
view is called and continues normally.  You’ll use this decorator when
writing the blog views.

1.5.4.6 Endpoints and URLs
..........................

The url_for() function generates the URL to a view based on a
name and arguments.  The name associated with a view is also called the
`endpoint', and by default it’s the same as the name of the view
function.

For example, the ‘hello()’ view that was added to the app factory
earlier in the tutorial has the name ‘'hello'’ and can be linked to with
‘url_for('hello')’.  If it took an argument, which you’ll see later, it
would be linked to using ‘url_for('hello', who='World')’.

When using a blueprint, the name of the blueprint is prepended to the
name of the function, so the endpoint for the ‘login’ function you wrote
above is ‘'auth.login'’ because you added it to the ‘'auth'’ blueprint.

Continue to Templates.

1.5.5 Templates
---------------

You’ve written the authentication views for your application, but if
you’re running the server and try to go to any of the URLs, you’ll see a
‘TemplateNotFound’ error.  That’s because the views are calling
render_template(), but you haven’t written the templates yet.  The
template files will be stored in the ‘templates’ directory inside the
‘flaskr’ package.

Templates are files that contain static data as well as placeholders for
dynamic data.  A template is rendered with specific data to produce a
final document.  Flask uses the Jinja(1) template library to render
templates.

In your application, you will use templates to render HTML(2) which will
display in the user’s browser.  In Flask, Jinja is configured to
`autoescape' any data that is rendered in HTML templates.  This means
that it’s safe to render user input; any characters they’ve entered that
could mess with the HTML, such as ‘<’ and ‘>’ will be `escaped' with
`safe' values that look the same in the browser but don’t cause unwanted
effects.

Jinja looks and behaves mostly like Python.  Special delimiters are used
to distinguish Jinja syntax from the static data in the template.
Anything between ‘{{’ and ‘}}’ is an expression that will be output to
the final document.  ‘{%’ and ‘%}’ denotes a control flow statement like
‘if’ and ‘for’.  Unlike Python, blocks are denoted by start and end tags
rather than indentation since static text within a block could change
indentation.

   ---------- Footnotes ----------

   (1) https://jinja.palletsprojects.com/templates/

   (2) https://developer.mozilla.org/docs/Web/HTML

1.5.5.1 The Base Layout
.......................

Each page in the application will have the same basic layout around a
different body.  Instead of writing the entire HTML structure in each
template, each template will `extend' a base template and override
specific sections.

     <!doctype html>
     <title>{% block title %}{% endblock %} - Flaskr</title>
     <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
     <nav>
       <h1>Flaskr</h1>
       <ul>
         {% if g.user %}
           <li><span>{{ g.user['username'] }}</span>
           <li><a href="{{ url_for('auth.logout') }}">Log Out</a>
         {% else %}
           <li><a href="{{ url_for('auth.register') }}">Register</a>
           <li><a href="{{ url_for('auth.login') }}">Log In</a>
         {% endif %}
       </ul>
     </nav>
     <section class="content">
       <header>
         {% block header %}{% endblock %}
       </header>
       {% for message in get_flashed_messages() %}
         <div class="flash">{{ message }}</div>
       {% endfor %}
       {% block content %}{% endblock %}
     </section>

LiteralBlock: ‘flaskr/templates/base.html’

g is automatically available in templates.  Based on if
‘g.user’ is set (from ‘load_logged_in_user’), either the username and a
log out link are displayed, or links to register and log in are
displayed.  url_for() is also automatically available, and is
used to generate URLs to views instead of writing them out manually.

After the page title, and before the content, the template loops over
each message returned by get_flashed_messages().  You used
flash() in the views to show error messages, and this is the
code that will display them.

There are three blocks defined here that will be overridden in the other
templates:

  1. ‘{% block title %}’ will change the title displayed in the
     browser’s tab and window title.

  2. ‘{% block header %}’ is similar to ‘title’ but will change the
     title displayed on the page.

  3. ‘{% block content %}’ is where the content of each page goes, such
     as the login form or a blog post.

The base template is directly in the ‘templates’ directory.  To keep the
others organized, the templates for a blueprint will be placed in a
directory with the same name as the blueprint.

1.5.5.2 Register
................

     {% extends 'base.html' %}

     {% block header %}
       <h1>{% block title %}Register{% endblock %}</h1>
     {% endblock %}

     {% block content %}
       <form method="post">
         <label for="username">Username</label>
         <input name="username" id="username" required>
         <label for="password">Password</label>
         <input type="password" name="password" id="password" required>
         <input type="submit" value="Register">
       </form>
     {% endblock %}

LiteralBlock: ‘flaskr/templates/auth/register.html’

‘{% extends 'base.html' %}’ tells Jinja that this template should
replace the blocks from the base template.  All the rendered content
must appear inside ‘{% block %}’ tags that override blocks from the base
template.

A useful pattern used here is to place ‘{% block title %}’ inside ‘{%
block header %}’.  This will set the title block and then output the
value of it into the header block, so that both the window and page
share the same title without writing it twice.

The ‘input’ tags are using the ‘required’ attribute here.  This tells
the browser not to submit the form until those fields are filled in.  If
the user is using an older browser that doesn’t support that attribute,
or if they are using something besides a browser to make requests, you
still want to validate the data in the Flask view.  It’s important to
always fully validate the data on the server, even if the client does
some validation as well.

1.5.5.3 Log In
..............

This is identical to the register template except for the title and
submit button.

     {% extends 'base.html' %}

     {% block header %}
       <h1>{% block title %}Log In{% endblock %}</h1>
     {% endblock %}

     {% block content %}
       <form method="post">
         <label for="username">Username</label>
         <input name="username" id="username" required>
         <label for="password">Password</label>
         <input type="password" name="password" id="password" required>
         <input type="submit" value="Log In">
       </form>
     {% endblock %}

LiteralBlock: ‘flaskr/templates/auth/login.html’

1.5.5.4 Register A User
.......................

Now that the authentication templates are written, you can register a
user.  Make sure the server is still running (‘flask run’ if it’s not),
then go to ‘http://127.0.0.1:5000/auth/register’.

Try clicking the “Register” button without filling out the form and see
that the browser shows an error message.  Try removing the ‘required’
attributes from the ‘register.html’ template and click “Register” again.
Instead of the browser showing an error, the page will reload and the
error from flash() in the view will be shown.

Fill out a username and password and you’ll be redirected to the login
page.  Try entering an incorrect username, or the correct username and
incorrect password.  If you log in you’ll get an error because there’s
no ‘index’ view to redirect to yet.

Continue to Static Files.

1.5.6 Static Files
------------------

The authentication views and templates work, but they look very plain
right now.  Some CSS(1) can be added to add style to the HTML layout you
constructed.  The style won’t change, so it’s a `static' file rather
than a template.

Flask automatically adds a ‘static’ view that takes a path relative to
the ‘flaskr/static’ directory and serves it.  The ‘base.html’ template
already has a link to the ‘style.css’ file:

     {{ url_for('static', filename='style.css') }}

Besides CSS, other types of static files might be files with JavaScript
functions, or a logo image.  They are all placed under the
‘flaskr/static’ directory and referenced with ‘url_for('static',
filename='...')’.

This tutorial isn’t focused on how to write CSS, so you can just copy
the following into the ‘flaskr/static/style.css’ file:

     html { font-family: sans-serif; background: #eee; padding: 1rem; }
     body { max-width: 960px; margin: 0 auto; background: white; }
     h1 { font-family: serif; color: #377ba8; margin: 1rem 0; }
     a { color: #377ba8; }
     hr { border: none; border-top: 1px solid lightgray; }
     nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; }
     nav h1 { flex: auto; margin: 0; }
     nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; }
     nav ul  { display: flex; list-style: none; margin: 0; padding: 0; }
     nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; }
     .content { padding: 0 1rem 1rem; }
     .content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; }
     .content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; }
     .flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; }
     .post > header { display: flex; align-items: flex-end; font-size: 0.85em; }
     .post > header > div:first-of-type { flex: auto; }
     .post > header h1 { font-size: 1.5em; margin-bottom: 0; }
     .post .about { color: slategray; font-style: italic; }
     .post .body { white-space: pre-line; }
     .content:last-child { margin-bottom: 0; }
     .content form { margin: 1em 0; display: flex; flex-direction: column; }
     .content label { font-weight: bold; margin-bottom: 0.5em; }
     .content input, .content textarea { margin-bottom: 1em; }
     .content textarea { min-height: 12em; resize: vertical; }
     input.danger { color: #cc2f2e; }
     input[type=submit] { align-self: start; min-width: 10em; }

LiteralBlock: ‘flaskr/static/style.css’

You can find a less compact version of ‘style.css’ in the example
code(2).

Go to ‘http://127.0.0.1:5000/auth/login’ and the page should look like
the screenshot below.

You can read more about CSS from Mozilla’s documentation(3).  If you
change a static file, refresh the browser page.  If the change doesn’t
show up, try clearing your browser’s cache.

Continue to Blog Blueprint.

   ---------- Footnotes ----------

   (1) https://developer.mozilla.org/docs/Web/CSS

   (2)
https://github.com/pallets/flask/tree/2.1.2/examples/tutorial/flaskr/static/style.css

   (3) https://developer.mozilla.org/docs/Web/CSS

1.5.7 Blog Blueprint
--------------------

You’ll use the same techniques you learned about when writing the
authentication blueprint to write the blog blueprint.  The blog should
list all posts, allow logged in users to create posts, and allow the
author of a post to edit or delete it.

As you implement each view, keep the development server running.  As you
save your changes, try going to the URL in your browser and testing them
out.

1.5.7.1 The Blueprint
.....................

Define the blueprint and register it in the application factory.

     from flask import (
         Blueprint, flash, g, redirect, render_template, request, url_for
     )
     from werkzeug.exceptions import abort

     from flaskr.auth import login_required
     from flaskr.db import get_db

     bp = Blueprint('blog', __name__)

LiteralBlock: ‘flaskr/blog.py’

Import and register the blueprint from the factory using
app.register_blueprint().  Place the new code at the end of the
factory function before returning the app.

     def create_app():
         app = ...
         # existing code omitted

         from . import blog
         app.register_blueprint(blog.bp)
         app.add_url_rule('/', endpoint='index')

         return app

LiteralBlock: ‘flaskr/__init__.py’

Unlike the auth blueprint, the blog blueprint does not have a
‘url_prefix’.  So the ‘index’ view will be at ‘/’, the ‘create’ view at
‘/create’, and so on.  The blog is the main feature of Flaskr, so it
makes sense that the blog index will be the main index.

However, the endpoint for the ‘index’ view defined below will be
‘blog.index’.  Some of the authentication views referred to a plain
‘index’ endpoint.  app.add_url_rule() associates the endpoint
name ‘'index'’ with the ‘/’ url so that ‘url_for('index')’ or
‘url_for('blog.index')’ will both work, generating the same ‘/’ URL
either way.

In another application you might give the blog blueprint a ‘url_prefix’
and define a separate ‘index’ view in the application factory, similar
to the ‘hello’ view.  Then the ‘index’ and ‘blog.index’ endpoints and
URLs would be different.

1.5.7.2 Index
.............

The index will show all of the posts, most recent first.  A ‘JOIN’ is
used so that the author information from the ‘user’ table is available
in the result.

     @bp.route('/')
     def index():
         db = get_db()
         posts = db.execute(
             'SELECT p.id, title, body, created, author_id, username'
             ' FROM post p JOIN user u ON p.author_id = u.id'
             ' ORDER BY created DESC'
         ).fetchall()
         return render_template('blog/index.html', posts=posts)

LiteralBlock: ‘flaskr/blog.py’

     {% extends 'base.html' %}

     {% block header %}
       <h1>{% block title %}Posts{% endblock %}</h1>
       {% if g.user %}
         <a class="action" href="{{ url_for('blog.create') }}">New</a>
       {% endif %}
     {% endblock %}

     {% block content %}
       {% for post in posts %}
         <article class="post">
           <header>
             <div>
               <h1>{{ post['title'] }}</h1>
               <div class="about">by {{ post['username'] }} on {{ post['created'].strftime('%Y-%m-%d') }}</div>
             </div>
             {% if g.user['id'] == post['author_id'] %}
               <a class="action" href="{{ url_for('blog.update', id=post['id']) }}">Edit</a>
             {% endif %}
           </header>
           <p class="body">{{ post['body'] }}</p>
         </article>
         {% if not loop.last %}
           <hr>
         {% endif %}
       {% endfor %}
     {% endblock %}

LiteralBlock: ‘flaskr/templates/blog/index.html’

When a user is logged in, the ‘header’ block adds a link to the ‘create’
view.  When the user is the author of a post, they’ll see an “Edit” link
to the ‘update’ view for that post.  ‘loop.last’ is a special variable
available inside Jinja for loops(1).  It’s used to display a line after
each post except the last one, to visually separate them.

   ---------- Footnotes ----------

   (1) https://jinja.palletsprojects.com/templates/#for

1.5.7.3 Create
..............

The ‘create’ view works the same as the auth ‘register’ view.  Either
the form is displayed, or the posted data is validated and the post is
added to the database or an error is shown.

The ‘login_required’ decorator you wrote earlier is used on the blog
views.  A user must be logged in to visit these views, otherwise they
will be redirected to the login page.

     @bp.route('/create', methods=('GET', 'POST'))
     @login_required
     def create():
         if request.method == 'POST':
             title = request.form['title']
             body = request.form['body']
             error = None

             if not title:
                 error = 'Title is required.'

             if error is not None:
                 flash(error)
             else:
                 db = get_db()
                 db.execute(
                     'INSERT INTO post (title, body, author_id)'
                     ' VALUES (?, ?, ?)',
                     (title, body, g.user['id'])
                 )
                 db.commit()
                 return redirect(url_for('blog.index'))

         return render_template('blog/create.html')

LiteralBlock: ‘flaskr/blog.py’

     {% extends 'base.html' %}

     {% block header %}
       <h1>{% block title %}New Post{% endblock %}</h1>
     {% endblock %}

     {% block content %}
       <form method="post">
         <label for="title">Title</label>
         <input name="title" id="title" value="{{ request.form['title'] }}" required>
         <label for="body">Body</label>
         <textarea name="body" id="body">{{ request.form['body'] }}</textarea>
         <input type="submit" value="Save">
       </form>
     {% endblock %}

LiteralBlock: ‘flaskr/templates/blog/create.html’

1.5.7.4 Update
..............

Both the ‘update’ and ‘delete’ views will need to fetch a ‘post’ by ‘id’
and check if the author matches the logged in user.  To avoid
duplicating code, you can write a function to get the ‘post’ and call it
from each view.

     def get_post(id, check_author=True):
         post = get_db().execute(
             'SELECT p.id, title, body, created, author_id, username'
             ' FROM post p JOIN user u ON p.author_id = u.id'
             ' WHERE p.id = ?',
             (id,)
         ).fetchone()

         if post is None:
             abort(404, f"Post id {id} doesn't exist.")

         if check_author and post['author_id'] != g.user['id']:
             abort(403)

         return post

LiteralBlock: ‘flaskr/blog.py’

abort() will raise a special exception that returns an HTTP
status code.  It takes an optional message to show with the error,
otherwise a default message is used.  ‘404’ means “Not Found”, and ‘403’
means “Forbidden”.  (‘401’ means “Unauthorized”, but you redirect to the
login page instead of returning that status.)

The ‘check_author’ argument is defined so that the function can be used
to get a ‘post’ without checking the author.  This would be useful if
you wrote a view to show an individual post on a page, where the user
doesn’t matter because they’re not modifying the post.

     @bp.route('/<int:id>/update', methods=('GET', 'POST'))
     @login_required
     def update(id):
         post = get_post(id)

         if request.method == 'POST':
             title = request.form['title']
             body = request.form['body']
             error = None

             if not title:
                 error = 'Title is required.'

             if error is not None:
                 flash(error)
             else:
                 db = get_db()
                 db.execute(
                     'UPDATE post SET title = ?, body = ?'
                     ' WHERE id = ?',
                     (title, body, id)
                 )
                 db.commit()
                 return redirect(url_for('blog.index'))

         return render_template('blog/update.html', post=post)

LiteralBlock: ‘flaskr/blog.py’

Unlike the views you’ve written so far, the ‘update’ function takes an
argument, ‘id’.  That corresponds to the ‘<int:id>’ in the route.  A
real URL will look like ‘/1/update’.  Flask will capture the ‘1’, ensure
it’s an int(1), and pass it as the ‘id’ argument.  If you don’t specify
‘int:’ and instead do ‘<id>’, it will be a string.  To generate a URL to
the update page, see url_for() needs to be passed the ‘id’ so it
knows what to fill in: ‘url_for('blog.update', id=post['id'])’.  This is
also in the ‘index.html’ file above.

The ‘create’ and ‘update’ views look very similar.  The main difference
is that the ‘update’ view uses a ‘post’ object and an ‘UPDATE’ query
instead of an ‘INSERT’.  With some clever refactoring, you could use one
view and template for both actions, but for the tutorial it’s clearer to
keep them separate.

     {% extends 'base.html' %}

     {% block header %}
       <h1>{% block title %}Edit "{{ post['title'] }}"{% endblock %}</h1>
     {% endblock %}

     {% block content %}
       <form method="post">
         <label for="title">Title</label>
         <input name="title" id="title"
           value="{{ request.form['title'] or post['title'] }}" required>
         <label for="body">Body</label>
         <textarea name="body" id="body">{{ request.form['body'] or post['body'] }}</textarea>
         <input type="submit" value="Save">
       </form>
       <hr>
       <form action="{{ url_for('blog.delete', id=post['id']) }}" method="post">
         <input class="danger" type="submit" value="Delete" onclick="return confirm('Are you sure?');">
       </form>
     {% endblock %}

LiteralBlock: ‘flaskr/templates/blog/update.html’

This template has two forms.  The first posts the edited data to the
current page (‘/<id>/update’).  The other form contains only a button
and specifies an ‘action’ attribute that posts to the delete view
instead.  The button uses some JavaScript to show a confirmation dialog
before submitting.

The pattern ‘{{ request.form['title'] or post['title'] }}’ is used to
choose what data appears in the form.  When the form hasn’t been
submitted, the original ‘post’ data appears, but if invalid form data
was posted you want to display that so the user can fix the error, so
‘request.form’ is used instead.  request is another variable
that’s automatically available in templates.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/functions.html#int

1.5.7.5 Delete
..............

The delete view doesn’t have its own template, the delete button is part
of ‘update.html’ and posts to the ‘/<id>/delete’ URL. Since there is no
template, it will only handle the ‘POST’ method and then redirect to the
‘index’ view.

     @bp.route('/<int:id>/delete', methods=('POST',))
     @login_required
     def delete(id):
         get_post(id)
         db = get_db()
         db.execute('DELETE FROM post WHERE id = ?', (id,))
         db.commit()
         return redirect(url_for('blog.index'))

LiteralBlock: ‘flaskr/blog.py’

Congratulations, you’ve now finished writing your application!  Take
some time to try out everything in the browser.  However, there’s still
more to do before the project is complete.

Continue to Make the Project Installable.

1.5.8 Make the Project Installable
----------------------------------

Making your project installable means that you can build a
`distribution' file and install that in another environment, just like
you installed Flask in your project’s environment.  This makes deploying
your project the same as installing any other library, so you’re using
all the standard Python tools to manage everything.

Installing also comes with other benefits that might not be obvious from
the tutorial or as a new Python user, including:

   * Currently, Python and Flask understand how to use the ‘flaskr’
     package only because you’re running from your project’s directory.
     Installing means you can import it no matter where you run from.

   * You can manage your project’s dependencies just like other packages
     do, so ‘pip install yourproject.whl’ installs them.

   * Test tools can isolate your test environment from your development
     environment.

     Note: This is being introduced late in the tutorial, but in your
     future projects you should always start with this.

1.5.8.1 Describe the Project
............................

The ‘setup.py’ file describes your project and the files that belong to
it.

     from setuptools import find_packages, setup

     setup(
         name='flaskr',
         version='1.0.0',
         packages=find_packages(),
         include_package_data=True,
         zip_safe=False,
         install_requires=[
             'flask',
         ],
     )

LiteralBlock: ‘setup.py’

‘packages’ tells Python what package directories (and the Python files
they contain) to include.  ‘find_packages()’ finds these directories
automatically so you don’t have to type them out.  To include other
files, such as the static and templates directories,
‘include_package_data’ is set.  Python needs another file named
‘MANIFEST.in’ to tell what this other data is.

     include flaskr/schema.sql
     graft flaskr/static
     graft flaskr/templates
     global-exclude *.pyc

LiteralBlock: ‘MANIFEST.in’

This tells Python to copy everything in the ‘static’ and ‘templates’
directories, and the ‘schema.sql’ file, but to exclude all bytecode
files.

See the official packaging guide(1) for another explanation of the files
and options used.

   ---------- Footnotes ----------

   (1) https://packaging.python.org/tutorials/packaging-projects/

1.5.8.2 Install the Project
...........................

Use ‘pip’ to install your project in the virtual environment.

     $ pip install -e .

This tells pip to find ‘setup.py’ in the current directory and install
it in `editable' or `development' mode.  Editable mode means that as you
make changes to your local code, you’ll only need to re-install if you
change the metadata about the project, such as its dependencies.

You can observe that the project is now installed with ‘pip list’.

     $ pip list

     Package        Version   Location
     -------------- --------- ----------------------------------
     click          6.7
     Flask          1.0
     flaskr         1.0.0     /home/user/Projects/flask-tutorial
     itsdangerous   0.24
     Jinja2         2.10
     MarkupSafe     1.0
     pip            9.0.3
     setuptools     39.0.1
     Werkzeug       0.14.1
     wheel          0.30.0

Nothing changes from how you’ve been running your project so far.
‘FLASK_APP’ is still set to ‘flaskr’ and ‘flask run’ still runs the
application, but you can call it from anywhere, not just the
‘flask-tutorial’ directory.

Continue to Test Coverage.

1.5.9 Test Coverage
-------------------

Writing unit tests for your application lets you check that the code you
wrote works the way you expect.  Flask provides a test client that
simulates requests to the application and returns the response data.

You should test as much of your code as possible.  Code in functions
only runs when the function is called, and code in branches, such as
‘if’ blocks, only runs when the condition is met.  You want to make sure
that each function is tested with data that covers each branch.

The closer you get to 100% coverage, the more comfortable you can be
that making a change won’t unexpectedly change other behavior.  However,
100% coverage doesn’t guarantee that your application doesn’t have bugs.
In particular, it doesn’t test how the user interacts with the
application in the browser.  Despite this, test coverage is an important
tool to use during development.

     Note: This is being introduced late in the tutorial, but in your
     future projects you should test as you develop.

You’ll use pytest(1) and coverage(2) to test and measure your code.
Install them both:

     $ pip install pytest coverage

   ---------- Footnotes ----------

   (1) https://pytest.readthedocs.io/

   (2) https://coverage.readthedocs.io/

1.5.9.1 Setup and Fixtures
..........................

The test code is located in the ‘tests’ directory.  This directory is
`next to' the ‘flaskr’ package, not inside it.  The ‘tests/conftest.py’
file contains setup functions called `fixtures' that each test will use.
Tests are in Python modules that start with ‘test_’, and each test
function in those modules also starts with ‘test_’.

Each test will create a new temporary database file and populate some
data that will be used in the tests.  Write a SQL file to insert that
data.

     INSERT INTO user (username, password)
     VALUES
       ('test', 'pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f'),
       ('other', 'pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79');

     INSERT INTO post (title, body, author_id, created)
     VALUES
       ('test title', 'test' || x'0a' || 'body', 1, '2018-01-01 00:00:00');

LiteralBlock: ‘tests/data.sql’

The ‘app’ fixture will call the factory and pass ‘test_config’ to
configure the application and database for testing instead of using your
local development configuration.

     import os
     import tempfile

     import pytest
     from flaskr import create_app
     from flaskr.db import get_db, init_db

     with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f:
         _data_sql = f.read().decode('utf8')

     @pytest.fixture
     def app():
         db_fd, db_path = tempfile.mkstemp()

         app = create_app({
             'TESTING': True,
             'DATABASE': db_path,
         })

         with app.app_context():
             init_db()
             get_db().executescript(_data_sql)

         yield app

         os.close(db_fd)
         os.unlink(db_path)

     @pytest.fixture
     def client(app):
         return app.test_client()

     @pytest.fixture
     def runner(app):
         return app.test_cli_runner()

LiteralBlock: ‘tests/conftest.py’

tempfile.mkstemp()(1) creates and opens a temporary file, returning the
file descriptor and the path to it.  The ‘DATABASE’ path is overridden
so it points to this temporary path instead of the instance folder.
After setting the path, the database tables are created and the test
data is inserted.  After the test is over, the temporary file is closed
and removed.

TESTING tells Flask that the app is in test mode.  Flask
changes some internal behavior so it’s easier to test, and other
extensions can also use the flag to make testing them easier.

The ‘client’ fixture calls app.test_client() with the
application object created by the ‘app’ fixture.  Tests will use the
client to make requests to the application without running the server.

The ‘runner’ fixture is similar to ‘client’.
app.test_cli_runner() creates a runner that can call the Click
commands registered with the application.

Pytest uses fixtures by matching their function names with the names of
arguments in the test functions.  For example, the ‘test_hello’ function
you’ll write next takes a ‘client’ argument.  Pytest matches that with
the ‘client’ fixture function, calls it, and passes the returned value
to the test function.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/tempfile.html#tempfile.mkstemp

1.5.9.2 Factory
...............

There’s not much to test about the factory itself.  Most of the code
will be executed for each test already, so if something fails the other
tests will notice.

The only behavior that can change is passing test config.  If config is
not passed, there should be some default configuration, otherwise the
configuration should be overridden.

     from flaskr import create_app

     def test_config():
         assert not create_app().testing
         assert create_app({'TESTING': True}).testing

     def test_hello(client):
         response = client.get('/hello')
         assert response.data == b'Hello, World!'

LiteralBlock: ‘tests/test_factory.py’

You added the ‘hello’ route as an example when writing the factory at
the beginning of the tutorial.  It returns “Hello, World!”, so the test
checks that the response data matches.

1.5.9.3 Database
................

Within an application context, ‘get_db’ should return the same
connection each time it’s called.  After the context, the connection
should be closed.

     import sqlite3

     import pytest
     from flaskr.db import get_db

     def test_get_close_db(app):
         with app.app_context():
             db = get_db()
             assert db is get_db()

         with pytest.raises(sqlite3.ProgrammingError) as e:
             db.execute('SELECT 1')

         assert 'closed' in str(e.value)

LiteralBlock: ‘tests/test_db.py’

The ‘init-db’ command should call the ‘init_db’ function and output a
message.

     def test_init_db_command(runner, monkeypatch):
         class Recorder(object):
             called = False

         def fake_init_db():
             Recorder.called = True

         monkeypatch.setattr('flaskr.db.init_db', fake_init_db)
         result = runner.invoke(args=['init-db'])
         assert 'Initialized' in result.output
         assert Recorder.called

LiteralBlock: ‘tests/test_db.py’

This test uses Pytest’s ‘monkeypatch’ fixture to replace the ‘init_db’
function with one that records that it’s been called.  The ‘runner’
fixture you wrote above is used to call the ‘init-db’ command by name.

1.5.9.4 Authentication
......................

For most of the views, a user needs to be logged in.  The easiest way to
do this in tests is to make a ‘POST’ request to the ‘login’ view with
the client.  Rather than writing that out every time, you can write a
class with methods to do that, and use a fixture to pass it the client
for each test.

     class AuthActions(object):
         def __init__(self, client):
             self._client = client

         def login(self, username='test', password='test'):
             return self._client.post(
                 '/auth/login',
                 data={'username': username, 'password': password}
             )

         def logout(self):
             return self._client.get('/auth/logout')

     @pytest.fixture
     def auth(client):
         return AuthActions(client)

LiteralBlock: ‘tests/conftest.py’

With the ‘auth’ fixture, you can call ‘auth.login()’ in a test to log in
as the ‘test’ user, which was inserted as part of the test data in the
‘app’ fixture.

The ‘register’ view should render successfully on ‘GET’.  On ‘POST’ with
valid form data, it should redirect to the login URL and the user’s data
should be in the database.  Invalid data should display error messages.

     import pytest
     from flask import g, session
     from flaskr.db import get_db

     def test_register(client, app):
         assert client.get('/auth/register').status_code == 200
         response = client.post(
             '/auth/register', data={'username': 'a', 'password': 'a'}
         )
         assert response.headers["Location"] == "/auth/login"

         with app.app_context():
             assert get_db().execute(
                 "SELECT * FROM user WHERE username = 'a'",
             ).fetchone() is not None

     @pytest.mark.parametrize(('username', 'password', 'message'), (
         ('', '', b'Username is required.'),
         ('a', '', b'Password is required.'),
         ('test', 'test', b'already registered'),
     ))
     def test_register_validate_input(client, username, password, message):
         response = client.post(
             '/auth/register',
             data={'username': username, 'password': password}
         )
         assert message in response.data

LiteralBlock: ‘tests/test_auth.py’

client.get()(1) makes a ‘GET’ request and returns the Response
object returned by Flask.  Similarly, client.post()(2) makes a
‘POST’ request, converting the ‘data’ dict into form data.

To test that the page renders successfully, a simple request is made and
checked for a ‘200 OK’ status_code.  If rendering failed,
Flask would return a ‘500 Internal Server Error’ code.

‘headers’ will have a ‘Location’ header with the login URL when the
register view redirects to the login view.

data contains the body of the response as bytes.  If you
expect a certain value to render on the page, check that it’s in ‘data’.
Bytes must be compared to bytes.  If you want to compare text, use
get_data(as_text=True)(3) instead.

‘pytest.mark.parametrize’ tells Pytest to run the same test function
with different arguments.  You use it here to test different invalid
input and error messages without writing the same code three times.

The tests for the ‘login’ view are very similar to those for ‘register’.
Rather than testing the data in the database, see session should
have ‘user_id’ set after logging in.

     def test_login(client, auth):
         assert client.get('/auth/login').status_code == 200
         response = auth.login()
         assert response.headers["Location"] == "/"

         with client:
             client.get('/')
             assert session['user_id'] == 1
             assert g.user['username'] == 'test'

     @pytest.mark.parametrize(('username', 'password', 'message'), (
         ('a', 'test', b'Incorrect username.'),
         ('test', 'a', b'Incorrect password.'),
     ))
     def test_login_validate_input(auth, username, password, message):
         response = auth.login(username, password)
         assert message in response.data

LiteralBlock: ‘tests/test_auth.py’

Using ‘client’ in a ‘with’ block allows accessing context variables such
as session after the response is returned.  Normally,
accessing ‘session’ outside of a request would raise an error.

Testing ‘logout’ is the opposite of ‘login’.  session should
not contain ‘user_id’ after logging out.

     def test_logout(client, auth):
         auth.login()

         with client:
             auth.logout()
             assert 'user_id' not in session

LiteralBlock: ‘tests/test_auth.py’

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.Client.get

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.Client.post

   (3)
https://werkzeug.palletsprojects.com/en/2.1.x/wrappers/#werkzeug.wrappers.Response.get_data

1.5.9.5 Blog
............

All the blog views use the ‘auth’ fixture you wrote earlier.  Call
‘auth.login()’ and subsequent requests from the client will be logged in
as the ‘test’ user.

The ‘index’ view should display information about the post that was
added with the test data.  When logged in as the author, there should be
a link to edit the post.

You can also test some more authentication behavior while testing the
‘index’ view.  When not logged in, each page shows links to log in or
register.  When logged in, there’s a link to log out.

     import pytest
     from flaskr.db import get_db

     def test_index(client, auth):
         response = client.get('/')
         assert b"Log In" in response.data
         assert b"Register" in response.data

         auth.login()
         response = client.get('/')
         assert b'Log Out' in response.data
         assert b'test title' in response.data
         assert b'by test on 2018-01-01' in response.data
         assert b'test\nbody' in response.data
         assert b'href="/1/update"' in response.data

LiteralBlock: ‘tests/test_blog.py’

A user must be logged in to access the ‘create’, ‘update’, and ‘delete’
views.  The logged in user must be the author of the post to access
‘update’ and ‘delete’, otherwise a ‘403 Forbidden’ status is returned.
If a ‘post’ with the given ‘id’ doesn’t exist, ‘update’ and ‘delete’
should return ‘404 Not Found’.

     @pytest.mark.parametrize('path', (
         '/create',
         '/1/update',
         '/1/delete',
     ))
     def test_login_required(client, path):
         response = client.post(path)
         assert response.headers["Location"] == "/auth/login"

     def test_author_required(app, client, auth):
         # change the post author to another user
         with app.app_context():
             db = get_db()
             db.execute('UPDATE post SET author_id = 2 WHERE id = 1')
             db.commit()

         auth.login()
         # current user can't modify other user's post
         assert client.post('/1/update').status_code == 403
         assert client.post('/1/delete').status_code == 403
         # current user doesn't see edit link
         assert b'href="/1/update"' not in client.get('/').data

     @pytest.mark.parametrize('path', (
         '/2/update',
         '/2/delete',
     ))
     def test_exists_required(client, auth, path):
         auth.login()
         assert client.post(path).status_code == 404

LiteralBlock: ‘tests/test_blog.py’

The ‘create’ and ‘update’ views should render and return a ‘200 OK’
status for a ‘GET’ request.  When valid data is sent in a ‘POST’
request, ‘create’ should insert the new post data into the database, and
‘update’ should modify the existing data.  Both pages should show an
error message on invalid data.

     def test_create(client, auth, app):
         auth.login()
         assert client.get('/create').status_code == 200
         client.post('/create', data={'title': 'created', 'body': ''})

         with app.app_context():
             db = get_db()
             count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0]
             assert count == 2

     def test_update(client, auth, app):
         auth.login()
         assert client.get('/1/update').status_code == 200
         client.post('/1/update', data={'title': 'updated', 'body': ''})

         with app.app_context():
             db = get_db()
             post = db.execute('SELECT * FROM post WHERE id = 1').fetchone()
             assert post['title'] == 'updated'

     @pytest.mark.parametrize('path', (
         '/create',
         '/1/update',
     ))
     def test_create_update_validate(client, auth, path):
         auth.login()
         response = client.post(path, data={'title': '', 'body': ''})
         assert b'Title is required.' in response.data

LiteralBlock: ‘tests/test_blog.py’

The ‘delete’ view should redirect to the index URL and the post should
no longer exist in the database.

     def test_delete(client, auth, app):
         auth.login()
         response = client.post('/1/delete')
         assert response.headers["Location"] == "/"

         with app.app_context():
             db = get_db()
             post = db.execute('SELECT * FROM post WHERE id = 1').fetchone()
             assert post is None

LiteralBlock: ‘tests/test_blog.py’

1.5.9.6 Running the Tests
.........................

Some extra configuration, which is not required but makes running tests
with coverage less verbose, can be added to the project’s ‘setup.cfg’
file.

     [tool:pytest]
     testpaths = tests

     [coverage:run]
     branch = True
     source =
         flaskr

LiteralBlock: ‘setup.cfg’

To run the tests, use the ‘pytest’ command.  It will find and run all
the test functions you’ve written.

     $ pytest

     ========================= test session starts ==========================
     platform linux -- Python 3.6.4, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
     rootdir: /home/user/Projects/flask-tutorial, inifile: setup.cfg
     collected 23 items

     tests/test_auth.py ........                                      [ 34%]
     tests/test_blog.py ............                                  [ 86%]
     tests/test_db.py ..                                              [ 95%]
     tests/test_factory.py ..                                         [100%]

     ====================== 24 passed in 0.64 seconds =======================

If any tests fail, pytest will show the error that was raised.  You can
run ‘pytest -v’ to get a list of each test function rather than dots.

To measure the code coverage of your tests, use the ‘coverage’ command
to run pytest instead of running it directly.

     $ coverage run -m pytest

You can either view a simple coverage report in the terminal:

     $ coverage report

     Name                 Stmts   Miss Branch BrPart  Cover
     ------------------------------------------------------
     flaskr/__init__.py      21      0      2      0   100%
     flaskr/auth.py          54      0     22      0   100%
     flaskr/blog.py          54      0     16      0   100%
     flaskr/db.py            24      0      4      0   100%
     ------------------------------------------------------
     TOTAL                  153      0     44      0   100%

An HTML report allows you to see which lines were covered in each file:

     $ coverage html

This generates files in the ‘htmlcov’ directory.  Open
‘htmlcov/index.html’ in your browser to see the report.

Continue to Deploy to Production.

1.5.10 Deploy to Production
---------------------------

This part of the tutorial assumes you have a server that you want to
deploy your application to.  It gives an overview of how to create the
distribution file and install it, but won’t go into specifics about what
server or software to use.  You can set up a new environment on your
development computer to try out the instructions below, but probably
shouldn’t use it for hosting a real public application.  See
Deployment Options for a list of many different ways to host your
application.

1.5.10.1 Build and Install
..........................

When you want to deploy your application elsewhere, you build a
distribution file.  The current standard for Python distribution is the
`wheel' format, with the ‘.whl’ extension.  Make sure the wheel library
is installed first:

     $ pip install wheel

Running ‘setup.py’ with Python gives you a command line tool to issue
build-related commands.  The ‘bdist_wheel’ command will build a wheel
distribution file.

     $ python setup.py bdist_wheel

You can find the file in ‘dist/flaskr-1.0.0-py3-none-any.whl’.  The file
name is in the format of {project name}-{version}-{python tag} -{abi
tag}-{platform tag}.

Copy this file to another machine, see set up a new virtualenv,
then install the file with ‘pip’.

     $ pip install flaskr-1.0.0-py3-none-any.whl

Pip will install your project along with its dependencies.

Since this is a different machine, you need to run ‘init-db’ again to
create the database in the instance folder.

Bash

Fish

CMD

Powershell

     $ export FLASK_APP=flaskr
     $ flask init-db

     $ set -x FLASK_APP flaskr
     $ flask init-db

     > set FLASK_APP=flaskr
     > flask init-db

     > $env:FLASK_APP = "flaskr"
     > flask init-db

When Flask detects that it’s installed (not in editable mode), it uses a
different directory for the instance folder.  You can find it at
‘venv/var/flaskr-instance’ instead.

1.5.10.2 Configure the Secret Key
.................................

In the beginning of the tutorial that you gave a default value for
SECRET_KEY.  This should be changed to some random bytes in
production.  Otherwise, attackers could use the public ‘'dev'’ key to
modify the session cookie, or anything else that uses the secret key.

You can use the following command to output a random secret key:

     $ python -c 'import secrets; print(secrets.token_hex())'

     '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

Create the ‘config.py’ file in the instance folder, which the factory
will read from if it exists.  Copy the generated value into it.

     SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

LiteralBlock: ‘venv/var/flaskr-instance/config.py’

You can also set any other necessary configuration here, although
‘SECRET_KEY’ is the only one needed for Flaskr.

1.5.10.3 Run with a Production Server
.....................................

When running publicly rather than in development, you should not use the
built-in development server (‘flask run’).  The development server is
provided by Werkzeug for convenience, but is not designed to be
particularly efficient, stable, or secure.

Instead, use a production WSGI server.  For example, to use Waitress(1),
first install it in the virtual environment:

     $ pip install waitress

You need to tell Waitress about your application, but it doesn’t use
‘FLASK_APP’ like ‘flask run’ does.  You need to tell it to import and
call the application factory to get an application object.

     $ waitress-serve --call 'flaskr:create_app'

     Serving on http://0.0.0.0:8080

See Deployment Options for a list of many different ways to
host your application.  Waitress is just an example, chosen for the
tutorial because it supports both Windows and Linux.  There are many
more WSGI servers and deployment options that you may choose for your
project.

Continue to Keep Developing!.

   ---------- Footnotes ----------

   (1) https://docs.pylonsproject.org/projects/waitress/en/stable/

1.5.11 Keep Developing!
-----------------------

You’ve learned about quite a few Flask and Python concepts throughout
the tutorial.  Go back and review the tutorial and compare your code
with the steps you took to get there.  Compare your project to the
example project(1), which might look a bit different due to the
step-by-step nature of the tutorial.

There’s a lot more to Flask than what you’ve seen so far.  Even so,
you’re now equipped to start developing your own web applications.
Check out the Quickstart for an overview of what Flask can do,
then dive into the docs to keep learning.  Flask uses Jinja(2),
Click(3), Werkzeug(4), and ItsDangerous(5) behind the scenes, and they
all have their own documentation too.  You’ll also be interested in
Extensions which make tasks like working with the database or
validating form data easier and more powerful.

If you want to keep developing your Flaskr project, here are some ideas
for what to try next:

   * A detail view to show a single post.  Click a post’s title to go to
     its page.

   * Like / unlike a post.

   * Comments.

   * Tags.  Clicking a tag shows all the posts with that tag.

   * A search box that filters the index page by name.

   * Paged display.  Only show 5 posts per page.

   * Upload an image to go along with a post.

   * Format posts using Markdown.

   * An RSS feed of new posts.

Have fun and make awesome applications!

This tutorial will walk you through creating a basic blog application
called Flaskr.  Users will be able to register, log in, create posts,
and edit or delete their own posts.  You will be able to package and
install the application on other computers.

It’s assumed that you’re already familiar with Python.  The official
tutorial(6) in the Python docs is a great way to learn or review first.

While it’s designed to give a good starting point, the tutorial doesn’t
cover all of Flask’s features.  Check out the Quickstart for
an overview of what Flask can do, then dive into the docs to find out
more.  The tutorial only uses what’s provided by Flask and Python.  In
another project, you might decide to use Extensions or other
libraries to make some tasks simpler.

Flask is flexible.  It doesn’t require you to use any particular project
or code layout.  However, when first starting, it’s helpful to use a
more structured approach.  This means that the tutorial will require a
bit of boilerplate up front, but it’s done to avoid many common pitfalls
that new developers encounter, and it creates a project that’s easy to
expand on.  Once you become more comfortable with Flask, you can step
out of this structure and take full advantage of Flask’s flexibility.

The tutorial project is available as an example in the Flask
repository(7), if you want to compare your project with the final
product as you follow the tutorial.

Continue to Project Layout.

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/tree/2.1.2/examples/tutorial

   (2) https://palletsprojects.com/p/jinja/

   (3) https://palletsprojects.com/p/click/

   (4) https://palletsprojects.com/p/werkzeug/

   (5) https://palletsprojects.com/p/itsdangerous/

   (6) https://docs.python.org/3/tutorial/

   (7) https://github.com/pallets/flask/tree/2.1.2/examples/tutorial

1.6 Templates
=============

Flask leverages Jinja2 as its template engine.  You are obviously free
to use a different template engine, but you still have to install Jinja2
to run Flask itself.  This requirement is necessary to enable rich
extensions.  An extension can depend on Jinja2 being present.

This section only gives a very quick introduction into how Jinja2 is
integrated into Flask.  If you want information on the template engine’s
syntax itself, head over to the official Jinja2 Template
Documentation(1) for more information.

   ---------- Footnotes ----------

   (1) https://jinja.palletsprojects.com/templates/

1.6.1 Jinja Setup
-----------------

Unless customized, Jinja2 is configured by Flask as follows:

   - autoescaping is enabled for all templates ending in ‘.html’,
     ‘.htm’, ‘.xml’ as well as ‘.xhtml’ when using ‘render_template()’.

   - autoescaping is enabled for all strings when using
     ‘render_template_string()’.

   - a template has the ability to opt in/out autoescaping with the ‘{%
     autoescape %}’ tag.

   - Flask inserts a couple of global functions and helpers into the
     Jinja2 context, additionally to the values that are present by
     default.

1.6.2 Standard Context
----------------------

The following global variables are available within Jinja2 templates by
default:

 -- Data: config

     The current configuration object (see flask.Flask.config)

     Changed in version 0.10: This is now always available, even in
     imported templates.

     New in version 0.6.

 -- Data: request

     The current request object (see flask.request).  This
     variable is unavailable if the template was rendered without an
     active request context.

 -- Data: session

     The current session object (see flask.session).  This
     variable is unavailable if the template was rendered without an
     active request context.

 -- Data: g

     The request-bound object for global variables (see flask.g).
     This variable is unavailable if the template was rendered without
     an active request context.

 -- Function: url_for ()

     The flask.url_for() function.

 -- Function: get_flashed_messages ()

     The flask.get_flashed_messages() function.

     The Jinja Context Behavior: These variables are added to the
     context of variables, they are not global variables.  The
     difference is that by default these will not show up in the context
     of imported templates.  This is partially caused by performance
     considerations, partially to keep things explicit.

     What does this mean for you?  If you have a macro you want to
     import, that needs to access the request object you have two
     possibilities:

       1. you explicitly pass the request to the macro as parameter, or
          the attribute of the request object you are interested in.

       2. you import the macro “with context”.

     Importing with context looks like this:

          {% from '_helpers.html' import my_macro with context %}

1.6.3 Controlling Autoescaping
------------------------------

Autoescaping is the concept of automatically escaping special characters
for you.  Special characters in the sense of HTML (or XML, and thus
XHTML) are ‘&’, ‘>’, ‘<’, ‘"’ as well as ‘'’.  Because these characters
carry specific meanings in documents on their own you have to replace
them by so called “entities” if you want to use them for text.  Not
doing so would not only cause user frustration by the inability to use
these characters in text, but can also lead to security problems.  (see
Cross-Site Scripting (XSS))

Sometimes however you will need to disable autoescaping in templates.
This can be the case if you want to explicitly inject HTML into pages,
for example if they come from a system that generates secure HTML like a
markdown to HTML converter.

There are three ways to accomplish that:

   - In the Python code, wrap the HTML string in a Markup.
     object before passing it to the template.  This is in general the
     recommended way.

   - Inside the template, use the ‘|safe’ filter to explicitly mark a
     string as safe HTML (‘{{ myvariable|safe }}’)

   - Temporarily disable the autoescape system altogether.

To disable the autoescape system in templates, you can use the ‘{%
autoescape %}’ block:

     {% autoescape false %}
         <p>autoescaping is disabled here
         <p>{{ will_not_be_escaped }}
     {% endautoescape %}

Whenever you do this, please be very cautious about the variables you
are using in this block.

1.6.4 Registering Filters
-------------------------

If you want to register your own filters in Jinja2 you have two ways to
do that.  You can either put them by hand into the jinja_env.
of the application or use the template_filter() decorator.

The two following examples work the same and both reverse an object:

     @app.template_filter('reverse')
     def reverse_filter(s):
         return s[::-1]

     def reverse_filter(s):
         return s[::-1]
     app.jinja_env.filters['reverse'] = reverse_filter

In case of the decorator the argument is optional if you want to use the
function name as name of the filter.  Once registered, you can use the
filter in your templates in the same way as Jinja2’s builtin filters,
for example if you have a Python list in context called ‘mylist’:

     {% for x in mylist | reverse %}
     {% endfor %}

1.6.5 Context Processors
------------------------

To inject new variables automatically into the context of a template,
context processors exist in Flask.  Context processors run before the
template is rendered and have the ability to inject new values into the
template context.  A context processor is a function that returns a
dictionary.  The keys and values of this dictionary are then merged with
the template context, for all templates in the app:

     @app.context_processor
     def inject_user():
         return dict(user=g.user)

The context processor above makes a variable called ‘user’ available in
the template with the value of ‘g.user’.  This example is not very
interesting because ‘g’ is available in templates anyways, but it gives
an idea how this works.

Variables are not limited to values; a context processor can also make
functions available to templates (since Python allows passing around
functions):

     @app.context_processor
     def utility_processor():
         def format_price(amount, currency="€"):
             return f"{amount:.2f}{currency}"
         return dict(format_price=format_price)

The context processor above makes the ‘format_price’ function available
to all templates:

     {{ format_price(0.33) }}

You could also build ‘format_price’ as a template filter (see
Registering Filters), but this demonstrates how to pass functions
in a context processor.

1.7 Testing Flask Applications
==============================

Flask provides utilities for testing an application.  This documentation
goes over techniques for working with different parts of the application
in tests.

We will use the pytest(1) framework to set up and run our tests.

     $ pip install pytest

The tutorial goes over how to write tests for 100% coverage of
the sample Flaskr blog application.  See the tutorial on tests
for a detailed explanation of specific tests for an application.

   ---------- Footnotes ----------

   (1) https://docs.pytest.org/

1.7.1 Identifying Tests
-----------------------

Tests are typically located in the ‘tests’ folder.  Tests are functions
that start with ‘test_’, in Python modules that start with ‘test_’.
Tests can also be further grouped in classes that start with ‘Test’.

It can be difficult to know what to test.  Generally, try to test the
code that you write, not the code of libraries that you use, since they
are already tested.  Try to extract complex behaviors as separate
functions to test individually.

1.7.2 Fixtures
--------------

Pytest `fixtures' allow writing pieces of code that are reusable across
tests.  A simple fixture returns a value, but a fixture can also do
setup, yield a value, then do teardown.  Fixtures for the application,
test client, and CLI runner are shown below, they can be placed in
‘tests/conftest.py’.

If you’re using an application factory, define an ‘app’
fixture to create and configure an app instance.  You can add code
before and after the ‘yield’ to set up and tear down other resources,
such as creating and clearing a database.

If you’re not using a factory, you already have an app object you can
import and configure directly.  You can still use an ‘app’ fixture to
set up and tear down resources.

     import pytest
     from my_project import create_app

     @pytest.fixture()
     def app():
         app = create_app()
         app.config.update({
             "TESTING": True,
         })

         # other setup can go here

         yield app

         # clean up / reset resources here

     @pytest.fixture()
     def client(app):
         return app.test_client()

     @pytest.fixture()
     def runner(app):
         return app.test_cli_runner()

1.7.3 Sending Requests with the Test Client
-------------------------------------------

The test client makes requests to the application without running a live
server.  Flask’s client extends Werkzeug’s client(1), see those docs for
additional information.

The ‘client’ has methods that match the common HTTP request methods,
such as ‘client.get()’ and ‘client.post()’.  They take many arguments
for building the request; you can find the full documentation in
EnvironBuilder(2).  Typically you’ll use ‘path’, ‘query’, ‘headers’, and
‘data’ or ‘json’.

To make a request, call the method the request should use with the path
to the route to test.  A TestResponse(3) is returned to examine the
response data.  It has all the usual properties of a response object.
You’ll usually look at ‘response.data’, which is the bytes returned by
the view.  If you want to use text, Werkzeug 2.1 provides
‘response.text’, or use ‘response.get_data(as_text=True)’.

     def test_request_example(client):
         response = client.get("/posts")
         assert b"<h2>Hello, World!</h2>" in response.data

Pass a dict ‘query={"key": "value", ...}’ to set arguments in the query
string (after the ‘?’ in the URL). Pass a dict ‘headers={}’ to set
request headers.

To send a request body in a POST or PUT request, pass a value to ‘data’.
If raw bytes are passed, that exact body is used.  Usually, you’ll pass
a dict to set form data.

   ---------- Footnotes ----------

   (1) https://werkzeug.palletsprojects.com/en/2.1.x/test/

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.EnvironBuilder

   (3)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.TestResponse

1.7.3.1 Form Data
.................

To send form data, pass a dict to ‘data’.  The ‘Content-Type’ header
will be set to ‘multipart/form-data’ or
‘application/x-www-form-urlencoded’ automatically.

If a value is a file object opened for reading bytes (‘"rb"’ mode), it
will be treated as an uploaded file.  To change the detected filename
and content type, pass a ‘(file, filename, content_type)’ tuple.  File
objects will be closed after making the request, so they do not need to
use the usual ‘with open() as f:’ pattern.

It can be useful to store files in a ‘tests/resources’ folder, then use
‘pathlib.Path’ to get files relative to the current test file.

     from pathlib import Path

     # get the resources folder in the tests folder
     resources = Path(__file__).parent / "resources"

     def test_edit_user(client):
         response = client.post("/user/2/edit", data={
             "name": "Flask",
             "theme": "dark",
             "picture": (resources / "picture.png").open("rb"),
         })
         assert response.status_code == 200

1.7.3.2 JSON Data
.................

To send JSON data, pass an object to ‘json’.  The ‘Content-Type’ header
will be set to ‘application/json’ automatically.

Similarly, if the response contains JSON data, the ‘response.json’
attribute will contain the deserialized object.

     def test_json_data(client):
         response = client.post("/graphql", json={
             "query": """
                 query User($id: String!) {
                     user(id: $id) {
                         name
                         theme
                         picture_url
                     }
                 }
             """,
             variables={"id": 2},
         })
         assert response.json["data"]["user"]["name"] == "Flask"

1.7.4 Following Redirects
-------------------------

By default, the client does not make additional requests if the response
is a redirect.  By passing ‘follow_redirects=True’ to a request method,
the client will continue to make requests until a non-redirect response
is returned.

TestResponse.history(1) is a tuple of the responses that led up to the
final response.  Each response has a request(2) attribute which records
the request that produced that response.

     def test_logout_redirect(client):
         response = client.get("/logout")
         # Check that there was one redirect response.
         assert len(response.history) == 1
         # Check that the second request was to the index page.
         assert response.request.path == "/index"

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.TestResponse.history

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.TestResponse.request

1.7.5 Accessing and Modifying the Session
-----------------------------------------

To access Flask’s context variables, mainly session, use the
client in a ‘with’ statement.  The app and request context will remain
active `after' making a request, until the ‘with’ block ends.

     from flask import session

     def test_access_session(client):
         with client:
             client.post("/auth/login", data={"username": "flask"})
             # session is still accessible
             assert session["user_id"] == 1

         # session is no longer accessible

If you want to access or set a value in the session `before' making a
request, use the client’s session_transaction() method in a
‘with’ statement.  It returns a session object, and will save the
session once the block ends.

     from flask import session

     def test_modify_session(client):
         with client.session_transaction() as session:
             # set a user id without going through the login route
             session["user_id"] = 1

         # session is saved now

         response = client.get("/users/me")
         assert response.json["username"] == "flask"

1.7.6 Running Commands with the CLI Runner
------------------------------------------

Flask provides test_cli_runner() to create a
FlaskCliRunner, which runs CLI commands in isolation and captures
the output in a Result(1) object.  Flask’s runner extends Click’s
runner(2), see those docs for additional information.

Use the runner’s invoke() method to call commands in the same
way they would be called with the ‘flask’ command from the command line.

     import click

     @app.cli.command("hello")
     @click.option("--name", default="World")
     def hello_command(name):
         click.echo(f"Hello, {name}!")

     def test_hello_command(runner):
         result = runner.invoke(args="hello")
         assert "World" in result.output

         result = runner.invoke(args=["hello", "--name", "Flask"])
         assert "Flask" in result.output

   ---------- Footnotes ----------

   (1)
https://click.palletsprojects.com/en/8.1.x/api/#click.testing.Result

   (2) https://click.palletsprojects.com/en/8.1.x/testing/

1.7.7 Tests that depend on an Active Context
--------------------------------------------

You may have functions that are called from views or commands, that
expect an active application context or request context
because they access ‘request’, ‘session’, or ‘current_app’.
Rather than testing them by making a request or invoking the command,
you can create and activate a context directly.

Use ‘with app.app_context()’ to push an application context.  For
example, database extensions usually require an active app context to
make queries.

     def test_db_post_model(app):
         with app.app_context():
             post = db.session.query(Post).get(1)

Use ‘with app.test_request_context()’ to push a request context.  It
takes the same arguments as the test client’s request methods.

     def test_validate_user_edit(app):
         with app.test_request_context(
             "/user/2/edit", method="POST", data={"name": ""}
         ):
             # call a function that accesses `request`
             messages = validate_edit_user()

         assert messages["name"][0] == "Name cannot be empty."

Creating a test request context doesn’t run any of the Flask dispatching
code, so ‘before_request’ functions are not called.  If you need to call
these, usually it’s better to make a full request instead.  However,
it’s possible to call them manually.

     def test_auth_token(app):
         with app.test_request_context("/user/2/edit", headers={"X-Auth-Token": "1"}):
             app.preprocess_request()
             assert g.user.name == "Flask"

1.8 Handling Application Errors
===============================

Applications fail, servers fail.  Sooner or later you will see an
exception in production.  Even if your code is 100% correct, you will
still see exceptions from time to time.  Why?  Because everything else
involved will fail.  Here are some situations where perfectly fine code
can lead to server errors:

   - the client terminated the request early and the application was
     still reading from the incoming data

   - the database server was overloaded and could not handle the query

   - a filesystem is full

   - a harddrive crashed

   - a backend server overloaded

   - a programming error in a library you are using

   - network connection of the server to another system failed

And that’s just a small sample of issues you could be facing.  So how do
we deal with that sort of problem?  By default if your application runs
in production mode, and an exception is raised Flask will display a very
simple page for you and log the exception to the logger.

But there is more you can do, and we will cover some better setups to
deal with errors including custom exceptions and 3rd party tools.

1.8.1 Error Logging Tools
-------------------------

Sending error mails, even if just for critical ones, can become
overwhelming if enough users are hitting the error and log files are
typically never looked at.  This is why we recommend using Sentry(1) for
dealing with application errors.  It’s available as a source-available
project on GitHub(2) and is also available as a hosted version(3) which
you can try for free.  Sentry aggregates duplicate errors, captures the
full stack trace and local variables for debugging, and sends you mails
based on new errors or frequency thresholds.

To use Sentry you need to install the ‘sentry-sdk’ client with extra
‘flask’ dependencies.

     $ pip install sentry-sdk[flask]

And then add this to your Flask app:

     import sentry_sdk
     from sentry_sdk.integrations.flask import FlaskIntegration

     sentry_sdk.init('YOUR_DSN_HERE', integrations=[FlaskIntegration()])

The ‘YOUR_DSN_HERE’ value needs to be replaced with the DSN value you
get from your Sentry installation.

After installation, failures leading to an Internal Server Error are
automatically reported to Sentry and from there you can receive error
notifications.

See also:

   - Sentry also supports catching errors from a worker queue (RQ,
     Celery, etc.)  in a similar fashion.  See the Python SDK docs(4)
     for more information.

   - Getting started with Sentry(5)

   - Flask-specific documentation(6)

   ---------- Footnotes ----------

   (1) https://sentry.io/

   (2) https://github.com/getsentry/sentry

   (3) https://sentry.io/signup/

   (4) https://docs.sentry.io/platforms/python/

   (5) https://docs.sentry.io/quickstart/?platform=python

   (6) https://docs.sentry.io/platforms/python/guides/flask/

1.8.2 Error Handlers
--------------------

When an error occurs in Flask, an appropriate HTTP status code(1) will
be returned.  400-499 indicate errors with the client’s request data, or
about the data requested.  500-599 indicate errors with the server or
application itself.

You might want to show custom error pages to the user when an error
occurs.  This can be done by registering error handlers.

An error handler is a function that returns a response when a type of
error is raised, similar to how a view is a function that returns a
response when a request URL is matched.  It is passed the instance of
the error being handled, which is most likely a HTTPException(2).

The status code of the response will not be set to the handler’s code.
Make sure to provide the appropriate HTTP status code when returning a
response from a handler.

   ---------- Footnotes ----------

   (1) https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

1.8.2.1 Registering
...................

Register handlers by decorating a function with errorhandler().
Or use register_error_handler() to register the function
later.  Remember to set the error code when returning the response.

     @app.errorhandler(werkzeug.exceptions.BadRequest)
     def handle_bad_request(e):
         return 'bad request!', 400

     # or, without the decorator
     app.register_error_handler(400, handle_bad_request)

werkzeug.exceptions.HTTPException(1) subclasses like BadRequest(2) and
their HTTP codes are interchangeable when registering handlers.
(‘BadRequest.code == 400’)

Non-standard HTTP codes cannot be registered by code because they are
not known by Werkzeug.  Instead, define a subclass of HTTPException(3)
with the appropriate code and register and raise that exception class.

     class InsufficientStorage(werkzeug.exceptions.HTTPException):
         code = 507
         description = 'Not enough storage space.'

     app.register_error_handler(InsufficientStorage, handle_507)

     raise InsufficientStorage()

Handlers can be registered for any exception class, not just
HTTPException(4) subclasses or HTTP status codes.  Handlers can be
registered for a specific class, or for all subclasses of a parent
class.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.BadRequest

   (3)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (4)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

1.8.2.2 Handling
................

When building a Flask application you `will' run into exceptions.  If
some part of your code breaks while handling a request (and you have no
error handlers registered), a “500 Internal Server Error”
(InternalServerError(1)) will be returned by default.  Similarly, “404
Not Found” (NotFound(2)) error will occur if a request is sent to an
unregistered route.  If a route receives an unallowed request method, a
“405 Method Not Allowed” (MethodNotAllowed(3)) will be raised.  These
are all subclasses of HTTPException(4) and are provided by default in
Flask.

Flask gives you the ability to raise any HTTP exception registered by
Werkzeug.  However, the default HTTP exceptions return simple exception
pages.  You might want to show custom error pages to the user when an
error occurs.  This can be done by registering error handlers.

When Flask catches an exception while handling a request, it is first
looked up by code.  If no handler is registered for the code, Flask
looks up the error by its class hierarchy; the most specific handler is
chosen.  If no handler is registered, HTTPException(5) subclasses show a
generic message about their code, while other exceptions are converted
to a generic “500 Internal Server Error”.

For example, if an instance of ConnectionRefusedError(6) is raised, and
a handler is registered for ConnectionError(7) and
ConnectionRefusedError(8), the more specific ConnectionRefusedError(9)
handler is called with the exception instance to generate the response.

Handlers registered on the blueprint take precedence over those
registered globally on the application, assuming a blueprint is handling
the request that raises the exception.  However, the blueprint cannot
handle 404 routing errors because the 404 occurs at the routing level
before the blueprint can be determined.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.InternalServerError

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.NotFound

   (3)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.MethodNotAllowed

   (4)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (5)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (6)
https://docs.python.org/3/library/exceptions.html#ConnectionRefusedError

   (7) https://docs.python.org/3/library/exceptions.html#ConnectionError

   (8)
https://docs.python.org/3/library/exceptions.html#ConnectionRefusedError

   (9)
https://docs.python.org/3/library/exceptions.html#ConnectionRefusedError

1.8.2.3 Generic Exception Handlers
..................................

It is possible to register error handlers for very generic base classes
such as ‘HTTPException’ or even ‘Exception’.  However, be aware that
these will catch more than you might expect.

For example, an error handler for ‘HTTPException’ might be useful for
turning the default HTML errors pages into JSON. However, this handler
will trigger for things you don’t cause directly, such as 404 and 405
errors during routing.  Be sure to craft your handler carefully so you
don’t lose information about the HTTP error.

     from flask import json
     from werkzeug.exceptions import HTTPException

     @app.errorhandler(HTTPException)
     def handle_exception(e):
         """Return JSON instead of HTML for HTTP errors."""
         # start with the correct headers and status code from the error
         response = e.get_response()
         # replace the body with JSON
         response.data = json.dumps({
             "code": e.code,
             "name": e.name,
             "description": e.description,
         })
         response.content_type = "application/json"
         return response

An error handler for ‘Exception’ might seem useful for changing how all
errors, even unhandled ones, are presented to the user.  However, this
is similar to doing ‘except Exception:’ in Python, it will capture `all'
otherwise unhandled errors, including all HTTP status codes.

In most cases it will be safer to register handlers for more specific
exceptions.  Since ‘HTTPException’ instances are valid WSGI responses,
you could also pass them through directly.

     from werkzeug.exceptions import HTTPException

     @app.errorhandler(Exception)
     def handle_exception(e):
         # pass through HTTP errors
         if isinstance(e, HTTPException):
             return e

         # now you're handling non-HTTP exceptions only
         return render_template("500_generic.html", e=e), 500

Error handlers still respect the exception class hierarchy.  If you
register handlers for both ‘HTTPException’ and ‘Exception’, the
‘Exception’ handler will not handle ‘HTTPException’ subclasses because
it the ‘HTTPException’ handler is more specific.

1.8.2.4 Unhandled Exceptions
............................

When there is no error handler registered for an exception, a 500
Internal Server Error will be returned instead.  See
flask.Flask.handle_exception() for information about this behavior.

If there is an error handler registered for ‘InternalServerError’, this
will be invoked.  As of Flask 1.1.0, this error handler will always be
passed an instance of ‘InternalServerError’, not the original unhandled
error.

The original error is available as ‘e.original_exception’.

An error handler for “500 Internal Server Error” will be passed uncaught
exceptions in addition to explicit 500 errors.  In debug mode, a handler
for “500 Internal Server Error” will not be used.  Instead, the
interactive debugger will be shown.

1.8.3 Custom Error Pages
------------------------

Sometimes when building a Flask application, you might want to raise a
HTTPException(1) to signal to the user that something is wrong with the
request.  Fortunately, Flask comes with a handy abort().
function that aborts a request with a HTTP error from werkzeug as
desired.  It will also provide a plain black and white error page for
you with a basic description, but nothing fancy.

Depending on the error code it is less or more likely for the user to
actually see such an error.

Consider the code below, we might have a user profile route, and if the
user fails to pass a username we can raise a “400 Bad Request”.  If the
user passes a username and we can’t find it, we raise a “404 Not Found”.

     from flask import abort, render_template, request

     # a username needs to be supplied in the query args
     # a successful request would be like /profile?username=jack
     @app.route("/profile")
     def user_profile():
         username = request.arg.get("username")
         # if a username isn't supplied in the request, return a 400 bad request
         if username is None:
             abort(400)

         user = get_user(username=username)
         # if a user can't be found by their username, return 404 not found
         if user is None:
             abort(404)

         return render_template("profile.html", user=user)

Here is another example implementation for a “404 Page Not Found”
exception:

     from flask import render_template

     @app.errorhandler(404)
     def page_not_found(e):
         # note that we set the 404 status explicitly
         return render_template('404.html'), 404

When using Application Factories.:

     from flask import Flask, render_template

     def page_not_found(e):
       return render_template('404.html'), 404

     def create_app(config_filename):
         app = Flask(__name__)
         app.register_error_handler(404, page_not_found)
         return app

An example template might be this:

     {% extends "layout.html" %}
     {% block title %}Page Not Found{% endblock %}
     {% block body %}
       <h1>Page Not Found</h1>
       <p>What you were looking for is just not there.
       <p><a href="{{ url_for('index') }}">go somewhere nice</a>
     {% endblock %}

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

1.8.3.1 Further Examples
........................

The above examples wouldn’t actually be an improvement on the default
exception pages.  We can create a custom 500.html template like this:

     {% extends "layout.html" %}
     {% block title %}Internal Server Error{% endblock %}
     {% block body %}
       <h1>Internal Server Error</h1>
       <p>Oops... we seem to have made a mistake, sorry!</p>
       <p><a href="{{ url_for('index') }}">Go somewhere nice instead</a>
     {% endblock %}

It can be implemented by rendering the template on “500 Internal Server
Error”:

     from flask import render_template

     @app.errorhandler(500)
     def internal_server_error(e):
         # note that we set the 500 status explicitly
         return render_template('500.html'), 500

When using Application Factories.:

     from flask import Flask, render_template

     def internal_server_error(e):
       return render_template('500.html'), 500

     def create_app():
         app = Flask(__name__)
         app.register_error_handler(500, internal_server_error)
         return app

When using Modular Applications with Blueprints.:

     from flask import Blueprint

     blog = Blueprint('blog', __name__)

     # as a decorator
     @blog.errorhandler(500)
     def internal_server_error(e):
         return render_template('500.html'), 500

     # or with register_error_handler
     blog.register_error_handler(500, internal_server_error)

1.8.4 Blueprint Error Handlers
------------------------------

In Modular Applications with Blueprints, most error handlers
will work as expected.  However, there is a caveat concerning handlers
for 404 and 405 exceptions.  These error handlers are only invoked from
an appropriate ‘raise’ statement or a call to ‘abort’ in another of the
blueprint’s view functions; they are not invoked by, e.g., an invalid
URL access.

This is because the blueprint does not “own” a certain URL space, so the
application instance has no way of knowing which blueprint error handler
it should run if given an invalid URL. If you would like to execute
different handling strategies for these errors based on URL prefixes,
they may be defined at the application level using the ‘request’ proxy
object.

     from flask import jsonify, render_template

     # at the application level
     # not the blueprint level
     @app.errorhandler(404)
     def page_not_found(e):
         # if a request is in our blog URL space
         if request.path.startswith('/blog/'):
             # we return a custom blog 404 page
             return render_template("blog/404.html"), 404
         else:
             # otherwise we return our generic site-wide 404 page
             return render_template("404.html"), 404

     @app.errorhandler(405)
     def method_not_allowed(e):
         # if a request has the wrong method to our API
         if request.path.startswith('/api/'):
             # we return a json saying so
             return jsonify(message="Method Not Allowed"), 405
         else:
             # otherwise we return a generic site-wide 405 page
             return render_template("405.html"), 405

1.8.5 Returning API Errors as JSON
----------------------------------

When building APIs in Flask, some developers realise that the built-in
exceptions are not expressive enough for APIs and that the content type
of ‘text/html’ they are emitting is not very useful for API consumers.

Using the same techniques as above and jsonify() we can
return JSON responses to API errors.  abort() is called with
a ‘description’ parameter.  The error handler will use that as the JSON
error message, and set the status code to 404.

     from flask import abort, jsonify

     @app.errorhandler(404)
     def resource_not_found(e):
         return jsonify(error=str(e)), 404

     @app.route("/cheese")
     def get_one_cheese():
         resource = get_resource()

         if resource is None:
             abort(404, description="Resource not found")

         return jsonify(resource)

We can also create custom exception classes.  For instance, we can
introduce a new custom exception for an API that can take a proper human
readable message, a status code for the error and some optional payload
to give more context for the error.

This is a simple example:

     from flask import jsonify, request

     class InvalidAPIUsage(Exception):
         status_code = 400

         def __init__(self, message, status_code=None, payload=None):
             super().__init__()
             self.message = message
             if status_code is not None:
                 self.status_code = status_code
             self.payload = payload

         def to_dict(self):
             rv = dict(self.payload or ())
             rv['message'] = self.message
             return rv

     @app.errorhandler(InvalidAPIUsage)
     def invalid_api_usage(e):
         return jsonify(e.to_dict()), e.status_code

     # an API app route for getting user information
     # a correct request might be /api/user?user_id=420
     @app.route("/api/user")
     def user_api(user_id):
         user_id = request.arg.get("user_id")
         if not user_id:
             raise InvalidAPIUsage("No user id provided!")

         user = get_user(user_id=user_id)
         if not user:
             raise InvalidAPIUsage("No such user!", status_code=404)

         return jsonify(user.to_dict())

A view can now raise that exception with an error message.  Additionally
some extra payload can be provided as a dictionary through the ‘payload’
parameter.

1.8.6 Logging
-------------

See Logging for information about how to log exceptions, such
as by emailing them to admins.

1.8.7 Debugging
---------------

See Debugging Application Errors for information about how to
debug errors in development and production.

1.9 Debugging Application Errors
================================

1.9.1 In Production
-------------------

`Do not run the development server, or enable the built-in debugger, in
a production environment.'  The debugger allows executing arbitrary
Python code from the browser.  It’s protected by a pin, but that should
not be relied on for security.

Use an error logging tool, such as Sentry, as described in Error Logging Tools,
or enable logging and notifications as described in
Logging.

If you have access to the server, you could add some code to start an
external debugger if ‘request.remote_addr’ matches your IP. Some IDE
debuggers also have a remote mode so breakpoints on the server can be
interacted with locally.  Only enable a debugger temporarily.

1.9.2 The Built-In Debugger
---------------------------

The built-in Werkzeug development server provides a debugger which shows
an interactive traceback in the browser when an unhandled error occurs
during a request.  This debugger should only be used during development.

     Warning: The debugger allows executing arbitrary Python code from
     the browser.  It is protected by a pin, but still represents a
     major security risk.  Do not run the development server or debugger
     in a production environment.

To enable the debugger, run the development server with the ‘FLASK_ENV’
environment variable set to ‘development’.  This puts Flask in debug
mode, which changes how it handles some errors, and enables the debugger
and reloader.

Bash

Fish

CMD

Powershell

     $ export FLASK_ENV=development
     $ flask run

     $ set -x FLASK_ENV development
     $ flask run

     > set FLASK_ENV=development
     > flask run

     > $env:FLASK_ENV = "development"
     > flask run

‘FLASK_ENV’ can only be set as an environment variable.  When running
from Python code, passing ‘debug=True’ enables debug mode, which is
mostly equivalent.  Debug mode can be controlled separately from
‘FLASK_ENV’ with the ‘FLASK_DEBUG’ environment variable as well.

     app.run(debug=True)

Development Server and Command Line Interface have
more information about running the debugger, debug mode, and development
mode.  More information about the debugger can be found in the Werkzeug
documentation(1).

   ---------- Footnotes ----------

   (1) https://werkzeug.palletsprojects.com/debug/

1.9.3 External Debuggers
------------------------

External debuggers, such as those provided by IDEs, can offer a more
powerful debugging experience than the built-in debugger.  They can also
be used to step through code during a request before an error is raised,
or if no error is raised.  Some even have a remote mode so you can debug
code running on another machine.

When using an external debugger, the app should still be in debug mode,
but it can be useful to disable the built-in debugger and reloader,
which can interfere.

When running from the command line:

Bash

Fish

CMD

Powershell

     $ export FLASK_ENV=development
     $ flask run --no-debugger --no-reload

     $ set -x FLASK_ENV development
     $ flask run --no-debugger --no-reload

     > set FLASK_ENV=development
     > flask run --no-debugger --no-reload

     > $env:FLASK_ENV = "development"
     > flask run --no-debugger --no-reload

When running from Python:

     app.run(debug=True, use_debugger=False, use_reloader=False)

Disabling these isn’t required, an external debugger will continue to
work with the following caveats.  If the built-in debugger is not
disabled, it will catch unhandled exceptions before the external
debugger can.  If the reloader is not disabled, it could cause an
unexpected reload if code changes during debugging.

1.10 Logging
============

Flask uses standard Python logging(1).  Messages about your Flask
application are logged with app.logger, which takes the same
name as app.name.  This logger can also be used to log your
own messages.

     @app.route('/login', methods=['POST'])
     def login():
         user = get_user(request.form['username'])

         if user.check_password(request.form['password']):
             login_user(user)
             app.logger.info('%s logged in successfully', user.username)
             return redirect(url_for('index'))
         else:
             app.logger.info('%s failed to log in', user.username)
             abort(401)

If you don’t configure logging, Python’s default log level is usually
‘warning’.  Nothing below the configured level will be visible.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/logging.html#module-logging

1.10.1 Basic Configuration
--------------------------

When you want to configure logging for your project, you should do it as
soon as possible when the program starts.  If app.logger is
accessed before logging is configured, it will add a default handler.
If possible, configure logging before creating the application object.

This example uses dictConfig()(1) to create a logging configuration
similar to Flask’s default, except for all logs:

     from logging.config import dictConfig

     dictConfig({
         'version': 1,
         'formatters': {'default': {
             'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
         }},
         'handlers': {'wsgi': {
             'class': 'logging.StreamHandler',
             'stream': 'ext://flask.logging.wsgi_errors_stream',
             'formatter': 'default'
         }},
         'root': {
             'level': 'INFO',
             'handlers': ['wsgi']
         }
     })

     app = Flask(__name__)

   ---------- Footnotes ----------

   (1)
https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig

1.10.1.1 Default Configuration
..............................

If you do not configure logging yourself, Flask will add a
StreamHandler(1) to app.logger automatically.  During
requests, it will write to the stream specified by the WSGI server in
‘environ['wsgi.errors']’ (which is usually sys.stderr(2)).  Outside a
request, it will log to sys.stderr(3).

   ---------- Footnotes ----------

   (1)
https://docs.python.org/3/library/logging.handlers.html#logging.StreamHandler

   (2) https://docs.python.org/3/library/sys.html#sys.stderr

   (3) https://docs.python.org/3/library/sys.html#sys.stderr

1.10.1.2 Removing the Default Handler
.....................................

If you configured logging after accessing app.logger, and need
to remove the default handler, you can import and remove it:

     from flask.logging import default_handler

     app.logger.removeHandler(default_handler)

1.10.2 Email Errors to Admins
-----------------------------

When running the application on a remote server for production, you
probably won’t be looking at the log messages very often.  The WSGI
server will probably send log messages to a file, and you’ll only check
that file if a user tells you something went wrong.

To be proactive about discovering and fixing bugs, you can configure a
logging.handlers.SMTPHandler(1) to send an email when errors and higher
are logged.

     import logging
     from logging.handlers import SMTPHandler

     mail_handler = SMTPHandler(
         mailhost='127.0.0.1',
         fromaddr='server-error@example.com',
         toaddrs=['admin@example.com'],
         subject='Application Error'
     )
     mail_handler.setLevel(logging.ERROR)
     mail_handler.setFormatter(logging.Formatter(
         '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
     ))

     if not app.debug:
         app.logger.addHandler(mail_handler)

This requires that you have an SMTP server set up on the same server.
See the Python docs for more information about configuring the handler.

   ---------- Footnotes ----------

   (1)
https://docs.python.org/3/library/logging.handlers.html#logging.handlers.SMTPHandler

1.10.3 Injecting Request Information
------------------------------------

Seeing more information about the request, such as the IP address, may
help debugging some errors.  You can subclass logging.Formatter(1) to
inject your own fields that can be used in messages.  You can change the
formatter for Flask’s default handler, the mail handler defined above,
or any other handler.

     from flask import has_request_context, request
     from flask.logging import default_handler

     class RequestFormatter(logging.Formatter):
         def format(self, record):
             if has_request_context():
                 record.url = request.url
                 record.remote_addr = request.remote_addr
             else:
                 record.url = None
                 record.remote_addr = None

             return super().format(record)

     formatter = RequestFormatter(
         '[%(asctime)s] %(remote_addr)s requested %(url)s\n'
         '%(levelname)s in %(module)s: %(message)s'
     )
     default_handler.setFormatter(formatter)
     mail_handler.setFormatter(formatter)

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/logging.html#logging.Formatter

1.10.4 Other Libraries
----------------------

Other libraries may use logging extensively, and you want to see
relevant messages from those logs too.  The simplest way to do this is
to add handlers to the root logger instead of only the app logger.

     from flask.logging import default_handler

     root = logging.getLogger()
     root.addHandler(default_handler)
     root.addHandler(mail_handler)

Depending on your project, it may be more useful to configure each
logger you care about separately, instead of configuring only the root
logger.

     for logger in (
         app.logger,
         logging.getLogger('sqlalchemy'),
         logging.getLogger('other_package'),
     ):
         logger.addHandler(default_handler)
         logger.addHandler(mail_handler)

1.10.4.1 Werkzeug
.................

Werkzeug logs basic request/response information to the ‘'werkzeug'’
logger.  If the root logger has no handlers configured, Werkzeug adds a
StreamHandler(1) to its logger.

   ---------- Footnotes ----------

   (1)
https://docs.python.org/3/library/logging.handlers.html#logging.StreamHandler

1.10.4.2 Flask Extensions
.........................

Depending on the situation, an extension may choose to log to
app.logger or its own named logger.  Consult each extension’s
documentation for details.

1.11 Configuration Handling
===========================

Applications need some kind of configuration.  There are different
settings you might want to change depending on the application
environment like toggling the debug mode, setting the secret key, and
other such environment-specific things.

The way Flask is designed usually requires the configuration to be
available when the application starts up.  You can hard code the
configuration in the code, which for many small applications is not
actually that bad, but there are better ways.

Independent of how you load your config, there is a config object
available which holds the loaded configuration values: The config
attribute of the Flask object.  This is the place where
Flask itself puts certain configuration values and also where extensions
can put their configuration values.  But this is also where you can have
your own configuration.

1.11.1 Configuration Basics
---------------------------

The config is actually a subclass of a dictionary and can be
modified just like any dictionary:

     app = Flask(__name__)
     app.config['TESTING'] = True

Certain configuration values are also forwarded to the Flask.
object so you can read and write them from there:

     app.testing = True

To update multiple keys at once you can use the dict.update()(1) method:

     app.config.update(
         TESTING=True,
         SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
     )

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#dict.update

1.11.2 Environment and Debug Features
-------------------------------------

The ENV and DEBUG config values are special
because they may behave inconsistently if changed after the app has
begun setting up.  In order to set the environment and debug mode
reliably, Flask uses environment variables.

The environment is used to indicate to Flask, extensions, and other
programs, like Sentry, what context Flask is running in.  It is
controlled with the ‘FLASK_ENV’ environment variable and defaults to
‘production’.

Setting ‘FLASK_ENV’ to ‘development’ will enable debug mode.  ‘flask
run’ will use the interactive debugger and reloader by default in debug
mode.  To control this separately from the environment, use the
‘FLASK_DEBUG’ flag.

Changed in version 1.0: Added ‘FLASK_ENV’ to control the environment
separately from debug mode.  The development environment enables debug
mode.

To switch Flask to the development environment and enable debug mode,
set ‘FLASK_ENV’:

Bash

Fish

CMD

Powershell

     $ export FLASK_ENV=development
     $ flask run

     $ set -x FLASK_ENV development
     $ flask run

     > set FLASK_ENV=development
     > flask run

     > $env:FLASK_ENV = "development"
     > flask run

Using the environment variables as described above is recommended.
While it is possible to set ENV and DEBUG in your
config or code, this is strongly discouraged.  They can’t be read early
by the ‘flask’ command, and some systems or extensions may have already
configured themselves based on a previous value.

1.11.3 Builtin Configuration Values
-----------------------------------

The following configuration values are used internally by Flask:

 -- Data: ENV

     What environment the app is running in.  Flask and extensions may
     enable behaviors based on the environment, such as enabling debug
     mode.  The env attribute maps to this config key.  This
     is set by the ‘FLASK_ENV’ environment variable and may not behave
     as expected if set in code.

     `Do not enable development when deploying in production.'

     Default: ‘'production'’

     New in version 1.0.

 -- Data: DEBUG

     Whether debug mode is enabled.  When using ‘flask run’ to start the
     development server, an interactive debugger will be shown for
     unhandled exceptions, and the server will be reloaded when code
     changes.  The debug attribute maps to this config key.
     This is enabled when ENV is ‘'development'’ and is
     overridden by the ‘FLASK_DEBUG’ environment variable.  It may not
     behave as expected if set in code.

     `Do not enable debug mode when deploying in production.'

     Default: ‘True’ if ENV is ‘'development'’, or ‘False’
     otherwise.

 -- Data: TESTING

     Enable testing mode.  Exceptions are propagated rather than handled
     by the the app’s error handlers.  Extensions may also change their
     behavior to facilitate easier testing.  You should enable this in
     your own tests.

     Default: ‘False’

 -- Data: PROPAGATE_EXCEPTIONS

     Exceptions are re-raised rather than being handled by the app’s
     error handlers.  If not set, this is implicitly true if ‘TESTING’
     or ‘DEBUG’ is enabled.

     Default: ‘None’

 -- Data: PRESERVE_CONTEXT_ON_EXCEPTION

     Don’t pop the request context when an exception occurs.  If not
     set, this is true if ‘DEBUG’ is true.  This allows debuggers to
     introspect the request data on errors, and should normally not need
     to be set directly.

     Default: ‘None’

 -- Data: TRAP_HTTP_EXCEPTIONS

     If there is no handler for an ‘HTTPException’-type exception,
     re-raise it to be handled by the interactive debugger instead of
     returning it as a simple error response.

     Default: ‘False’

 -- Data: TRAP_BAD_REQUEST_ERRORS

     Trying to access a key that doesn’t exist from request dicts like
     ‘args’ and ‘form’ will return a 400 Bad Request error page.  Enable
     this to treat the error as an unhandled exception instead so that
     you get the interactive debugger.  This is a more specific version
     of ‘TRAP_HTTP_EXCEPTIONS’.  If unset, it is enabled in debug mode.

     Default: ‘None’

 -- Data: SECRET_KEY

     A secret key that will be used for securely signing the session
     cookie and can be used for any other security related needs by
     extensions or your application.  It should be a long random ‘bytes’
     or ‘str’.  For example, copy the output of this to your config:

          $ python -c 'import secrets; print(secrets.token_hex())'
          '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

     `Do not reveal the secret key when posting questions or committing
     code.'

     Default: ‘None’

 -- Data: SESSION_COOKIE_NAME

     The name of the session cookie.  Can be changed in case you already
     have a cookie with the same name.

     Default: ‘'session'’

 -- Data: SESSION_COOKIE_DOMAIN

     The domain match rule that the session cookie will be valid for.
     If not set, the cookie will be valid for all subdomains of
     SERVER_NAME.  If ‘False’, the cookie’s domain will not be set.

     Default: ‘None’

 -- Data: SESSION_COOKIE_PATH

     The path that the session cookie will be valid for.  If not set,
     the cookie will be valid underneath ‘APPLICATION_ROOT’ or ‘/’ if
     that is not set.

     Default: ‘None’

 -- Data: SESSION_COOKIE_HTTPONLY

     Browsers will not allow JavaScript access to cookies marked as
     “HTTP only” for security.

     Default: ‘True’

 -- Data: SESSION_COOKIE_SECURE

     Browsers will only send cookies with requests over HTTPS if the
     cookie is marked “secure”.  The application must be served over
     HTTPS for this to make sense.

     Default: ‘False’

 -- Data: SESSION_COOKIE_SAMESITE

     Restrict how cookies are sent with requests from external sites.
     Can be set to ‘'Lax'’ (recommended) or ‘'Strict'’.  See
     Set-Cookie options.

     Default: ‘None’

     New in version 1.0.

 -- Data: PERMANENT_SESSION_LIFETIME

     If ‘session.permanent’ is true, the cookie’s expiration will be set
     this number of seconds in the future.  Can either be a
     datetime.timedelta(1) or an ‘int’.

     Flask’s default cookie implementation validates that the
     cryptographic signature is not older than this value.

     Default: ‘timedelta(days=31)’ (‘2678400’ seconds)

 -- Data: SESSION_REFRESH_EACH_REQUEST

     Control whether the cookie is sent with every response when
     ‘session.permanent’ is true.  Sending the cookie every time (the
     default) can more reliably keep the session from expiring, but uses
     more bandwidth.  Non-permanent sessions are not affected.

     Default: ‘True’

 -- Data: USE_X_SENDFILE

     When serving files, set the ‘X-Sendfile’ header instead of serving
     the data with Flask.  Some web servers, such as Apache, recognize
     this and serve the data more efficiently.  This only makes sense
     when using such a server.

     Default: ‘False’

 -- Data: SEND_FILE_MAX_AGE_DEFAULT

     When serving files, set the cache control max age to this number of
     seconds.  Can be a datetime.timedelta(2) or an ‘int’.  Override
     this value on a per-file basis using get_send_file_max_age()
     on the application or blueprint.

     If ‘None’, ‘send_file’ tells the browser to use conditional
     requests will be used instead of a timed cache, which is usually
     preferable.

     Default: ‘None’

 -- Data: SERVER_NAME

     Inform the application what host and port it is bound to.  Required
     for subdomain route matching support.

     If set, will be used for the session cookie domain if
     SESSION_COOKIE_DOMAIN is not set.  Modern web browsers will
     not allow setting cookies for domains without a dot.  To use a
     domain locally, add any names that should route to the app to your
     ‘hosts’ file.

          127.0.0.1 localhost.dev

     If set, ‘url_for’ can generate external URLs with only an
     application context instead of a request context.

     Default: ‘None’

 -- Data: APPLICATION_ROOT

     Inform the application what path it is mounted under by the
     application / web server.  This is used for generating URLs outside
     the context of a request (inside a request, the dispatcher is
     responsible for setting ‘SCRIPT_NAME’ instead; see
     Application Dispatching for examples of dispatch
     configuration).

     Will be used for the session cookie path if ‘SESSION_COOKIE_PATH’
     is not set.

     Default: ‘'/'’

 -- Data: PREFERRED_URL_SCHEME

     Use this scheme for generating external URLs when not in a request
     context.

     Default: ‘'http'’

 -- Data: MAX_CONTENT_LENGTH

     Don’t read more than this many bytes from the incoming request
     data.  If not set and the request does not specify a
     ‘CONTENT_LENGTH’, no data will be read for security.

     Default: ‘None’

 -- Data: JSON_AS_ASCII

     Serialize objects to ASCII-encoded JSON. If this is disabled, the
     JSON returned from ‘jsonify’ will contain Unicode characters.  This
     has security implications when rendering the JSON into JavaScript
     in templates, and should typically remain enabled.

     Default: ‘True’

 -- Data: JSON_SORT_KEYS

     Sort the keys of JSON objects alphabetically.  This is useful for
     caching because it ensures the data is serialized the same way no
     matter what Python’s hash seed is.  While not recommended, you can
     disable this for a possible performance improvement at the cost of
     caching.

     Default: ‘True’

 -- Data: JSONIFY_PRETTYPRINT_REGULAR

     ‘jsonify’ responses will be output with newlines, spaces, and
     indentation for easier reading by humans.  Always enabled in debug
     mode.

     Default: ‘False’

 -- Data: JSONIFY_MIMETYPE

     The mimetype of ‘jsonify’ responses.

     Default: ‘'application/json'’

 -- Data: TEMPLATES_AUTO_RELOAD

     Reload templates when they are changed.  If not set, it will be
     enabled in debug mode.

     Default: ‘None’

 -- Data: EXPLAIN_TEMPLATE_LOADING

     Log debugging information tracing how a template file was loaded.
     This can be useful to figure out why a template was not loaded or
     the wrong file appears to be loaded.

     Default: ‘False’

 -- Data: MAX_COOKIE_SIZE

     Warn if cookie headers are larger than this many bytes.  Defaults
     to ‘4093’.  Larger cookies may be silently ignored by browsers.
     Set to ‘0’ to disable the warning.

Changed in version 1.0: ‘LOGGER_NAME’ and ‘LOGGER_HANDLER_POLICY’ were
removed.  See Logging for information about configuration.

Added ENV to reflect the ‘FLASK_ENV’ environment variable.

Added SESSION_COOKIE_SAMESITE to control the session
cookie’s ‘SameSite’ option.

Added MAX_COOKIE_SIZE to control a warning from Werkzeug.

New in version 0.11: ‘SESSION_REFRESH_EACH_REQUEST’,
‘TEMPLATES_AUTO_RELOAD’, ‘LOGGER_HANDLER_POLICY’,
‘EXPLAIN_TEMPLATE_LOADING’

New in version 0.10: ‘JSON_AS_ASCII’, ‘JSON_SORT_KEYS’,
‘JSONIFY_PRETTYPRINT_REGULAR’

New in version 0.9: ‘PREFERRED_URL_SCHEME’

New in version 0.8: ‘TRAP_BAD_REQUEST_ERRORS’, ‘TRAP_HTTP_EXCEPTIONS’,
‘APPLICATION_ROOT’, ‘SESSION_COOKIE_DOMAIN’, ‘SESSION_COOKIE_PATH’,
‘SESSION_COOKIE_HTTPONLY’, ‘SESSION_COOKIE_SECURE’

New in version 0.7: ‘PROPAGATE_EXCEPTIONS’,
‘PRESERVE_CONTEXT_ON_EXCEPTION’

New in version 0.6: ‘MAX_CONTENT_LENGTH’

New in version 0.5: ‘SERVER_NAME’

New in version 0.4: ‘LOGGER_NAME’

   ---------- Footnotes ----------

   (1)
https://docs.python.org/3/library/datetime.html#datetime.timedelta

   (2)
https://docs.python.org/3/library/datetime.html#datetime.timedelta

1.11.4 Configuring from Python Files
------------------------------------

Configuration becomes more useful if you can store it in a separate
file, ideally located outside the actual application package.  This
makes packaging and distributing your application possible via various
package handling tools (see Deploying with Setuptools) and
finally modifying the configuration file afterwards.

So a common pattern is this:

     app = Flask(__name__)
     app.config.from_object('yourapplication.default_settings')
     app.config.from_envvar('YOURAPPLICATION_SETTINGS')

This first loads the configuration from the
‘yourapplication.default_settings’ module and then overrides the values
with the contents of the file the ‘YOURAPPLICATION_SETTINGS’ environment
variable points to.  This environment variable can be set in the shell
before starting the server:

Bash

Fish

CMD

Powershell

     $ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
     $ flask run
      * Running on http://127.0.0.1:5000/

     $ set -x YOURAPPLICATION_SETTINGS /path/to/settings.cfg
     $ flask run
      * Running on http://127.0.0.1:5000/

     > set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
     > flask run
      * Running on http://127.0.0.1:5000/

     > $env:YOURAPPLICATION_SETTINGS = "\path\to\settings.cfg"
     > flask run
      * Running on http://127.0.0.1:5000/

The configuration files themselves are actual Python files.  Only values
in uppercase are actually stored in the config object later on.  So make
sure to use uppercase letters for your config keys.

Here is an example of a configuration file:

     # Example configuration
     SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

Make sure to load the configuration very early on, so that extensions
have the ability to access the configuration when starting up.  There
are other methods on the config object as well to load from individual
files.  For a complete reference, read the Config object’s
documentation.

1.11.5 Configuring from Data Files
----------------------------------

It is also possible to load configuration from a file in a format of
your choice using from_file().  For example to load from a
TOML file:

     import toml
     app.config.from_file("config.toml", load=toml.load)

Or from a JSON file:

     import json
     app.config.from_file("config.json", load=json.load)

1.11.6 Configuring from Environment Variables
---------------------------------------------

In addition to pointing to configuration files using environment
variables, you may find it useful (or necessary) to control your
configuration values directly from the environment.  Flask can be
instructed to load all environment variables starting with a specific
prefix into the config using from_prefixed_env().

Environment variables can be set in the shell before starting the
server:

Bash

Fish

CMD

Powershell

     $ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
     $ export FLASK_MAIL_ENABLED=false
     $ flask run
      * Running on http://127.0.0.1:5000/

     $ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f"
     $ set -x FLASK_MAIL_ENABLED false
     $ flask run
      * Running on http://127.0.0.1:5000/

     > set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
     > set FLASK_MAIL_ENABLED=false
     > flask run
      * Running on http://127.0.0.1:5000/

     > $env:FLASK_SECRET_KEY = "5f352379324c22463451387a0aec5d2f"
     > $env:FLASK_MAIL_ENABLED = "false"
     > flask run
      * Running on http://127.0.0.1:5000/

The variables can then be loaded and accessed via the config with a key
equal to the environment variable name without the prefix i.e.

     app.config.from_prefixed_env()
     app.config["SECRET_KEY"]  # Is "5f352379324c22463451387a0aec5d2f"

The prefix is ‘FLASK_’ by default.  This is configurable via the
‘prefix’ argument of from_prefixed_env().

Values will be parsed to attempt to convert them to a more specific type
than strings.  By default json.loads()(1) is used, so any valid JSON
value is possible, including lists and dicts.  This is configurable via
the ‘loads’ argument of from_prefixed_env().

When adding a boolean value with the default JSON parsing, only “true”
and “false”, lowercase, are valid values.  Keep in mind that any
non-empty string is considered ‘True’ by Python.

It is possible to set keys in nested dictionaries by separating the keys
with double underscore (‘__’).  Any intermediate keys that don’t exist
on the parent dict will be initialized to an empty dict.

     $ export FLASK_MYAPI__credentials__username=user123

     app.config["MYAPI"]["credentials"]["username"]  # Is "user123"

On Windows, environment variable keys are always uppercase, therefore
the above example would end up as ‘MYAPI__CREDENTIALS__USERNAME’.

For even more config loading features, including merging and
case-insensitive Windows support, try a dedicated library such as
Dynaconf(2), which includes integration with Flask.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/json.html#json.loads

   (2) https://www.dynaconf.com/

1.11.7 Configuration Best Practices
-----------------------------------

The downside with the approach mentioned earlier is that it makes
testing a little harder.  There is no single 100% solution for this
problem in general, but there are a couple of things you can keep in
mind to improve that experience:

  1. Create your application in a function and register blueprints on
     it.  That way you can create multiple instances of your application
     with different configurations attached which makes unit testing a
     lot easier.  You can use this to pass in configuration as needed.

  2. Do not write code that needs the configuration at import time.  If
     you limit yourself to request-only accesses to the configuration
     you can reconfigure the object later on as needed.

  3. Make sure to load the configuration very early on, so that
     extensions can access the configuration when calling ‘init_app’.

1.11.8 Development / Production
-------------------------------

Most applications need more than one configuration.  There should be at
least separate configurations for the production server and the one used
during development.  The easiest way to handle this is to use a default
configuration that is always loaded and part of the version control, and
a separate configuration that overrides the values as necessary as
mentioned in the example above:

     app = Flask(__name__)
     app.config.from_object('yourapplication.default_settings')
     app.config.from_envvar('YOURAPPLICATION_SETTINGS')

Then you just have to add a separate ‘config.py’ file and export
‘YOURAPPLICATION_SETTINGS=/path/to/config.py’ and you are done.  However
there are alternative ways as well.  For example you could use imports
or subclassing.

What is very popular in the Django world is to make the import explicit
in the config file by adding ‘from yourapplication.default_settings
import *’ to the top of the file and then overriding the changes by
hand.  You could also inspect an environment variable like
‘YOURAPPLICATION_MODE’ and set that to ‘production’, ‘development’ etc
and import different hard-coded files based on that.

An interesting pattern is also to use classes and inheritance for
configuration:

     class Config(object):
         TESTING = False

     class ProductionConfig(Config):
         DATABASE_URI = 'mysql://user@localhost/foo'

     class DevelopmentConfig(Config):
         DATABASE_URI = "sqlite:////tmp/foo.db"

     class TestingConfig(Config):
         DATABASE_URI = 'sqlite:///:memory:'
         TESTING = True

To enable such a config you just have to call into from_object().:

     app.config.from_object('configmodule.ProductionConfig')

Note that from_object() does not instantiate the class
object.  If you need to instantiate the class, such as to access a
property, then you must do so before calling from_object().:

     from configmodule import ProductionConfig
     app.config.from_object(ProductionConfig())

     # Alternatively, import via string:
     from werkzeug.utils import import_string
     cfg = import_string('configmodule.ProductionConfig')()
     app.config.from_object(cfg)

Instantiating the configuration object allows you to use ‘@property’ in
your configuration classes:

     class Config(object):
         """Base config, uses staging database server."""
         TESTING = False
         DB_SERVER = '192.168.1.56'

         @property
         def DATABASE_URI(self):  # Note: all caps
             return f"mysql://user@{self.DB_SERVER}/foo"

     class ProductionConfig(Config):
         """Uses production database server."""
         DB_SERVER = '192.168.19.32'

     class DevelopmentConfig(Config):
         DB_SERVER = 'localhost'

     class TestingConfig(Config):
         DB_SERVER = 'localhost'
         DATABASE_URI = 'sqlite:///:memory:'

There are many different ways and it’s up to you how you want to manage
your configuration files.  However here a list of good recommendations:

   - Keep a default configuration in version control.  Either populate
     the config with this default configuration or import it in your own
     configuration files before overriding values.

   - Use an environment variable to switch between the configurations.
     This can be done from outside the Python interpreter and makes
     development and deployment much easier because you can quickly and
     easily switch between different configs without having to touch the
     code at all.  If you are working often on different projects you
     can even create your own script for sourcing that activates a
     virtualenv and exports the development configuration for you.

   - Use a tool like fabric(1) in production to push code and
     configurations separately to the production server(s).  For some
     details about how to do that, head over to the Deploying with Fabric
     pattern.

   ---------- Footnotes ----------

   (1) https://www.fabfile.org/

1.11.9 Instance Folders
-----------------------

New in version 0.8.

Flask 0.8 introduces instance folders.  Flask for a long time made it
possible to refer to paths relative to the application’s folder directly
(via ‘Flask.root_path’).  This was also how many developers loaded
configurations stored next to the application.  Unfortunately however
this only works well if applications are not packages in which case the
root path refers to the contents of the package.

With Flask 0.8 a new attribute was introduced: ‘Flask.instance_path’.
It refers to a new concept called the “instance folder”.  The instance
folder is designed to not be under version control and be deployment
specific.  It’s the perfect place to drop things that either change at
runtime or configuration files.

You can either explicitly provide the path of the instance folder when
creating the Flask application or you can let Flask autodetect the
instance folder.  For explicit configuration use the ‘instance_path’
parameter:

     app = Flask(__name__, instance_path='/path/to/instance/folder')

Please keep in mind that this path `must' be absolute when provided.

If the ‘instance_path’ parameter is not provided the following default
locations are used:

   - Uninstalled module:

          /myapp.py
          /instance

   - Uninstalled package:

          /myapp
              /__init__.py
          /instance

   - Installed module or package:

          $PREFIX/lib/pythonX.Y/site-packages/myapp
          $PREFIX/var/myapp-instance

     ‘$PREFIX’ is the prefix of your Python installation.  This can be
     ‘/usr’ or the path to your virtualenv.  You can print the value of
     ‘sys.prefix’ to see what the prefix is set to.

Since the config object provided loading of configuration files from
relative filenames we made it possible to change the loading via
filenames to be relative to the instance path if wanted.  The behavior
of relative paths in config files can be flipped between “relative to
the application root” (the default) to “relative to instance folder” via
the ‘instance_relative_config’ switch to the application constructor:

     app = Flask(__name__, instance_relative_config=True)

Here is a full example of how to configure Flask to preload the config
from a module and then override the config from a file in the instance
folder if it exists:

     app = Flask(__name__, instance_relative_config=True)
     app.config.from_object('yourapplication.default_settings')
     app.config.from_pyfile('application.cfg', silent=True)

The path to the instance folder can be found via the
‘Flask.instance_path’.  Flask also provides a shortcut to open a file
from the instance folder with ‘Flask.open_instance_resource()’.

Example usage for both:

     filename = os.path.join(app.instance_path, 'application.cfg')
     with open(filename) as f:
         config = f.read()

     # or via open_instance_resource:
     with app.open_instance_resource('application.cfg') as f:
         config = f.read()

1.12 Signals
============

New in version 0.6.

Starting with Flask 0.6, there is integrated support for signalling in
Flask.  This support is provided by the excellent blinker(1) library and
will gracefully fall back if it is not available.

What are signals?  Signals help you decouple applications by sending
notifications when actions occur elsewhere in the core framework or
another Flask extensions.  In short, signals allow certain senders to
notify subscribers that something happened.

Flask comes with a couple of signals and other extensions might provide
more.  Also keep in mind that signals are intended to notify subscribers
and should not encourage subscribers to modify data.  You will notice
that there are signals that appear to do the same thing like some of the
builtin decorators do (eg: request_started is very similar
to before_request()).  However, there are differences in how
they work.  The core before_request() handler, for example,
is executed in a specific order and is able to abort the request early
by returning a response.  In contrast all signal handlers are executed
in undefined order and do not modify any data.

The big advantage of signals over handlers is that you can safely
subscribe to them for just a split second.  These temporary
subscriptions are helpful for unit testing for example.  Say you want to
know what templates were rendered as part of a request: signals allow
you to do exactly that.

   ---------- Footnotes ----------

   (1) https://pypi.org/project/blinker/

1.12.1 Subscribing to Signals
-----------------------------

To subscribe to a signal, you can use the connect()(1) method of a
signal.  The first argument is the function that should be called when
the signal is emitted, the optional second argument specifies a sender.
To unsubscribe from a signal, you can use the disconnect()(2) method.

For all core Flask signals, the sender is the application that issued
the signal.  When you subscribe to a signal, be sure to also provide a
sender unless you really want to listen for signals from all
applications.  This is especially true if you are developing an
extension.

For example, here is a helper context manager that can be used in a unit
test to determine which templates were rendered and what variables were
passed to the template:

     from flask import template_rendered
     from contextlib import contextmanager

     @contextmanager
     def captured_templates(app):
         recorded = []
         def record(sender, template, context, **extra):
             recorded.append((template, context))
         template_rendered.connect(record, app)
         try:
             yield recorded
         finally:
             template_rendered.disconnect(record, app)

This can now easily be paired with a test client:

     with captured_templates(app) as templates:
         rv = app.test_client().get('/')
         assert rv.status_code == 200
         assert len(templates) == 1
         template, context = templates[0]
         assert template.name == 'index.html'
         assert len(context['items']) == 10

Make sure to subscribe with an extra ‘**extra’ argument so that your
calls don’t fail if Flask introduces new arguments to the signals.

All the template rendering in the code issued by the application ‘app’
in the body of the ‘with’ block will now be recorded in the ‘templates’
variable.  Whenever a template is rendered, the template object as well
as context are appended to it.

Additionally there is a convenient helper method (connected_to()(3))
that allows you to temporarily subscribe a function to a signal with a
context manager on its own.  Because the return value of the context
manager cannot be specified that way, you have to pass the list in as an
argument:

     from flask import template_rendered

     def captured_templates(app, recorded, **extra):
         def record(sender, template, context):
             recorded.append((template, context))
         return template_rendered.connected_to(record, app)

The example above would then look like this:

     templates = []
     with captured_templates(app, templates, **extra):
         ...
         template, context = templates[0]

     Blinker API Changes: The connected_to()(4) method arrived in
     Blinker with version 1.1.

   ---------- Footnotes ----------

   (1)
https://pythonhosted.org/blinker/index.html#blinker.base.Signal.connect

   (2)
https://pythonhosted.org/blinker/index.html#blinker.base.Signal.disconnect

   (3)
https://pythonhosted.org/blinker/index.html#blinker.base.Signal.connected_to

   (4)
https://pythonhosted.org/blinker/index.html#blinker.base.Signal.connected_to

1.12.2 Creating Signals
-----------------------

If you want to use signals in your own application, you can use the
blinker library directly.  The most common use case are named signals in
a custom Namespace(1)..  This is what is recommended most of the time:

     from blinker import Namespace
     my_signals = Namespace()

Now you can create new signals like this:

     model_saved = my_signals.signal('model-saved')

The name for the signal here makes it unique and also simplifies
debugging.  You can access the name of the signal with the name(2)
attribute.

     For Extension Developers: If you are writing a Flask extension and
     you want to gracefully degrade for missing blinker installations,
     you can do so by using the flask.signals.Namespace.
     class.

   ---------- Footnotes ----------

   (1)
https://pythonhosted.org/blinker/index.html#blinker.base.Namespace

   (2)
https://pythonhosted.org/blinker/index.html#blinker.base.NamedSignal.name

1.12.3 Sending Signals
----------------------

If you want to emit a signal, you can do so by calling the send()(1)
method.  It accepts a sender as first argument and optionally some
keyword arguments that are forwarded to the signal subscribers:

     class Model(object):
         ...

         def save(self):
             model_saved.send(self)

Try to always pick a good sender.  If you have a class that is emitting
a signal, pass ‘self’ as sender.  If you are emitting a signal from a
random function, you can pass ‘current_app._get_current_object()’ as
sender.

     Passing Proxies as Senders: Never pass current_app as
     sender to a signal.  Use ‘current_app._get_current_object()’
     instead.  The reason for this is that current_app is a
     proxy and not the real application object.

   ---------- Footnotes ----------

   (1)
https://pythonhosted.org/blinker/index.html#blinker.base.Signal.send

1.12.4 Signals and Flask’s Request Context
------------------------------------------

Signals fully support The Request Context when receiving
signals.  Context-local variables are consistently available between
request_started and request_finished, so you can
rely on flask.g and others as needed.  Note the limitations
described in Sending Signals and the
request_tearing_down signal.

1.12.5 Decorator Based Signal Subscriptions
-------------------------------------------

With Blinker 1.1 you can also easily subscribe to signals by using the
new ‘connect_via()’ decorator:

     from flask import template_rendered

     @template_rendered.connect_via(app)
     def when_template_rendered(sender, template, context, **extra):
         print(f'Template {template.name} is rendered with {context}')

1.12.6 Core Signals
-------------------

Take a look at Signals for a list of all builtin signals.

1.13 Pluggable Views
====================

New in version 0.7.

Flask 0.7 introduces pluggable views inspired by the generic views from
Django which are based on classes instead of functions.  The main
intention is that you can replace parts of the implementations and this
way have customizable pluggable views.

1.13.1 Basic Principle
----------------------

Consider you have a function that loads a list of objects from the
database and renders into a template:

     @app.route('/users/')
     def show_users(page):
         users = User.query.all()
         return render_template('users.html', users=users)

This is simple and flexible, but if you want to provide this view in a
generic fashion that can be adapted to other models and templates as
well you might want more flexibility.  This is where pluggable
class-based views come into place.  As the first step to convert this
into a class based view you would do this:

     from flask.views import View

     class ShowUsers(View):

         def dispatch_request(self):
             users = User.query.all()
             return render_template('users.html', objects=users)

     app.add_url_rule('/users/', view_func=ShowUsers.as_view('show_users'))

As you can see what you have to do is to create a subclass of
flask.views.View and implement dispatch_request().
Then we have to convert that class into an actual view function by using
the as_view() class method.  The string you pass to that
function is the name of the endpoint that view will then have.  But this
by itself is not helpful, so let’s refactor the code a bit:

     from flask.views import View

     class ListView(View):

         def get_template_name(self):
             raise NotImplementedError()

         def render_template(self, context):
             return render_template(self.get_template_name(), **context)

         def dispatch_request(self):
             context = {'objects': self.get_objects()}
             return self.render_template(context)

     class UserView(ListView):

         def get_template_name(self):
             return 'users.html'

         def get_objects(self):
             return User.query.all()

This of course is not that helpful for such a small example, but it’s
good enough to explain the basic principle.  When you have a class-based
view the question comes up what ‘self’ points to.  The way this works is
that whenever the request is dispatched a new instance of the class is
created and the dispatch_request() method is called with the
parameters from the URL rule.  The class itself is instantiated with the
parameters passed to the as_view() function.  For instance
you can write a class like this:

     class RenderTemplateView(View):
         def __init__(self, template_name):
             self.template_name = template_name
         def dispatch_request(self):
             return render_template(self.template_name)

And then you can register it like this:

     app.add_url_rule('/about', view_func=RenderTemplateView.as_view(
         'about_page', template_name='about.html'))

1.13.2 Method Hints
-------------------

Pluggable views are attached to the application like a regular function
by either using route() or better add_url_rule().
That however also means that you would have to provide the names of the
HTTP methods the view supports when you attach this.  In order to move
that information to the class you can provide a methods.
attribute that has this information:

     class MyView(View):
         methods = ['GET', 'POST']

         def dispatch_request(self):
             if request.method == 'POST':
                 ...
             ...

     app.add_url_rule('/myview', view_func=MyView.as_view('myview'))

1.13.3 Method Based Dispatching
-------------------------------

For RESTful APIs it’s especially helpful to execute a different function
for each HTTP method.  With the flask.views.MethodView you
can easily do that.  Each HTTP method maps to a method of the class with
the same name (just in lowercase):

     from flask.views import MethodView

     class UserAPI(MethodView):

         def get(self):
             users = User.query.all()
             ...

         def post(self):
             user = User.from_form_data(request.form)
             ...

     app.add_url_rule('/users/', view_func=UserAPI.as_view('users'))

That way you also don’t have to provide the methods.
attribute.  It’s automatically set based on the methods defined in the
class.

1.13.4 Decorating Views
-----------------------

Since the view class itself is not the view function that is added to
the routing system it does not make much sense to decorate the class
itself.  Instead you either have to decorate the return value of
as_view() by hand:

     def user_required(f):
         """Checks whether user is logged in or raises error 401."""
         def decorator(*args, **kwargs):
             if not g.user:
                 abort(401)
             return f(*args, **kwargs)
         return decorator

     view = user_required(UserAPI.as_view('users'))
     app.add_url_rule('/users/', view_func=view)

Starting with Flask 0.8 there is also an alternative way where you can
specify a list of decorators to apply in the class declaration:

     class UserAPI(MethodView):
         decorators = [user_required]

Due to the implicit self from the caller’s perspective you cannot use
regular view decorators on the individual methods of the view however,
keep this in mind.

1.13.5 Method Views for APIs
----------------------------

Web APIs are often working very closely with HTTP verbs so it makes a
lot of sense to implement such an API based on the MethodView.
That said, you will notice that the API will require different URL
rules that go to the same method view most of the time.  For instance
consider that you are exposing a user object on the web:

URL                 Method              Description

‘/users/’           ‘GET’               Gives a list of all users

‘/users/’           ‘POST’              Creates a new user

‘/users/<id>’       ‘GET’               Shows a single user

‘/users/<id>’       ‘PUT’               Updates a single user

‘/users/<id>’       ‘DELETE’            Deletes a single user

So how would you go about doing that with the MethodView.?
The trick is to take advantage of the fact that you can provide multiple
rules to the same view.

Let’s assume for the moment the view would look like this:

     class UserAPI(MethodView):

         def get(self, user_id):
             if user_id is None:
                 # return a list of users
                 pass
             else:
                 # expose a single user
                 pass

         def post(self):
             # create a new user
             pass

         def delete(self, user_id):
             # delete a single user
             pass

         def put(self, user_id):
             # update a single user
             pass

So how do we hook this up with the routing system?  By adding two rules
and explicitly mentioning the methods for each:

     user_view = UserAPI.as_view('user_api')
     app.add_url_rule('/users/', defaults={'user_id': None},
                      view_func=user_view, methods=['GET',])
     app.add_url_rule('/users/', view_func=user_view, methods=['POST',])
     app.add_url_rule('/users/<int:user_id>', view_func=user_view,
                      methods=['GET', 'PUT', 'DELETE'])

If you have a lot of APIs that look similar you can refactor that
registration code:

     def register_api(view, endpoint, url, pk='id', pk_type='int'):
         view_func = view.as_view(endpoint)
         app.add_url_rule(url, defaults={pk: None},
                          view_func=view_func, methods=['GET',])
         app.add_url_rule(url, view_func=view_func, methods=['POST',])
         app.add_url_rule(f'{url}<{pk_type}:{pk}>', view_func=view_func,
                          methods=['GET', 'PUT', 'DELETE'])

     register_api(UserAPI, 'user_api', '/users/', pk='user_id')

1.14 The Application Context
============================

The application context keeps track of the application-level data during
a request, CLI command, or other activity.  Rather than passing the
application around to each function, the current_app and
g proxies are accessed instead.

This is similar to The Request Context, which keeps track of
request-level data during a request.  A corresponding application
context is pushed when a request context is pushed.

1.14.1 Purpose of the Context
-----------------------------

The Flask application object has attributes, such as
config, that are useful to access within views and CLI commands.
However, importing the ‘app’ instance within the modules
in your project is prone to circular import issues.  When using the
app factory pattern or writing reusable blueprints.
or extensions there won’t be an ‘app’ instance to import at
all.

Flask solves this issue with the `application context'.  Rather than
referring to an ‘app’ directly, you use the current_app.
proxy, which points to the application handling the current activity.

Flask automatically `pushes' an application context when handling a
request.  View functions, error handlers, and other functions that run
during a request will have access to current_app.

Flask will also automatically push an app context when running CLI
commands registered with Flask.cli using
‘@app.cli.command()’.

1.14.2 Lifetime of the Context
------------------------------

The application context is created and destroyed as necessary.  When a
Flask application begins handling a request, it pushes an application
context and a request context.  When the request ends it pops
the request context then the application context.  Typically, an
application context will have the same lifetime as a request.

See The Request Context for more information about how the
contexts work and the full life cycle of a request.

1.14.3 Manually Push a Context
------------------------------

If you try to access current_app, or anything that uses it,
outside an application context, you’ll get this error message:

     RuntimeError: Working outside of application context.

     This typically means that you attempted to use functionality that
     needed to interface with the current application object in some way.
     To solve this, set up an application context with app.app_context().

If you see that error while configuring your application, such as when
initializing an extension, you can push a context manually since you
have direct access to the ‘app’.  Use app_context() in a
‘with’ block, and everything that runs in the block will have access to
current_app.

     def create_app():
         app = Flask(__name__)

         with app.app_context():
             init_db()

         return app

If you see that error somewhere else in your code not related to
configuring the application, it most likely indicates that you should
move that code into a view function or CLI command.

1.14.4 Storing Data
-------------------

The application context is a good place to store common data during a
request or CLI command.  Flask provides the g object for this
purpose.  It is a simple namespace object that has the same lifetime as
an application context.

     Note: The ‘g’ name stands for “global”, but that is referring to
     the data being global `within a context'.  The data on ‘g’ is lost
     after the context ends, and it is not an appropriate place to store
     data between requests.  Use the session or a database to
     store data across requests.

A common use for g is to manage resources during a request.

  1. ‘get_X()’ creates resource ‘X’ if it does not exist, caching it as
     ‘g.X’.

  2. ‘teardown_X()’ closes or otherwise deallocates the resource if it
     exists.  It is registered as a teardown_appcontext().
     handler.

For example, you can manage a database connection using this pattern:

     from flask import g

     def get_db():
         if 'db' not in g:
             g.db = connect_to_database()

         return g.db

     @app.teardown_appcontext
     def teardown_db(exception):
         db = g.pop('db', None)

         if db is not None:
             db.close()

During a request, every call to ‘get_db()’ will return the same
connection, and it will be closed automatically at the end of the
request.

You can use LocalProxy(1) to make a new context local from ‘get_db()’:

     from werkzeug.local import LocalProxy
     db = LocalProxy(get_db)

Accessing ‘db’ will call ‘get_db’ internally, in the same way that
current_app works.

__________________________________________________________________

If you’re writing an extension, see g should be reserved for user
code.  You may store internal data on the context itself, but be sure to
use a sufficiently unique name.  The current context is accessed with
_app_ctx_stack.top.  For more information see Flask Extension Development.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/local/#werkzeug.local.LocalProxy

1.14.5 Events and Signals
-------------------------

The application will call functions registered with
teardown_appcontext() when the application context is popped.

If signals_available is true, the following signals are
sent: appcontext_pushed, appcontext_tearing_down,
and appcontext_popped.

1.15 The Request Context
========================

The request context keeps track of the request-level data during a
request.  Rather than passing the request object to each function that
runs during a request, the request and session.
proxies are accessed instead.

This is similar to The Application Context, which keeps track
of the application-level data independent of a request.  A corresponding
application context is pushed when a request context is pushed.

1.15.1 Purpose of the Context
-----------------------------

When the Flask application handles a request, it creates a
Request object based on the environment it received from the
WSGI server.  Because a `worker' (thread, process, or coroutine
depending on the server) handles only one request at a time, the request
data can be considered global to that worker during that request.  Flask
uses the term `context local' for this.

Flask automatically `pushes' a request context when handling a request.
View functions, error handlers, and other functions that run during a
request will have access to the request proxy, which points
to the request object for the current request.

1.15.2 Lifetime of the Context
------------------------------

When a Flask application begins handling a request, it pushes a request
context, which also pushes an app context.  When the request
ends it pops the request context then the application context.

The context is unique to each thread (or other worker type).
request cannot be passed to another thread, the other thread will
have a different context stack and will not know about the request the
parent thread was pointing to.

Context locals are implemented in Werkzeug.  See Context Locals(1) for
more information on how this works internally.

   ---------- Footnotes ----------

   (1) https://werkzeug.palletsprojects.com/en/2.1.x/local/

1.15.3 Manually Push a Context
------------------------------

If you try to access request, or anything that uses it,
outside a request context, you’ll get this error message:

     RuntimeError: Working outside of request context.

     This typically means that you attempted to use functionality that
     needed an active HTTP request. Consult the documentation on testing
     for information about how to avoid this problem.

This should typically only happen when testing code that expects an
active request.  One option is to use the test client to
simulate a full request.  Or you can use test_request_context()
in a ‘with’ block, and everything that runs in the block will have
access to request, populated with your test data.

     def generate_report(year):
         format = request.args.get('format')
         ...

     with app.test_request_context(
             '/make_report/2017', data={'format': 'short'}):
         generate_report()

If you see that error somewhere else in your code not related to
testing, it most likely indicates that you should move that code into a
view function.

For information on how to use the request context from the interactive
Python shell, see Working with the Shell.

1.15.4 How the Context Works
----------------------------

The Flask.wsgi_app() method is called to handle each
request.  It manages the contexts during the request.  Internally, the
request and application contexts work as stacks, see
_request_ctx_stack and _app_ctx_stack.  When contexts
are pushed onto the stack, the proxies that depend on them are available
and point at information from the top context on the stack.

When the request starts, a RequestContext is created and
pushed, which creates and pushes an AppContext first if a
context for that application is not already the top context.  While
these contexts are pushed, the current_app, g,
request, and session proxies are available to the
original thread handling the request.

Because the contexts are stacks, other contexts may be pushed to change
the proxies during a request.  While this is not a common pattern, it
can be used in advanced applications to, for example, do internal
redirects or chain different applications together.

After the request is dispatched and a response is generated and sent,
the request context is popped, which then pops the application context.
Immediately before they are popped, the teardown_request().
and teardown_appcontext() functions are executed.  These
execute even if an unhandled exception occurred during dispatch.

1.15.5 Callbacks and Errors
---------------------------

Flask dispatches a request in multiple stages which can affect the
request, response, and how errors are handled.  The contexts are active
during all of these stages.

A Blueprint can add handlers for these events that are
specific to the blueprint.  The handlers for a blueprint will run if the
blueprint owns the route that matches the request.

  1. Before each request, see before_request() functions are
     called.  If one of these functions return a value, the other
     functions are skipped.  The return value is treated as the response
     and the view function is not called.

  2. If the before_request() functions did not return a
     response, the view function for the matched route is called and
     returns a response.

  3. The return value of the view is converted into an actual response
     object and passed to the after_request() functions.
     Each function returns a modified or new response object.

  4. After the response is returned, the contexts are popped, which
     calls the teardown_request() and
     teardown_appcontext() functions.  These functions are called
     even if an unhandled exception was raised at any point above.

If an exception is raised before the teardown functions, Flask tries to
match it with an errorhandler() function to handle the
exception and return a response.  If no error handler is found, or the
handler itself raises an exception, Flask returns a generic ‘500
Internal Server Error’ response.  The teardown functions are still
called, and are passed the exception object.

If debug mode is enabled, unhandled exceptions are not converted to a
‘500’ response and instead are propagated to the WSGI server.  This
allows the development server to present the interactive debugger with
the traceback.

1.15.5.1 Teardown Callbacks
...........................

The teardown callbacks are independent of the request dispatch, and are
instead called by the contexts when they are popped.  The functions are
called even if there is an unhandled exception during dispatch, and for
manually pushed contexts.  This means there is no guarantee that any
other parts of the request dispatch have run first.  Be sure to write
these functions in a way that does not depend on other callbacks and
will not fail.

During testing, it can be useful to defer popping the contexts after the
request ends, so that their data can be accessed in the test function.
Use the test_client() as a ‘with’ block to preserve the
contexts until the ‘with’ block exits.

     from flask import Flask, request

     app = Flask(__name__)

     @app.route('/')
     def hello():
         print('during view')
         return 'Hello, World!'

     @app.teardown_request
     def show_teardown(exception):
         print('after with block')

     with app.test_request_context():
         print('during with block')

     # teardown functions are called after the context with block exits

     with app.test_client() as client:
         client.get('/')
         # the contexts are not popped even though the request ended
         print(request.path)

     # the contexts are popped and teardown functions are called after
     # the client with block exits

1.15.5.2 Signals
................

If signals_available is true, the following signals are
sent:

  1. request_started is sent before the
     before_request() functions are called.

  2. request_finished is sent after the
     after_request() functions are called.

  3. got_request_exception is sent when an exception begins
     to be handled, but before an errorhandler() is looked up
     or called.

  4. request_tearing_down is sent after the
     teardown_request() functions are called.

1.15.6 Context Preservation on Error
------------------------------------

At the end of a request, the request context is popped and all data
associated with it is destroyed.  If an error occurs during development,
it is useful to delay destroying the data for debugging purposes.

When the development server is running in development mode (the
‘FLASK_ENV’ environment variable is set to ‘'development'’), the error
and data will be preserved and shown in the interactive debugger.

This behavior can be controlled with the
PRESERVE_CONTEXT_ON_EXCEPTION config.  As described above, it
defaults to ‘True’ in the development environment.

Do not enable PRESERVE_CONTEXT_ON_EXCEPTION in production,
as it will cause your application to leak memory on exceptions.

1.15.7 Notes On Proxies
-----------------------

Some of the objects provided by Flask are proxies to other objects.  The
proxies are accessed in the same way for each worker thread, but point
to the unique object bound to each worker behind the scenes as described
on this page.

Most of the time you don’t have to care about that, but there are some
exceptions where it is good to know that this object is actually a
proxy:

   - The proxy objects cannot fake their type as the actual object
     types.  If you want to perform instance checks, you have to do that
     on the object being proxied.

   - The reference to the proxied object is needed in some situations,
     such as sending Signals or passing data to a background
     thread.

If you need to access the underlying object that is proxied, use the
_get_current_object()(1) method:

     app = current_app._get_current_object()
     my_signal.send(app)

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/local/#werkzeug.local.LocalProxy._get_current_object

1.16 Modular Applications with Blueprints
=========================================

New in version 0.7.

Flask uses a concept of `blueprints' for making application components
and supporting common patterns within an application or across
applications.  Blueprints can greatly simplify how large applications
work and provide a central means for Flask extensions to register
operations on applications.  A Blueprint object works
similarly to a Flask application object, but it is not
actually an application.  Rather it is a `blueprint' of how to construct
or extend an application.

1.16.1 Why Blueprints?
----------------------

Blueprints in Flask are intended for these cases:

   * Factor an application into a set of blueprints.  This is ideal for
     larger applications; a project could instantiate an application
     object, initialize several extensions, and register a collection of
     blueprints.

   * Register a blueprint on an application at a URL prefix and/or
     subdomain.  Parameters in the URL prefix/subdomain become common
     view arguments (with defaults) across all view functions in the
     blueprint.

   * Register a blueprint multiple times on an application with
     different URL rules.

   * Provide template filters, static files, templates, and other
     utilities through blueprints.  A blueprint does not have to
     implement applications or view functions.

   * Register a blueprint on an application for any of these cases when
     initializing a Flask extension.

A blueprint in Flask is not a pluggable app because it is not actually
an application – it’s a set of operations which can be registered on an
application, even multiple times.  Why not have multiple application
objects?  You can do that (see Application Dispatching), but
your applications will have separate configs and will be managed at the
WSGI layer.

Blueprints instead provide separation at the Flask level, share
application config, and can change an application object as necessary
with being registered.  The downside is that you cannot unregister a
blueprint once an application was created without having to destroy the
whole application object.

1.16.2 The Concept of Blueprints
--------------------------------

The basic concept of blueprints is that they record operations to
execute when registered on an application.  Flask associates view
functions with blueprints when dispatching requests and generating URLs
from one endpoint to another.

1.16.3 My First Blueprint
-------------------------

This is what a very basic blueprint looks like.  In this case we want to
implement a blueprint that does simple rendering of static templates:

     from flask import Blueprint, render_template, abort
     from jinja2 import TemplateNotFound

     simple_page = Blueprint('simple_page', __name__,
                             template_folder='templates')

     @simple_page.route('/', defaults={'page': 'index'})
     @simple_page.route('/<page>')
     def show(page):
         try:
             return render_template(f'pages/{page}.html')
         except TemplateNotFound:
             abort(404)

When you bind a function with the help of the ‘@simple_page.route’
decorator, the blueprint will record the intention of registering the
function ‘show’ on the application when it’s later registered.
Additionally it will prefix the endpoint of the function with the name
of the blueprint which was given to the Blueprint constructor
(in this case also ‘simple_page’).  The blueprint’s name does not modify
the URL, only the endpoint.

1.16.4 Registering Blueprints
-----------------------------

So how do you register that blueprint?  Like this:

     from flask import Flask
     from yourapplication.simple_page import simple_page

     app = Flask(__name__)
     app.register_blueprint(simple_page)

If you check the rules registered on the application, you will find
these:

     >>> app.url_map
     Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
      <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
      <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>])

The first one is obviously from the application itself for the static
files.  The other two are for the ‘show’ function of the ‘simple_page’
blueprint.  As you can see, they are also prefixed with the name of the
blueprint and separated by a dot (‘.’).

Blueprints however can also be mounted at different locations:

     app.register_blueprint(simple_page, url_prefix='/pages')

And sure enough, these are the generated rules:

     >>> app.url_map
     Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
      <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
      <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>])

On top of that you can register blueprints multiple times though not
every blueprint might respond properly to that.  In fact it depends on
how the blueprint is implemented if it can be mounted more than once.

1.16.5 Nesting Blueprints
-------------------------

It is possible to register a blueprint on another blueprint.

     parent = Blueprint('parent', __name__, url_prefix='/parent')
     child = Blueprint('child', __name__, url_prefix='/child')
     parent.register_blueprint(child)
     app.register_blueprint(parent)

The child blueprint will gain the parent’s name as a prefix to its name,
and child URLs will be prefixed with the parent’s URL prefix.

     url_for('parent.child.create')
     /parent/child/create

Blueprint-specific before request functions, etc.  registered with the
parent will trigger for the child.  If a child does not have an error
handler that can handle a given exception, the parent’s will be tried.

1.16.6 Blueprint Resources
--------------------------

Blueprints can provide resources as well.  Sometimes you might want to
introduce a blueprint only for the resources it provides.

1.16.6.1 Blueprint Resource Folder
..................................

Like for regular applications, blueprints are considered to be contained
in a folder.  While multiple blueprints can originate from the same
folder, it does not have to be the case and it’s usually not
recommended.

The folder is inferred from the second argument to Blueprint.
which is usually ‘__name__’.  This argument specifies what logical
Python module or package corresponds to the blueprint.  If it points to
an actual Python package that package (which is a folder on the
filesystem) is the resource folder.  If it’s a module, the package the
module is contained in will be the resource folder.  You can access the
Blueprint.root_path property to see what the resource folder
is:

     >>> simple_page.root_path
     '/Users/username/TestProject/yourapplication'

To quickly open sources from this folder you can use the
open_resource() function:

     with simple_page.open_resource('static/style.css') as f:
         code = f.read()

1.16.6.2 Static Files
.....................

A blueprint can expose a folder with static files by providing the path
to the folder on the filesystem with the ‘static_folder’ argument.  It
is either an absolute path or relative to the blueprint’s location:

     admin = Blueprint('admin', __name__, static_folder='static')

By default the rightmost part of the path is where it is exposed on the
web.  This can be changed with the ‘static_url_path’ argument.  Because
the folder is called ‘static’ here it will be available at the
‘url_prefix’ of the blueprint + ‘/static’.  If the blueprint has the
prefix ‘/admin’, the static URL will be ‘/admin/static’.

The endpoint is named ‘blueprint_name.static’.  You can generate URLs to
it with url_for() like you would with the static folder of
the application:

     url_for('admin.static', filename='style.css')

However, if the blueprint does not have a ‘url_prefix’, it is not
possible to access the blueprint’s static folder.  This is because the
URL would be ‘/static’ in this case, and the application’s ‘/static’
route takes precedence.  Unlike template folders, blueprint static
folders are not searched if the file does not exist in the application
static folder.

1.16.6.3 Templates
..................

If you want the blueprint to expose templates you can do that by
providing the ‘template_folder’ parameter to the Blueprint.
constructor:

     admin = Blueprint('admin', __name__, template_folder='templates')

For static files, the path can be absolute or relative to the blueprint
resource folder.

The template folder is added to the search path of templates but with a
lower priority than the actual application’s template folder.  That way
you can easily override templates that a blueprint provides in the
actual application.  This also means that if you don’t want a blueprint
template to be accidentally overridden, make sure that no other
blueprint or actual application template has the same relative path.
When multiple blueprints provide the same relative template path the
first blueprint registered takes precedence over the others.

So if you have a blueprint in the folder ‘yourapplication/admin’ and you
want to render the template ‘'admin/index.html'’ and you have provided
‘templates’ as a ‘template_folder’ you will have to create a file like
this: ‘yourapplication/admin/templates/admin/index.html’.  The reason
for the extra ‘admin’ folder is to avoid getting our template overridden
by a template named ‘index.html’ in the actual application template
folder.

To further reiterate this: if you have a blueprint named ‘admin’ and you
want to render a template called ‘index.html’ which is specific to this
blueprint, the best idea is to lay out your templates like this:

     yourpackage/
         blueprints/
             admin/
                 templates/
                     admin/
                         index.html
                 __init__.py

And then when you want to render the template, use ‘admin/index.html’ as
the name to look up the template by.  If you encounter problems loading
the correct templates enable the ‘EXPLAIN_TEMPLATE_LOADING’ config
variable which will instruct Flask to print out the steps it goes
through to locate templates on every ‘render_template’ call.

1.16.7 Building URLs
--------------------

If you want to link from one page to another you can use the
url_for() function just like you normally would do just that you
prefix the URL endpoint with the name of the blueprint and a dot (‘.’):

     url_for('admin.index')

Additionally if you are in a view function of a blueprint or a rendered
template and you want to link to another endpoint of the same blueprint,
you can use relative redirects by prefixing the endpoint with a dot
only:

     url_for('.index')

This will link to ‘admin.index’ for instance in case the current request
was dispatched to any other admin blueprint endpoint.

1.16.8 Blueprint Error Handlers
-------------------------------

Blueprints support the ‘errorhandler’ decorator just like the
Flask application object, so it is easy to make Blueprint-specific
custom error pages.

Here is an example for a “404 Page Not Found” exception:

     @simple_page.errorhandler(404)
     def page_not_found(e):
         return render_template('pages/404.html')

Most errorhandlers will simply work as expected; however, there is a
caveat concerning handlers for 404 and 405 exceptions.  These
errorhandlers are only invoked from an appropriate ‘raise’ statement or
a call to ‘abort’ in another of the blueprint’s view functions; they are
not invoked by, e.g., an invalid URL access.  This is because the
blueprint does not “own” a certain URL space, so the application
instance has no way of knowing which blueprint error handler it should
run if given an invalid URL. If you would like to execute different
handling strategies for these errors based on URL prefixes, they may be
defined at the application level using the ‘request’ proxy object:

     @app.errorhandler(404)
     @app.errorhandler(405)
     def _handle_api_error(ex):
         if request.path.startswith('/api/'):
             return jsonify(error=str(ex)), ex.code
         else:
             return ex

See Handling Application Errors.

1.17 Extensions
===============

Extensions are extra packages that add functionality to a Flask
application.  For example, an extension might add support for sending
email or connecting to a database.  Some extensions add entire new
frameworks to help build certain types of applications, like a REST API.

1.17.1 Finding Extensions
-------------------------

Flask extensions are usually named “Flask-Foo” or “Foo-Flask”.  You can
search PyPI for packages tagged with Framework :: Flask(1).

   ---------- Footnotes ----------

   (1) https://pypi.org/search/?c=Framework+%3A%3A+Flask

1.17.2 Using Extensions
-----------------------

Consult each extension’s documentation for installation, configuration,
and usage instructions.  Generally, extensions pull their own
configuration from app.config and are passed an application
instance during initialization.  For example, an extension called
“Flask-Foo” might be used like this:

     from flask_foo import Foo

     foo = Foo()

     app = Flask(__name__)
     app.config.update(
         FOO_BAR='baz',
         FOO_SPAM='eggs',
     )

     foo.init_app(app)

1.17.3 Building Extensions
--------------------------

While the PyPI(1) contains many Flask extensions, you may not find an
extension that fits your need.  If this is the case, you can create your
own.  Read Flask Extension Development to develop your own
Flask extension.

   ---------- Footnotes ----------

   (1) https://pypi.org/search/?c=Framework+%3A%3A+Flask

1.18 Command Line Interface
===========================

Installing Flask installs the ‘flask’ script, a Click(1) command line
interface, in your virtualenv.  Executed from the terminal, this script
gives access to built-in, extension, and application-defined commands.
The ‘--help’ option will give more information about any commands and
options.

   ---------- Footnotes ----------

   (1) https://click.palletsprojects.com/

1.18.1 Application Discovery
----------------------------

The ‘flask’ command is installed by Flask, not your application; it must
be told where to find your application in order to use it.  The
‘FLASK_APP’ environment variable is used to specify how to load the
application.

Bash

Fish

CMD

Powershell

     $ export FLASK_APP=hello
     $ flask run

     $ set -x FLASK_APP hello
     $ flask run

     > set FLASK_APP=hello
     > flask run

     > $env:FLASK_APP = "hello"
     > flask run

While ‘FLASK_APP’ supports a variety of options for specifying your
application, most use cases should be simple.  Here are the typical
values:

(nothing)

     The name “app” or “wsgi” is imported (as a “.py” file, or package),
     automatically detecting an app (‘app’ or ‘application’) or factory
     (‘create_app’ or ‘make_app’).

‘FLASK_APP=hello’

     The given name is imported, automatically detecting an app (‘app’
     or ‘application’) or factory (‘create_app’ or ‘make_app’).

__________________________________________________________________

‘FLASK_APP’ has three parts: an optional path that sets the current
working directory, a Python file or dotted import path, and an optional
variable name of the instance or factory.  If the name is a factory, it
can optionally be followed by arguments in parentheses.  The following
values demonstrate these parts:

‘FLASK_APP=src/hello’

     Sets the current working directory to ‘src’ then imports ‘hello’.

‘FLASK_APP=hello.web’

     Imports the path ‘hello.web’.

‘FLASK_APP=hello:app2’

     Uses the ‘app2’ Flask instance in ‘hello’.

‘FLASK_APP="hello:create_app('dev')"’

     The ‘create_app’ factory in ‘hello’ is called with the string
     ‘'dev'’ as the argument.

If ‘FLASK_APP’ is not set, the command will try to import “app” or
“wsgi” (as a “.py” file, or package) and try to detect an application
instance or factory.

Within the given import, the command looks for an application instance
named ‘app’ or ‘application’, then any application instance.  If no
instance is found, the command looks for a factory function named
‘create_app’ or ‘make_app’ that returns an instance.

If parentheses follow the factory name, their contents are parsed as
Python literals and passed as arguments and keyword arguments to the
function.  This means that strings must still be in quotes.

1.18.2 Run the Development Server
---------------------------------

The run command will start the development server.  It
replaces the Flask.run() method in most cases.

     $ flask run
      * Serving Flask app "hello"
      * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

     Warning: Do not use this command to run your application in
     production.  Only use the development server during development.
     The development server is provided for convenience, but is not
     designed to be particularly secure, stable, or efficient.  See
     Deployment Options for how to run in production.

If another program is already using port 5000, you’ll see ‘OSError:
[Errno 98]’ or ‘OSError: [WinError 10013]’ when the server tries to
start.  See Address already in use for how to handle that.

1.18.3 Open a Shell
-------------------

To explore the data in your application, you can start an interactive
Python shell with the shell command.  An application context
will be active, and the app instance will be imported.

     $ flask shell
     Python 3.10.0 (default, Oct 27 2021, 06:59:51) [GCC 11.1.0] on linux
     App: example [production]
     Instance: /home/david/Projects/pallets/flask/instance
     >>>

Use shell_context_processor() to add other automatic
imports.

1.18.4 Environments
-------------------

New in version 1.0.

The environment in which the Flask app runs is set by the ‘FLASK_ENV’
environment variable.  If not set it defaults to ‘production’.  The
other recognized environment is ‘development’.  Flask and extensions may
choose to enable behaviors based on the environment.

If the env is set to ‘development’, the ‘flask’ command will enable
debug mode and ‘flask run’ will enable the interactive debugger and
reloader.

Bash

Fish

CMD

Powershell

     $ export FLASK_ENV=development
     $ flask run
      * Serving Flask app "hello"
      * Environment: development
      * Debug mode: on
      * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
      * Restarting with inotify reloader
      * Debugger is active!
      * Debugger PIN: 223-456-919

     $ set -x FLASK_ENV development
     $ flask run
      * Serving Flask app "hello"
      * Environment: development
      * Debug mode: on
      * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
      * Restarting with inotify reloader
      * Debugger is active!
      * Debugger PIN: 223-456-919

     > set FLASK_ENV=development
     > flask run
      * Serving Flask app "hello"
      * Environment: development
      * Debug mode: on
      * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
      * Restarting with inotify reloader
      * Debugger is active!
      * Debugger PIN: 223-456-919

     > $env:FLASK_ENV = "development"
     > flask run
      * Serving Flask app "hello"
      * Environment: development
      * Debug mode: on
      * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
      * Restarting with inotify reloader
      * Debugger is active!
      * Debugger PIN: 223-456-919

1.18.4.1 Watch Extra Files with the Reloader
............................................

When using development mode, the reloader will trigger whenever your
Python code or imported modules change.  The reloader can watch
additional files with the ‘--extra-files’ option, or the
‘FLASK_RUN_EXTRA_FILES’ environment variable.  Multiple paths are
separated with ‘:’, or ‘;’ on Windows.

Bash

Fish

CMD

Powershell

     $ flask run --extra-files file1:dirA/file2:dirB/
     # or
     $ export FLASK_RUN_EXTRA_FILES=file1:dirA/file2:dirB/
     $ flask run
      * Running on http://127.0.0.1:8000/
      * Detected change in '/path/to/file1', reloading

     $ flask run --extra-files file1:dirA/file2:dirB/
     # or
     $ set -x FLASK_RUN_EXTRA_FILES file1 dirA/file2 dirB/
     $ flask run
      * Running on http://127.0.0.1:8000/
      * Detected change in '/path/to/file1', reloading

     > flask run --extra-files file1:dirA/file2:dirB/
     # or
     > set FLASK_RUN_EXTRA_FILES=file1:dirA/file2:dirB/
     > flask run
      * Running on http://127.0.0.1:8000/
      * Detected change in '/path/to/file1', reloading

     > flask run --extra-files file1:dirA/file2:dirB/
     # or
     > $env:FLASK_RUN_EXTRA_FILES = "file1:dirA/file2:dirB/"
     > flask run
      * Running on http://127.0.0.1:8000/
      * Detected change in '/path/to/file1', reloading

1.18.4.2 Ignore files with the Reloader
.......................................

The reloader can also ignore files using fnmatch(1) patterns with the
‘--exclude-patterns’ option, or the ‘FLASK_RUN_EXCLUDE_PATTERNS’
environment variable.  Multiple patterns are separated with ‘:’, or ‘;’
on Windows.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/fnmatch.html#module-fnmatch

1.18.5 Debug Mode
-----------------

Debug mode will be enabled when ‘FLASK_ENV’ is ‘development’, as
described above.  If you want to control debug mode separately, use
‘FLASK_DEBUG’.  The value ‘1’ enables it, ‘0’ disables it.

1.18.6 Environment Variables From dotenv
----------------------------------------

Rather than setting ‘FLASK_APP’ each time you open a new terminal, you
can use Flask’s dotenv support to set environment variables
automatically.

If python-dotenv(1) is installed, running the ‘flask’ command will set
environment variables defined in the files ‘.env’ and ‘.flaskenv’.  This
can be used to avoid having to set ‘FLASK_APP’ manually every time you
open a new terminal, and to set configuration using environment
variables similar to how some deployment services work.

Variables set on the command line are used over those set in ‘.env’,
which are used over those set in ‘.flaskenv’.  ‘.flaskenv’ should be
used for public variables, such as ‘FLASK_APP’, while ‘.env’ should not
be committed to your repository so that it can set private variables.

Directories are scanned upwards from the directory you call ‘flask’ from
to locate the files.  The current working directory will be set to the
location of the file, with the assumption that that is the top level
project directory.

The files are only loaded by the ‘flask’ command or calling run().
If you would like to load these files when running in production,
you should call load_dotenv() manually.

   ---------- Footnotes ----------

   (1) https://github.com/theskumar/python-dotenv#readme

1.18.6.1 Setting Command Options
................................

Click is configured to load default values for command options from
environment variables.  The variables use the pattern
‘FLASK_COMMAND_OPTION’.  For example, to set the port for the run
command, instead of ‘flask run --port 8000’:

Bash

Fish

CMD

Powershell

     $ export FLASK_RUN_PORT=8000
     $ flask run
      * Running on http://127.0.0.1:8000/

     $ set -x FLASK_RUN_PORT 8000
     $ flask run
      * Running on http://127.0.0.1:8000/

     > set FLASK_RUN_PORT=8000
     > flask run
      * Running on http://127.0.0.1:8000/

     > $env:FLASK_RUN_PORT = 8000
     > flask run
      * Running on http://127.0.0.1:8000/

These can be added to the ‘.flaskenv’ file just like ‘FLASK_APP’ to
control default command options.

1.18.6.2 Disable dotenv
.......................

The ‘flask’ command will show a message if it detects dotenv files but
python-dotenv is not installed.

     $ flask run
      * Tip: There are .env files present. Do "pip install python-dotenv" to use them.

You can tell Flask not to load dotenv files even when python-dotenv is
installed by setting the ‘FLASK_SKIP_DOTENV’ environment variable.  This
can be useful if you want to load them manually, or if you’re using a
project runner that loads them already.  Keep in mind that the
environment variables must be set before the app loads or it won’t
configure as expected.

Bash

Fish

CMD

Powershell

     $ export FLASK_SKIP_DOTENV=1
     $ flask run

     $ set -x FLASK_SKIP_DOTENV 1
     $ flask run

     > set FLASK_SKIP_DOTENV=1
     > flask run

     > $env:FLASK_SKIP_DOTENV = 1
     > flask run

1.18.7 Environment Variables From virtualenv
--------------------------------------------

If you do not want to install dotenv support, you can still set
environment variables by adding them to the end of the virtualenv’s
‘activate’ script.  Activating the virtualenv will set the variables.

Bash

Fish

CMD

Powershell

Unix Bash, ‘venv/bin/activate’:

     $ export FLASK_APP=hello

Fish, ‘venv/bin/activate.fish’:

     $ set -x FLASK_APP hello

Windows CMD, ‘venv\Scripts\activate.bat’:

     > set FLASK_APP=hello

Windows Powershell, ‘venv\Scripts\activate.ps1’:

     > $env:FLASK_APP = "hello"

It is preferred to use dotenv support over this, since ‘.flaskenv’ can
be committed to the repository so that it works automatically wherever
the project is checked out.

1.18.8 Custom Commands
----------------------

The ‘flask’ command is implemented using Click(1).  See that project’s
documentation for full information about writing commands.

This example adds the command ‘create-user’ that takes the argument
‘name’.

     import click
     from flask import Flask

     app = Flask(__name__)

     @app.cli.command("create-user")
     @click.argument("name")
     def create_user(name):
         ...

     $ flask create-user admin

This example adds the same command, but as ‘user create’, a command in a
group.  This is useful if you want to organize multiple related
commands.

     import click
     from flask import Flask
     from flask.cli import AppGroup

     app = Flask(__name__)
     user_cli = AppGroup('user')

     @user_cli.command('create')
     @click.argument('name')
     def create_user(name):
         ...

     app.cli.add_command(user_cli)

     $ flask user create demo

See Running Commands with the CLI Runner for an overview of
how to test your custom commands.

   ---------- Footnotes ----------

   (1) https://click.palletsprojects.com/

1.18.8.1 Registering Commands with Blueprints
.............................................

If your application uses blueprints, you can optionally register CLI
commands directly onto them.  When your blueprint is registered onto
your application, the associated commands will be available to the
‘flask’ command.  By default, those commands will be nested in a group
matching the name of the blueprint.

     from flask import Blueprint

     bp = Blueprint('students', __name__)

     @bp.cli.command('create')
     @click.argument('name')
     def create(name):
         ...

     app.register_blueprint(bp)

     $ flask students create alice

You can alter the group name by specifying the ‘cli_group’ parameter
when creating the Blueprint object, or later with
app.register_blueprint(bp, cli_group=’...’).  The following are
equivalent:

     bp = Blueprint('students', __name__, cli_group='other')
     # or
     app.register_blueprint(bp, cli_group='other')

     $ flask other create alice

Specifying ‘cli_group=None’ will remove the nesting and merge the
commands directly to the application’s level:

     bp = Blueprint('students', __name__, cli_group=None)
     # or
     app.register_blueprint(bp, cli_group=None)

     $ flask create alice

1.18.8.2 Application Context
............................

Commands added using the Flask app’s cli. command()
decorator will be executed with an application context pushed, so
your command and extensions have access to the app and its
configuration.  If you create a command using the Click command()(1)
decorator instead of the Flask decorator, you can use
with_appcontext() to get the same behavior.

     import click
     from flask.cli import with_appcontext

     @click.command()
     @with_appcontext
     def do_work():
         ...

     app.cli.add_command(do_work)

If you’re sure a command doesn’t need the context, you can disable it:

     @app.cli.command(with_appcontext=False)
     def do_work():
         ...

   ---------- Footnotes ----------

   (1) https://click.palletsprojects.com/en/8.1.x/api/#click.command

1.18.9 Plugins
--------------

Flask will automatically load commands specified in the ‘flask.commands’
entry point(1).  This is useful for extensions that want to add commands
when they are installed.  Entry points are specified in ‘setup.py’

     from setuptools import setup

     setup(
         name='flask-my-extension',
         ...,
         entry_points={
             'flask.commands': [
                 'my-command=flask_my_extension.commands:cli'
             ],
         },
     )

Inside ‘flask_my_extension/commands.py’ you can then export a Click
object:

     import click

     @click.command()
     def cli():
         ...

Once that package is installed in the same virtualenv as your Flask
project, you can run ‘flask my-command’ to invoke the command.

   ---------- Footnotes ----------

   (1)
https://packaging.python.org/tutorials/packaging-projects/#entry-points

1.18.10 Custom Scripts
----------------------

When you are using the app factory pattern, it may be more convenient to
define your own Click script.  Instead of using ‘FLASK_APP’ and letting
Flask load your application, you can create your own Click object and
export it as a console script(1) entry point.

Create an instance of FlaskGroup and pass it the factory:

     import click
     from flask import Flask
     from flask.cli import FlaskGroup

     def create_app():
         app = Flask('wiki')
         # other setup
         return app

     @click.group(cls=FlaskGroup, create_app=create_app)
     def cli():
         """Management script for the Wiki application."""

Define the entry point in ‘setup.py’:

     from setuptools import setup

     setup(
         name='flask-my-extension',
         ...,
         entry_points={
             'console_scripts': [
                 'wiki=wiki:cli'
             ],
         },
     )

Install the application in the virtualenv in editable mode and the
custom script is available.  Note that you don’t need to set
‘FLASK_APP’.

     $ pip install -e .
     $ wiki run

     Errors in Custom Scripts: When using a custom script, if you
     introduce an error in your module-level code, the reloader will
     fail because it can no longer load the entry point.

     The ‘flask’ command, being separate from your code, does not have
     this issue and is recommended in most cases.

   ---------- Footnotes ----------

   (1)
https://packaging.python.org/tutorials/packaging-projects/#console-scripts

1.18.11 PyCharm Integration
---------------------------

PyCharm Professional provides a special Flask run configuration.  For
the Community Edition, we need to configure it to call the ‘flask run’
CLI command with the correct environment variables.  These instructions
should be similar for any other IDE you might want to use.

In PyCharm, with your project open, click on `Run' from the menu bar and
go to `Edit Configurations'.  You’ll be greeted by a screen similar to
this:

There’s quite a few options to change, but once we’ve done it for one
command, we can easily copy the entire configuration and make a single
tweak to give us access to other commands, including any custom ones you
may implement yourself.

Click the + (`Add New Configuration') button and select `Python'.  Give
the configuration a name such as “flask run”.  For the ‘flask run’
command, check “Single instance only” since you can’t run the server
more than once at the same time.

Select `Module name' from the dropdown (`A') then input ‘flask’.

The `Parameters' field (`B') is set to the CLI command to execute (with
any arguments).  In this example we use ‘run’, which will run the
development server.

You can skip this next step if you’re using Environment Variables From dotenv.
We need to add an environment variable (`C') to
identify our application.  Click on the browse button and add an entry
with ‘FLASK_APP’ on the left and the Python import or file on the right
(‘hello’ for example).  Add an entry with ‘FLASK_ENV’ and set it to
‘development’.

Next we need to set the working directory (`D') to be the folder where
our application resides.

If you have installed your project as a package in your virtualenv, you
may untick the `PYTHONPATH' options (`E'). This will more accurately
match how you deploy the app later.

Click `Apply' to save the configuration, or `OK' to save and close the
window.  Select the configuration in the main PyCharm window and click
the play button next to it to run the server.

Now that we have a configuration which runs ‘flask run’ from within
PyCharm, we can copy that configuration and alter the `Script' argument
to run a different CLI command, e.g.  ‘flask shell’.

1.19 Development Server
=======================

Flask provides a ‘run’ command to run the application with a development
server.  In development mode, this server provides an interactive
debugger and will reload when code is changed.

     Warning: Do not use the development server when deploying to
     production.  It is intended for use only during local development.
     It is not designed to be particularly efficient, stable, or secure.

     See Deployment Options for deployment options.

1.19.1 Command Line
-------------------

The ‘flask run’ command line script is the recommended way to run the
development server.  It requires setting the ‘FLASK_APP’ environment
variable to point to your application, and ‘FLASK_ENV=development’ to
fully enable development mode.

Bash

Fish

CMD

Powershell

     $ export FLASK_APP=hello
     $ export FLASK_ENV=development
     $ flask run

     $ set -x FLASK_APP hello
     $ export FLASK_ENV=development
     $ flask run

     > set FLASK_APP=hello
     > set FLASK_ENV=development
     > flask run

     > $env:FLASK_APP = "hello"
     > $env:FLASK_ENV = "development"
     > flask run

This enables the development environment, including the interactive
debugger and reloader, and then starts the server on
‘http://localhost:5000/’.  Use ‘flask run --help’ to see the available
options, and Command Line Interface for detailed instructions
about configuring and using the CLI.

     Note: Prior to Flask 1.0 the ‘FLASK_ENV’ environment variable was
     not supported and you needed to enable debug mode by exporting
     ‘FLASK_DEBUG=1’.  This can still be used to control debug mode, but
     you should prefer setting the development environment as shown
     above.

1.19.1.1 Address already in use
...............................

If another program is already using port 5000, you’ll see an ‘OSError’
when the server tries to start.  It may have one of the following
messages:

   - ‘OSError: [Errno 98] Address already in use’

   - ‘OSError: [WinError 10013] An attempt was made to access a socket
     in a way forbidden by its access permissions’

Either identify and stop the other program, or use ‘flask run --port
5001’ to pick a different port.

You can use ‘netstat’ or ‘lsof’ to identify what process id is using a
port, then use other operating system tools stop that process.  The
following example shows that process id 6847 is using port 5000.

‘netstat’ (Linux)

‘lsof’ (macOS / Linux)

‘netstat’ (Windows)

     $ netstat -nlp | grep 5000
     tcp 0 0 127.0.0.1:5000 0.0.0.0:* LISTEN 6847/python

     $ lsof -P -i :5000
     Python 6847 IPv4 TCP localhost:5000 (LISTEN)

     > netstat -ano | findstr 5000
     TCP 127.0.0.1:5000 0.0.0.0:0 LISTENING 6847

macOS Monterey and later automatically starts a service that uses port
5000.  To disable the service, go to System Preferences, Sharing, and
disable “AirPlay Receiver”.

1.19.1.2 Lazy or Eager Loading
..............................

When using the ‘flask run’ command with the reloader, the server will
continue to run even if you introduce syntax errors or other
initialization errors into the code.  Accessing the site will show the
interactive debugger for the error, rather than crashing the server.
This feature is called “lazy loading”.

If a syntax error is already present when calling ‘flask run’, it will
fail immediately and show the traceback rather than waiting until the
site is accessed.  This is intended to make errors more visible
initially while still allowing the server to handle errors on reload.

To override this behavior and always fail immediately, even on reload,
pass the ‘--eager-loading’ option.  To always keep the server running,
even on the initial call, pass ‘--lazy-loading’.

1.19.2 In Code
--------------

As an alternative to the ‘flask run’ command, the development server can
also be started from Python with the Flask.run() method.
This method takes arguments similar to the CLI options to control the
server.  The main difference from the CLI command is that the server
will crash if there are errors when reloading.

‘debug=True’ can be passed to enable the debugger and reloader, but the
‘FLASK_ENV=development’ environment variable is still required to fully
enable development mode.

Place the call in a main block, otherwise it will interfere when trying
to import and run the application with a production server later.

     if __name__ == "__main__":
         app.run(debug=True)

     $ python hello.py

1.20 Working with the Shell
===========================

New in version 0.3.

One of the reasons everybody loves Python is the interactive shell.  It
basically allows you to execute Python commands in real time and
immediately get results back.  Flask itself does not come with an
interactive shell, because it does not require any specific setup
upfront, just import your application and start playing around.

There are however some handy helpers to make playing around in the shell
a more pleasant experience.  The main issue with interactive console
sessions is that you’re not triggering a request like a browser does
which means that g, request and others are not
available.  But the code you want to test might depend on them, so what
can you do?

This is where some helper functions come in handy.  Keep in mind however
that these functions are not only there for interactive shell usage, but
also for unit testing and other situations that require a faked request
context.

Generally it’s recommended that you read The Request Context.
first.

1.20.1 Command Line Interface
-----------------------------

Starting with Flask 0.11 the recommended way to work with the shell is
the ‘flask shell’ command which does a lot of this automatically for
you.  For instance the shell is automatically initialized with a loaded
application context.

For more information see Command Line Interface.

1.20.2 Creating a Request Context
---------------------------------

The easiest way to create a proper request context from the shell is by
using the test_request_context method which creates us a
RequestContext.:

     >>> ctx = app.test_request_context()

Normally you would use the ‘with’ statement to make this request object
active, but in the shell it’s easier to use the push() and
pop() methods by hand:

     >>> ctx.push()

From that point onwards you can work with the request object until you
call ‘pop’:

     >>> ctx.pop()

1.20.3 Firing Before/After Request
----------------------------------

By just creating a request context, you still don’t have run the code
that is normally run before a request.  This might result in your
database being unavailable if you are connecting to the database in a
before-request callback or the current user not being stored on the
g object etc.

This however can easily be done yourself.  Just call
preprocess_request().:

     >>> ctx = app.test_request_context()
     >>> ctx.push()
     >>> app.preprocess_request()

Keep in mind that the preprocess_request() function might
return a response object, in that case just ignore it.

To shutdown a request, you need to trick a bit before the after request
functions (triggered by process_response()) operate on a
response object:

     >>> app.process_response(app.response_class())
     <Response 0 bytes [200 OK]>
     >>> ctx.pop()

The functions registered as teardown_request() are
automatically called when the context is popped.  So this is the perfect
place to automatically tear down resources that were needed by the
request context (such as database connections).

1.20.4 Further Improving the Shell Experience
---------------------------------------------

If you like the idea of experimenting in a shell, create yourself a
module with stuff you want to star import into your interactive session.
There you could also define some more helper methods for common things
such as initializing the database, dropping tables etc.

Just put them into a module (like ‘shelltools’) and import from there:

     >>> from shelltools import *

1.21 Patterns for Flask
=======================

Certain features and interactions are common enough that you will find
them in most web applications.  For example, many applications use a
relational database and user authentication.  They will open a database
connection at the beginning of the request and get the information for
the logged in user.  At the end of the request, the database connection
is closed.

These types of patterns may be a bit outside the scope of Flask itself,
but Flask makes it easy to implement them.  Some common patterns are
collected in the following pages.

1.21.1 Large Applications as Packages
-------------------------------------

Imagine a simple flask application structure that looks like this:

     /yourapplication
         yourapplication.py
         /static
             style.css
         /templates
             layout.html
             index.html
             login.html
             ...

While this is fine for small applications, for larger applications it’s
a good idea to use a package instead of a module.  The Tutorial
is structured to use the package pattern, see the example code(1).

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/tree/2.1.2/examples/tutorial

1.21.1.1 Simple Packages
........................

To convert that into a larger one, just create a new folder
‘yourapplication’ inside the existing one and move everything below it.
Then rename ‘yourapplication.py’ to ‘__init__.py’.  (Make sure to delete
all ‘.pyc’ files first, otherwise things would most likely break)

You should then end up with something like that:

     /yourapplication
         /yourapplication
             __init__.py
             /static
                 style.css
             /templates
                 layout.html
                 index.html
                 login.html
                 ...

But how do you run your application now?  The naive ‘python
yourapplication/__init__.py’ will not work.  Let’s just say that Python
does not want modules in packages to be the startup file.  But that is
not a big problem, just add a new file called ‘setup.py’ next to the
inner ‘yourapplication’ folder with the following contents:

     from setuptools import setup

     setup(
         name='yourapplication',
         packages=['yourapplication'],
         include_package_data=True,
         install_requires=[
             'flask',
         ],
     )

In order to run the application you need to export an environment
variable that tells Flask where to find the application instance:

Bash

Fish

CMD

Powershell

     $ export FLASK_APP=yourapplication

     $ set -x FLASK_APP yourapplication

     > set FLASK_APP=yourapplication

     > $env:FLASK_APP = "yourapplication"

If you are outside of the project directory make sure to provide the
exact path to your application directory.  Similarly you can turn on the
development features like this:

Bash

Fish

CMD

Powershell

     $ export FLASK_ENV=development

     $ set -x FLASK_ENV development

     > set FLASK_ENV=development

     > $env:FLASK_ENV = "development"

In order to install and run the application you need to issue the
following commands:

     $ pip install -e .
     $ flask run

What did we gain from this?  Now we can restructure the application a
bit into multiple modules.  The only thing you have to remember is the
following quick checklist:

  1. the ‘Flask’ application object creation has to be in the
     ‘__init__.py’ file.  That way each module can import it safely and
     the ‘__name__’ variable will resolve to the correct package.

  2. all the view functions (the ones with a route().
     decorator on top) have to be imported in the ‘__init__.py’ file.
     Not the object itself, but the module it is in.  Import the view
     module `after the application object is created'.

Here’s an example ‘__init__.py’:

     from flask import Flask
     app = Flask(__name__)

     import yourapplication.views

And this is what ‘views.py’ would look like:

     from yourapplication import app

     @app.route('/')
     def index():
         return 'Hello World!'

You should then end up with something like that:

     /yourapplication
         setup.py
         /yourapplication
             __init__.py
             views.py
             /static
                 style.css
             /templates
                 layout.html
                 index.html
                 login.html
                 ...

     Circular Imports: Every Python programmer hates them, and yet we
     just added some: circular imports (That’s when two modules depend
     on each other.  In this case ‘views.py’ depends on ‘__init__.py’).
     Be advised that this is a bad idea in general but here it is
     actually fine.  The reason for this is that we are not actually
     using the views in ‘__init__.py’ and just ensuring the module is
     imported and we are doing that at the bottom of the file.

     There are still some problems with that approach but if you want to
     use decorators there is no way around that.  Check out the
     Becoming Big section for some inspiration how to deal with
     that.

1.21.1.2 Working with Blueprints
................................

If you have larger applications it’s recommended to divide them into
smaller groups where each group is implemented with the help of a
blueprint.  For a gentle introduction into this topic refer to the
Modular Applications with Blueprints chapter of the documentation.

1.21.2 Application Factories
----------------------------

If you are already using packages and blueprints for your application
(see Modular Applications with Blueprints) there are a couple of
really nice ways to further improve the experience.  A common pattern is
creating the application object when the blueprint is imported.  But if
you move the creation of this object into a function, you can then
create multiple instances of this app later.

So why would you want to do this?

  1. Testing.  You can have instances of the application with different
     settings to test every case.

  2. Multiple instances.  Imagine you want to run different versions of
     the same application.  Of course you could have multiple instances
     with different configs set up in your webserver, but if you use
     factories, you can have multiple instances of the same application
     running in the same application process which can be handy.

So how would you then actually implement that?

1.21.2.1 Basic Factories
........................

The idea is to set up the application in a function.  Like this:

     def create_app(config_filename):
         app = Flask(__name__)
         app.config.from_pyfile(config_filename)

         from yourapplication.model import db
         db.init_app(app)

         from yourapplication.views.admin import admin
         from yourapplication.views.frontend import frontend
         app.register_blueprint(admin)
         app.register_blueprint(frontend)

         return app

The downside is that you cannot use the application object in the
blueprints at import time.  You can however use it from within a
request.  How do you get access to the application with the config?  Use
current_app.:

     from flask import current_app, Blueprint, render_template
     admin = Blueprint('admin', __name__, url_prefix='/admin')

     @admin.route('/')
     def index():
         return render_template(current_app.config['INDEX_TEMPLATE'])

Here we look up the name of a template in the config.

1.21.2.2 Factories & Extensions
...............................

It’s preferable to create your extensions and app factories so that the
extension object does not initially get bound to the application.

Using Flask-SQLAlchemy(1), as an example, you should not do something
along those lines:

     def create_app(config_filename):
         app = Flask(__name__)
         app.config.from_pyfile(config_filename)

         db = SQLAlchemy(app)

But, rather, in model.py (or equivalent):

     db = SQLAlchemy()

and in your application.py (or equivalent):

     def create_app(config_filename):
         app = Flask(__name__)
         app.config.from_pyfile(config_filename)

         from yourapplication.model import db
         db.init_app(app)

Using this design pattern, no application-specific state is stored on
the extension object, so one extension object can be used for multiple
apps.  For more information about the design of extensions refer to
Flask Extension Development.

   ---------- Footnotes ----------

   (1) https://flask-sqlalchemy.palletsprojects.com/

1.21.2.3 Using Applications
...........................

To run such an application, you can use the ‘flask’ command:

Bash

Fish

CMD

Powershell

     $ export FLASK_APP=myapp
     $ flask run

     $ set -x FLASK_APP myapp
     $ flask run

     > set FLASK_APP=myapp
     > flask run

     > $env:FLASK_APP = "myapp"
     > flask run

Flask will automatically detect the factory (‘create_app’ or ‘make_app’)
in ‘myapp’.  You can also pass arguments to the factory like this:

Bash

Fish

CMD

Powershell

     $ export FLASK_APP="myapp:create_app('dev')"
     $ flask run

     $ set -x FLASK_APP "myapp:create_app('dev')"
     $ flask run

     > set FLASK_APP="myapp:create_app('dev')"
     > flask run

     > $env:FLASK_APP = "myapp:create_app('dev')"
     > flask run

Then the ‘create_app’ factory in ‘myapp’ is called with the string
‘'dev'’ as the argument.  See Command Line Interface for more
detail.

1.21.2.4 Factory Improvements
.............................

The factory function above is not very clever, but you can improve it.
The following changes are straightforward to implement:

  1. Make it possible to pass in configuration values for unit tests so
     that you don’t have to create config files on the filesystem.

  2. Call a function from a blueprint when the application is setting up
     so that you have a place to modify attributes of the application
     (like hooking in before/after request handlers etc.)

  3. Add in WSGI middlewares when the application is being created if
     necessary.

1.21.3 Application Dispatching
------------------------------

Application dispatching is the process of combining multiple Flask
applications on the WSGI level.  You can combine not only Flask
applications but any WSGI application.  This would allow you to run a
Django and a Flask application in the same interpreter side by side if
you want.  The usefulness of this depends on how the applications work
internally.

The fundamental difference from Large Applications as Packages
is that in this case you are running the same or different Flask
applications that are entirely isolated from each other.  They run
different configurations and are dispatched on the WSGI level.

1.21.3.1 Working with this Document
...................................

Each of the techniques and examples below results in an ‘application’
object that can be run with any WSGI server.  For production, see
Deployment Options.  For development, Werkzeug provides a server
through werkzeug.serving.run_simple()(1):

     from werkzeug.serving import run_simple
     run_simple('localhost', 5000, application, use_reloader=True)

Note that run_simple(2) is not intended for use in production.  Use a
production WSGI server.  See Deployment Options.

In order to use the interactive debugger, debugging must be enabled both
on the application and the simple server.  Here is the “hello world”
example with debugging and run_simple(3):

     from flask import Flask
     from werkzeug.serving import run_simple

     app = Flask(__name__)
     app.debug = True

     @app.route('/')
     def hello_world():
         return 'Hello World!'

     if __name__ == '__main__':
         run_simple('localhost', 5000, app,
                    use_reloader=True, use_debugger=True, use_evalex=True)

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/serving/#werkzeug.serving.run_simple

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/serving/#werkzeug.serving.run_simple

   (3)
https://werkzeug.palletsprojects.com/en/2.1.x/serving/#werkzeug.serving.run_simple

1.21.3.2 Combining Applications
...............................

If you have entirely separated applications and you want them to work
next to each other in the same Python interpreter process you can take
advantage of the ‘werkzeug.wsgi.DispatcherMiddleware’.  The idea here is
that each Flask application is a valid WSGI application and they are
combined by the dispatcher middleware into a larger one that is
dispatched based on prefix.

For example you could have your main application run on ‘/’ and your
backend interface on ‘/backend’:

     from werkzeug.middleware.dispatcher import DispatcherMiddleware
     from frontend_app import application as frontend
     from backend_app import application as backend

     application = DispatcherMiddleware(frontend, {
         '/backend': backend
     })

1.21.3.3 Dispatch by Subdomain
..............................

Sometimes you might want to use multiple instances of the same
application with different configurations.  Assuming the application is
created inside a function and you can call that function to instantiate
it, that is really easy to implement.  In order to develop your
application to support creating new instances in functions have a look
at the Application Factories pattern.

A very common example would be creating applications per subdomain.  For
instance you configure your webserver to dispatch all requests for all
subdomains to your application and you then use the subdomain
information to create user-specific instances.  Once you have your
server set up to listen on all subdomains you can use a very simple WSGI
application to do the dynamic application creation.

The perfect level for abstraction in that regard is the WSGI layer.  You
write your own WSGI application that looks at the request that comes and
delegates it to your Flask application.  If that application does not
exist yet, it is dynamically created and remembered:

     from threading import Lock

     class SubdomainDispatcher(object):

         def __init__(self, domain, create_app):
             self.domain = domain
             self.create_app = create_app
             self.lock = Lock()
             self.instances = {}

         def get_application(self, host):
             host = host.split(':')[0]
             assert host.endswith(self.domain), 'Configuration error'
             subdomain = host[:-len(self.domain)].rstrip('.')
             with self.lock:
                 app = self.instances.get(subdomain)
                 if app is None:
                     app = self.create_app(subdomain)
                     self.instances[subdomain] = app
                 return app

         def __call__(self, environ, start_response):
             app = self.get_application(environ['HTTP_HOST'])
             return app(environ, start_response)

This dispatcher can then be used like this:

     from myapplication import create_app, get_user_for_subdomain
     from werkzeug.exceptions import NotFound

     def make_app(subdomain):
         user = get_user_for_subdomain(subdomain)
         if user is None:
             # if there is no user for that subdomain we still have
             # to return a WSGI application that handles that request.
             # We can then just return the NotFound() exception as
             # application which will render a default 404 page.
             # You might also redirect the user to the main page then
             return NotFound()

         # otherwise create the application for the specific user
         return create_app(user)

     application = SubdomainDispatcher('example.com', make_app)

1.21.3.4 Dispatch by Path
.........................

Dispatching by a path on the URL is very similar.  Instead of looking at
the ‘Host’ header to figure out the subdomain one simply looks at the
request path up to the first slash:

     from threading import Lock
     from werkzeug.wsgi import pop_path_info, peek_path_info

     class PathDispatcher(object):

         def __init__(self, default_app, create_app):
             self.default_app = default_app
             self.create_app = create_app
             self.lock = Lock()
             self.instances = {}

         def get_application(self, prefix):
             with self.lock:
                 app = self.instances.get(prefix)
                 if app is None:
                     app = self.create_app(prefix)
                     if app is not None:
                         self.instances[prefix] = app
                 return app

         def __call__(self, environ, start_response):
             app = self.get_application(peek_path_info(environ))
             if app is not None:
                 pop_path_info(environ)
             else:
                 app = self.default_app
             return app(environ, start_response)

The big difference between this and the subdomain one is that this one
falls back to another application if the creator function returns
‘None’:

     from myapplication import create_app, default_app, get_user_for_prefix

     def make_app(prefix):
         user = get_user_for_prefix(prefix)
         if user is not None:
             return create_app(user)

     application = PathDispatcher(default_app, make_app)

1.21.4 Using URL Processors
---------------------------

New in version 0.7.

Flask 0.7 introduces the concept of URL processors.  The idea is that
you might have a bunch of resources with common parts in the URL that
you don’t always explicitly want to provide.  For instance you might
have a bunch of URLs that have the language code in it but you don’t
want to have to handle it in every single function yourself.

URL processors are especially helpful when combined with blueprints.  We
will handle both application specific URL processors here as well as
blueprint specifics.

1.21.4.1 Internationalized Application URLs
...........................................

Consider an application like this:

     from flask import Flask, g

     app = Flask(__name__)

     @app.route('/<lang_code>/')
     def index(lang_code):
         g.lang_code = lang_code
         ...

     @app.route('/<lang_code>/about')
     def about(lang_code):
         g.lang_code = lang_code
         ...

This is an awful lot of repetition as you have to handle the language
code setting on the g object yourself in every single
function.  Sure, a decorator could be used to simplify this, but if you
want to generate URLs from one function to another you would have to
still provide the language code explicitly which can be annoying.

For the latter, this is where url_defaults() functions come
in.  They can automatically inject values into a call to
url_for().  The code below checks if the language code is not yet in
the dictionary of URL values and if the endpoint wants a value named
‘'lang_code'’:

     @app.url_defaults
     def add_language_code(endpoint, values):
         if 'lang_code' in values or not g.lang_code:
             return
         if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
             values['lang_code'] = g.lang_code

The method is_endpoint_expecting()(1) of the URL map can be used to
figure out if it would make sense to provide a language code for the
given endpoint.

The reverse of that function are url_value_preprocessor().s.
They are executed right after the request was matched and can execute
code based on the URL values.  The idea is that they pull information
out of the values dictionary and put it somewhere else:

     @app.url_value_preprocessor
     def pull_lang_code(endpoint, values):
         g.lang_code = values.pop('lang_code', None)

That way you no longer have to do the ‘lang_code’ assignment to g
in every function.  You can further improve that by writing your own
decorator that prefixes URLs with the language code, but the more
beautiful solution is using a blueprint.  Once the ‘'lang_code'’ is
popped from the values dictionary and it will no longer be forwarded to
the view function reducing the code to this:

     from flask import Flask, g

     app = Flask(__name__)

     @app.url_defaults
     def add_language_code(endpoint, values):
         if 'lang_code' in values or not g.lang_code:
             return
         if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
             values['lang_code'] = g.lang_code

     @app.url_value_preprocessor
     def pull_lang_code(endpoint, values):
         g.lang_code = values.pop('lang_code', None)

     @app.route('/<lang_code>/')
     def index():
         ...

     @app.route('/<lang_code>/about')
     def about():
         ...

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Map.is_endpoint_expecting

1.21.4.2 Internationalized Blueprint URLs
.........................................

Because blueprints can automatically prefix all URLs with a common
string it’s easy to automatically do that for every function.
Furthermore blueprints can have per-blueprint URL processors which
removes a whole lot of logic from the url_defaults().
function because it no longer has to check if the URL is really
interested in a ‘'lang_code'’ parameter:

     from flask import Blueprint, g

     bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>')

     @bp.url_defaults
     def add_language_code(endpoint, values):
         values.setdefault('lang_code', g.lang_code)

     @bp.url_value_preprocessor
     def pull_lang_code(endpoint, values):
         g.lang_code = values.pop('lang_code')

     @bp.route('/')
     def index():
         ...

     @bp.route('/about')
     def about():
         ...

1.21.5 Deploying with Setuptools
--------------------------------

Setuptools(1), is an extension library that is commonly used to
distribute Python libraries and extensions.  It extends distutils, a
basic module installation system shipped with Python to also support
various more complex constructs that make larger applications easier to
distribute:

   - `support for dependencies': a library or application can declare a
     list of other libraries it depends on which will be installed
     automatically for you.

   - `package registry': setuptools registers your package with your
     Python installation.  This makes it possible to query information
     provided by one package from another package.  The best known
     feature of this system is the entry point support which allows one
     package to declare an “entry point” that another package can hook
     into to extend the other package.

   - `installation manager': ‘pip’ can install other libraries for you.

Flask itself, and all the libraries you can find on PyPI are distributed
with either setuptools or distutils.

In this case we assume your application is called ‘yourapplication.py’
and you are not using a module, but a package.  If you have not yet
converted your application into a package, head over to Large Applications as Packages
to see how this can be done.

A working deployment with setuptools is the first step into more complex
and more automated deployment scenarios.  If you want to fully automate
the process, also read the Deploying with Fabric chapter.

   ---------- Footnotes ----------

   (1) https://pypi.org/project/setuptools/

1.21.5.1 Basic Setup Script
...........................

Because you have Flask installed, you have setuptools available on your
system.  Flask already depends upon setuptools.

Standard disclaimer applies: use a virtualenv.

Your setup code always goes into a file named ‘setup.py’ next to your
application.  The name of the file is only convention, but because
everybody will look for a file with that name, you better not change it.

A basic ‘setup.py’ file for a Flask application looks like this:

     from setuptools import setup

     setup(
         name='Your Application',
         version='1.0',
         long_description=__doc__,
         packages=['yourapplication'],
         include_package_data=True,
         zip_safe=False,
         install_requires=['Flask']
     )

Please keep in mind that you have to list subpackages explicitly.  If
you want setuptools to lookup the packages for you automatically, you
can use the ‘find_packages’ function:

     from setuptools import setup, find_packages

     setup(
         ...
         packages=find_packages()
     )

Most parameters to the ‘setup’ function should be self explanatory,
‘include_package_data’ and ‘zip_safe’ might not be.
‘include_package_data’ tells setuptools to look for a ‘MANIFEST.in’ file
and install all the entries that match as package data.  We will use
this to distribute the static files and templates along with the Python
module (see Distributing Resources).  The ‘zip_safe’ flag
can be used to force or prevent zip Archive creation.  In general you
probably don’t want your packages to be installed as zip files because
some tools do not support them and they make debugging a lot harder.

1.21.5.2 Tagging Builds
.......................

It is useful to distinguish between release and development builds.  Add
a ‘setup.cfg’ file to configure these options.

     [egg_info]
     tag_build = .dev
     tag_date = 1

     [aliases]
     release = egg_info -Db ''

Running ‘python setup.py sdist’ will create a development package with
“.dev” and the current date appended: ‘flaskr-1.0.dev20160314.tar.gz’.
Running ‘python setup.py release sdist’ will create a release package
with only the version: ‘flaskr-1.0.tar.gz’.

1.21.5.3 Distributing Resources
...............................

If you try to install the package you just created, you will notice that
folders like ‘static’ or ‘templates’ are not installed for you.  The
reason for this is that setuptools does not know which files to add for
you.  What you should do, is to create a ‘MANIFEST.in’ file next to your
‘setup.py’ file.  This file lists all the files that should be added to
your tarball:

     recursive-include yourapplication/templates *
     recursive-include yourapplication/static *

Don’t forget that even if you enlist them in your ‘MANIFEST.in’ file,
they won’t be installed for you unless you set the
‘include_package_data’ parameter of the ‘setup’ function to ‘True’!

1.21.5.4 Declaring Dependencies
...............................

Dependencies are declared in the ‘install_requires’ parameter as a list.
Each item in that list is the name of a package that should be pulled
from PyPI on installation.  By default it will always use the most
recent version, but you can also provide minimum and maximum version
requirements.  Here some examples:

     install_requires=[
         'Flask>=0.2',
         'SQLAlchemy>=0.6',
         'BrokenPackage>=0.7,<=1.0'
     ]

As mentioned earlier, dependencies are pulled from PyPI. What if you
want to depend on a package that cannot be found on PyPI and won’t be
because it is an internal package you don’t want to share with anyone?
Just do it as if there was a PyPI entry and provide a list of
alternative locations where setuptools should look for tarballs:

     dependency_links=['http://example.com/yourfiles']

Make sure that page has a directory listing and the links on the page
are pointing to the actual tarballs with their correct filenames as this
is how setuptools will find the files.  If you have an internal company
server that contains the packages, provide the URL to that server.

1.21.5.5 Installing / Developing
................................

To install your application (ideally into a virtualenv) just run the
‘setup.py’ script with the ‘install’ parameter.  It will install your
application into the virtualenv’s site-packages folder and also download
and install all dependencies:

     $ python setup.py install

If you are developing on the package and also want the requirements to
be installed, you can use the ‘develop’ command instead:

     $ python setup.py develop

This has the advantage of just installing a link to the site-packages
folder instead of copying the data over.  You can then continue to work
on the code without having to run ‘install’ again after each change.

1.21.6 Deploying with Fabric
----------------------------

Fabric(1) is a tool for Python similar to Makefiles but with the ability
to execute commands on a remote server.  In combination with a properly
set up Python package (see Large Applications as Packages) and a
good concept for configurations (see Configuration Handling) it
is very easy to deploy Flask applications to external servers.

Before we get started, here a quick checklist of things we have to
ensure upfront:

   - Fabric 1.0 has to be installed locally.  This tutorial assumes the
     latest version of Fabric.

   - The application already has to be a package and requires a working
     ‘setup.py’ file (see Deploying with Setuptools).

   - In the following example we are using ‘mod_wsgi’ for the remote
     servers.  You can of course use your own favourite server there,
     but for this example we chose Apache + ‘mod_wsgi’ because it’s very
     easy to setup and has a simple way to reload applications without
     root access.

   ---------- Footnotes ----------

   (1) https://www.fabfile.org/

1.21.6.1 Creating the first Fabfile
...................................

A fabfile is what controls what Fabric executes.  It is named
‘fabfile.py’ and executed by the ‘fab’ command.  All the functions
defined in that file will show up as ‘fab’ subcommands.  They are
executed on one or more hosts.  These hosts can be defined either in the
fabfile or on the command line.  In this case we will add them to the
fabfile.

This is a basic first example that has the ability to upload the current
source code to the server and install it into a pre-existing virtual
environment:

     from fabric.api import *

     # the user to use for the remote commands
     env.user = 'appuser'
     # the servers where the commands are executed
     env.hosts = ['server1.example.com', 'server2.example.com']

     def pack():
         # build the package
         local('python setup.py sdist --formats=gztar', capture=False)

     def deploy():
         # figure out the package name and version
         dist = local('python setup.py --fullname', capture=True).strip()
         filename = f'{dist}.tar.gz'

         # upload the package to the temporary folder on the server
         put(f'dist/{filename}', f'/tmp/{filename}')

         # install the package in the application's virtualenv with pip
         run(f'/var/www/yourapplication/env/bin/pip install /tmp/{filename}')

         # remove the uploaded package
         run(f'rm -r /tmp/{filename}')

         # touch the .wsgi file to trigger a reload in mod_wsgi
         run('touch /var/www/yourapplication.wsgi')

1.21.6.2 Running Fabfiles
.........................

Now how do you execute that fabfile?  You use the ‘fab’ command.  To
deploy the current version of the code on the remote server you would
use this command:

     $ fab pack deploy

However this requires that our server already has the
‘/var/www/yourapplication’ folder created and
‘/var/www/yourapplication/env’ to be a virtual environment.  Furthermore
are we not creating the configuration or ‘.wsgi’ file on the server.  So
how do we bootstrap a new server into our infrastructure?

This now depends on the number of servers we want to set up.  If we just
have one application server (which the majority of applications will
have), creating a command in the fabfile for this is overkill.  But
obviously you can do that.  In that case you would probably call it
‘setup’ or ‘bootstrap’ and then pass the servername explicitly on the
command line:

     $ fab -H newserver.example.com bootstrap

To setup a new server you would roughly do these steps:

  1. Create the directory structure in ‘/var/www’:

          $ mkdir /var/www/yourapplication
          $ cd /var/www/yourapplication
          $ virtualenv --distribute env

  2. Upload a new ‘application.wsgi’ file to the server and the
     configuration file for the application (eg: ‘application.cfg’)

  3. Create a new Apache config for ‘yourapplication’ and activate it.
     Make sure to activate watching for changes of the ‘.wsgi’ file so
     that we can automatically reload the application by touching it.
     See mod_wsgi (Apache).

So now the question is, where do the ‘application.wsgi’ and
‘application.cfg’ files come from?

1.21.6.3 The WSGI File
......................

The WSGI file has to import the application and also to set an
environment variable so that the application knows where to look for the
config.  This is a short example that does exactly that:

     import os
     os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg'
     from yourapplication import app

The application itself then has to initialize itself like this to look
for the config at that environment variable:

     app = Flask(__name__)
     app.config.from_object('yourapplication.default_config')
     app.config.from_envvar('YOURAPPLICATION_CONFIG')

This approach is explained in detail in the Configuration Handling
section of the documentation.

1.21.6.4 The Configuration File
...............................

Now as mentioned above, the application will find the correct
configuration file by looking up the ‘YOURAPPLICATION_CONFIG’
environment variable.  So we have to put the configuration in a place
where the application will able to find it.  Configuration files have
the unfriendly quality of being different on all computers, so you do
not version them usually.

A popular approach is to store configuration files for different servers
in a separate version control repository and check them out on all
servers.  Then symlink the file that is active for the server into the
location where it’s expected (eg: ‘/var/www/yourapplication’).

Either way, in our case here we only expect one or two servers and we
can upload them ahead of time by hand.

1.21.6.5 First Deployment
.........................

Now we can do our first deployment.  We have set up the servers so that
they have their virtual environments and activated apache configs.  Now
we can pack up the application and deploy it:

     $ fab pack deploy

Fabric will now connect to all servers and run the commands as written
down in the fabfile.  First it will execute pack so that we have our
tarball ready and then it will execute deploy and upload the source code
to all servers and install it there.  Thanks to the ‘setup.py’ file we
will automatically pull in the required libraries into our virtual
environment.

1.21.6.6 Next Steps
...................

From that point onwards there is so much that can be done to make
deployment actually fun:

   - Create a ‘bootstrap’ command that initializes new servers.  It
     could initialize a new virtual environment, setup apache
     appropriately etc.

   - Put configuration files into a separate version control repository
     and symlink the active configs into place.

   - You could also put your application code into a repository and
     check out the latest version on the server and then install.  That
     way you can also easily go back to older versions.

   - hook in testing functionality so that you can deploy to an external
     server and run the test suite.

Working with Fabric is fun and you will notice that it’s quite magical
to type ‘fab deploy’ and see your application being deployed
automatically to one or more remote servers.

1.21.7 Using SQLite 3 with Flask
--------------------------------

In Flask you can easily implement the opening of database connections on
demand and closing them when the context dies (usually at the end of the
request).

Here is a simple example of how you can use SQLite 3 with Flask:

     import sqlite3
     from flask import g

     DATABASE = '/path/to/database.db'

     def get_db():
         db = getattr(g, '_database', None)
         if db is None:
             db = g._database = sqlite3.connect(DATABASE)
         return db

     @app.teardown_appcontext
     def close_connection(exception):
         db = getattr(g, '_database', None)
         if db is not None:
             db.close()

Now, to use the database, the application must either have an active
application context (which is always true if there is a request in
flight) or create an application context itself.  At that point the
‘get_db’ function can be used to get the current database connection.
Whenever the context is destroyed the database connection will be
terminated.

Note: if you use Flask 0.9 or older you need to use
‘flask._app_ctx_stack.top’ instead of ‘g’ as the flask.g.
object was bound to the request and not application context.

Example:

     @app.route('/')
     def index():
         cur = get_db().cursor()
         ...

     Note: Please keep in mind that the teardown request and appcontext
     functions are always executed, even if a before-request handler
     failed or was never executed.  Because of this we have to make sure
     here that the database is there before we close it.

1.21.7.1 Connect on Demand
..........................

The upside of this approach (connecting on first use) is that this will
only open the connection if truly necessary.  If you want to use this
code outside a request context you can use it in a Python shell by
opening the application context by hand:

     with app.app_context():
         # now you can use get_db()

1.21.7.2 Easy Querying
......................

Now in each request handling function you can access ‘get_db()’ to get
the current open database connection.  To simplify working with SQLite,
a row factory function is useful.  It is executed for every result
returned from the database to convert the result.  For instance, in
order to get dictionaries instead of tuples, this could be inserted into
the ‘get_db’ function we created above:

     def make_dicts(cursor, row):
         return dict((cursor.description[idx][0], value)
                     for idx, value in enumerate(row))

     db.row_factory = make_dicts

This will make the sqlite3 module return dicts for this database
connection, which are much nicer to deal with.  Even more simply, we
could place this in ‘get_db’ instead:

     db.row_factory = sqlite3.Row

This would use Row objects rather than dicts to return the results of
queries.  These are ‘namedtuple’ s, so we can access them either by
index or by key.  For example, assuming we have a ‘sqlite3.Row’ called
‘r’ for the rows ‘id’, ‘FirstName’, ‘LastName’, and ‘MiddleInitial’:

     >>> # You can get values based on the row's name
     >>> r['FirstName']
     John
     >>> # Or, you can get them based on index
     >>> r[1]
     John
     # Row objects are also iterable:
     >>> for value in r:
     ...     print(value)
     1
     John
     Doe
     M

Additionally, it is a good idea to provide a query function that
combines getting the cursor, executing and fetching the results:

     def query_db(query, args=(), one=False):
         cur = get_db().execute(query, args)
         rv = cur.fetchall()
         cur.close()
         return (rv[0] if rv else None) if one else rv

This handy little function, in combination with a row factory, makes
working with the database much more pleasant than it is by just using
the raw cursor and connection objects.

Here is how you can use it:

     for user in query_db('select * from users'):
         print(user['username'], 'has the id', user['user_id'])

Or if you just want a single result:

     user = query_db('select * from users where username = ?',
                     [the_username], one=True)
     if user is None:
         print('No such user')
     else:
         print(the_username, 'has the id', user['user_id'])

To pass variable parts to the SQL statement, use a question mark in the
statement and pass in the arguments as a list.  Never directly add them
to the SQL statement with string formatting because this makes it
possible to attack the application using SQL Injections(1).

   ---------- Footnotes ----------

   (1) https://en.wikipedia.org/wiki/SQL_injection

1.21.7.3 Initial Schemas
........................

Relational databases need schemas, so applications often ship a
‘schema.sql’ file that creates the database.  It’s a good idea to
provide a function that creates the database based on that schema.  This
function can do that for you:

     def init_db():
         with app.app_context():
             db = get_db()
             with app.open_resource('schema.sql', mode='r') as f:
                 db.cursor().executescript(f.read())
             db.commit()

You can then create such a database from the Python shell:

     >>> from yourapplication import init_db
     >>> init_db()

1.21.8 SQLAlchemy in Flask
--------------------------

Many people prefer SQLAlchemy(1) for database access.  In this case it’s
encouraged to use a package instead of a module for your flask
application and drop the models into a separate module (see Large Applications as Packages).
While that is not necessary, it makes
a lot of sense.

There are four very common ways to use SQLAlchemy.  I will outline each
of them here:

   ---------- Footnotes ----------

   (1) https://www.sqlalchemy.org/

1.21.8.1 Flask-SQLAlchemy Extension
...................................

Because SQLAlchemy is a common database abstraction layer and object
relational mapper that requires a little bit of configuration effort,
there is a Flask extension that handles that for you.  This is
recommended if you want to get started quickly.

You can download Flask-SQLAlchemy(1) from PyPI(2).

   ---------- Footnotes ----------

   (1) https://flask-sqlalchemy.palletsprojects.com/

   (2) https://pypi.org/project/Flask-SQLAlchemy/

1.21.8.2 Declarative
....................

The declarative extension in SQLAlchemy is the most recent method of
using SQLAlchemy.  It allows you to define tables and models in one go,
similar to how Django works.  In addition to the following text I
recommend the official documentation on the declarative(1) extension.

Here’s the example ‘database.py’ module for your application:

     from sqlalchemy import create_engine
     from sqlalchemy.orm import scoped_session, sessionmaker
     from sqlalchemy.ext.declarative import declarative_base

     engine = create_engine('sqlite:////tmp/test.db')
     db_session = scoped_session(sessionmaker(autocommit=False,
                                              autoflush=False,
                                              bind=engine))
     Base = declarative_base()
     Base.query = db_session.query_property()

     def init_db():
         # import all modules here that might define models so that
         # they will be registered properly on the metadata.  Otherwise
         # you will have to import them first before calling init_db()
         import yourapplication.models
         Base.metadata.create_all(bind=engine)

To define your models, just subclass the ‘Base’ class that was created
by the code above.  If you are wondering why we don’t have to care about
threads here (like we did in the SQLite3 example above with the g
object): that’s because SQLAlchemy does that for us already with the
‘scoped_session’.

To use SQLAlchemy in a declarative way with your application, you just
have to put the following code into your application module.  Flask will
automatically remove database sessions at the end of the request or when
the application shuts down:

     from yourapplication.database import db_session

     @app.teardown_appcontext
     def shutdown_session(exception=None):
         db_session.remove()

Here is an example model (put this into ‘models.py’, e.g.):

     from sqlalchemy import Column, Integer, String
     from yourapplication.database import Base

     class User(Base):
         __tablename__ = 'users'
         id = Column(Integer, primary_key=True)
         name = Column(String(50), unique=True)
         email = Column(String(120), unique=True)

         def __init__(self, name=None, email=None):
             self.name = name
             self.email = email

         def __repr__(self):
             return f'<User {self.name!r}>'

To create the database you can use the ‘init_db’ function:

     >>> from yourapplication.database import init_db
     >>> init_db()

You can insert entries into the database like this:

     >>> from yourapplication.database import db_session
     >>> from yourapplication.models import User
     >>> u = User('admin', 'admin@localhost')
     >>> db_session.add(u)
     >>> db_session.commit()

Querying is simple as well:

     >>> User.query.all()
     [<User 'admin'>]
     >>> User.query.filter(User.name == 'admin').first()
     <User 'admin'>

   ---------- Footnotes ----------

   (1) https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/

1.21.8.3 Manual Object Relational Mapping
.........................................

Manual object relational mapping has a few upsides and a few downsides
versus the declarative approach from above.  The main difference is that
you define tables and classes separately and map them together.  It’s
more flexible but a little more to type.  In general it works like the
declarative approach, so make sure to also split up your application
into multiple modules in a package.

Here is an example ‘database.py’ module for your application:

     from sqlalchemy import create_engine, MetaData
     from sqlalchemy.orm import scoped_session, sessionmaker

     engine = create_engine('sqlite:////tmp/test.db')
     metadata = MetaData()
     db_session = scoped_session(sessionmaker(autocommit=False,
                                              autoflush=False,
                                              bind=engine))
     def init_db():
         metadata.create_all(bind=engine)

As in the declarative approach, you need to close the session after each
request or application context shutdown.  Put this into your application
module:

     from yourapplication.database import db_session

     @app.teardown_appcontext
     def shutdown_session(exception=None):
         db_session.remove()

Here is an example table and model (put this into ‘models.py’):

     from sqlalchemy import Table, Column, Integer, String
     from sqlalchemy.orm import mapper
     from yourapplication.database import metadata, db_session

     class User(object):
         query = db_session.query_property()

         def __init__(self, name=None, email=None):
             self.name = name
             self.email = email

         def __repr__(self):
             return f'<User {self.name!r}>'

     users = Table('users', metadata,
         Column('id', Integer, primary_key=True),
         Column('name', String(50), unique=True),
         Column('email', String(120), unique=True)
     )
     mapper(User, users)

Querying and inserting works exactly the same as in the example above.

1.21.8.4 SQL Abstraction Layer
..............................

If you just want to use the database system (and SQL) abstraction layer
you basically only need the engine:

     from sqlalchemy import create_engine, MetaData, Table

     engine = create_engine('sqlite:////tmp/test.db')
     metadata = MetaData(bind=engine)

Then you can either declare the tables in your code like in the examples
above, or automatically load them:

     from sqlalchemy import Table

     users = Table('users', metadata, autoload=True)

To insert data you can use the ‘insert’ method.  We have to get a
connection first so that we can use a transaction:

     >>> con = engine.connect()
     >>> con.execute(users.insert(), name='admin', email='admin@localhost')

SQLAlchemy will automatically commit for us.

To query your database, you use the engine directly or use a connection:

     >>> users.select(users.c.id == 1).execute().first()
     (1, 'admin', 'admin@localhost')

These results are also dict-like tuples:

     >>> r = users.select(users.c.id == 1).execute().first()
     >>> r['name']
     'admin'

You can also pass strings of SQL statements to the ‘execute()’ method:

     >>> engine.execute('select * from users where id = :1', [1]).first()
     (1, 'admin', 'admin@localhost')

For more information about SQLAlchemy, head over to the website(1).

   ---------- Footnotes ----------

   (1) https://www.sqlalchemy.org/

1.21.9 Uploading Files
----------------------

Ah yes, the good old problem of file uploads.  The basic idea of file
uploads is actually quite simple.  It basically works like this:

  1. A ‘<form>’ tag is marked with ‘enctype=multipart/form-data’ and an
     ‘<input type=file>’ is placed in that form.

  2. The application accesses the file from the ‘files’ dictionary on
     the request object.

  3. use the save()(1) method of the file to save the file permanently
     somewhere on the filesystem.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.FileStorage.save

1.21.9.1 A Gentle Introduction
..............................

Let’s start with a very basic application that uploads a file to a
specific upload folder and displays a file to the user.  Let’s look at
the bootstrapping code for our application:

     import os
     from flask import Flask, flash, request, redirect, url_for
     from werkzeug.utils import secure_filename

     UPLOAD_FOLDER = '/path/to/the/uploads'
     ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}

     app = Flask(__name__)
     app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

So first we need a couple of imports.  Most should be straightforward,
the ‘werkzeug.secure_filename()’ is explained a little bit later.  The
‘UPLOAD_FOLDER’ is where we will store the uploaded files and the
‘ALLOWED_EXTENSIONS’ is the set of allowed file extensions.

Why do we limit the extensions that are allowed?  You probably don’t
want your users to be able to upload everything there if the server is
directly sending out the data to the client.  That way you can make sure
that users are not able to upload HTML files that would cause XSS
problems (see Cross-Site Scripting (XSS)).  Also make sure to
disallow ‘.php’ files if the server executes them, but who has PHP
installed on their server, right?  :)

Next the functions that check if an extension is valid and that uploads
the file and redirects the user to the URL for the uploaded file:

     def allowed_file(filename):
         return '.' in filename and \
                filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

     @app.route('/', methods=['GET', 'POST'])
     def upload_file():
         if request.method == 'POST':
             # check if the post request has the file part
             if 'file' not in request.files:
                 flash('No file part')
                 return redirect(request.url)
             file = request.files['file']
             # If the user does not select a file, the browser submits an
             # empty file without a filename.
             if file.filename == '':
                 flash('No selected file')
                 return redirect(request.url)
             if file and allowed_file(file.filename):
                 filename = secure_filename(file.filename)
                 file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
                 return redirect(url_for('download_file', name=filename))
         return '''
         <!doctype html>
         <title>Upload new File</title>
         <h1>Upload new File</h1>
         <form method=post enctype=multipart/form-data>
           <input type=file name=file>
           <input type=submit value=Upload>
         </form>
         '''

So what does that secure_filename()(1) function actually do?  Now the
problem is that there is that principle called “never trust user input”.
This is also true for the filename of an uploaded file.  All submitted
form data can be forged, and filenames can be dangerous.  For the moment
just remember: always use that function to secure a filename before
storing it directly on the filesystem.

     Information for the Pros: So you’re interested in what that
     secure_filename()(2) function does and what the problem is if
     you’re not using it?  So just imagine someone would send the
     following information as ‘filename’ to your application:

          filename = "../../../../home/username/.bashrc"

     Assuming the number of ‘../’ is correct and you would join this
     with the ‘UPLOAD_FOLDER’ the user might have the ability to modify
     a file on the server’s filesystem he or she should not modify.
     This does require some knowledge about how the application looks
     like, but trust me, hackers are patient :)

     Now let’s look how that function works:

          >>> secure_filename('../../../../home/username/.bashrc')
          'home_username_.bashrc'

We want to be able to serve the uploaded files so they can be downloaded
by users.  We’ll define a ‘download_file’ view to serve files in the
upload folder by name.  ‘url_for("download_file", name=name)’ generates
download URLs.

     from flask import send_from_directory

     @app.route('/uploads/<name>')
     def download_file(name):
         return send_from_directory(app.config["UPLOAD_FOLDER"], name)

If you’re using middleware or the HTTP server to serve files, you can
register the ‘download_file’ endpoint as ‘build_only’ so ‘url_for’ will
work without a view function.

     app.add_url_rule(
         "/uploads/<name>", endpoint="download_file", build_only=True
     )

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.utils.secure_filename

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.utils.secure_filename

1.21.9.2 Improving Uploads
..........................

New in version 0.6.

So how exactly does Flask handle uploads?  Well it will store them in
the webserver’s memory if the files are reasonably small, otherwise in a
temporary location (as returned by tempfile.gettempdir()(1)).  But how
do you specify the maximum file size after which an upload is aborted?
By default Flask will happily accept file uploads with an unlimited
amount of memory, but you can limit that by setting the
‘MAX_CONTENT_LENGTH’ config key:

     from flask import Flask, Request

     app = Flask(__name__)
     app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000

The code above will limit the maximum allowed payload to 16 megabytes.
If a larger file is transmitted, Flask will raise a
RequestEntityTooLarge(2) exception.

     Connection Reset Issue: When using the local development server,
     you may get a connection reset error instead of a 413 response.
     You will get the correct status response when running the app with
     a production WSGI server.

This feature was added in Flask 0.6 but can be achieved in older
versions as well by subclassing the request object.  For more
information on that consult the Werkzeug documentation on file handling.

   ---------- Footnotes ----------

   (1)
https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.RequestEntityTooLarge

1.21.9.3 Upload Progress Bars
.............................

A while ago many developers had the idea to read the incoming file in
small chunks and store the upload progress in the database to be able to
poll the progress with JavaScript from the client.  The client asks the
server every 5 seconds how much it has transmitted, but this is
something it should already know.

1.21.9.4 An Easier Solution
...........................

Now there are better solutions that work faster and are more reliable.
There are JavaScript libraries like jQuery(1) that have form plugins to
ease the construction of progress bar.

Because the common pattern for file uploads exists almost unchanged in
all applications dealing with uploads, there are also some Flask
extensions that implement a full fledged upload mechanism that allows
controlling which file extensions are allowed to be uploaded.

   ---------- Footnotes ----------

   (1) https://jquery.com/

1.21.10 Caching
---------------

When your application runs slow, throw some caches in.  Well, at least
it’s the easiest way to speed up things.  What does a cache do?  Say you
have a function that takes some time to complete but the results would
still be good enough if they were 5 minutes old.  So then the idea is
that you actually put the result of that calculation into a cache for
some time.

Flask itself does not provide caching for you, but Flask-Caching(1), an
extension for Flask does.  Flask-Caching supports various backends, and
it is even possible to develop your own caching backend.

   ---------- Footnotes ----------

   (1) https://flask-caching.readthedocs.io/en/latest/

1.21.11 View Decorators
-----------------------

Python has a really interesting feature called function decorators.
This allows some really neat things for web applications.  Because each
view in Flask is a function, decorators can be used to inject additional
functionality to one or more functions.  The route().
decorator is the one you probably used already.  But there are use cases
for implementing your own decorator.  For instance, imagine you have a
view that should only be used by people that are logged in.  If a user
goes to the site and is not logged in, they should be redirected to the
login page.  This is a good example of a use case where a decorator is
an excellent solution.

1.21.11.1 Login Required Decorator
..................................

So let’s implement such a decorator.  A decorator is a function that
wraps and replaces another function.  Since the original function is
replaced, you need to remember to copy the original function’s
information to the new function.  Use functools.wraps()(1) to handle
this for you.

This example assumes that the login page is called ‘'login'’ and that
the current user is stored in ‘g.user’ and is ‘None’ if there is no-one
logged in.

     from functools import wraps
     from flask import g, request, redirect, url_for

     def login_required(f):
         @wraps(f)
         def decorated_function(*args, **kwargs):
             if g.user is None:
                 return redirect(url_for('login', next=request.url))
             return f(*args, **kwargs)
         return decorated_function

To use the decorator, apply it as innermost decorator to a view
function.  When applying further decorators, always remember that the
route() decorator is the outermost.

     @app.route('/secret_page')
     @login_required
     def secret_page():
         pass

     Note: The ‘next’ value will exist in ‘request.args’ after a ‘GET’
     request for the login page.  You’ll have to pass it along when
     sending the ‘POST’ request from the login form.  You can do this
     with a hidden input tag, then retrieve it from ‘request.form’ when
     logging the user in.

          <input type="hidden" value="{{ request.args.get('next', '') }}"/>

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/functools.html#functools.wraps

1.21.11.2 Caching Decorator
...........................

Imagine you have a view function that does an expensive calculation and
because of that you would like to cache the generated results for a
certain amount of time.  A decorator would be nice for that.  We’re
assuming you have set up a cache like mentioned in Caching.

Here is an example cache function.  It generates the cache key from a
specific prefix (actually a format string) and the current path of the
request.  Notice that we are using a function that first creates the
decorator that then decorates the function.  Sounds awful?
Unfortunately it is a little bit more complex, but the code should still
be straightforward to read.

The decorated function will then work as follows

  1. get the unique cache key for the current request based on the
     current path.

  2. get the value for that key from the cache.  If the cache returned
     something we will return that value.

  3. otherwise the original function is called and the return value is
     stored in the cache for the timeout provided (by default 5
     minutes).

Here the code:

     from functools import wraps
     from flask import request

     def cached(timeout=5 * 60, key='view/{}'):
         def decorator(f):
             @wraps(f)
             def decorated_function(*args, **kwargs):
                 cache_key = key.format(request.path)
                 rv = cache.get(cache_key)
                 if rv is not None:
                     return rv
                 rv = f(*args, **kwargs)
                 cache.set(cache_key, rv, timeout=timeout)
                 return rv
             return decorated_function
         return decorator

Notice that this assumes an instantiated ‘cache’ object is available,
see Caching.

1.21.11.3 Templating Decorator
..............................

A common pattern invented by the TurboGears guys a while back is a
templating decorator.  The idea of that decorator is that you return a
dictionary with the values passed to the template from the view function
and the template is automatically rendered.  With that, the following
three examples do exactly the same:

     @app.route('/')
     def index():
         return render_template('index.html', value=42)

     @app.route('/')
     @templated('index.html')
     def index():
         return dict(value=42)

     @app.route('/')
     @templated()
     def index():
         return dict(value=42)

As you can see, if no template name is provided it will use the endpoint
of the URL map with dots converted to slashes + ‘'.html'’.  Otherwise
the provided template name is used.  When the decorated function
returns, the dictionary returned is passed to the template rendering
function.  If ‘None’ is returned, an empty dictionary is assumed, if
something else than a dictionary is returned we return it from the
function unchanged.  That way you can still use the redirect function or
return simple strings.

Here is the code for that decorator:

     from functools import wraps
     from flask import request, render_template

     def templated(template=None):
         def decorator(f):
             @wraps(f)
             def decorated_function(*args, **kwargs):
                 template_name = template
                 if template_name is None:
                     template_name = f"{request.endpoint.replace('.', '/')}.html"
                 ctx = f(*args, **kwargs)
                 if ctx is None:
                     ctx = {}
                 elif not isinstance(ctx, dict):
                     return ctx
                 return render_template(template_name, **ctx)
             return decorated_function
         return decorator

1.21.11.4 Endpoint Decorator
............................

When you want to use the werkzeug routing system for more flexibility
you need to map the endpoint as defined in the Rule(1) to a view
function.  This is possible with this decorator.  For example:

     from flask import Flask
     from werkzeug.routing import Rule

     app = Flask(__name__)
     app.url_map.add(Rule('/', endpoint='index'))

     @app.endpoint('index')
     def my_index():
         return "Hello world"

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Rule

1.21.12 Form Validation with WTForms
------------------------------------

When you have to work with form data submitted by a browser view, code
quickly becomes very hard to read.  There are libraries out there
designed to make this process easier to manage.  One of them is
WTForms(1) which we will handle here.  If you find yourself in the
situation of having many forms, you might want to give it a try.

When you are working with WTForms you have to define your forms as
classes first.  I recommend breaking up the application into multiple
modules (see Large Applications as Packages) for that and adding
a separate module for the forms.

     Getting the most out of WTForms with an Extension: The Flask-WTF(2)
     extension expands on this pattern and adds a few little helpers
     that make working with forms and Flask more fun.  You can get it
     from PyPI(3).

   ---------- Footnotes ----------

   (1) https://wtforms.readthedocs.io/

   (2) https://flask-wtf.readthedocs.io/

   (3) https://pypi.org/project/Flask-WTF/

1.21.12.1 The Forms
...................

This is an example form for a typical registration page:

     from wtforms import Form, BooleanField, StringField, PasswordField, validators

     class RegistrationForm(Form):
         username = StringField('Username', [validators.Length(min=4, max=25)])
         email = StringField('Email Address', [validators.Length(min=6, max=35)])
         password = PasswordField('New Password', [
             validators.DataRequired(),
             validators.EqualTo('confirm', message='Passwords must match')
         ])
         confirm = PasswordField('Repeat Password')
         accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])

1.21.12.2 In the View
.....................

In the view function, the usage of this form looks like this:

     @app.route('/register', methods=['GET', 'POST'])
     def register():
         form = RegistrationForm(request.form)
         if request.method == 'POST' and form.validate():
             user = User(form.username.data, form.email.data,
                         form.password.data)
             db_session.add(user)
             flash('Thanks for registering')
             return redirect(url_for('login'))
         return render_template('register.html', form=form)

Notice we’re implying that the view is using SQLAlchemy here (see
SQLAlchemy in Flask), but that’s not a requirement, of course.
Adapt the code as necessary.

Things to remember:

  1. create the form from the request ‘form’ value if the data is
     submitted via the HTTP ‘POST’ method and ‘args’ if the data is
     submitted as ‘GET’.

  2. to validate the data, call the ‘validate()’ method, which will
     return ‘True’ if the data validates, ‘False’ otherwise.

  3. to access individual values from the form, access
     ‘form.<NAME>.data’.

1.21.12.3 Forms in Templates
............................

Now to the template side.  When you pass the form to the templates, you
can easily render them there.  Look at the following example template to
see how easy this is.  WTForms does half the form generation for us
already.  To make it even nicer, we can write a macro that renders a
field with label and a list of errors if there are any.

Here’s an example ‘_formhelpers.html’ template with such a macro:

     {% macro render_field(field) %}
       <dt>{{ field.label }}
       <dd>{{ field(**kwargs)|safe }}
       {% if field.errors %}
         <ul class=errors>
         {% for error in field.errors %}
           <li>{{ error }}</li>
         {% endfor %}
         </ul>
       {% endif %}
       </dd>
     {% endmacro %}

This macro accepts a couple of keyword arguments that are forwarded to
WTForm’s field function, which renders the field for us.  The keyword
arguments will be inserted as HTML attributes.  So, for example, you can
call ‘render_field(form.username, class='username')’ to add a class to
the input element.  Note that WTForms returns standard Python strings,
so we have to tell Jinja2 that this data is already HTML-escaped with
the ‘|safe’ filter.

Here is the ‘register.html’ template for the function we used above,
which takes advantage of the ‘_formhelpers.html’ template:

     {% from "_formhelpers.html" import render_field %}
     <form method=post>
       <dl>
         {{ render_field(form.username) }}
         {{ render_field(form.email) }}
         {{ render_field(form.password) }}
         {{ render_field(form.confirm) }}
         {{ render_field(form.accept_tos) }}
       </dl>
       <p><input type=submit value=Register>
     </form>

For more information about WTForms, head over to the WTForms website(1).

   ---------- Footnotes ----------

   (1) https://wtforms.readthedocs.io/

1.21.13 Template Inheritance
----------------------------

The most powerful part of Jinja is template inheritance.  Template
inheritance allows you to build a base “skeleton” template that contains
all the common elements of your site and defines `blocks' that child
templates can override.

Sounds complicated but is very basic.  It’s easiest to understand it by
starting with an example.

1.21.13.1 Base Template
.......................

This template, which we’ll call ‘layout.html’, defines a simple HTML
skeleton document that you might use for a simple two-column page.  It’s
the job of “child” templates to fill the empty blocks with content:

     <!doctype html>
     <html>
       <head>
         {% block head %}
         <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
         <title>{% block title %}{% endblock %} - My Webpage</title>
         {% endblock %}
       </head>
       <body>
         <div id="content">{% block content %}{% endblock %}</div>
         <div id="footer">
           {% block footer %}
           &copy; Copyright 2010 by <a href="http://domain.invalid/">you</a>.
           {% endblock %}
         </div>
       </body>
     </html>

In this example, the ‘{% block %}’ tags define four blocks that child
templates can fill in.  All the ‘block’ tag does is tell the template
engine that a child template may override those portions of the
template.

1.21.13.2 Child Template
........................

A child template might look like this:

     {% extends "layout.html" %}
     {% block title %}Index{% endblock %}
     {% block head %}
       {{ super() }}
       <style type="text/css">
         .important { color: #336699; }
       </style>
     {% endblock %}
     {% block content %}
       <h1>Index</h1>
       <p class="important">
         Welcome on my awesome homepage.
     {% endblock %}

The ‘{% extends %}’ tag is the key here.  It tells the template engine
that this template “extends” another template.  When the template system
evaluates this template, first it locates the parent.  The extends tag
must be the first tag in the template.  To render the contents of a
block defined in the parent template, use ‘{{ super() }}’.

1.21.14 Message Flashing
------------------------

Good applications and user interfaces are all about feedback.  If the
user does not get enough feedback they will probably end up hating the
application.  Flask provides a really simple way to give feedback to a
user with the flashing system.  The flashing system basically makes it
possible to record a message at the end of a request and access it next
request and only next request.  This is usually combined with a layout
template that does this.  Note that browsers and sometimes web servers
enforce a limit on cookie sizes.  This means that flashing messages that
are too large for session cookies causes message flashing to fail
silently.

1.21.14.1 Simple Flashing
.........................

So here is a full example:

     from flask import Flask, flash, redirect, render_template, \
          request, url_for

     app = Flask(__name__)
     app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

     @app.route('/')
     def index():
         return render_template('index.html')

     @app.route('/login', methods=['GET', 'POST'])
     def login():
         error = None
         if request.method == 'POST':
             if request.form['username'] != 'admin' or \
                     request.form['password'] != 'secret':
                 error = 'Invalid credentials'
             else:
                 flash('You were successfully logged in')
                 return redirect(url_for('index'))
         return render_template('login.html', error=error)

And here is the ‘layout.html’ template which does the magic:

     <!doctype html>
     <title>My Application</title>
     {% with messages = get_flashed_messages() %}
       {% if messages %}
         <ul class=flashes>
         {% for message in messages %}
           <li>{{ message }}</li>
         {% endfor %}
         </ul>
       {% endif %}
     {% endwith %}
     {% block body %}{% endblock %}

Here is the ‘index.html’ template which inherits from ‘layout.html’:

     {% extends "layout.html" %}
     {% block body %}
       <h1>Overview</h1>
       <p>Do you want to <a href="{{ url_for('login') }}">log in?</a>
     {% endblock %}

And here is the ‘login.html’ template which also inherits from
‘layout.html’:

     {% extends "layout.html" %}
     {% block body %}
       <h1>Login</h1>
       {% if error %}
         <p class=error><strong>Error:</strong> {{ error }}
       {% endif %}
       <form method=post>
         <dl>
           <dt>Username:
           <dd><input type=text name=username value="{{
               request.form.username }}">
           <dt>Password:
           <dd><input type=password name=password>
         </dl>
         <p><input type=submit value=Login>
       </form>
     {% endblock %}

1.21.14.2 Flashing With Categories
..................................

New in version 0.3.

It is also possible to provide categories when flashing a message.  The
default category if nothing is provided is ‘'message'’.  Alternative
categories can be used to give the user better feedback.  For example
error messages could be displayed with a red background.

To flash a message with a different category, just use the second
argument to the flash() function:

     flash('Invalid password provided', 'error')

Inside the template you then have to tell the
get_flashed_messages() function to also return the categories.  The
loop looks slightly different in that situation then:

     {% with messages = get_flashed_messages(with_categories=true) %}
       {% if messages %}
         <ul class=flashes>
         {% for category, message in messages %}
           <li class="{{ category }}">{{ message }}</li>
         {% endfor %}
         </ul>
       {% endif %}
     {% endwith %}

This is just one example of how to render these flashed messages.  One
might also use the category to add a prefix such as
‘<strong>Error:</strong>’ to the message.

1.21.14.3 Filtering Flash Messages
..................................

New in version 0.9.

Optionally you can pass a list of categories which filters the results
of get_flashed_messages().  This is useful if you wish to
render each category in a separate block.

     {% with errors = get_flashed_messages(category_filter=["error"]) %}
     {% if errors %}
     <div class="alert-message block-message error">
       <a class="close" href="#">×</a>
       <ul>
         {%- for msg in errors %}
         <li>{{ msg }}</li>
         {% endfor -%}
       </ul>
     </div>
     {% endif %}
     {% endwith %}

1.21.15 AJAX with jQuery
------------------------

jQuery(1) is a small JavaScript library commonly used to simplify
working with the DOM and JavaScript in general.  It is the perfect tool
to make web applications more dynamic by exchanging JSON between server
and client.

JSON itself is a very lightweight transport format, very similar to how
Python primitives (numbers, strings, dicts and lists) look like which is
widely supported and very easy to parse.  It became popular a few years
ago and quickly replaced XML as transport format in web applications.

   ---------- Footnotes ----------

   (1) https://jquery.com/

1.21.15.1 Loading jQuery
........................

In order to use jQuery, you have to download it first and place it in
the static folder of your application and then ensure it’s loaded.
Ideally you have a layout template that is used for all pages where you
just have to add a script statement to the bottom of your ‘<body>’ to
load jQuery:

     <script src="{{ url_for('static', filename='jquery.js') }}"></script>

Another method is using Google’s AJAX Libraries API(1) to load jQuery:

     <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
     <script>window.jQuery || document.write('<script src="{{
       url_for('static', filename='jquery.js') }}">\x3C/script>')</script>

In this case you have to put jQuery into your static folder as a
fallback, but it will first try to load it directly from Google.  This
has the advantage that your website will probably load faster for users
if they went to at least one other website before using the same jQuery
version from Google because it will already be in the browser cache.

   ---------- Footnotes ----------

   (1) https://developers.google.com/speed/libraries/

1.21.15.2 Where is My Site?
...........................

Do you know where your application is?  If you are developing the answer
is quite simple: it’s on localhost port something and directly on the
root of that server.  But what if you later decide to move your
application to a different location?  For example to
‘http://example.com/myapp’?  On the server side this never was a problem
because we were using the handy url_for() function that could
answer that question for us, but if we are using jQuery we should not
hardcode the path to the application but make that dynamic, so how can
we do that?

A simple method would be to add a script tag to our page that sets a
global variable to the prefix to the root of the application.  Something
like this:

     <script>
       $SCRIPT_ROOT = {{ request.script_root|tojson }};
     </script>

1.21.15.3 JSON View Functions
.............................

Now let’s create a server side function that accepts two URL arguments
of numbers which should be added together and then sent back to the
application in a JSON object.  This is a really ridiculous example and
is something you usually would do on the client side alone, but a simple
example that shows how you would use jQuery and Flask nonetheless:

     from flask import Flask, jsonify, render_template, request
     app = Flask(__name__)

     @app.route('/_add_numbers')
     def add_numbers():
         a = request.args.get('a', 0, type=int)
         b = request.args.get('b', 0, type=int)
         return jsonify(result=a + b)

     @app.route('/')
     def index():
         return render_template('index.html')

As you can see I also added an ‘index’ method here that renders a
template.  This template will load jQuery as above and have a little
form where we can add two numbers and a link to trigger the function on
the server side.

Note that we are using the get()(1) method here which will never fail.
If the key is missing a default value (here ‘0’) is returned.
Furthermore it can convert values to a specific type (like in our case
‘int’).  This is especially handy for code that is triggered by a script
(APIs, JavaScript etc.)  because you don’t need special error reporting
in that case.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.MultiDict.get

1.21.15.4 The HTML
..................

Your index.html template either has to extend a ‘layout.html’ template
with jQuery loaded and the ‘$SCRIPT_ROOT’ variable set, or do that on
the top.  Here’s the HTML code needed for our little application
(‘index.html’).  Notice that we also drop the script directly into the
HTML here.  It is usually a better idea to have that in a separate
script file:

     <script>
       $(function() {
         $('a#calculate').bind('click', function() {
           $.getJSON($SCRIPT_ROOT + '/_add_numbers', {
             a: $('input[name="a"]').val(),
             b: $('input[name="b"]').val()
           }, function(data) {
             $("#result").text(data.result);
           });
           return false;
         });
       });
     </script>
     <h1>jQuery Example</h1>
     <p><input type=text size=5 name=a> +
        <input type=text size=5 name=b> =
        <span id=result>?</span>
     <p><a href=# id=calculate>calculate server side</a>

I won’t go into detail here about how jQuery works, just a very quick
explanation of the little bit of code above:

  1. ‘$(function() { ... })’ specifies code that should run once the
     browser is done loading the basic parts of the page.

  2. ‘$('selector')’ selects an element and lets you operate on it.

  3. ‘element.bind('event', func)’ specifies a function that should run
     when the user clicked on the element.  If that function returns
     ‘false’, the default behavior will not kick in (in this case,
     navigate to the ‘#’ URL).

  4. ‘$.getJSON(url, data, func)’ sends a ‘GET’ request to ‘url’ and
     will send the contents of the ‘data’ object as query parameters.
     Once the data arrived, it will call the given function with the
     return value as argument.  Note that we can use the ‘$SCRIPT_ROOT’
     variable here that we set earlier.

Check out the example source(1) for a full application demonstrating the
code on this page, as well as the same thing using ‘XMLHttpRequest’ and
‘fetch’.

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/tree/2.1.2/examples/javascript

1.21.16 Lazily Loading Views
----------------------------

Flask is usually used with the decorators.  Decorators are simple and
you have the URL right next to the function that is called for that
specific URL. However there is a downside to this approach: it means all
your code that uses decorators has to be imported upfront or Flask will
never actually find your function.

This can be a problem if your application has to import quick.  It might
have to do that on systems like Google’s App Engine or other systems.
So if you suddenly notice that your application outgrows this approach
you can fall back to a centralized URL mapping.

The system that enables having a central URL map is the
add_url_rule() function.  Instead of using decorators, you have a
file that sets up the application with all URLs.

1.21.16.1 Converting to Centralized URL Map
...........................................

Imagine the current application looks somewhat like this:

     from flask import Flask
     app = Flask(__name__)

     @app.route('/')
     def index():
         pass

     @app.route('/user/<username>')
     def user(username):
         pass

Then, with the centralized approach you would have one file with the
views (‘views.py’) but without any decorator:

     def index():
         pass

     def user(username):
         pass

And then a file that sets up an application which maps the functions to
URLs:

     from flask import Flask
     from yourapplication import views
     app = Flask(__name__)
     app.add_url_rule('/', view_func=views.index)
     app.add_url_rule('/user/<username>', view_func=views.user)

1.21.16.2 Loading Late
......................

So far we only split up the views and the routing, but the module is
still loaded upfront.  The trick is to actually load the view function
as needed.  This can be accomplished with a helper class that behaves
just like a function but internally imports the real function on first
use:

     from werkzeug.utils import import_string, cached_property

     class LazyView(object):

         def __init__(self, import_name):
             self.__module__, self.__name__ = import_name.rsplit('.', 1)
             self.import_name = import_name

         @cached_property
         def view(self):
             return import_string(self.import_name)

         def __call__(self, *args, **kwargs):
             return self.view(*args, **kwargs)

What’s important here is is that ‘__module__’ and ‘__name__’ are
properly set.  This is used by Flask internally to figure out how to
name the URL rules in case you don’t provide a name for the rule
yourself.

Then you can define your central place to combine the views like this:

     from flask import Flask
     from yourapplication.helpers import LazyView
     app = Flask(__name__)
     app.add_url_rule('/',
                      view_func=LazyView('yourapplication.views.index'))
     app.add_url_rule('/user/<username>',
                      view_func=LazyView('yourapplication.views.user'))

You can further optimize this in terms of amount of keystrokes needed to
write this by having a function that calls into add_url_rule()
by prefixing a string with the project name and a dot, and by
wrapping ‘view_func’ in a ‘LazyView’ as needed.

     def url(import_name, url_rules=[], **options):
         view = LazyView(f"yourapplication.{import_name}")
         for url_rule in url_rules:
             app.add_url_rule(url_rule, view_func=view, **options)

     # add a single route to the index view
     url('views.index', ['/'])

     # add two routes to a single function endpoint
     url_rules = ['/user/','/user/<username>']
     url('views.user', url_rules)

One thing to keep in mind is that before and after request handlers have
to be in a file that is imported upfront to work properly on the first
request.  The same goes for any kind of remaining decorator.

1.21.17 MongoDB with MongoEngine
--------------------------------

Using a document database like MongoDB is a common alternative to
relational SQL databases.  This pattern shows how to use MongoEngine(1),
a document mapper library, to integrate with MongoDB.

A running MongoDB server and Flask-MongoEngine(2) are required.

     pip install flask-mongoengine

   ---------- Footnotes ----------

   (1) http://mongoengine.org

   (2) https://flask-mongoengine.readthedocs.io

1.21.17.1 Configuration
.......................

Basic setup can be done by defining ‘MONGODB_SETTINGS’ on ‘app.config’
and creating a ‘MongoEngine’ instance.

     from flask import Flask
     from flask_mongoengine import MongoEngine

     app = Flask(__name__)
     app.config['MONGODB_SETTINGS'] = {
         "db": "myapp",
     }
     db = MongoEngine(app)

1.21.17.2 Mapping Documents
...........................

To declare a model that represents a Mongo document, create a class that
inherits from ‘Document’ and declare each of the fields.

     import mongoengine as me

     class Movie(me.Document):
         title = me.StringField(required=True)
         year = me.IntField()
         rated = me.StringField()
         director = me.StringField()
         actors = me.ListField()

If the document has nested fields, use ‘EmbeddedDocument’ to defined the
fields of the embedded document and ‘EmbeddedDocumentField’ to declare
it on the parent document.

     class Imdb(me.EmbeddedDocument):
         imdb_id = me.StringField()
         rating = me.DecimalField()
         votes = me.IntField()

     class Movie(me.Document):
         ...
         imdb = me.EmbeddedDocumentField(Imdb)

1.21.17.3 Creating Data
.......................

Instantiate your document class with keyword arguments for the fields.
You can also assign values to the field attributes after instantiation.
Then call ‘doc.save()’.

     bttf = Movie(title="Back To The Future", year=1985)
     bttf.actors = [
         "Michael J. Fox",
         "Christopher Lloyd"
     ]
     bttf.imdb = Imdb(imdb_id="tt0088763", rating=8.5)
     bttf.save()

1.21.17.4 Queries
.................

Use the class ‘objects’ attribute to make queries.  A keyword argument
looks for an equal value on the field.

     bttf = Movies.objects(title="Back To The Future").get_or_404()

Query operators may be used by concatenating them with the field name
using a double-underscore.  ‘objects’, and queries returned by calling
it, are iterable.

     some_theron_movie = Movie.objects(actors__in=["Charlize Theron"]).first()

     for recents in Movie.objects(year__gte=2017):
         print(recents.title)

1.21.17.5 Documentation
.......................

There are many more ways to define and query documents with MongoEngine.
For more information, check out the official documentation(1).

Flask-MongoEngine adds helpful utilities on top of MongoEngine.  Check
out their documentation(2) as well.

   ---------- Footnotes ----------

   (1) http://mongoengine.org

   (2) https://flask-mongoengine.readthedocs.io

1.21.18 Adding a favicon
------------------------

A “favicon” is an icon used by browsers for tabs and bookmarks.  This
helps to distinguish your website and to give it a unique brand.

A common question is how to add a favicon to a Flask application.
First, of course, you need an icon.  It should be 16 × 16 pixels and in
the ICO file format.  This is not a requirement but a de-facto standard
supported by all relevant browsers.  Put the icon in your static
directory as ‘favicon.ico’.

Now, to get browsers to find your icon, the correct way is to add a link
tag in your HTML. So, for example:

     <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">

That’s all you need for most browsers, however some really old ones do
not support this standard.  The old de-facto standard is to serve this
file, with this name, at the website root.  If your application is not
mounted at the root path of the domain you either need to configure the
web server to serve the icon at the root or if you can’t do that you’re
out of luck.  If however your application is the root you can simply
route a redirect:

     app.add_url_rule('/favicon.ico',
                      redirect_to=url_for('static', filename='favicon.ico'))

If you want to save the extra redirect request you can also write a view
using send_from_directory().:

     import os
     from flask import send_from_directory

     @app.route('/favicon.ico')
     def favicon():
         return send_from_directory(os.path.join(app.root_path, 'static'),
                                    'favicon.ico', mimetype='image/vnd.microsoft.icon')

We can leave out the explicit mimetype and it will be guessed, but we
may as well specify it to avoid the extra guessing, as it will always be
the same.

The above will serve the icon via your application and if possible it’s
better to configure your dedicated web server to serve it; refer to the
web server’s documentation.

1.21.18.1 See also
..................

   * The Favicon(1) article on Wikipedia

   ---------- Footnotes ----------

   (1) https://en.wikipedia.org/wiki/Favicon

1.21.19 Streaming Contents
--------------------------

Sometimes you want to send an enormous amount of data to the client,
much more than you want to keep in memory.  When you are generating the
data on the fly though, how do you send that back to the client without
the roundtrip to the filesystem?

The answer is by using generators and direct responses.

1.21.19.1 Basic Usage
.....................

This is a basic view function that generates a lot of CSV data on the
fly.  The trick is to have an inner function that uses a generator to
generate data and to then invoke that function and pass it to a response
object:

     @app.route('/large.csv')
     def generate_large_csv():
         def generate():
             for row in iter_all_rows():
                 yield f"{','.join(row)}\n"
         return app.response_class(generate(), mimetype='text/csv')

Each ‘yield’ expression is directly sent to the browser.  Note though
that some WSGI middlewares might break streaming, so be careful there in
debug environments with profilers and other things you might have
enabled.

1.21.19.2 Streaming from Templates
..................................

The Jinja2 template engine also supports rendering templates piece by
piece.  This functionality is not directly exposed by Flask because it
is quite uncommon, but you can easily do it yourself:

     def stream_template(template_name, **context):
         app.update_template_context(context)
         t = app.jinja_env.get_template(template_name)
         rv = t.stream(context)
         rv.enable_buffering(5)
         return rv

     @app.route('/my-large-page.html')
     def render_large_template():
         rows = iter_all_rows()
         return app.response_class(stream_template('the_template.html', rows=rows))

The trick here is to get the template object from the Jinja2 environment
on the application and to call stream()(1) instead of render()(2) which
returns a stream object instead of a string.  Since we’re bypassing the
Flask template render functions and using the template object itself we
have to make sure to update the render context ourselves by calling
update_template_context().  The template is then evaluated as
the stream is iterated over.  Since each time you do a yield the server
will flush the content to the client you might want to buffer up a few
items in the template which you can do with ‘rv.enable_buffering(size)’.
‘5’ is a sane default.

   ---------- Footnotes ----------

   (1)
https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Template.stream

   (2)
https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Template.render

1.21.19.3 Streaming with Context
................................

New in version 0.9.

Note that when you stream data, the request context is already gone the
moment the function executes.  Flask 0.9 provides you with a helper that
can keep the request context around during the execution of the
generator:

     from flask import stream_with_context, request

     @app.route('/stream')
     def streamed_response():
         def generate():
             yield 'Hello '
             yield request.args['name']
             yield '!'
         return app.response_class(stream_with_context(generate()))

Without the stream_with_context() function you would get a
RuntimeError(1) at that point.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/exceptions.html#RuntimeError

1.21.20 Deferred Request Callbacks
----------------------------------

One of the design principles of Flask is that response objects are
created and passed down a chain of potential callbacks that can modify
them or replace them.  When the request handling starts, there is no
response object yet.  It is created as necessary either by a view
function or by some other component in the system.

What happens if you want to modify the response at a point where the
response does not exist yet?  A common example for that would be a
before_request() callback that wants to set a cookie on the
response object.

One way is to avoid the situation.  Very often that is possible.  For
instance you can try to move that logic into a after_request()
callback instead.  However, sometimes moving code there makes it
more complicated or awkward to reason about.

As an alternative, you can use after_this_request() to
register callbacks that will execute after only the current request.
This way you can defer code execution from anywhere in the application,
based on the current request.

At any time during a request, we can register a function to be called at
the end of the request.  For example you can remember the current
language of the user in a cookie in a before_request().
callback:

     from flask import request, after_this_request

     @app.before_request
     def detect_user_language():
         language = request.cookies.get('user_lang')

         if language is None:
             language = guess_language_from_request()

             # when the response exists, set a cookie with the language
             @after_this_request
             def remember_language(response):
                 response.set_cookie('user_lang', language)
                 return response

         g.language = language

1.21.21 Adding HTTP Method Overrides
------------------------------------

Some HTTP proxies do not support arbitrary HTTP methods or newer HTTP
methods (such as PATCH). In that case it’s possible to “proxy” HTTP
methods through another HTTP method in total violation of the protocol.

The way this works is by letting the client do an HTTP POST request and
set the ‘X-HTTP-Method-Override’ header.  Then the method is replaced
with the header value before being passed to Flask.

This can be accomplished with an HTTP middleware:

     class HTTPMethodOverrideMiddleware(object):
         allowed_methods = frozenset([
             'GET',
             'HEAD',
             'POST',
             'DELETE',
             'PUT',
             'PATCH',
             'OPTIONS'
         ])
         bodyless_methods = frozenset(['GET', 'HEAD', 'OPTIONS', 'DELETE'])

         def __init__(self, app):
             self.app = app

         def __call__(self, environ, start_response):
             method = environ.get('HTTP_X_HTTP_METHOD_OVERRIDE', '').upper()
             if method in self.allowed_methods:
                 environ['REQUEST_METHOD'] = method
             if method in self.bodyless_methods:
                 environ['CONTENT_LENGTH'] = '0'
             return self.app(environ, start_response)

To use this with Flask, wrap the app object with the middleware:

     from flask import Flask

     app = Flask(__name__)
     app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app)

1.21.22 Request Content Checksums
---------------------------------

Various pieces of code can consume the request data and preprocess it.
For instance JSON data ends up on the request object already read and
processed, form data ends up there as well but goes through a different
code path.  This seems inconvenient when you want to calculate the
checksum of the incoming request data.  This is necessary sometimes for
some APIs.

Fortunately this is however very simple to change by wrapping the input
stream.

The following example calculates the SHA1 checksum of the incoming data
as it gets read and stores it in the WSGI environment:

     import hashlib

     class ChecksumCalcStream(object):

         def __init__(self, stream):
             self._stream = stream
             self._hash = hashlib.sha1()

         def read(self, bytes):
             rv = self._stream.read(bytes)
             self._hash.update(rv)
             return rv

         def readline(self, size_hint):
             rv = self._stream.readline(size_hint)
             self._hash.update(rv)
             return rv

     def generate_checksum(request):
         env = request.environ
         stream = ChecksumCalcStream(env['wsgi.input'])
         env['wsgi.input'] = stream
         return stream._hash

To use this, all you need to do is to hook the calculating stream in
before the request starts consuming data.  (Eg: be careful accessing
‘request.form’ or anything of that nature.  ‘before_request_handlers’
for instance should be careful not to access it).

Example usage:

     @app.route('/special-api', methods=['POST'])
     def special_api():
         hash = generate_checksum(request)
         # Accessing this parses the input stream
         files = request.files
         # At this point the hash is fully constructed.
         checksum = hash.hexdigest()
         return f"Hash was: {checksum}"

1.21.23 Celery Background Tasks
-------------------------------

If your application has a long running task, such as processing some
uploaded data or sending email, you don’t want to wait for it to finish
during a request.  Instead, use a task queue to send the necessary data
to another process that will run the task in the background while the
request returns immediately.

Celery is a powerful task queue that can be used for simple background
tasks as well as complex multi-stage programs and schedules.  This guide
will show you how to configure Celery using Flask, but assumes you’ve
already read the First Steps with Celery(1) guide in the Celery
documentation.

   ---------- Footnotes ----------

   (1)
https://celery.readthedocs.io/en/latest/getting-started/first-steps-with-celery.html

1.21.23.1 Install
.................

Celery is a separate Python package.  Install it from PyPI using pip:

     $ pip install celery

1.21.23.2 Configure
...................

The first thing you need is a Celery instance, this is called the celery
application.  It serves the same purpose as the Flask object
in Flask, just for Celery.  Since this instance is used as the
entry-point for everything you want to do in Celery, like creating tasks
and managing workers, it must be possible for other modules to import
it.

For instance you can place this in a ‘tasks’ module.  While you can use
Celery without any reconfiguration with Flask, it becomes a bit nicer by
subclassing tasks and adding support for Flask’s application contexts
and hooking it up with the Flask configuration.

This is all that is necessary to properly integrate Celery with Flask:

     from celery import Celery

     def make_celery(app):
         celery = Celery(
             app.import_name,
             backend=app.config['CELERY_RESULT_BACKEND'],
             broker=app.config['CELERY_BROKER_URL']
         )
         celery.conf.update(app.config)

         class ContextTask(celery.Task):
             def __call__(self, *args, **kwargs):
                 with app.app_context():
                     return self.run(*args, **kwargs)

         celery.Task = ContextTask
         return celery

The function creates a new Celery object, configures it with the broker
from the application config, updates the rest of the Celery config from
the Flask config and then creates a subclass of the task that wraps the
task execution in an application context.

1.21.23.3 An example task
.........................

Let’s write a task that adds two numbers together and returns the
result.  We configure Celery’s broker and backend to use Redis, create a
‘celery’ application using the factory from above, and then use it to
define the task.

     from flask import Flask

     flask_app = Flask(__name__)
     flask_app.config.update(
         CELERY_BROKER_URL='redis://localhost:6379',
         CELERY_RESULT_BACKEND='redis://localhost:6379'
     )
     celery = make_celery(flask_app)

     @celery.task()
     def add_together(a, b):
         return a + b

This task can now be called in the background:

     result = add_together.delay(23, 42)
     result.wait()  # 65

1.21.23.4 Run a worker
......................

If you jumped in and already executed the above code you will be
disappointed to learn that ‘.wait()’ will never actually return.  That’s
because you also need to run a Celery worker to receive and execute the
task.

     $ celery -A your_application.celery worker

The ‘your_application’ string has to point to your application’s package
or module that creates the ‘celery’ object.

Now that the worker is running, ‘wait’ will return the result once the
task is finished.

1.21.24 Subclassing Flask
-------------------------

The Flask class is designed for subclassing.

For example, you may want to override how request parameters are handled
to preserve their order:

     from flask import Flask, Request
     from werkzeug.datastructures import ImmutableOrderedMultiDict
     class MyRequest(Request):
         """Request subclass to override request parameter storage"""
         parameter_storage_class = ImmutableOrderedMultiDict
     class MyFlask(Flask):
         """Flask subclass using the custom request class"""
         request_class = MyRequest

This is the recommended approach for overriding or augmenting Flask’s
internal functionality.

1.21.25 Single-Page Applications
--------------------------------

Flask can be used to serve Single-Page Applications (SPA) by placing
static files produced by your frontend framework in a subfolder inside
of your project.  You will also need to create a catch-all endpoint that
routes all requests to your SPA.

The following example demonstrates how to serve an SPA along with an
API:

     from flask import Flask, jsonify

     app = Flask(__name__, static_folder='app', static_url_path="/app")

     @app.route("/heartbeat")
     def heartbeat():
         return jsonify({"status": "healthy"})

     @app.route('/', defaults={'path': ''})
     @app.route('/<path:path>')
     def catch_all(path):
         return app.send_static_file("index.html")

1.22 Deployment Options
=======================

While lightweight and easy to use, `Flask’s built-in server is not
suitable for production' as it doesn’t scale well.  Some of the options
available for properly running Flask in production are documented here.

If you want to deploy your Flask application to a WSGI server not listed
here, look up the server documentation about how to use a WSGI app with
it.  Just remember that your ‘Flask’ application object is the actual
WSGI application.

1.22.1 Hosted options
---------------------

   - Deploying Flask on Heroku(1)

   - Deploying Flask on Google App Engine(2)

   - Deploying Flask on Google Cloud Run(3)

   - Deploying Flask on AWS Elastic Beanstalk(4)

   - Deploying on Azure (IIS)(5)

   - Deploying on PythonAnywhere(6)

   ---------- Footnotes ----------

   (1) https://devcenter.heroku.com/articles/getting-started-with-python

   (2) https://cloud.google.com/appengine/docs/standard/python3/runtime

   (3)
https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python

   (4)
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html

   (5)
https://docs.microsoft.com/en-us/azure/app-service/containers/how-to-configure-python

   (6) https://help.pythonanywhere.com/pages/Flask/

1.22.2 Self-hosted options
--------------------------

1.22.2.1 Standalone WSGI Servers
................................

Most WSGI servers also provide HTTP servers, so they can run a WSGI
application and make it available externally.

It may still be a good idea to run the server behind a dedicated HTTP
server such as Apache or Nginx.  See Proxy Setups if you run
into issues with that.

1.22.2.2 Gunicorn
.................

Gunicorn(1) is a WSGI and HTTP server for UNIX. To run a Flask
application, tell Gunicorn how to import your Flask app object.

     $ gunicorn -w 4 -b 0.0.0.0:5000 your_project:app

The ‘-w 4’ option uses 4 workers to handle 4 requests at once.  The ‘-b
0.0.0.0:5000’ serves the application on all interfaces on port 5000.

Gunicorn provides many options for configuring the server, either
through a configuration file or with command line options.  Use
‘gunicorn --help’ or see the docs for more information.

The command expects the name of your module or package to import and the
application instance within the module.  If you use the application
factory pattern, you can pass a call to that.

     $ gunicorn -w 4 -b 0.0.0.0:5000 "myproject:create_app()"

   ---------- Footnotes ----------

   (1) https://gunicorn.org/

1.22.2.3 Async with Gevent or Eventlet
......................................

The default sync worker is appropriate for many use cases.  If you need
asynchronous support, Gunicorn provides workers using either gevent(1)
or eventlet(2).  This is not the same as Python’s ‘async/await’, or the
ASGI server spec.

When using either gevent or eventlet, greenlet>=1.0 is required,
otherwise context locals such as ‘request’ will not work as expected.
When using PyPy, PyPy>=7.3.7 is required.

To use gevent:

     $ gunicorn -k gevent -b 0.0.0.0:5000 your_project:app

To use eventlet:

     $ gunicorn -k eventlet -b 0.0.0.0:5000 your_project:app

   ---------- Footnotes ----------

   (1) http://www.gevent.org/

   (2) https://eventlet.net/

1.22.2.4 uWSGI
..............

uWSGI(1) is a fast application server written in C. It is very
configurable, which makes it more complicated to setup than Gunicorn.
It also provides many other utilities for writing robust web
applications.  To run a Flask application, tell uWSGI how to import your
Flask app object.

     $ uwsgi --master -p 4 --http 0.0.0.0:5000 -w your_project:app

The ‘-p 4’ option uses 4 workers to handle 4 requests at once.  The
‘--http 0.0.0.0:5000’ serves the application on all interfaces on port
5000.

uWSGI has optimized integration with Nginx and Apache instead of using a
standard HTTP proxy.  See configuring uWSGI and Nginx.

   ---------- Footnotes ----------

   (1) https://uwsgi-docs.readthedocs.io/en/latest/

1.22.2.5 Async with Gevent
..........................

The default sync worker is appropriate for many use cases.  If you need
asynchronous support, uWSGI provides workers using gevent(1).  It also
supports other async modes, see the docs for more information.  This is
not the same as Python’s ‘async/await’, or the ASGI server spec.

When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ‘request’ will not work as expected.  When using PyPy,
PyPy>=7.3.7 is required.

     $ uwsgi --master --gevent 100 --http 0.0.0.0:5000 -w your_project:app

   ---------- Footnotes ----------

   (1) http://www.gevent.org/

1.22.2.6 Gevent
...............

Prefer using Gunicorn(1) with Gevent workers rather than using Gevent
directly.  Gunicorn provides a much more configurable and
production-tested server.  See the section on Gunicorn above.

Gevent(2) allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python.  It uses greenlet(3) to enable task
switching without writing ‘async/await’ or using ‘asyncio’.

It provides a WSGI server that can handle many connections at once
instead of one per worker process.

Eventlet(4), described below, is another library that does the same
thing.  Certain dependencies you have, or other consideration, may
affect which of the two you choose to use

To use gevent to serve your application, import its ‘WSGIServer’ and use
it to run your ‘app’.

     from gevent.pywsgi import WSGIServer
     from your_project import app

     http_server = WSGIServer(("", 5000), app)
     http_server.serve_forever()

   ---------- Footnotes ----------

   (1) https://gunicorn.org/

   (2) http://www.gevent.org/

   (3) https://greenlet.readthedocs.io/en/latest/

   (4) https://eventlet.net/

1.22.2.7 Eventlet
.................

Prefer using Gunicorn(1) with Eventlet workers rather than using
Eventlet directly.  Gunicorn provides a much more configurable and
production-tested server.  See the section on Gunicorn above.

Eventlet(2) allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python.  It uses greenlet(3) to enable task
switching without writing ‘async/await’ or using ‘asyncio’.

It provides a WSGI server that can handle many connections at once
instead of one per worker process.

Gevent(4), described above, is another library that does the same thing.
Certain dependencies you have, or other consideration, may affect which
of the two you choose to use

To use eventlet to serve your application, import its ‘wsgi.server’ and
use it to run your ‘app’.

     import eventlet
     from eventlet import wsgi
     from your_project import app

     wsgi.server(eventlet.listen(("", 5000), app)

   ---------- Footnotes ----------

   (1) https://gunicorn.org/

   (2) https://eventlet.net/

   (3) https://greenlet.readthedocs.io/en/latest/

   (4) http://www.gevent.org/

1.22.2.8 Twisted Web
....................

Twisted Web(1) is the web server shipped with Twisted(2), a mature,
non-blocking event-driven networking library.  Twisted Web comes with a
standard WSGI container which can be controlled from the command line
using the ‘twistd’ utility:

     $ twistd web --wsgi myproject.app

This example will run a Flask application called ‘app’ from a module
named ‘myproject’.

Twisted Web supports many flags and options, and the ‘twistd’ utility
does as well; see ‘twistd -h’ and ‘twistd web -h’ for more information.
For example, to run a Twisted Web server in the foreground, on port
8080, with an application from ‘myproject’:

     $ twistd -n web --port tcp:8080 --wsgi myproject.app

   ---------- Footnotes ----------

   (1) https://twistedmatrix.com/trac/wiki/TwistedWeb

   (2) https://twistedmatrix.com/trac/

1.22.2.9 Proxy Setups
.....................

If you deploy your application using one of these servers behind an HTTP
proxy you will need to rewrite a few headers in order for the
application to work.  The two problematic values in the WSGI environment
usually are ‘REMOTE_ADDR’ and ‘HTTP_HOST’.  You can configure your httpd
to pass these headers, or you can fix them in middleware.  Werkzeug
ships a fixer that will solve some common setups, but you might want to
write your own WSGI middleware for specific setups.

Here’s a simple nginx configuration which proxies to an application
served on localhost at port 8000, setting appropriate headers:

     server {
         listen 80;

         server_name _;

         access_log  /var/log/nginx/access.log;
         error_log  /var/log/nginx/error.log;

         location / {
             proxy_pass         http://127.0.0.1:8000/;
             proxy_redirect     off;

             proxy_set_header   Host                 $host;
             proxy_set_header   X-Real-IP            $remote_addr;
             proxy_set_header   X-Forwarded-For      $proxy_add_x_forwarded_for;
             proxy_set_header   X-Forwarded-Proto    $scheme;
         }
     }

If your httpd is not providing these headers, the most common setup
invokes the host being set from ‘X-Forwarded-Host’ and the remote
address from ‘X-Forwarded-For’:

     from werkzeug.middleware.proxy_fix import ProxyFix
     app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

     Trusting Headers: Please keep in mind that it is a security issue
     to use such a middleware in a non-proxy setup because it will
     blindly trust the incoming headers which might be forged by
     malicious clients.

If you want to rewrite the headers from another header, you might want
to use a fixer like this:

     class CustomProxyFix(object):

         def __init__(self, app):
             self.app = app

         def __call__(self, environ, start_response):
             host = environ.get('HTTP_X_FHOST', '')
             if host:
                 environ['HTTP_HOST'] = host
             return self.app(environ, start_response)

     app.wsgi_app = CustomProxyFix(app.wsgi_app)

1.22.2.10 uWSGI
...............

uWSGI is a deployment option on servers like nginx(1), lighttpd(2), and
cherokee(3); see FastCGI and Standalone WSGI Servers
for other options.  To use your WSGI application with uWSGI
protocol you will need a uWSGI server first.  uWSGI is both a protocol
and an application server; the application server can serve uWSGI,
FastCGI, and HTTP protocols.

The most popular uWSGI server is uwsgi(4), which we will use for this
guide.  Make sure to have it installed to follow along.

     Watch Out: Please make sure in advance that any ‘app.run()’ calls
     you might have in your application file are inside an ‘if __name__
     == '__main__':’ block or moved to a separate file.  Just make sure
     it’s not called because this will always start a local WSGI server
     which we do not want if we deploy that application to uWSGI.

   ---------- Footnotes ----------

   (1) https://nginx.org/

   (2) https://www.lighttpd.net/

   (3) https://cherokee-project.com/

   (4) https://uwsgi-docs.readthedocs.io/en/latest/

1.22.2.11 Starting your app with uwsgi
......................................

‘uwsgi’ is designed to operate on WSGI callables found in python
modules.

Given a flask application in myapp.py, use the following command:

     $ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app

The ‘--manage-script-name’ will move the handling of ‘SCRIPT_NAME’ to
uwsgi, since it is smarter about that.  It is used together with the
‘--mount’ directive which will make requests to ‘/yourapplication’ be
directed to ‘myapp:app’.  If your application is accessible at root
level, you can use a single ‘/’ instead of ‘/yourapplication’.  ‘myapp’
refers to the name of the file of your flask application (without
extension) or the module which provides ‘app’.  ‘app’ is the callable
inside of your application (usually the line reads ‘app =
Flask(__name__)’).

If you want to deploy your flask application inside of a virtual
environment, you need to also add ‘--virtualenv
/path/to/virtual/environment’.  You might also need to add ‘--plugin
python’ or ‘--plugin python3’ depending on which python version you use
for your project.

1.22.2.12 Configuring nginx
...........................

A basic flask nginx configuration looks like this:

     location = /yourapplication { rewrite ^ /yourapplication/; }
     location /yourapplication { try_files $uri @yourapplication; }
     location @yourapplication {
       include uwsgi_params;
       uwsgi_pass unix:/tmp/yourapplication.sock;
     }

This configuration binds the application to ‘/yourapplication’.  If you
want to have it in the URL root its a bit simpler:

     location / { try_files $uri @yourapplication; }
     location @yourapplication {
         include uwsgi_params;
         uwsgi_pass unix:/tmp/yourapplication.sock;
     }

1.22.2.13 mod_wsgi (Apache)
...........................

If you are using the Apache(1) webserver, consider using mod_wsgi(2).

     Watch Out: Please make sure in advance that any ‘app.run()’ calls
     you might have in your application file are inside an ‘if __name__
     == '__main__':’ block or moved to a separate file.  Just make sure
     it’s not called because this will always start a local WSGI server
     which we do not want if we deploy that application to mod_wsgi.

   ---------- Footnotes ----------

   (1) https://httpd.apache.org/

   (2) https://github.com/GrahamDumpleton/mod_wsgi

1.22.2.14 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(1) cover 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-py3

If you are using a yum based distribution (Fedora, OpenSUSE, etc..)  you
can install it as follows:

     $ yum install mod_wsgi

On FreeBSD install ‘mod_wsgi’ by compiling the ‘www/mod_wsgi’ port or by
using pkg_add:

     $ pkg install ap24-py37-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.

   ---------- Footnotes ----------

   (1) https://modwsgi.readthedocs.io/en/develop/installation.html

1.22.2.15 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 app as application

If a factory function is used in a ‘__init__.py’ file, then the function
should be imported:

     from yourapplication import create_app
     application = create_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 that you will find it again (e.g.:
‘/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(1)
instance.  Keep in mind that you will have to actually install your
application into the virtualenv as well.  Alternatively there is the
option to just patch the path in the ‘.wsgi’ file before the import:

     import sys
     sys.path.insert(0, '/path/to/the/application')

   ---------- Footnotes ----------

   (1) https://pypi.org/project/virtualenv/

1.22.2.16 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:

     <VirtualHost *>
         ServerName example.com

         WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
         WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi

         <Directory /var/www/yourapplication>
             WSGIProcessGroup yourapplication
             WSGIApplicationGroup %{GLOBAL}
             Order deny,allow
             Allow from all
         </Directory>
     </VirtualHost>

Note: WSGIDaemonProcess isn’t implemented in Windows and Apache will
refuse to run with the above configuration.  On a Windows system,
eliminate those lines:

     <VirtualHost *>
         ServerName example.com
         WSGIScriptAlias / C:\yourdir\yourapp.wsgi
         <Directory C:\yourdir>
             Order deny,allow
             Allow from all
         </Directory>
     </VirtualHost>

Note: There have been some changes in access control configuration for
Apache 2.4(1).

Most notably, the syntax for directory permissions has changed from
httpd 2.2

     Order allow,deny
     Allow from all

to httpd 2.4 syntax

     Require all granted

For more information consult the mod_wsgi documentation(2).

   ---------- Footnotes ----------

   (1) https://httpd.apache.org/docs/trunk/upgrading.html

   (2) https://modwsgi.readthedocs.io/en/develop/index.html

1.22.2.17 Troubleshooting
.........................

If your application does not run, follow this guide to troubleshoot:

`Problem:' application does not run, errorlog shows SystemExit ignored

     You have an ‘app.run()’ call in your application file that is not
     guarded by an ‘if __name__ == '__main__':’ condition.  Either
     remove that run() call from the file and move it into a
     separate ‘run.py’ file or put it into such an if block.

`Problem:' application gives permission errors

     Probably caused by your application running as the wrong user.
     Make sure the folders the application needs access to have the
     proper privileges set and the application runs as the correct user
     (‘user’ and ‘group’ parameter to the ‘WSGIDaemonProcess’ directive)

`Problem:' application dies with an error on print

     Keep in mind that mod_wsgi disallows doing anything with
     sys.stdout(1) and sys.stderr(2).  You can disable this protection
     from the config by setting the ‘WSGIRestrictStdout’ to ‘off’:

          WSGIRestrictStdout Off

     Alternatively you can also replace the standard out in the .wsgi
     file with a different stream:

          import sys
          sys.stdout = sys.stderr

`Problem:' accessing resources gives IO errors

     Your application probably is a single .py file you symlinked into
     the site-packages folder.  Please be aware that this does not work,
     instead you either have to put the folder into the pythonpath the
     file is stored in, or convert your application into a package.

     The reason for this is that for non-installed packages, the module
     filename is used to locate the resources and for symlinks the wrong
     filename is picked up.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/sys.html#sys.stdout

   (2) https://docs.python.org/3/library/sys.html#sys.stderr

1.22.2.18 Support for Automatic Reloading
.........................................

To help deployment tools you can activate support for automatic
reloading.  Whenever something changes the ‘.wsgi’ file, ‘mod_wsgi’ will
reload all the daemon processes for us.

For that, just add the following directive to your ‘Directory’ section:

     WSGIScriptReloading On

1.22.2.19 Working with Virtual Environments
...........................................

Virtual environments have the advantage that they never install the
required dependencies system wide so you have a better control over what
is used where.  If you want to use a virtual environment with mod_wsgi
you have to modify your ‘.wsgi’ file slightly.

Add the following lines to the top of your ‘.wsgi’ file:

     activate_this = '/path/to/env/bin/activate_this.py'
     with open(activate_this) as file_:
         exec(file_.read(), dict(__file__=activate_this))

This sets up the load paths according to the settings of the virtual
environment.  Keep in mind that the path has to be absolute.

1.22.2.20 FastCGI
.................

FastCGI is a deployment option on servers like nginx(1), lighttpd(2),
and cherokee(3); see uWSGI and Standalone WSGI Servers
for other options.  To use your WSGI application with any
of them you will need a FastCGI server first.  The most popular one is
flup(4) which we will use for this guide.  Make sure to have it
installed to follow along.

     Watch Out: Please make sure in advance that any ‘app.run()’ calls
     you might have in your application file are inside an ‘if __name__
     == '__main__':’ block or moved to a separate file.  Just make sure
     it’s not called because this will always start a local WSGI server
     which we do not want if we deploy that application to FastCGI.

   ---------- Footnotes ----------

   (1) https://nginx.org/

   (2) https://www.lighttpd.net/

   (3) https://cherokee-project.com/

   (4) https://pypi.org/project/flup/

1.22.2.21 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 app

     if __name__ == '__main__':
         WSGIServer(app).run()

This is enough for Apache to work, however nginx 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 ‘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

1.22.2.22 Configuring Apache
............................

The example above is good enough for a basic Apache deployment but your
‘.fcgi’ file will appear in your application URL e.g.
‘example.com/yourapplication.fcgi/news/’.  There are few ways to
configure your application so that yourapplication.fcgi does not appear
in the URL. A preferable way is to use the ScriptAlias and SetHandler
configuration directives to route requests to the FastCGI server.  The
following example uses FastCgiServer to start 5 instances of the
application which will handle all incoming requests:

     LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so

     FastCgiServer /var/www/html/yourapplication/app.fcgi -idle-timeout 300 -processes 5

     <VirtualHost *>
         ServerName webapp1.mydomain.com
         DocumentRoot /var/www/html/yourapplication

         AddHandler fastcgi-script fcgi
         ScriptAlias / /var/www/html/yourapplication/app.fcgi/

         <Location />
             SetHandler fastcgi-script
         </Location>
     </VirtualHost>

These processes will be managed by Apache.  If you’re using a standalone
FastCGI server, you can use the FastCgiExternalServer directive instead.
Note that in the following the path is not real, it’s simply used as an
identifier to other directives such as AliasMatch:

     FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000

If you cannot set ScriptAlias, for example on a shared web host, you can
use WSGI middleware to remove yourapplication.fcgi from the URLs.  Set
.htaccess:

     <IfModule mod_fcgid.c>
        AddHandler fcgid-script .fcgi
        <Files ~ (\.fcgi)>
            SetHandler fcgid-script
            Options +FollowSymLinks +ExecCGI
        </Files>
     </IfModule>

     <IfModule mod_rewrite.c>
        Options +FollowSymlinks
        RewriteEngine On
        RewriteBase /
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^(.*)$ yourapplication.fcgi/$1 [QSA,L]
     </IfModule>

Set yourapplication.fcgi:

     #!/usr/bin/python
     #: optional path to your local python site-packages folder
     import sys
     sys.path.insert(0, '<your_local_path>/lib/python<your_python_version>/site-packages')

     from flup.server.fcgi import WSGIServer
     from yourapplication import app

     class ScriptNameStripper(object):
        def __init__(self, app):
            self.app = app

        def __call__(self, environ, start_response):
            environ['SCRIPT_NAME'] = ''
            return self.app(environ, start_response)

     app = ScriptNameStripper(app)

     if __name__ == '__main__':
         WSGIServer(app).run()

1.22.2.23 Configuring lighttpd
..............................

A basic FastCGI configuration for lighttpd looks like that:

     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 ‘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(1) (note that explicitly passing a socket to run() is no longer
necessary).

   ---------- Footnotes ----------

   (1)
https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI

1.22.2.24 Configuring nginx
...........................

Installing FastCGI applications on nginx is a bit different because by
default no FastCGI parameters are forwarded.

A basic Flask FastCGI configuration for nginx looks like this:

     location = /yourapplication { rewrite ^ /yourapplication/ last; }
     location /yourapplication { try_files $uri @yourapplication; }
     location @yourapplication {
         include fastcgi_params;
         fastcgi_split_path_info ^(/yourapplication)(.*)$;
         fastcgi_param PATH_INFO $fastcgi_path_info;
         fastcgi_param SCRIPT_NAME $fastcgi_script_name;
         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 simpler because you don’t
have to figure out how to calculate ‘PATH_INFO’ and ‘SCRIPT_NAME’:

     location / { try_files $uri @yourapplication; }
     location @yourapplication {
         include fastcgi_params;
         fastcgi_param PATH_INFO $fastcgi_script_name;
         fastcgi_param SCRIPT_NAME "";
         fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
     }

1.22.2.25 Running FastCGI Processes
...................................

Since nginx and others do not load FastCGI apps, you have to do it by
yourself.  Supervisor can manage FastCGI processes.(1)  You can look
around for other FastCGI process managers or write a script to run your
‘.fcgi’ file at boot, e.g.  using a SysV ‘init.d’ script.  For a
temporary solution, you can always run the ‘.fcgi’ script inside GNU
screen.  See ‘man screen’ for details, and note that this is a manual
solution which does not persist across system restart:

     $ screen
     $ /var/www/yourapplication/yourapplication.fcgi

   ---------- Footnotes ----------

   (1)
http://supervisord.org/configuration.html#fcgi-program-x-section-settings

1.22.2.26 Debugging
...................

FastCGI deployments tend to be hard to debug on most web servers.  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 web server user is ‘www-data’:

     $ su www-data
     $ cd /var/www/yourapplication
     $ python application.fcgi
     Traceback (most recent call last):
       File "yourapplication.fcgi", 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.

1.22.2.27 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 sub-optimal
performance.

This is also the way you can use a Flask application on Google’s App
Engine(1), where execution happens in a CGI-like environment.

     Watch Out: Please make sure in advance that any ‘app.run()’ calls
     you might have in your application file are inside an ‘if __name__
     == '__main__':’ block or moved to a separate file.  Just make sure
     it’s not called because this will always start a local WSGI server
     which we do not want if we deploy that application to CGI / app
     engine.

     With CGI, you will also have to make sure that your code does not
     contain any ‘print’ statements, or that ‘sys.stdout’ is overridden
     by something that doesn’t write into the HTTP response.

   ---------- Footnotes ----------

   (1) https://cloud.google.com/appengine/docs/

1.22.2.28 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 app

     CGIHandler().run(app)

1.22.2.29 Server Setup
......................

Usually there are two ways to configure the server.  Either just copy
the ‘.cgi’ into a ‘cgi-bin’ (and use ‘mod_rewrite’ or something similar
to rewrite the URL) or let the server point to the file directly.

In Apache for example you can put something like this into the config:

     ScriptAlias /app /path/to/the/application.cgi

On shared webhosting, though, you might not have access to your Apache
config.  In this case, a file called ‘.htaccess’, sitting in the public
directory you want your app to be available, works too but the
‘ScriptAlias’ directive won’t work in that case:

     RewriteEngine On
     RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files
     RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L]

For more information consult the documentation of your webserver.

1.22.2.30 ASGI
..............

If you’d like to use an ASGI server you will need to utilise WSGI to
ASGI middleware.  The asgiref WsgiToAsgi(1) adapter is recommended as it
integrates with the event loop used for Flask’s Using async and await
support.  You can use the adapter by wrapping the Flask app,

     from asgiref.wsgi import WsgiToAsgi
     from flask import Flask

     app = Flask(__name__)

     ...

     asgi_app = WsgiToAsgi(app)

and then serving the ‘asgi_app’ with the ASGI server, e.g.  using
Hypercorn(2),

     $ hypercorn module:asgi_app

   ---------- Footnotes ----------

   (1) https://github.com/django/asgiref#wsgi-to-asgi-adapter

   (2) https://gitlab.com/pgjones/hypercorn

1.23 Becoming Big
=================

Here are your options when growing your codebase or scaling your
application.

1.23.1 Read the Source.
-----------------------

Flask started in part to demonstrate how to build your own framework on
top of existing well-used tools Werkzeug (WSGI) and Jinja (templating),
and as it developed, it became useful to a wide audience.  As you grow
your codebase, don’t just use Flask – understand it.  Read the source.
Flask’s code is written to be read; its documentation is published so
you can use its internal APIs.  Flask sticks to documented APIs in
upstream libraries, and documents its internal utilities so that you can
find the hook points needed for your project.

1.23.2 Hook. Extend.
--------------------

The API docs are full of available overrides, hook points, and
Signals.  You can provide custom classes for things like the
request and response objects.  Dig deeper on the APIs you use, and look
for the customizations which are available out of the box in a Flask
release.  Look for ways in which your project can be refactored into a
collection of utilities and Flask extensions.  Explore the many
Extensions in the community, and look for patterns to build your
own extensions if you do not find the tools you need.

1.23.3 Subclass.
----------------

The Flask class has many methods designed for subclassing.
You can quickly add or customize behavior by subclassing Flask.
(see the linked method docs) and using that subclass wherever you
instantiate an application class.  This works well with
Application Factories.  See Subclassing Flask for an
example.

1.23.4 Wrap with middleware.
----------------------------

The Application Dispatching pattern shows in detail how to
apply middleware.  You can introduce WSGI middleware to wrap your Flask
instances and introduce fixes and changes at the layer between your
Flask application and your HTTP server.  Werkzeug includes several
middlewares(1).

   ---------- Footnotes ----------

   (1) https://werkzeug.palletsprojects.com/middleware/

1.23.5 Fork.
------------

If none of the above options work, fork Flask.  The majority of code of
Flask is within Werkzeug and Jinja2.  These libraries do the majority of
the work.  Flask is just the paste that glues those together.  For every
project there is the point where the underlying framework gets in the
way (due to assumptions the original developers had).  This is natural
because if this would not be the case, the framework would be a very
complex system to begin with which causes a steep learning curve and a
lot of user frustration.

This is not unique to Flask.  Many people use patched and modified
versions of their framework to counter shortcomings.  This idea is also
reflected in the license of Flask.  You don’t have to contribute any
changes back if you decide to modify the framework.

The downside of forking is of course that Flask extensions will most
likely break because the new framework has a different import name.
Furthermore integrating upstream changes can be a complex process,
depending on the number of changes.  Because of that, forking should be
the very last resort.

1.23.6 Scale like a pro.
------------------------

For many web applications the complexity of the code is less an issue
than the scaling for the number of users or data entries expected.
Flask by itself is only limited in terms of scaling by your application
code, the data store you want to use and the Python implementation and
webserver you are running on.

Scaling well means for example that if you double the amount of servers
you get about twice the performance.  Scaling bad means that if you add
a new server the application won’t perform any better or would not even
support a second server.

There is only one limiting factor regarding scaling in Flask which are
the context local proxies.  They depend on context which in Flask is
defined as being either a thread, process or greenlet.  If your server
uses some kind of concurrency that is not based on threads or greenlets,
Flask will no longer be able to support these global proxies.  However
the majority of servers are using either threads, greenlets or separate
processes to achieve concurrency which are all methods well supported by
the underlying Werkzeug library.

1.23.7 Discuss with the community.
----------------------------------

The Flask developers keep the framework accessible to users with
codebases big and small.  If you find an obstacle in your way, caused by
Flask, don’t hesitate to contact the developers on the mailing list or
Discord server.  The best way for the Flask and Flask extension
developers to improve the tools for larger applications is getting
feedback from users.

1.24 Using ‘async’ and ‘await’
==============================

New in version 2.0.

Routes, error handlers, before request, after request, and teardown
functions can all be coroutine functions if Flask is installed with the
‘async’ extra (‘pip install flask[async]’).  This allows views to be
defined with ‘async def’ and use ‘await’.

     @app.route("/get-data")
     async def get_data():
         data = await async_db_query(...)
         return jsonify(data)

Pluggable class-based views also support handlers that are implemented
as coroutines.  This applies to the dispatch_request().
method in views that inherit from the flask.views.View.
class, as well as all the HTTP method handlers in views that inherit
from the flask.views.MethodView class.

     Using async on Windows on Python 3.8: Python 3.8 has a bug related
     to asyncio on Windows.  If you encounter something like
     ‘ValueError: set_wakeup_fd only works in main thread’, please
     upgrade to Python 3.9.

     Using async with greenlet: When using gevent or eventlet to serve
     an application or patch the runtime, greenlet>=1.0 is required.
     When using PyPy, PyPy>=7.3.7 is required.

1.24.1 Performance
------------------

Async functions require an event loop to run.  Flask, as a WSGI
application, uses one worker to handle one request/response cycle.  When
a request comes in to an async view, Flask will start an event loop in a
thread, run the view function there, then return the result.

Each request still ties up one worker, even for async views.  The upside
is that you can run async code within a view, for example to make
multiple concurrent database queries, HTTP requests to an external API,
etc.  However, the number of requests your application can handle at one
time will remain the same.

`Async is not inherently faster than sync code.'  Async is beneficial
when performing concurrent IO-bound tasks, but will probably not improve
CPU-bound tasks.  Traditional Flask views will still be appropriate for
most use cases, but Flask’s async support enables writing and using code
that wasn’t possible natively before.

1.24.2 Background tasks
-----------------------

Async functions will run in an event loop until they complete, at which
stage the event loop will stop.  This means any additional spawned tasks
that haven’t completed when the async function completes will be
cancelled.  Therefore you cannot spawn background tasks, for example via
‘asyncio.create_task’.

If you wish to use background tasks it is best to use a task queue to
trigger background work, rather than spawn tasks in a view function.
With that in mind you can spawn asyncio tasks by serving Flask with an
ASGI server and utilising the asgiref WsgiToAsgi adapter as described in
ASGI.  This works as the adapter creates an event loop that
runs continually.

1.24.3 When to use Quart instead
--------------------------------

Flask’s async support is less performant than async-first frameworks due
to the way it is implemented.  If you have a mainly async codebase it
would make sense to consider Quart(1).  Quart is a reimplementation of
Flask based on the ASGI(2) standard instead of WSGI. This allows it to
handle many concurrent requests, long running requests, and websockets
without requiring multiple worker processes or threads.

It has also already been possible to run Flask with Gevent or Eventlet
to get many of the benefits of async request handling.  These libraries
patch low-level Python functions to accomplish this, whereas ‘async’/
‘await’ and ASGI use standard, modern Python capabilities.  Deciding
whether you should use Flask, Quart, or something else is ultimately up
to understanding the specific needs of your project.

   ---------- Footnotes ----------

   (1) https://gitlab.com/pgjones/quart

   (2) https://asgi.readthedocs.io/en/latest/

1.24.4 Extensions
-----------------

Flask extensions predating Flask’s async support do not expect async
views.  If they provide decorators to add functionality to views, those
will probably not work with async views because they will not await the
function or be awaitable.  Other functions they provide will not be
awaitable either and will probably be blocking if called within an async
view.

Extension authors can support async functions by utilising the
flask.Flask.ensure_sync() method.  For example, if the extension
provides a view function decorator add ‘ensure_sync’ before calling the
decorated function,

     def extension(func):
         @wraps(func)
         def wrapper(*args, **kwargs):
             ...  # Extension logic
             return current_app.ensure_sync(func)(*args, **kwargs)

         return wrapper

Check the changelog of the extension you want to use to see if they’ve
implemented async support, or make a feature request or PR to them.

1.24.5 Other event loops
------------------------

At the moment Flask only supports asyncio(1).  It’s possible to override
flask.Flask.ensure_sync() to change how async functions are
wrapped to use a different library.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/asyncio.html#module-asyncio

2 API Reference
***************

If you are looking for information on a specific function, class or
method, this part of the documentation is for you.

2.1 API
=======

This part of the documentation covers all the interfaces of Flask.  For
parts where Flask depends on external libraries, we document the most
important right here and provide links to the canonical documentation.

2.1.1 Application Object
------------------------

 -- Class: flask.Flask (import_name, static_url_path=None,
          static_folder='static', static_host=None, host_matching=False,
          subdomain_matching=False, template_folder='templates',
          instance_path=None, instance_relative_config=False,
          root_path=None)

     The flask object implements a WSGI application and acts as the
     central object.  It is passed the name of the module or package of
     the application.  Once it is created it will act as a central
     registry for the view functions, the URL rules, template
     configuration and much more.

     The name of the package is used to resolve resources from inside
     the package or the folder the module is contained in depending on
     if the package parameter resolves to an actual python package (a
     folder with an ‘__init__.py’ file inside) or a standard module
     (just a ‘.py’ file).

     For more information about resource loading, see
     open_resource().

     Usually you create a Flask instance in your main module
     or in the ‘__init__.py’ file of your package like this:

          from flask import Flask
          app = Flask(__name__)

          About the First Parameter: The idea of the first parameter is
          to give Flask an idea of what belongs to your application.
          This name is used to find resources on the filesystem, can be
          used by extensions to improve debugging information and a lot
          more.

          So it’s important what you provide there.  If you are using a
          single module, ‘__name__’ is always the correct value.  If you
          however are using a package, it’s usually recommended to
          hardcode the name of your package there.

          For example if your application is defined in
          ‘yourapplication/app.py’ you should create it with one of the
          two versions below:

               app = Flask('yourapplication')
               app = Flask(__name__.split('.')[0])

          Why is that?  The application will work even with ‘__name__’,
          thanks to how resources are looked up.  However it will make
          debugging more painful.  Certain extensions can make
          assumptions based on the import name of your application.  For
          example the Flask-SQLAlchemy extension will look for the code
          in your application that triggered an SQL query in debug mode.
          If the import name is not properly set up, that debugging
          information is lost.  (For example it would only pick up SQL
          queries in ‘yourapplication.app’ and not
          ‘yourapplication.views.frontend’)

     New in version 1.0: The ‘host_matching’ and ‘static_host’
     parameters were added.

     New in version 1.0: The ‘subdomain_matching’ parameter was added.
     Subdomain matching needs to be enabled manually now.  Setting
     SERVER_NAME does not implicitly enable it.

     New in version 0.11: The ‘root_path’ parameter was added.

     New in version 0.8: The ‘instance_path’ and
     ‘instance_relative_config’ parameters were added.

     New in version 0.7: The ‘static_url_path’, ‘static_folder’, and
     ‘template_folder’ parameters were added.

     Parameters:

        * ‘import_name’ (str(1)) – the name of the application package

        * ‘static_url_path’ (Optional(2)‘[’str(3)‘]’) – can be used to
          specify a different path for the static files on the web.
          Defaults to the name of the ‘static_folder’ folder.

        * ‘static_folder’ (Optional(4)‘[’Union(5)‘[’str(6)‘,
          ’os.PathLike(7)‘]’‘]’) – The folder with static files that is
          served at ‘static_url_path’.  Relative to the application
          ‘root_path’ or an absolute path.  Defaults to ‘'static'’.

        * ‘static_host’ (Optional(8)‘[’str(9)‘]’) – the host to use when
          adding the static route.  Defaults to None.  Required when
          using ‘host_matching=True’ with a ‘static_folder’ configured.

        * ‘host_matching’ (bool(10)) – set ‘url_map.host_matching’
          attribute.  Defaults to False.

        * ‘subdomain_matching’ (bool(11)) – consider the subdomain
          relative to SERVER_NAME when matching routes.
          Defaults to False.

        * ‘template_folder’ (Optional(12)‘[’str(13)‘]’) – the folder
          that contains the templates that should be used by the
          application.  Defaults to ‘'templates'’ folder in the root
          path of the application.

        * ‘instance_path’ (Optional(14)‘[’str(15)‘]’) – An alternative
          instance path for the application.  By default the folder
          ‘'instance'’ next to the package or module is assumed to be
          the instance path.

        * ‘instance_relative_config’ (bool(16)) – if set to ‘True’
          relative filenames for loading the config are assumed to be
          relative to the instance path instead of the application root.

        * ‘root_path’ (Optional(17)‘[’str(18)‘]’) – The path to the root
          of the application files.  This should only be set manually
          when it can’t be detected automatically, such as for namespace
          packages.

      -- Method: add_template_filter (f, name=None)

          Register a custom template filter.  Works exactly like the
          template_filter() decorator.

          Parameters:

             * ‘name’ (Optional(19)‘[’str(20)‘]’) – the optional name of
               the filter, otherwise the function name will be used.

             * ‘f’ (Callable(21)‘[’‘[’‘...’‘]’‘, ’Any(22)‘]’) –

          Return type: None

      -- Method: add_template_global (f, name=None)

          Register a custom template global function.  Works exactly
          like the template_global() decorator.

          New in version 0.10.

          Parameters:

             * ‘name’ (Optional(23)‘[’str(24)‘]’) – the optional name of
               the global function, otherwise the function name will be
               used.

             * ‘f’ (Callable(25)‘[’‘[’‘...’‘]’‘, ’Any(26)‘]’) –

          Return type: None

      -- Method: add_template_test (f, name=None)

          Register a custom template test.  Works exactly like the
          template_test() decorator.

          New in version 0.10.

          Parameters:

             * ‘name’ (Optional(27)‘[’str(28)‘]’) – the optional name of
               the test, otherwise the function name will be used.

             * ‘f’ (Callable(29)‘[’‘[’‘...’‘]’‘, ’bool(30)‘]’) –

          Return type: None

      -- Method: add_url_rule (rule, endpoint=None, view_func=None,
               provide_automatic_options=None, **options)

          Register a rule for routing incoming requests and building
          URLs.  The route() decorator is a shortcut to call
          this with the ‘view_func’ argument.  These are equivalent:

               @app.route("/")
               def index():
                   ...

               def index():
                   ...

               app.add_url_rule("/", view_func=index)

          See URL Route Registrations.

          The endpoint name for the route defaults to the name of the
          view function if the ‘endpoint’ parameter isn’t passed.  An
          error will be raised if a function has already been registered
          for the endpoint.

          The ‘methods’ parameter defaults to ‘["GET"]’.  ‘HEAD’ is
          always added automatically, and ‘OPTIONS’ is added
          automatically by default.

          ‘view_func’ does not necessarily need to be passed, but if the
          rule should participate in routing an endpoint name must be
          associated with a view function at some point with the
          endpoint() decorator.

               app.add_url_rule("/", endpoint="index")

               @app.endpoint("index")
               def index():
                   ...

          If ‘view_func’ has a ‘required_methods’ attribute, those
          methods are added to the passed and automatic methods.  If it
          has a ‘provide_automatic_methods’ attribute, it is used as the
          default if the parameter is not passed.

          Parameters:

             * ‘rule’ (str(31)) – The URL rule string.

             * ‘endpoint’ (Optional(32)‘[’str(33)‘]’) – The endpoint
               name to associate with the rule and view function.  Used
               when routing and building URLs.  Defaults to
               ‘view_func.__name__’.

             * ‘view_func’ (Optional(34)‘[’Callable(35)‘]’) – The view
               function to associate with the endpoint name.

             * ‘provide_automatic_options’ (Optional(36)‘[’bool(37)‘]’)
               – Add the ‘OPTIONS’ method and respond to ‘OPTIONS’
               requests automatically.

             * ‘options’ (Any(38)) – Extra options passed to the
               Rule(39) object.

          Return type: None

      -- Method: after_request (f)

          Register a function to run after each request to this object.

          The function is called with the response object, and must
          return a response object.  This allows the functions to modify
          or replace the response before it is sent.

          If a function raises an exception, any remaining
          ‘after_request’ functions will not be called.  Therefore, this
          should not be used for actions that must execute, such as to
          close resources.  Use teardown_request() for that.

          Parameters: ‘f’ (Callable(40)‘[’‘[’Response.‘]’‘,
          ’Response.‘]’) –

          Return type: Callable(41)[[see Response], see
          Response]

      -- Attribute: after_request_funcs: t.Dict[AppOrBlueprintKey,
               t.List[AfterRequestCallable]]

          A data structure of functions to call at the end of each
          request, in the format ‘{scope: [functions]}’.  The ‘scope’
          key is the name of a blueprint the functions are active for,
          or ‘None’ for all requests.

          To register a function, use the after_request().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: app_context ()

          Create an AppContext.  Use as a ‘with’ block to
          push the context, which will make current_app point
          at this application.

          An application context is automatically pushed by
          RequestContext.push() when handling a request, and when
          running a CLI command.  Use this to manually create a context
          outside of these situations.

               with app.app_context():
                   init_db()

          See The Application Context.

          New in version 0.9.

          Return type: flask.ctx.AppContext.

      -- Attribute: app_ctx_globals_class

          alias of flask.ctx._AppCtxGlobals.

      -- Method: async_to_sync (func)

          Return a sync function that will run the coroutine function.

               result = app.async_to_sync(func)(*args, **kwargs)

          Override this method to change how the app converts async code
          to be synchronously callable.

          New in version 2.0.

          Parameters: ‘func’ (Callable(42)‘[’‘[’‘...’‘]’‘,
          ’Coroutine(43)‘]’) –

          Return type: Callable(44)[[…], Any(45)]

      -- Method: auto_find_instance_path ()

          Tries to locate the instance path if it was not provided to
          the constructor of the application class.  It will basically
          calculate the path to a folder named ‘instance’ next to your
          main file or the package.

          New in version 0.8.

          Return type: str(46)

      -- Method: before_first_request (f)

          Registers a function to be run before the first request to
          this instance of the application.

          The function will be called without any arguments and its
          return value is ignored.

          New in version 0.8.

          Parameters: ‘f’ (Callable(47)‘[’‘[’‘]’‘, ’‘None’‘]’) –

          Return type: Callable(48)[[], None]

      -- Attribute: before_first_request_funcs:
               t.List[BeforeFirstRequestCallable]

          A list of functions that will be called at the beginning of
          the first request to this instance.  To register a function,
          use the before_first_request() decorator.

          New in version 0.8.

      -- Method: before_request (f)

          Register a function to run before each request.

          For example, this can be used to open a database connection,
          or to load the logged in user from the session.

               @app.before_request
               def load_user():
                   if "user_id" in session:
                       g.user = db.session.get(session["user_id"])

          The function will be called without any arguments.  If it
          returns a non-‘None’ value, the value is handled as if it was
          the return value from the view, and further request handling
          is stopped.

          Parameters: ‘f’ (Callable(49)‘[’‘[’‘]’‘,
          ’Optional(50)‘[’Union(51)‘[’Response.‘, ’str(52)‘,
          ’bytes(53)‘, ’Dict(54)‘[’str(55)‘, ’Any(56)‘]’‘,
          ’Iterator(57)‘[’str(58)‘]’‘, ’Iterator(59)‘[’bytes(60)‘]’‘,
          ’Tuple(61)‘[’Union(62)‘[’Response.‘, ’str(63)‘,
          ’bytes(64)‘, ’Dict(65)‘[’str(66)‘, ’Any(67)‘]’‘,
          ’Iterator(68)‘[’str(69)‘]’‘, ’Iterator(70)‘[’bytes(71)‘]’‘]’‘,
          ’Union(72)‘[’‘Headers’‘, ’Dict(73)‘[’str(74)‘,
          ’Union(75)‘[’str(76)‘, ’List(77)‘[’str(78)‘]’‘,
          ’Tuple(79)‘[’str(80)‘, ’‘...’‘]’‘]’‘]’‘,
          ’List(81)‘[’Tuple(82)‘[’str(83)‘, ’Union(84)‘[’str(85)‘,
          ’List(86)‘[’str(87)‘]’‘, ’Tuple(88)‘[’str(89)‘,
          ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘, ’Tuple(90)‘[’Union(91)‘[’
          Response.‘, ’str(92)‘, ’bytes(93)‘, ’Dict(94)‘[’str(95)‘,
          ’Any(96)‘]’‘, ’Iterator(97)‘[’str(98)‘]’‘,
          ’Iterator(99)‘[’bytes(100)‘]’‘]’‘, ’int(101)‘]’‘,
          ’Tuple(102)‘[’Union(103)‘[’Response.‘, ’str(104)‘,
          ’bytes(105)‘, ’Dict(106)‘[’str(107)‘, ’Any(108)‘]’‘,
          ’Iterator(109)‘[’str(110)‘]’‘,
          ’Iterator(111)‘[’bytes(112)‘]’‘]’‘, ’int(113)‘,
          ’Union(114)‘[’‘Headers’‘, ’Dict(115)‘[’str(116)‘,
          ’Union(117)‘[’str(118)‘, ’List(119)‘[’str(120)‘]’‘,
          ’Tuple(121)‘[’str(122)‘, ’‘...’‘]’‘]’‘]’‘,
          ’List(123)‘[’Tuple(124)‘[’str(125)‘, ’Union(126)‘[’str(127)‘,
          ’List(128)‘[’str(129)‘]’‘, ’Tuple(130)‘[’str(131)‘,
          ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘, ’‘WSGIApplication’‘]’‘]’‘]’) –

          Return type: Callable(132)[[], Optional(133)[Union(134)[see
          Response, str(135), bytes(136), Dict(137)[str(138),
          Any(139)], Iterator(140)[str(141)], Iterator(142)[bytes(143)],
          Tuple(144)[Union(145)[see Response, str(146),
          bytes(147), Dict(148)[str(149), Any(150)],
          Iterator(151)[str(152)], Iterator(153)[bytes(154)]],
          Union(155)[Headers, Dict(156)[str(157), Union(158)[str(159),
          List(160)[str(161)], Tuple(162)[str(163), …]]],
          List(164)[Tuple(165)[str(166), Union(167)[str(168),
          List(169)[str(170)], Tuple(171)[str(172), …]]]]]],
          Tuple(173)[Union(174)[see Response, str(175),
          bytes(176), Dict(177)[str(178), Any(179)],
          Iterator(180)[str(181)], Iterator(182)[bytes(183)]],
          int(184)], Tuple(185)[Union(186)[see Response, str(187),
          bytes(188), Dict(189)[str(190), Any(191)],
          Iterator(192)[str(193)], Iterator(194)[bytes(195)]], int(196),
          Union(197)[Headers, Dict(198)[str(199), Union(200)[str(201),
          List(202)[str(203)], Tuple(204)[str(205), …]]],
          List(206)[Tuple(207)[str(208), Union(209)[str(210),
          List(211)[str(212)], Tuple(213)[str(214), …]]]]]],
          WSGIApplication]]]

      -- Attribute: before_request_funcs: t.Dict[AppOrBlueprintKey,
               t.List[BeforeRequestCallable]]

          A data structure of functions to call at the beginning of each
          request, in the format ‘{scope: [functions]}’.  The ‘scope’
          key is the name of a blueprint the functions are active for,
          or ‘None’ for all requests.

          To register a function, use the before_request().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Attribute: blueprints: t.Dict[str(215), 'Blueprint']

          Maps registered blueprint names to blueprint objects.  The
          dict retains the order the blueprints were registered in.
          Blueprints can be registered multiple times, this dict does
          not track how often they were attached.

          New in version 0.7.

      -- Attribute: cli

          The Click command group for registering CLI commands for this
          object.  The commands are available from the ‘flask’ command
          once the application has been discovered and blueprints have
          been registered.

      -- Attribute: config

          The configuration dictionary as Config.  This
          behaves exactly like a regular dictionary but supports
          additional methods to load a config from files.

      -- Attribute: config_class

          alias of flask.config.Config.

      -- Method: context_processor (f)

          Registers a template context processor function.

          Parameters: ‘f’ (Callable(216)‘[’‘[’‘]’‘,
          ’Dict(217)‘[’str(218)‘, ’Any(219)‘]’‘]’) –

          Return type: Callable(220)[[], Dict(221)[str(222), Any(223)]]

      -- Method: create_global_jinja_loader ()

          Creates the loader for the Jinja2 environment.  Can be used to
          override just the loader and keeping the rest unchanged.  It’s
          discouraged to override this function.  Instead one should
          override the jinja_loader() function instead.

          The global loader dispatches between the loaders of the
          application and the individual blueprints.

          New in version 0.7.

          Return type: flask.templating.DispatchingJinjaLoader

      -- Method: create_jinja_environment ()

          Create the Jinja environment based on jinja_options
          and the various Jinja-related methods of the app.
          Changing jinja_options after this will have no
          effect.  Also adds Flask-related globals and filters to the
          environment.

          Changed in version 0.11: ‘Environment.auto_reload’ set in
          accordance with ‘TEMPLATES_AUTO_RELOAD’ configuration option.

          New in version 0.5.

          Return type: flask.templating.Environment

      -- Method: create_url_adapter (request)

          Creates a URL adapter for the given request.  The URL adapter
          is created at a point where the request context is not yet set
          up so the request is passed explicitly.

          Changed in version 1.0: SERVER_NAME no longer
          implicitly enables subdomain matching.  Use
          ‘subdomain_matching’ instead.

          Changed in version 0.9: This can now also be called without a
          request object when the URL adapter is created for the
          application context.

          New in version 0.6.

          Parameters: ‘request’ (Optional(224)‘[’
          flask.wrappers.Request.‘]’) –

          Return type: Optional(225)[werkzeug.routing.MapAdapter(226)]

      -- Property: debug: bool(227)

          Whether debug mode is enabled.  When using ‘flask run’ to
          start the development server, an interactive debugger will be
          shown for unhandled exceptions, and the server will be
          reloaded when code changes.  This maps to the DEBUG.
          config key.  This is enabled when env is
          ‘'development'’ and is overridden by the ‘FLASK_DEBUG’
          environment variable.  It may not behave as expected if set in
          code.

          `Do not enable debug mode when deploying in production.'

          Default: ‘True’ if env is ‘'development'’, or
          ‘False’ otherwise.

      -- Attribute: default_config = {'APPLICATION_ROOT': '/', 'DEBUG':
               None, 'ENV': None, 'EXPLAIN_TEMPLATE_LOADING': False,
               'JSONIFY_MIMETYPE': 'application/json',
               'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSON_AS_ASCII':
               True, 'JSON_SORT_KEYS': True, 'MAX_CONTENT_LENGTH': None,
               'MAX_COOKIE_SIZE': 4093, 'PERMANENT_SESSION_LIFETIME':
               datetime.timedelta(days=31), 'PREFERRED_URL_SCHEME':
               'http', 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
               'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': None,
               'SEND_FILE_MAX_AGE_DEFAULT': None, 'SERVER_NAME': None,
               'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_HTTPONLY':
               True, 'SESSION_COOKIE_NAME': 'session',
               'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_SAMESITE':
               None, 'SESSION_COOKIE_SECURE': False,
               'SESSION_REFRESH_EACH_REQUEST': True,
               'TEMPLATES_AUTO_RELOAD': None, 'TESTING': False,
               'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS':
               False, 'USE_X_SENDFILE': False}

          Default configuration parameters.

      -- Method: delete (rule, **options)

          Shortcut for route() with ‘methods=["DELETE"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(228)) –

             * ‘options’ (Any(229)) –

          Return type: Callable(230)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: dispatch_request ()

          Does the request dispatching.  Matches the URL and returns the
          return value of the view or error handler.  This does not have
          to be a response object.  In order to convert the return value
          to a proper response object, call make_response().

          Changed in version 0.7: This no longer does the exception
          handling, this code was moved to the new
          full_dispatch_request().

          Return type: Union(231)[see Response, str(232),
          bytes(233), Dict(234)[str(235), Any(236)],
          Iterator(237)[str(238)], Iterator(239)[bytes(240)],
          Tuple(241)[Union(242)[see Response, str(243),
          bytes(244), Dict(245)[str(246), Any(247)],
          Iterator(248)[str(249)], Iterator(250)[bytes(251)]],
          Union(252)[Headers, Dict(253)[str(254), Union(255)[str(256),
          List(257)[str(258)], Tuple(259)[str(260), …]]],
          List(261)[Tuple(262)[str(263), Union(264)[str(265),
          List(266)[str(267)], Tuple(268)[str(269), …]]]]]],
          Tuple(270)[Union(271)[see Response, str(272),
          bytes(273), Dict(274)[str(275), Any(276)],
          Iterator(277)[str(278)], Iterator(279)[bytes(280)]],
          int(281)], Tuple(282)[Union(283)[see Response, str(284),
          bytes(285), Dict(286)[str(287), Any(288)],
          Iterator(289)[str(290)], Iterator(291)[bytes(292)]], int(293),
          Union(294)[Headers, Dict(295)[str(296), Union(297)[str(298),
          List(299)[str(300)], Tuple(301)[str(302), …]]],
          List(303)[Tuple(304)[str(305), Union(306)[str(307),
          List(308)[str(309)], Tuple(310)[str(311), …]]]]]],
          WSGIApplication]

      -- Method: do_teardown_appcontext (exc=<object object>)

          Called right before the application context is popped.

          When handling a request, the application context is popped
          after the request context.  See do_teardown_request().

          This calls all functions decorated with
          teardown_appcontext().  Then the
          appcontext_tearing_down signal is sent.

          This is called by AppContext.pop().

          New in version 0.9.

          Parameters: ‘exc’ (Optional(312)‘[’BaseException(313)‘]’) –

          Return type: None

      -- Method: do_teardown_request (exc=<object object>)

          Called after the request is dispatched and the response is
          returned, right before the request context is popped.

          This calls all functions decorated with
          teardown_request(), and
          Blueprint.teardown_request() if a blueprint handled the
          request.  Finally, the request_tearing_down signal
          is sent.

          This is called by RequestContext.pop(), which may
          be delayed during testing to maintain access to resources.

          Parameters: ‘exc’ (Optional(314)‘[’BaseException(315)‘]’) – An
          unhandled exception raised while dispatching the request.
          Detected from the current exception information if not passed.
          Passed to each teardown function.

          Return type: None

          Changed in version 0.9: Added the ‘exc’ argument.

      -- Method: endpoint (endpoint)

          Decorate a view function to register it for the given
          endpoint.  Used if a rule is added without a ‘view_func’ with
          add_url_rule().

               app.add_url_rule("/ex", endpoint="example")

               @app.endpoint("example")
               def example():
                   ...

          Parameters: ‘endpoint’ (str(316)) – The endpoint name to
          associate with the view function.

          Return type: Callable(317)

      -- Method: ensure_sync (func)

          Ensure that the function is synchronous for WSGI workers.
          Plain ‘def’ functions are returned as-is.  ‘async def’
          functions are wrapped to run and wait for the response.

          Override this method to change how the app runs async views.

          New in version 2.0.

          Parameters: ‘func’ (Callable(318)) –

          Return type: Callable(319)

      -- Attribute: env

          What environment the app is running in.  Flask and extensions
          may enable behaviors based on the environment, such as
          enabling debug mode.  This maps to the ENV config
          key.  This is set by the ‘FLASK_ENV’ environment variable and
          may not behave as expected if set in code.

          `Do not enable development when deploying in production.'

          Default: ‘'production'’

      -- Attribute: error_handler_spec: t.Dict[AppOrBlueprintKey,
               t.Dict[t.Optional[int(320)],
               t.Dict[t.Type[Exception(321)], 'ErrorHandlerCallable']]]

          A data structure of registered error handlers, in the format
          ‘{scope: {code: {class: handler}}}`’.  The ‘scope’ key is the
          name of a blueprint the handlers are active for, or ‘None’ for
          all requests.  The ‘code’ key is the HTTP status code for
          ‘HTTPException’, or ‘None’ for other exceptions.  The
          innermost dictionary maps exception classes to handler
          functions.

          To register an error handler, use the errorhandler()
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: errorhandler (code_or_exception)

          Register a function to handle errors by code or exception
          class.

          A decorator that is used to register a function given an error
          code.  Example:

               @app.errorhandler(404)
               def page_not_found(error):
                   return 'This page does not exist', 404

          You can also register handlers for arbitrary exceptions:

               @app.errorhandler(DatabaseError)
               def special_exception_handler(error):
                   return 'Database connection failed', 500

          New in version 0.7: Use register_error_handler().
          instead of modifying error_handler_spec directly,
          for application wide error handlers.

          New in version 0.7: One can now additionally also register
          custom exception types that do not necessarily have to be a
          subclass of the HTTPException(322) class.

          Parameters: ‘code_or_exception’
          (Union(323)‘[’Type(324)‘[’Exception(325)‘]’‘, ’int(326)‘]’) –
          the code as integer for the handler, or an arbitrary exception

          Return type: Callable(327)[[ErrorHandlerCallable],
          ErrorHandlerCallable]

      -- Attribute: extensions: dict(328)

          a place where extensions can store application specific state.
          For example this is where an extension could store database
          engines and similar things.

          The key must match the name of the extension module.  For
          example in case of a “Flask-Foo” extension in ‘flask_foo’, the
          key would be ‘'foo'’.

          New in version 0.7.

      -- Method: full_dispatch_request ()

          Dispatches the request and on top of that performs request pre
          and postprocessing as well as HTTP exception catching and
          error handling.

          New in version 0.7.

          Return type: flask.wrappers.Response.

      -- Method: get (rule, **options)

          Shortcut for route() with ‘methods=["GET"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(329)) –

             * ‘options’ (Any(330)) –

          Return type: Callable(331)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: get_send_file_max_age (filename)

          Used by send_file() to determine the ‘max_age’
          cache value for a given file path if it wasn’t passed.

          By default, this returns SEND_FILE_MAX_AGE_DEFAULT.
          from the configuration of current_app.  This
          defaults to ‘None’, which tells the browser to use conditional
          requests instead of a timed cache, which is usually
          preferable.

          Changed in version 2.0: The default configuration is ‘None’
          instead of 12 hours.

          New in version 0.9.

          Parameters: ‘filename’ (Optional(332)‘[’str(333)‘]’) –

          Return type: Optional(334)[int(335)]

      -- Property: got_first_request: bool(336)

          This attribute is set to ‘True’ if the application started
          handling the first request.

          New in version 0.8.

      -- Method: handle_exception (e)

          Handle an exception that did not have an error handler
          associated with it, or that was raised from an error handler.
          This always causes a 500 ‘InternalServerError’.

          Always sends the got_request_exception signal.

          If propagate_exceptions is ‘True’, such as in
          debug mode, the error will be re-raised so that the debugger
          can display it.  Otherwise, the original exception is logged,
          and an InternalServerError(337) is returned.

          If an error handler is registered for ‘InternalServerError’ or
          ‘500’, it will be used.  For consistency, the handler will
          always receive the ‘InternalServerError’.  The original
          unhandled exception is available as ‘e.original_exception’.

          Changed in version 1.1.0: Always passes the
          ‘InternalServerError’ instance to the handler, setting
          ‘original_exception’ to the unhandled error.

          Changed in version 1.1.0: ‘after_request’ functions and other
          finalization is done even for the default 500 response when
          there is no handler.

          New in version 0.3.

          Parameters: ‘e’ (Exception(338)) –

          Return type: flask.wrappers.Response.

      -- Method: handle_http_exception (e)

          Handles an HTTP exception.  By default this will invoke the
          registered error handlers and fall back to returning the
          exception as response.

          Changed in version 1.0.3: ‘RoutingException’, used internally
          for actions such as slash redirects during routing, is not
          passed to error handlers.

          Changed in version 1.0: Exceptions are looked up by code `and'
          by MRO, so ‘HTTPException’ subclasses can be handled with a
          catch-all handler for the base ‘HTTPException’.

          New in version 0.3.

          Parameters: ‘e’ (werkzeug.exceptions.HTTPException(339)) –

          Return type:
          Union(340)[werkzeug.exceptions.HTTPException(341), see
          Response, str(342), bytes(343), Dict(344)[str(345),
          Any(346)], Iterator(347)[str(348)], Iterator(349)[bytes(350)],
          Tuple(351)[Union(352)[see Response, str(353),
          bytes(354), Dict(355)[str(356), Any(357)],
          Iterator(358)[str(359)], Iterator(360)[bytes(361)]],
          Union(362)[Headers, Dict(363)[str(364), Union(365)[str(366),
          List(367)[str(368)], Tuple(369)[str(370), …]]],
          List(371)[Tuple(372)[str(373), Union(374)[str(375),
          List(376)[str(377)], Tuple(378)[str(379), …]]]]]],
          Tuple(380)[Union(381)[see Response, str(382),
          bytes(383), Dict(384)[str(385), Any(386)],
          Iterator(387)[str(388)], Iterator(389)[bytes(390)]],
          int(391)], Tuple(392)[Union(393)[see Response, str(394),
          bytes(395), Dict(396)[str(397), Any(398)],
          Iterator(399)[str(400)], Iterator(401)[bytes(402)]], int(403),
          Union(404)[Headers, Dict(405)[str(406), Union(407)[str(408),
          List(409)[str(410)], Tuple(411)[str(412), …]]],
          List(413)[Tuple(414)[str(415), Union(416)[str(417),
          List(418)[str(419)], Tuple(420)[str(421), …]]]]]],
          WSGIApplication]

      -- Method: handle_url_build_error (error, endpoint, values)

          Handle ‘BuildError’ on url_for().

          Parameters:

             * ‘error’ (Exception(422)) –

             * ‘endpoint’ (str(423)) –

             * ‘values’ (dict(424)) –

          Return type: str(425)

      -- Method: handle_user_exception (e)

          This method is called whenever an exception occurs that should
          be handled.  A special case is ‘HTTPException’ which is
          forwarded to the handle_http_exception() method.
          This function will either return a response value or reraise
          the exception with the same traceback.

          Changed in version 1.0: Key errors raised from request data
          like ‘form’ show the bad key in debug mode rather than a
          generic bad request message.

          New in version 0.7.

          Parameters: ‘e’ (Exception(426)) –

          Return type:
          Union(427)[werkzeug.exceptions.HTTPException(428), see
          Response, str(429), bytes(430), Dict(431)[str(432),
          Any(433)], Iterator(434)[str(435)], Iterator(436)[bytes(437)],
          Tuple(438)[Union(439)[see Response, str(440),
          bytes(441), Dict(442)[str(443), Any(444)],
          Iterator(445)[str(446)], Iterator(447)[bytes(448)]],
          Union(449)[Headers, Dict(450)[str(451), Union(452)[str(453),
          List(454)[str(455)], Tuple(456)[str(457), …]]],
          List(458)[Tuple(459)[str(460), Union(461)[str(462),
          List(463)[str(464)], Tuple(465)[str(466), …]]]]]],
          Tuple(467)[Union(468)[see Response, str(469),
          bytes(470), Dict(471)[str(472), Any(473)],
          Iterator(474)[str(475)], Iterator(476)[bytes(477)]],
          int(478)], Tuple(479)[Union(480)[see Response, str(481),
          bytes(482), Dict(483)[str(484), Any(485)],
          Iterator(486)[str(487)], Iterator(488)[bytes(489)]], int(490),
          Union(491)[Headers, Dict(492)[str(493), Union(494)[str(495),
          List(496)[str(497)], Tuple(498)[str(499), …]]],
          List(500)[Tuple(501)[str(502), Union(503)[str(504),
          List(505)[str(506)], Tuple(507)[str(508), …]]]]]],
          WSGIApplication]

      -- Property: has_static_folder: bool(509)

          ‘True’ if static_folder is set.

          New in version 0.5.

      -- Attribute: import_name

          The name of the package or module that this object belongs to.
          Do not change this once it is set by the constructor.

      -- Method: inject_url_defaults (endpoint, values)

          Injects the URL defaults for the given endpoint directly into
          the values dictionary passed.  This is used internally and
          automatically called on URL building.

          New in version 0.7.

          Parameters:

             * ‘endpoint’ (str(510)) –

             * ‘values’ (dict(511)) –

          Return type: None

      -- Attribute: instance_path

          Holds the path to the instance folder.

          New in version 0.8.

      -- Method: iter_blueprints ()

          Iterates over all blueprints by the order they were
          registered.

          New in version 0.11.

          Return type: ValuesView(512)[see Blueprint]

      -- Property: jinja_env: flask.templating.Environment

          The Jinja environment used to load templates.

          The environment is created the first time this property is
          accessed.  Changing jinja_options after that will
          have no effect.

      -- Attribute: jinja_environment

          alias of ‘flask.templating.Environment’

      -- Property: jinja_loader:
               Optional[jinja2.loaders.FileSystemLoader(513)]

          The Jinja loader for this object’s templates.  By default this
          is a class jinja2.loaders.FileSystemLoader(514) to
          template_folder if it is set.

          New in version 0.5.

      -- Attribute: jinja_options: dict(515) = {}

          Options that are passed to the Jinja environment in
          create_jinja_environment().  Changing these options after
          the environment is created (accessing jinja_env)
          will have no effect.

          Changed in version 1.1.0: This is a ‘dict’ instead of an
          ‘ImmutableDict’ to allow easier configuration.

      -- Attribute: json_decoder

          alias of flask.json.JSONDecoder.

      -- Attribute: json_encoder

          alias of flask.json.JSONEncoder.

      -- Method: log_exception (exc_info)

          Logs an exception.  This is called by
          handle_exception() if debugging is disabled and right
          before the handler is called.  The default implementation logs
          the exception as error on the logger.

          New in version 0.8.

          Parameters: ‘exc_info’ (Union(516)‘[’Tuple(517)‘[’type(518)‘,
          ’BaseException(519)‘, ’types.TracebackType(520)‘]’‘,
          ’Tuple(521)‘[’‘None’‘, ’‘None’‘, ’‘None’‘]’‘]’) –

          Return type: None

      -- Property: logger: logging.Logger(522)

          A standard Python Logger(523) for the app, with the same name
          as name.

          In debug mode, the logger’s ‘level’ will be set to ‘DEBUG’.

          If there are no handlers configured, a default handler will be
          added.  See Logging for more information.

          Changed in version 1.1.0: The logger takes the same name as
          name rather than hard-coding ‘"flask.app"’.

          Changed in version 1.0.0: Behavior was simplified.  The logger
          is always named ‘"flask.app"’.  The level is only set during
          configuration, it doesn’t check ‘app.debug’ each time.  Only
          one format is used, not different ones depending on
          ‘app.debug’.  No handlers are removed, and a handler is only
          added if no handlers are already configured.

          New in version 0.3.

      -- Method: make_config (instance_relative=False)

          Used to create the config attribute by the Flask constructor.
          The ‘instance_relative’ parameter is passed in from the
          constructor of Flask (there named ‘instance_relative_config’)
          and indicates if the config should be relative to the instance
          path or the root path of the application.

          New in version 0.8.

          Parameters: ‘instance_relative’ (bool(524)) –

          Return type: flask.config.Config.

      -- Method: make_default_options_response ()

          This method is called to create the default ‘OPTIONS’
          response.  This can be changed through subclassing to change
          the default behavior of ‘OPTIONS’ responses.

          New in version 0.7.

          Return type: flask.wrappers.Response.

      -- Method: make_response (rv)

          Convert the return value from a view function to an instance
          of response_class.

          Parameters: ‘rv’ (Union(525)‘[’Response.‘,
          ’str(526)‘, ’bytes(527)‘, ’Dict(528)‘[’str(529)‘,
          ’Any(530)‘]’‘, ’Iterator(531)‘[’str(532)‘]’‘,
          ’Iterator(533)‘[’bytes(534)‘]’‘,
          ’Tuple(535)‘[’Union(536)‘[’Response.‘, ’str(537)‘,
          ’bytes(538)‘, ’Dict(539)‘[’str(540)‘, ’Any(541)‘]’‘,
          ’Iterator(542)‘[’str(543)‘]’‘,
          ’Iterator(544)‘[’bytes(545)‘]’‘]’‘, ’Union(546)‘[’‘Headers’‘,
          ’Dict(547)‘[’str(548)‘, ’Union(549)‘[’str(550)‘,
          ’List(551)‘[’str(552)‘]’‘, ’Tuple(553)‘[’str(554)‘,
          ’‘...’‘]’‘]’‘]’‘, ’List(555)‘[’Tuple(556)‘[’str(557)‘,
          ’Union(558)‘[’str(559)‘, ’List(560)‘[’str(561)‘]’‘,
          ’Tuple(562)‘[’str(563)‘, ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘,
          ’Tuple(564)‘[’Union(565)‘[’Response.‘, ’str(566)‘,
          ’bytes(567)‘, ’Dict(568)‘[’str(569)‘, ’Any(570)‘]’‘,
          ’Iterator(571)‘[’str(572)‘]’‘,
          ’Iterator(573)‘[’bytes(574)‘]’‘]’‘, ’int(575)‘]’‘,
          ’Tuple(576)‘[’Union(577)‘[’Response.‘, ’str(578)‘,
          ’bytes(579)‘, ’Dict(580)‘[’str(581)‘, ’Any(582)‘]’‘,
          ’Iterator(583)‘[’str(584)‘]’‘,
          ’Iterator(585)‘[’bytes(586)‘]’‘]’‘, ’int(587)‘,
          ’Union(588)‘[’‘Headers’‘, ’Dict(589)‘[’str(590)‘,
          ’Union(591)‘[’str(592)‘, ’List(593)‘[’str(594)‘]’‘,
          ’Tuple(595)‘[’str(596)‘, ’‘...’‘]’‘]’‘]’‘,
          ’List(597)‘[’Tuple(598)‘[’str(599)‘, ’Union(600)‘[’str(601)‘,
          ’List(602)‘[’str(603)‘]’‘, ’Tuple(604)‘[’str(605)‘,
          ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘, ’‘WSGIApplication’‘]’) – the return
          value from the view function.  The view function must return a
          response.  Returning ‘None’, or the view ending without
          returning, is not allowed.  The following types are allowed
          for ‘view_rv’:

          ‘str’

               A response object is created with the string encoded to
               UTF-8 as the body.

          ‘bytes’

               A response object is created with the bytes as the body.

          ‘dict’

               A dictionary that will be jsonify’d before being
               returned.

          ‘tuple’

               Either ‘(body, status, headers)’, ‘(body, status)’, or
               ‘(body, headers)’, where ‘body’ is any of the other types
               allowed here, ‘status’ is a string or an integer, and
               ‘headers’ is a dictionary or a list of ‘(key, value)’
               tuples.  If ‘body’ is a response_class.
               instance, ‘status’ overwrites the exiting value and
               ‘headers’ are extended.

          See response_class.

               The object is returned unchanged.

          other Response(606) class

               The object is coerced to response_class.

          callable()(607)

               The function is called as a WSGI application.  The result
               is used to create a response object.

          Return type: flask.wrappers.Response.

          Changed in version 0.9: Previously a tuple was interpreted as
          the arguments for the response object.

      -- Method: make_shell_context ()

          Returns the shell context for an interactive shell for this
          application.  This runs all the registered shell context
          processors.

          New in version 0.11.

          Return type: dict(608)

      -- Property: name: str(609)

          The name of the application.  This is usually the import name
          with the difference that it’s guessed from the run file if the
          import name is main.  This name is used as a display name when
          Flask needs the name of the application.  It can be set and
          overridden to change the value.

          New in version 0.8.

      -- Method: open_instance_resource (resource, mode='rb')

          Opens a resource from the application’s instance folder (see
          instance_path).  Otherwise works like
          open_resource().  Instance resources can also be opened
          for writing.

          Parameters:

             * ‘resource’ (str(610)) – the name of the resource.  To
               access resources within subfolders use forward slashes as
               separator.

             * ‘mode’ (str(611)) – resource file opening mode, default
               is ‘rb’.

          Return type: IO(612)

      -- Method: open_resource (resource, mode='rb')

          Open a resource file relative to root_path for
          reading.

          For example, if the file ‘schema.sql’ is next to the file
          ‘app.py’ where the ‘Flask’ app is defined, it can be opened
          with:

               with app.open_resource("schema.sql") as f:
                   conn.executescript(f.read())

          Parameters:

             * ‘resource’ (str(613)) – Path to the resource relative to
               root_path.

             * ‘mode’ (str(614)) – Open the file in this mode.  Only
               reading is supported, valid values are “r” (or “rt”) and
               “rb”.

          Return type: IO(615)

      -- Method: patch (rule, **options)

          Shortcut for route() with ‘methods=["PATCH"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(616)) –

             * ‘options’ (Any(617)) –

          Return type: Callable(618)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Attribute: permanent_session_lifetime

          A timedelta(619) which is used to set the expiration date of a
          permanent session.  The default is 31 days which makes a
          permanent session survive for roughly one month.

          This attribute can also be configured from the config with the
          ‘PERMANENT_SESSION_LIFETIME’ configuration key.  Defaults to
          ‘timedelta(days=31)’

      -- Method: post (rule, **options)

          Shortcut for route() with ‘methods=["POST"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(620)) –

             * ‘options’ (Any(621)) –

          Return type: Callable(622)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: preprocess_request ()

          Called before the request is dispatched.  Calls
          url_value_preprocessors registered with the app and the
          current blueprint (if any).  Then calls
          before_request_funcs registered with the app and the
          blueprint.

          If any before_request() handler returns a non-None
          value, the value is handled as if it was the return value from
          the view, and further request handling is stopped.

          Return type: Optional(623)[Union(624)[see Response,
          str(625), bytes(626), Dict(627)[str(628), Any(629)],
          Iterator(630)[str(631)], Iterator(632)[bytes(633)],
          Tuple(634)[Union(635)[see Response, str(636),
          bytes(637), Dict(638)[str(639), Any(640)],
          Iterator(641)[str(642)], Iterator(643)[bytes(644)]],
          Union(645)[Headers, Dict(646)[str(647), Union(648)[str(649),
          List(650)[str(651)], Tuple(652)[str(653), …]]],
          List(654)[Tuple(655)[str(656), Union(657)[str(658),
          List(659)[str(660)], Tuple(661)[str(662), …]]]]]],
          Tuple(663)[Union(664)[see Response, str(665),
          bytes(666), Dict(667)[str(668), Any(669)],
          Iterator(670)[str(671)], Iterator(672)[bytes(673)]],
          int(674)], Tuple(675)[Union(676)[see Response, str(677),
          bytes(678), Dict(679)[str(680), Any(681)],
          Iterator(682)[str(683)], Iterator(684)[bytes(685)]], int(686),
          Union(687)[Headers, Dict(688)[str(689), Union(690)[str(691),
          List(692)[str(693)], Tuple(694)[str(695), …]]],
          List(696)[Tuple(697)[str(698), Union(699)[str(700),
          List(701)[str(702)], Tuple(703)[str(704), …]]]]]],
          WSGIApplication]]

      -- Property: preserve_context_on_exception: bool(705)

          Returns the value of the ‘PRESERVE_CONTEXT_ON_EXCEPTION’
          configuration value in case it’s set, otherwise a sensible
          default is returned.

          New in version 0.7.

      -- Method: process_response (response)

          Can be overridden in order to modify the response object
          before it’s sent to the WSGI server.  By default this will
          call all the after_request() decorated functions.

          Changed in version 0.5: As of Flask 0.5 the functions
          registered for after request execution are called in reverse
          order of registration.

          Parameters: ‘response’ (see flask.wrappers.Response) –
          a response_class object.

          Returns: a new response object or the same, has to be an
          instance of response_class.

          Return type: flask.wrappers.Response.

      -- Property: propagate_exceptions: bool(706)

          Returns the value of the ‘PROPAGATE_EXCEPTIONS’ configuration
          value in case it’s set, otherwise a sensible default is
          returned.

          New in version 0.7.

      -- Method: put (rule, **options)

          Shortcut for route() with ‘methods=["PUT"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(707)) –

             * ‘options’ (Any(708)) –

          Return type: Callable(709)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: register_blueprint (blueprint, **options)

          Register a Blueprint on the application.  Keyword
          arguments passed to this method will override the defaults set
          on the blueprint.

          Calls the blueprint’s register() method after
          recording the blueprint in the application’s blueprints.

          Parameters:

             * ‘blueprint’ (see Blueprint) – The blueprint to
               register.

             * ‘url_prefix’ – Blueprint routes will be prefixed with
               this.

             * ‘subdomain’ – Blueprint routes will match on this
               subdomain.

             * ‘url_defaults’ – Blueprint routes will use these default
               values for view arguments.

             * ‘options’ (Any(710)) – Additional keyword arguments are
               passed to BlueprintSetupState.  They can be
               accessed in record() callbacks.

          Return type: None

          Changed in version 2.0.1: The ‘name’ option can be used to
          change the (pre-dotted) name the blueprint is registered with.
          This allows the same blueprint to be registered multiple times
          with unique names for ‘url_for’.

          New in version 0.7.

      -- Method: register_error_handler (code_or_exception, f)

          Alternative error attach function to the errorhandler()
          decorator that is more straightforward to use for non
          decorator usage.

          New in version 0.7.

          Parameters:

             * ‘code_or_exception’
               (Union(711)‘[’Type(712)‘[’Exception(713)‘]’‘,
               ’int(714)‘]’) –

             * ‘f’ (‘ErrorHandlerCallable’) –

          Return type: None

      -- Attribute: request_class

          alias of flask.wrappers.Request.

      -- Method: request_context (environ)

          Create a RequestContext representing a WSGI
          environment.  Use a ‘with’ block to push the context, which
          will make request point at this request.

          See The Request Context.

          Typically you should not call this from your own code.  A
          request context is automatically pushed by the
          wsgi_app() when handling a request.  Use
          test_request_context() to create an environment and
          context instead of this method.

          Parameters: ‘environ’ (dict(715)) – a WSGI environment

          Return type: flask.ctx.RequestContext.

      -- Attribute: response_class

          alias of flask.wrappers.Response.

      -- Attribute: root_path

          Absolute path to the package on the filesystem.  Used to look
          up resources contained in the package.

      -- Method: route (rule, **options)

          Decorate a view function to register it with the given URL
          rule and options.  Calls add_url_rule(), which has
          more details about the implementation.

               @app.route("/")
               def index():
                   return "Hello, World!"

          See URL Route Registrations.

          The endpoint name for the route defaults to the name of the
          view function if the ‘endpoint’ parameter isn’t passed.

          The ‘methods’ parameter defaults to ‘["GET"]’.  ‘HEAD’ and
          ‘OPTIONS’ are added automatically.

          Parameters:

             * ‘rule’ (str(716)) – The URL rule string.

             * ‘options’ (Any(717)) – Extra options passed to the
               Rule(718) object.

          Return type: Callable(719)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: run (host=None, port=None, debug=None,
               load_dotenv=True, **options)

          Runs the application on a local development server.

          Do not use ‘run()’ in a production setting.  It is not
          intended to meet security and performance requirements for a
          production server.  Instead, see Deployment Options.
          for WSGI server recommendations.

          If the debug flag is set the server will
          automatically reload for code changes and show a debugger in
          case an exception happened.

          If you want to run the application in debug mode, but disable
          the code execution on the interactive debugger, you can pass
          ‘use_evalex=False’ as parameter.  This will keep the
          debugger’s traceback screen active, but disable code
          execution.

          It is not recommended to use this function for development
          with automatic reloading as this is badly supported.  Instead
          you should be using the ‘flask’ command line script’s ‘run’
          support.

               Keep in Mind: Flask will suppress any server error with a
               generic error page unless it is in debug mode.  As such
               to enable just the interactive debugger without the code
               reloading, you have to invoke run() with
               ‘debug=True’ and ‘use_reloader=False’.  Setting
               ‘use_debugger’ to ‘True’ without being in debug mode
               won’t catch any exceptions because there won’t be any to
               catch.

          Parameters:

             * ‘host’ (Optional(720)‘[’str(721)‘]’) – the hostname to
               listen on.  Set this to ‘'0.0.0.0'’ to have the server
               available externally as well.  Defaults to ‘'127.0.0.1'’
               or the host in the ‘SERVER_NAME’ config variable if
               present.

             * ‘port’ (Optional(722)‘[’int(723)‘]’) – the port of the
               webserver.  Defaults to ‘5000’ or the port defined in the
               ‘SERVER_NAME’ config variable if present.

             * ‘debug’ (Optional(724)‘[’bool(725)‘]’) – if given, enable
               or disable debug mode.  See debug.

             * ‘load_dotenv’ (bool(726)) – Load the nearest ‘.env’ and
               ‘.flaskenv’ files to set environment variables.  Will
               also change the working directory to the directory
               containing the first file found.

             * ‘options’ (Any(727)) – the options to be forwarded to the
               underlying Werkzeug server.  See
               werkzeug.serving.run_simple()(728) for more information.

          Return type: None

          Changed in version 1.0: If installed, python-dotenv will be
          used to load environment variables from ‘.env’ and ‘.flaskenv’
          files.

          If set, the ‘FLASK_ENV’ and ‘FLASK_DEBUG’ environment
          variables will override env and debug.

          Threaded mode is enabled by default.

          Changed in version 0.10: The default port is now picked from
          the ‘SERVER_NAME’ variable.

      -- Attribute: secret_key

          If a secret key is set, cryptographic components can use this
          to sign cookies and other things.  Set this to a complex
          random value when you want to use the secure cookie for
          instance.

          This attribute can also be configured from the config with the
          SECRET_KEY configuration key.  Defaults to ‘None’.

      -- Method: select_jinja_autoescape (filename)

          Returns ‘True’ if autoescaping should be active for the given
          template name.  If no template name is given, returns ‘True’.

          New in version 0.5.

          Parameters: ‘filename’ (str(729)) –

          Return type: bool(730)

      -- Attribute: send_file_max_age_default

          A timedelta(731) or number of seconds which is used as the
          default ‘max_age’ for send_file().  The default is
          ‘None’, which tells the browser to use conditional requests
          instead of a timed cache.

          Configured with the SEND_FILE_MAX_AGE_DEFAULT.
          configuration key.

          Changed in version 2.0: Defaults to ‘None’ instead of 12
          hours.

      -- Method: send_static_file (filename)

          The view function used to serve files from
          static_folder.  A route is automatically registered for
          this view at static_url_path if
          static_folder is set.

          New in version 0.5.

          Parameters: ‘filename’ (str(732)) –

          Return type: Response.

      -- Attribute: session_cookie_name

          The secure cookie uses this for the name of the session
          cookie.

          This attribute can also be configured from the config with the
          ‘SESSION_COOKIE_NAME’ configuration key.  Defaults to
          ‘'session'’

      -- Attribute: session_interface:
               flask.sessions.SessionInterface. =
               <flask.sessions.SecureCookieSessionInterface object>

          the session interface to use.  By default an instance of
          SecureCookieSessionInterface is used here.

          New in version 0.8.

      -- Method: shell_context_processor (f)

          Registers a shell context processor function.

          New in version 0.11.

          Parameters: ‘f’ (Callable(733)) –

          Return type: Callable(734)

      -- Attribute: shell_context_processors: t.List[t.Callable[[],
               t.Dict[str(735), t.Any]]]

          A list of shell context processor functions that should be run
          when a shell context is created.

          New in version 0.11.

      -- Method: should_ignore_error (error)

          This is called to figure out if an error should be ignored or
          not as far as the teardown system is concerned.  If this
          function returns ‘True’ then the teardown handlers will not be
          passed the error.

          New in version 0.10.

          Parameters: ‘error’ (Optional(736)‘[’BaseException(737)‘]’) –

          Return type: bool(738)

      -- Property: static_folder: Optional[str(739)]

          The absolute path to the configured static folder.  ‘None’ if
          no static folder is set.

      -- Property: static_url_path: Optional[str(740)]

          The URL prefix that the static route will be accessible from.

          If it was not configured during init, it is derived from
          static_folder.

      -- Method: teardown_appcontext (f)

          Registers a function to be called when the application context
          ends.  These functions are typically also called when the
          request context is popped.

          Example:

               ctx = app.app_context()
               ctx.push()
               ...
               ctx.pop()

          When ‘ctx.pop()’ is executed in the above example, the
          teardown functions are called just before the app context
          moves from the stack of active contexts.  This becomes
          relevant if you are using such constructs in tests.

          Since a request context typically also manages an application
          context it would also be called when you pop a request
          context.

          When a teardown function was called because of an unhandled
          exception it will be passed an error object.  If an
          errorhandler() is registered, it will handle the
          exception and the teardown will not receive it.

          The return values of teardown functions are ignored.

          New in version 0.9.

          Parameters: ‘f’
          (Callable(741)‘[’‘[’Optional(742)‘[’BaseException(743)‘]’‘]’‘,
          ’‘None’‘]’) –

          Return type:
          Callable(744)[[Optional(745)[BaseException(746)]], None]

      -- Attribute: teardown_appcontext_funcs: t.List[TeardownCallable]

          A list of functions that are called when the application
          context is destroyed.  Since the application context is also
          torn down if the request ends this is the place to store code
          that disconnects from databases.

          New in version 0.9.

      -- Method: teardown_request (f)

          Register a function to be run at the end of each request,
          regardless of whether there was an exception or not.  These
          functions are executed when the request context is popped,
          even if not an actual request was performed.

          Example:

               ctx = app.test_request_context()
               ctx.push()
               ...
               ctx.pop()

          When ‘ctx.pop()’ is executed in the above example, the
          teardown functions are called just before the request context
          moves from the stack of active contexts.  This becomes
          relevant if you are using such constructs in tests.

          Teardown functions must avoid raising exceptions.  If they
          execute code that might fail they will have to surround the
          execution of that code with try/except statements and log any
          errors.

          When a teardown function was called because of an exception it
          will be passed an error object.

          The return values of teardown functions are ignored.

               Debug Note: In debug mode Flask will not tear down a
               request on an exception immediately.  Instead it will
               keep it alive so that the interactive debugger can still
               access it.  This behavior can be controlled by the
               ‘PRESERVE_CONTEXT_ON_EXCEPTION’ configuration variable.

          Parameters: ‘f’
          (Callable(747)‘[’‘[’Optional(748)‘[’BaseException(749)‘]’‘]’‘,
          ’‘None’‘]’) –

          Return type:
          Callable(750)[[Optional(751)[BaseException(752)]], None]

      -- Attribute: teardown_request_funcs: t.Dict[AppOrBlueprintKey,
               t.List[TeardownCallable]]

          A data structure of functions to call at the end of each
          request even if an exception is raised, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the teardown_request().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Attribute: template_context_processors:
               t.Dict[AppOrBlueprintKey,
               t.List[TemplateContextProcessorCallable]]

          A data structure of functions to call to pass extra context
          values when rendering templates, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the context_processor()
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: template_filter (name=None)

          A decorator that is used to register custom template filter.
          You can specify a name for the filter, otherwise the function
          name will be used.  Example:

               @app.template_filter()
               def reverse(s):
                   return s[::-1]

          Parameters: ‘name’ (Optional(753)‘[’str(754)‘]’) – the
          optional name of the filter, otherwise the function name will
          be used.

          Return type: Callable(755)[[Callable(756)[[…], Any(757)]],
          Callable(758)[[…], Any(759)]]

      -- Attribute: template_folder

          The path to the templates folder, relative to root_path,
          to add to the template loader.  ‘None’ if templates
          should not be added.

      -- Method: template_global (name=None)

          A decorator that is used to register a custom template global
          function.  You can specify a name for the global function,
          otherwise the function name will be used.  Example:

               @app.template_global()
               def double(n):
                   return 2 * n

          New in version 0.10.

          Parameters: ‘name’ (Optional(760)‘[’str(761)‘]’) – the
          optional name of the global function, otherwise the function
          name will be used.

          Return type: Callable(762)[[Callable(763)[[…], Any(764)]],
          Callable(765)[[…], Any(766)]]

      -- Method: template_test (name=None)

          A decorator that is used to register custom template test.
          You can specify a name for the test, otherwise the function
          name will be used.  Example:

               @app.template_test()
               def is_prime(n):
                   if n == 2:
                       return True
                   for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
                       if n % i == 0:
                           return False
                   return True

          New in version 0.10.

          Parameters: ‘name’ (Optional(767)‘[’str(768)‘]’) – the
          optional name of the test, otherwise the function name will be
          used.

          Return type: Callable(769)[[Callable(770)[[…], bool(771)]],
          Callable(772)[[…], bool(773)]]

      -- Property: templates_auto_reload: bool(774)

          Reload templates when they are changed.  Used by
          create_jinja_environment().

          This attribute can be configured with
          TEMPLATES_AUTO_RELOAD.  If not set, it will be enabled in
          debug mode.

          New in version 1.0: This property was added but the underlying
          config and behavior already existed.

      -- Method: test_cli_runner (**kwargs)

          Create a CLI runner for testing CLI commands.  See
          Running Commands with the CLI Runner.

          Returns an instance of test_cli_runner_class, by
          default FlaskCliRunner.  The Flask app object is
          passed as the first argument.

          New in version 1.0.

          Parameters: ‘kwargs’ (Any(775)) –

          Return type: FlaskCliRunner.

      -- Attribute: test_cli_runner_class: Optional[Type[see
               FlaskCliRunner]] = None

          The CliRunner(776) subclass, by default FlaskCliRunner
          that is used by test_cli_runner().  Its
          ‘__init__’ method should take a Flask app object as the first
          argument.

          New in version 1.0.

      -- Method: test_client (use_cookies=True, **kwargs)

          Creates a test client for this application.  For information
          about unit testing head over to Testing Flask Applications.

          Note that if you are testing for assertions or exceptions in
          your application code, you must set ‘app.testing = True’ in
          order for the exceptions to propagate to the test client.
          Otherwise, the exception will be handled by the application
          (not visible to the test client) and the only indication of an
          AssertionError or other exception will be a 500 status code
          response to the test client.  See the testing.
          attribute.  For example:

               app.testing = True
               client = app.test_client()

          The test client can be used in a ‘with’ block to defer the
          closing down of the context until the end of the ‘with’ block.
          This is useful if you want to access the context locals for
          testing:

               with app.test_client() as c:
                   rv = c.get('/?vodka=42')
                   assert request.args['vodka'] == '42'

          Additionally, you may pass optional keyword arguments that
          will then be passed to the application’s
          test_client_class constructor.  For example:

               from flask.testing import FlaskClient

               class CustomClient(FlaskClient):
                   def __init__(self, *args, **kwargs):
                       self._authentication = kwargs.pop("authentication")
                       super(CustomClient,self).__init__( *args, **kwargs)

               app.test_client_class = CustomClient
               client = app.test_client(authentication='Basic ....')

          See FlaskClient for more information.

          Changed in version 0.11: Added ‘**kwargs’ to support passing
          additional keyword arguments to the constructor of
          test_client_class.

          New in version 0.7: The ‘use_cookies’ parameter was added as
          well as the ability to override the client to be used by
          setting the test_client_class attribute.

          Changed in version 0.4: added support for ‘with’ block usage
          for the client.

          Parameters:

             * ‘use_cookies’ (bool(777)) –

             * ‘kwargs’ (Any(778)) –

          Return type: FlaskClient.

      -- Attribute: test_client_class: Optional[Type[see FlaskClient]]
               = None

          The test_client() method creates an instance of
          this test client class.  Defaults to FlaskClient.

          New in version 0.7.

      -- Method: test_request_context (*args, **kwargs)

          Create a RequestContext for a WSGI environment
          created from the given values.  This is mostly useful during
          testing, where you may want to run a function that uses
          request data without dispatching a full request.

          See The Request Context.

          Use a ‘with’ block to push the context, which will make
          request point at the request for the created environment.

               with test_request_context(...):
                   generate_report()

          When using the shell, it may be easier to push and pop the
          context manually to avoid indentation.

               ctx = app.test_request_context(...)
               ctx.push()
               ...
               ctx.pop()

          Takes the same arguments as Werkzeug’s EnvironBuilder(779),
          with some defaults from the application.  See the linked
          Werkzeug docs for most of the available arguments.
          Flask-specific behavior is listed here.

          Parameters:

             * ‘path’ – URL path being requested.

             * ‘base_url’ – Base URL where the app is being served,
               which ‘path’ is relative to.  If not given, built from
               PREFERRED_URL_SCHEME, ‘subdomain’, see
               SERVER_NAME, and APPLICATION_ROOT.

             * ‘subdomain’ – Subdomain name to append to
               SERVER_NAME.

             * ‘url_scheme’ – Scheme to use instead of
               PREFERRED_URL_SCHEME.

             * ‘data’ – The request body, either as a string or a dict
               of form keys and values.

             * ‘json’ – If given, this is serialized as JSON and passed
               as ‘data’.  Also defaults ‘content_type’ to
               ‘application/json’.

             * ‘args’ (Any(780)) – other positional arguments passed to
               EnvironBuilder(781).

             * ‘kwargs’ (Any(782)) – other keyword arguments passed to
               EnvironBuilder(783).

          Return type: flask.ctx.RequestContext.

      -- Attribute: testing

          The testing flag.  Set this to ‘True’ to enable the test mode
          of Flask extensions (and in the future probably also Flask
          itself).  For example this might activate test helpers that
          have an additional runtime cost which should not be enabled by
          default.

          If this is enabled and PROPAGATE_EXCEPTIONS is not changed
          from the default it’s implicitly enabled.

          This attribute can also be configured from the config with the
          ‘TESTING’ configuration key.  Defaults to ‘False’.

      -- Method: trap_http_exception (e)

          Checks if an HTTP exception should be trapped or not.  By
          default this will return ‘False’ for all exceptions except for
          a bad request key error if ‘TRAP_BAD_REQUEST_ERRORS’ is set to
          ‘True’.  It also returns ‘True’ if ‘TRAP_HTTP_EXCEPTIONS’ is
          set to ‘True’.

          This is called for all HTTP exceptions raised by a view
          function.  If it returns ‘True’ for any exception the error
          handler for this exception is not called and it shows up as
          regular exception in the traceback.  This is helpful for
          debugging implicitly raised HTTP exceptions.

          Changed in version 1.0: Bad request errors are not trapped by
          default in debug mode.

          New in version 0.8.

          Parameters: ‘e’ (Exception(784)) –

          Return type: bool(785)

      -- Method: update_template_context (context)

          Update the template context with some commonly used variables.
          This injects request, session, config and g into the template
          context as well as everything template context processors want
          to inject.  Note that the as of Flask 0.6, the original values
          in the context will not be overridden if a context processor
          decides to return a value with the same key.

          Parameters: ‘context’ (dict(786)) – the context as a
          dictionary that is updated in place to add extra variables.

          Return type: None

      -- Attribute: url_build_error_handlers:
               t.List[t.Callable[[Exception(787), str(788), dict(789)],
               str(790)]]

          A list of functions that are called when url_for().
          raises a ‘BuildError’.  Each function registered here is
          called with ‘error’, ‘endpoint’ and ‘values’.  If a function
          returns ‘None’ or raises a ‘BuildError’ the next function is
          tried.

          New in version 0.9.

      -- Attribute: url_default_functions: t.Dict[AppOrBlueprintKey,
               t.List[URLDefaultCallable]]

          A data structure of functions to call to modify the keyword
          arguments when generating URLs, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the url_defaults().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: url_defaults (f)

          Callback function for URL defaults for all view functions of
          the application.  It’s called with the endpoint and values and
          should update the values passed in place.

          Parameters: ‘f’ (Callable(791)‘[’‘[’str(792)‘, ’dict(793)‘]’‘,
          ’‘None’‘]’) –

          Return type: Callable(794)[[str(795), dict(796)], None]

      -- Attribute: url_map

          The Map(797) for this instance.  You can use this to change
          the routing converters after the class was created but before
          any routes are connected.  Example:

               from werkzeug.routing import BaseConverter

               class ListConverter(BaseConverter):
                   def to_python(self, value):
                       return value.split(',')
                   def to_url(self, values):
                       return ','.join(super(ListConverter, self).to_url(value)
                                       for value in values)

               app = Flask(__name__)
               app.url_map.converters['list'] = ListConverter

      -- Attribute: url_map_class

          alias of werkzeug.routing.Map(798)

      -- Attribute: url_rule_class

          alias of werkzeug.routing.Rule(799)

      -- Method: url_value_preprocessor (f)

          Register a URL value preprocessor function for all view
          functions in the application.  These functions will be called
          before the before_request() functions.

          The function can modify the values captured from the matched
          url before they are passed to the view.  For example, this can
          be used to pop a common language code value and place it in
          ‘g’ rather than pass it to every view.

          The function is passed the endpoint name and values dict.  The
          return value is ignored.

          Parameters: ‘f’
          (Callable(800)‘[’‘[’Optional(801)‘[’str(802)‘]’‘,
          ’Optional(803)‘[’dict(804)‘]’‘]’‘, ’‘None’‘]’) –

          Return type: Callable(805)[[Optional(806)[str(807)],
          Optional(808)[dict(809)]], None]

      -- Attribute: url_value_preprocessors: t.Dict[AppOrBlueprintKey,
               t.List[URLValuePreprocessorCallable]]

          A data structure of functions to call to modify the keyword
          arguments passed to the view function, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the
          url_value_preprocessor() decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Attribute: use_x_sendfile

          Enable this if you want to use the X-Sendfile feature.  Keep
          in mind that the server has to support this.  This only
          affects files sent with the send_file() method.

          New in version 0.2.

          This attribute can also be configured from the config with the
          ‘USE_X_SENDFILE’ configuration key.  Defaults to ‘False’.

      -- Attribute: view_functions: t.Dict[str(810), t.Callable]

          A dictionary mapping endpoint names to view functions.

          To register a view function, use the route().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: wsgi_app (environ, start_response)

          The actual WSGI application.  This is not implemented in
          ‘__call__()’ so that middlewares can be applied without losing
          a reference to the app object.  Instead of doing this:

               app = MyMiddleware(app)

          It’s a better idea to do this instead:

               app.wsgi_app = MyMiddleware(app.wsgi_app)

          Then you still have the original application object around and
          can continue to call methods on it.

          Changed in version 0.7: Teardown events for the request and
          app contexts are called even if an unhandled error occurs.
          Other events may not be called depending on when an error
          occurs during dispatch.  See Callbacks and Errors.

          Parameters:

             * ‘environ’ (dict(811)) – A WSGI environment.

             * ‘start_response’ (Callable(812)) – A callable accepting a
               status code, a list of headers, and an optional exception
               context to start the response.

          Return type: Any(813)

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#str

   (2) https://docs.python.org/3/library/typing.html#typing.Optional

   (3) https://docs.python.org/3/library/stdtypes.html#str

   (4) https://docs.python.org/3/library/typing.html#typing.Optional

   (5) https://docs.python.org/3/library/typing.html#typing.Union

   (6) https://docs.python.org/3/library/stdtypes.html#str

   (7) https://docs.python.org/3/library/os.html#os.PathLike

   (8) https://docs.python.org/3/library/typing.html#typing.Optional

   (9) https://docs.python.org/3/library/stdtypes.html#str

   (10) https://docs.python.org/3/library/functions.html#bool

   (11) https://docs.python.org/3/library/functions.html#bool

   (12) https://docs.python.org/3/library/typing.html#typing.Optional

   (13) https://docs.python.org/3/library/stdtypes.html#str

   (14) https://docs.python.org/3/library/typing.html#typing.Optional

   (15) https://docs.python.org/3/library/stdtypes.html#str

   (16) https://docs.python.org/3/library/functions.html#bool

   (17) https://docs.python.org/3/library/typing.html#typing.Optional

   (18) https://docs.python.org/3/library/stdtypes.html#str

   (19) https://docs.python.org/3/library/typing.html#typing.Optional

   (20) https://docs.python.org/3/library/stdtypes.html#str

   (21) https://docs.python.org/3/library/typing.html#typing.Callable

   (22) https://docs.python.org/3/library/typing.html#typing.Any

   (23) https://docs.python.org/3/library/typing.html#typing.Optional

   (24) https://docs.python.org/3/library/stdtypes.html#str

   (25) https://docs.python.org/3/library/typing.html#typing.Callable

   (26) https://docs.python.org/3/library/typing.html#typing.Any

   (27) https://docs.python.org/3/library/typing.html#typing.Optional

   (28) https://docs.python.org/3/library/stdtypes.html#str

   (29) https://docs.python.org/3/library/typing.html#typing.Callable

   (30) https://docs.python.org/3/library/functions.html#bool

   (31) https://docs.python.org/3/library/stdtypes.html#str

   (32) https://docs.python.org/3/library/typing.html#typing.Optional

   (33) https://docs.python.org/3/library/stdtypes.html#str

   (34) https://docs.python.org/3/library/typing.html#typing.Optional

   (35) https://docs.python.org/3/library/typing.html#typing.Callable

   (36) https://docs.python.org/3/library/typing.html#typing.Optional

   (37) https://docs.python.org/3/library/functions.html#bool

   (38) https://docs.python.org/3/library/typing.html#typing.Any

   (39)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Rule

   (40) https://docs.python.org/3/library/typing.html#typing.Callable

   (41) https://docs.python.org/3/library/typing.html#typing.Callable

   (42) https://docs.python.org/3/library/typing.html#typing.Callable

   (43) https://docs.python.org/3/library/typing.html#typing.Coroutine

   (44) https://docs.python.org/3/library/typing.html#typing.Callable

   (45) https://docs.python.org/3/library/typing.html#typing.Any

   (46) https://docs.python.org/3/library/stdtypes.html#str

   (47) https://docs.python.org/3/library/typing.html#typing.Callable

   (48) https://docs.python.org/3/library/typing.html#typing.Callable

   (49) https://docs.python.org/3/library/typing.html#typing.Callable

   (50) https://docs.python.org/3/library/typing.html#typing.Optional

   (51) https://docs.python.org/3/library/typing.html#typing.Union

   (52) https://docs.python.org/3/library/stdtypes.html#str

   (53) https://docs.python.org/3/library/stdtypes.html#bytes

   (54) https://docs.python.org/3/library/typing.html#typing.Dict

   (55) https://docs.python.org/3/library/stdtypes.html#str

   (56) https://docs.python.org/3/library/typing.html#typing.Any

   (57) https://docs.python.org/3/library/typing.html#typing.Iterator

   (58) https://docs.python.org/3/library/stdtypes.html#str

   (59) https://docs.python.org/3/library/typing.html#typing.Iterator

   (60) https://docs.python.org/3/library/stdtypes.html#bytes

   (61) https://docs.python.org/3/library/typing.html#typing.Tuple

   (62) https://docs.python.org/3/library/typing.html#typing.Union

   (63) https://docs.python.org/3/library/stdtypes.html#str

   (64) https://docs.python.org/3/library/stdtypes.html#bytes

   (65) https://docs.python.org/3/library/typing.html#typing.Dict

   (66) https://docs.python.org/3/library/stdtypes.html#str

   (67) https://docs.python.org/3/library/typing.html#typing.Any

   (68) https://docs.python.org/3/library/typing.html#typing.Iterator

   (69) https://docs.python.org/3/library/stdtypes.html#str

   (70) https://docs.python.org/3/library/typing.html#typing.Iterator

   (71) https://docs.python.org/3/library/stdtypes.html#bytes

   (72) https://docs.python.org/3/library/typing.html#typing.Union

   (73) https://docs.python.org/3/library/typing.html#typing.Dict

   (74) https://docs.python.org/3/library/stdtypes.html#str

   (75) https://docs.python.org/3/library/typing.html#typing.Union

   (76) https://docs.python.org/3/library/stdtypes.html#str

   (77) https://docs.python.org/3/library/typing.html#typing.List

   (78) https://docs.python.org/3/library/stdtypes.html#str

   (79) https://docs.python.org/3/library/typing.html#typing.Tuple

   (80) https://docs.python.org/3/library/stdtypes.html#str

   (81) https://docs.python.org/3/library/typing.html#typing.List

   (82) https://docs.python.org/3/library/typing.html#typing.Tuple

   (83) https://docs.python.org/3/library/stdtypes.html#str

   (84) https://docs.python.org/3/library/typing.html#typing.Union

   (85) https://docs.python.org/3/library/stdtypes.html#str

   (86) https://docs.python.org/3/library/typing.html#typing.List

   (87) https://docs.python.org/3/library/stdtypes.html#str

   (88) https://docs.python.org/3/library/typing.html#typing.Tuple

   (89) https://docs.python.org/3/library/stdtypes.html#str

   (90) https://docs.python.org/3/library/typing.html#typing.Tuple

   (91) https://docs.python.org/3/library/typing.html#typing.Union

   (92) https://docs.python.org/3/library/stdtypes.html#str

   (93) https://docs.python.org/3/library/stdtypes.html#bytes

   (94) https://docs.python.org/3/library/typing.html#typing.Dict

   (95) https://docs.python.org/3/library/stdtypes.html#str

   (96) https://docs.python.org/3/library/typing.html#typing.Any

   (97) https://docs.python.org/3/library/typing.html#typing.Iterator

   (98) https://docs.python.org/3/library/stdtypes.html#str

   (99) https://docs.python.org/3/library/typing.html#typing.Iterator

   (100) https://docs.python.org/3/library/stdtypes.html#bytes

   (101) https://docs.python.org/3/library/functions.html#int

   (102) https://docs.python.org/3/library/typing.html#typing.Tuple

   (103) https://docs.python.org/3/library/typing.html#typing.Union

   (104) https://docs.python.org/3/library/stdtypes.html#str

   (105) https://docs.python.org/3/library/stdtypes.html#bytes

   (106) https://docs.python.org/3/library/typing.html#typing.Dict

   (107) https://docs.python.org/3/library/stdtypes.html#str

   (108) https://docs.python.org/3/library/typing.html#typing.Any

   (109) https://docs.python.org/3/library/typing.html#typing.Iterator

   (110) https://docs.python.org/3/library/stdtypes.html#str

   (111) https://docs.python.org/3/library/typing.html#typing.Iterator

   (112) https://docs.python.org/3/library/stdtypes.html#bytes

   (113) https://docs.python.org/3/library/functions.html#int

   (114) https://docs.python.org/3/library/typing.html#typing.Union

   (115) https://docs.python.org/3/library/typing.html#typing.Dict

   (116) https://docs.python.org/3/library/stdtypes.html#str

   (117) https://docs.python.org/3/library/typing.html#typing.Union

   (118) https://docs.python.org/3/library/stdtypes.html#str

   (119) https://docs.python.org/3/library/typing.html#typing.List

   (120) https://docs.python.org/3/library/stdtypes.html#str

   (121) https://docs.python.org/3/library/typing.html#typing.Tuple

   (122) https://docs.python.org/3/library/stdtypes.html#str

   (123) https://docs.python.org/3/library/typing.html#typing.List

   (124) https://docs.python.org/3/library/typing.html#typing.Tuple

   (125) https://docs.python.org/3/library/stdtypes.html#str

   (126) https://docs.python.org/3/library/typing.html#typing.Union

   (127) https://docs.python.org/3/library/stdtypes.html#str

   (128) https://docs.python.org/3/library/typing.html#typing.List

   (129) https://docs.python.org/3/library/stdtypes.html#str

   (130) https://docs.python.org/3/library/typing.html#typing.Tuple

   (131) https://docs.python.org/3/library/stdtypes.html#str

   (132) https://docs.python.org/3/library/typing.html#typing.Callable

   (133) https://docs.python.org/3/library/typing.html#typing.Optional

   (134) https://docs.python.org/3/library/typing.html#typing.Union

   (135) https://docs.python.org/3/library/stdtypes.html#str

   (136) https://docs.python.org/3/library/stdtypes.html#bytes

   (137) https://docs.python.org/3/library/typing.html#typing.Dict

   (138) https://docs.python.org/3/library/stdtypes.html#str

   (139) https://docs.python.org/3/library/typing.html#typing.Any

   (140) https://docs.python.org/3/library/typing.html#typing.Iterator

   (141) https://docs.python.org/3/library/stdtypes.html#str

   (142) https://docs.python.org/3/library/typing.html#typing.Iterator

   (143) https://docs.python.org/3/library/stdtypes.html#bytes

   (144) https://docs.python.org/3/library/typing.html#typing.Tuple

   (145) https://docs.python.org/3/library/typing.html#typing.Union

   (146) https://docs.python.org/3/library/stdtypes.html#str

   (147) https://docs.python.org/3/library/stdtypes.html#bytes

   (148) https://docs.python.org/3/library/typing.html#typing.Dict

   (149) https://docs.python.org/3/library/stdtypes.html#str

   (150) https://docs.python.org/3/library/typing.html#typing.Any

   (151) https://docs.python.org/3/library/typing.html#typing.Iterator

   (152) https://docs.python.org/3/library/stdtypes.html#str

   (153) https://docs.python.org/3/library/typing.html#typing.Iterator

   (154) https://docs.python.org/3/library/stdtypes.html#bytes

   (155) https://docs.python.org/3/library/typing.html#typing.Union

   (156) https://docs.python.org/3/library/typing.html#typing.Dict

   (157) https://docs.python.org/3/library/stdtypes.html#str

   (158) https://docs.python.org/3/library/typing.html#typing.Union

   (159) https://docs.python.org/3/library/stdtypes.html#str

   (160) https://docs.python.org/3/library/typing.html#typing.List

   (161) https://docs.python.org/3/library/stdtypes.html#str

   (162) https://docs.python.org/3/library/typing.html#typing.Tuple

   (163) https://docs.python.org/3/library/stdtypes.html#str

   (164) https://docs.python.org/3/library/typing.html#typing.List

   (165) https://docs.python.org/3/library/typing.html#typing.Tuple

   (166) https://docs.python.org/3/library/stdtypes.html#str

   (167) https://docs.python.org/3/library/typing.html#typing.Union

   (168) https://docs.python.org/3/library/stdtypes.html#str

   (169) https://docs.python.org/3/library/typing.html#typing.List

   (170) https://docs.python.org/3/library/stdtypes.html#str

   (171) https://docs.python.org/3/library/typing.html#typing.Tuple

   (172) https://docs.python.org/3/library/stdtypes.html#str

   (173) https://docs.python.org/3/library/typing.html#typing.Tuple

   (174) https://docs.python.org/3/library/typing.html#typing.Union

   (175) https://docs.python.org/3/library/stdtypes.html#str

   (176) https://docs.python.org/3/library/stdtypes.html#bytes

   (177) https://docs.python.org/3/library/typing.html#typing.Dict

   (178) https://docs.python.org/3/library/stdtypes.html#str

   (179) https://docs.python.org/3/library/typing.html#typing.Any

   (180) https://docs.python.org/3/library/typing.html#typing.Iterator

   (181) https://docs.python.org/3/library/stdtypes.html#str

   (182) https://docs.python.org/3/library/typing.html#typing.Iterator

   (183) https://docs.python.org/3/library/stdtypes.html#bytes

   (184) https://docs.python.org/3/library/functions.html#int

   (185) https://docs.python.org/3/library/typing.html#typing.Tuple

   (186) https://docs.python.org/3/library/typing.html#typing.Union

   (187) https://docs.python.org/3/library/stdtypes.html#str

   (188) https://docs.python.org/3/library/stdtypes.html#bytes

   (189) https://docs.python.org/3/library/typing.html#typing.Dict

   (190) https://docs.python.org/3/library/stdtypes.html#str

   (191) https://docs.python.org/3/library/typing.html#typing.Any

   (192) https://docs.python.org/3/library/typing.html#typing.Iterator

   (193) https://docs.python.org/3/library/stdtypes.html#str

   (194) https://docs.python.org/3/library/typing.html#typing.Iterator

   (195) https://docs.python.org/3/library/stdtypes.html#bytes

   (196) https://docs.python.org/3/library/functions.html#int

   (197) https://docs.python.org/3/library/typing.html#typing.Union

   (198) https://docs.python.org/3/library/typing.html#typing.Dict

   (199) https://docs.python.org/3/library/stdtypes.html#str

   (200) https://docs.python.org/3/library/typing.html#typing.Union

   (201) https://docs.python.org/3/library/stdtypes.html#str

   (202) https://docs.python.org/3/library/typing.html#typing.List

   (203) https://docs.python.org/3/library/stdtypes.html#str

   (204) https://docs.python.org/3/library/typing.html#typing.Tuple

   (205) https://docs.python.org/3/library/stdtypes.html#str

   (206) https://docs.python.org/3/library/typing.html#typing.List

   (207) https://docs.python.org/3/library/typing.html#typing.Tuple

   (208) https://docs.python.org/3/library/stdtypes.html#str

   (209) https://docs.python.org/3/library/typing.html#typing.Union

   (210) https://docs.python.org/3/library/stdtypes.html#str

   (211) https://docs.python.org/3/library/typing.html#typing.List

   (212) https://docs.python.org/3/library/stdtypes.html#str

   (213) https://docs.python.org/3/library/typing.html#typing.Tuple

   (214) https://docs.python.org/3/library/stdtypes.html#str

   (215) https://docs.python.org/3/library/stdtypes.html#str

   (216) https://docs.python.org/3/library/typing.html#typing.Callable

   (217) https://docs.python.org/3/library/typing.html#typing.Dict

   (218) https://docs.python.org/3/library/stdtypes.html#str

   (219) https://docs.python.org/3/library/typing.html#typing.Any

   (220) https://docs.python.org/3/library/typing.html#typing.Callable

   (221) https://docs.python.org/3/library/typing.html#typing.Dict

   (222) https://docs.python.org/3/library/stdtypes.html#str

   (223) https://docs.python.org/3/library/typing.html#typing.Any

   (224) https://docs.python.org/3/library/typing.html#typing.Optional

   (225) https://docs.python.org/3/library/typing.html#typing.Optional

   (226)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.MapAdapter

   (227) https://docs.python.org/3/library/functions.html#bool

   (228) https://docs.python.org/3/library/stdtypes.html#str

   (229) https://docs.python.org/3/library/typing.html#typing.Any

   (230) https://docs.python.org/3/library/typing.html#typing.Callable

   (231) https://docs.python.org/3/library/typing.html#typing.Union

   (232) https://docs.python.org/3/library/stdtypes.html#str

   (233) https://docs.python.org/3/library/stdtypes.html#bytes

   (234) https://docs.python.org/3/library/typing.html#typing.Dict

   (235) https://docs.python.org/3/library/stdtypes.html#str

   (236) https://docs.python.org/3/library/typing.html#typing.Any

   (237) https://docs.python.org/3/library/typing.html#typing.Iterator

   (238) https://docs.python.org/3/library/stdtypes.html#str

   (239) https://docs.python.org/3/library/typing.html#typing.Iterator

   (240) https://docs.python.org/3/library/stdtypes.html#bytes

   (241) https://docs.python.org/3/library/typing.html#typing.Tuple

   (242) https://docs.python.org/3/library/typing.html#typing.Union

   (243) https://docs.python.org/3/library/stdtypes.html#str

   (244) https://docs.python.org/3/library/stdtypes.html#bytes

   (245) https://docs.python.org/3/library/typing.html#typing.Dict

   (246) https://docs.python.org/3/library/stdtypes.html#str

   (247) https://docs.python.org/3/library/typing.html#typing.Any

   (248) https://docs.python.org/3/library/typing.html#typing.Iterator

   (249) https://docs.python.org/3/library/stdtypes.html#str

   (250) https://docs.python.org/3/library/typing.html#typing.Iterator

   (251) https://docs.python.org/3/library/stdtypes.html#bytes

   (252) https://docs.python.org/3/library/typing.html#typing.Union

   (253) https://docs.python.org/3/library/typing.html#typing.Dict

   (254) https://docs.python.org/3/library/stdtypes.html#str

   (255) https://docs.python.org/3/library/typing.html#typing.Union

   (256) https://docs.python.org/3/library/stdtypes.html#str

   (257) https://docs.python.org/3/library/typing.html#typing.List

   (258) https://docs.python.org/3/library/stdtypes.html#str

   (259) https://docs.python.org/3/library/typing.html#typing.Tuple

   (260) https://docs.python.org/3/library/stdtypes.html#str

   (261) https://docs.python.org/3/library/typing.html#typing.List

   (262) https://docs.python.org/3/library/typing.html#typing.Tuple

   (263) https://docs.python.org/3/library/stdtypes.html#str

   (264) https://docs.python.org/3/library/typing.html#typing.Union

   (265) https://docs.python.org/3/library/stdtypes.html#str

   (266) https://docs.python.org/3/library/typing.html#typing.List

   (267) https://docs.python.org/3/library/stdtypes.html#str

   (268) https://docs.python.org/3/library/typing.html#typing.Tuple

   (269) https://docs.python.org/3/library/stdtypes.html#str

   (270) https://docs.python.org/3/library/typing.html#typing.Tuple

   (271) https://docs.python.org/3/library/typing.html#typing.Union

   (272) https://docs.python.org/3/library/stdtypes.html#str

   (273) https://docs.python.org/3/library/stdtypes.html#bytes

   (274) https://docs.python.org/3/library/typing.html#typing.Dict

   (275) https://docs.python.org/3/library/stdtypes.html#str

   (276) https://docs.python.org/3/library/typing.html#typing.Any

   (277) https://docs.python.org/3/library/typing.html#typing.Iterator

   (278) https://docs.python.org/3/library/stdtypes.html#str

   (279) https://docs.python.org/3/library/typing.html#typing.Iterator

   (280) https://docs.python.org/3/library/stdtypes.html#bytes

   (281) https://docs.python.org/3/library/functions.html#int

   (282) https://docs.python.org/3/library/typing.html#typing.Tuple

   (283) https://docs.python.org/3/library/typing.html#typing.Union

   (284) https://docs.python.org/3/library/stdtypes.html#str

   (285) https://docs.python.org/3/library/stdtypes.html#bytes

   (286) https://docs.python.org/3/library/typing.html#typing.Dict

   (287) https://docs.python.org/3/library/stdtypes.html#str

   (288) https://docs.python.org/3/library/typing.html#typing.Any

   (289) https://docs.python.org/3/library/typing.html#typing.Iterator

   (290) https://docs.python.org/3/library/stdtypes.html#str

   (291) https://docs.python.org/3/library/typing.html#typing.Iterator

   (292) https://docs.python.org/3/library/stdtypes.html#bytes

   (293) https://docs.python.org/3/library/functions.html#int

   (294) https://docs.python.org/3/library/typing.html#typing.Union

   (295) https://docs.python.org/3/library/typing.html#typing.Dict

   (296) https://docs.python.org/3/library/stdtypes.html#str

   (297) https://docs.python.org/3/library/typing.html#typing.Union

   (298) https://docs.python.org/3/library/stdtypes.html#str

   (299) https://docs.python.org/3/library/typing.html#typing.List

   (300) https://docs.python.org/3/library/stdtypes.html#str

   (301) https://docs.python.org/3/library/typing.html#typing.Tuple

   (302) https://docs.python.org/3/library/stdtypes.html#str

   (303) https://docs.python.org/3/library/typing.html#typing.List

   (304) https://docs.python.org/3/library/typing.html#typing.Tuple

   (305) https://docs.python.org/3/library/stdtypes.html#str

   (306) https://docs.python.org/3/library/typing.html#typing.Union

   (307) https://docs.python.org/3/library/stdtypes.html#str

   (308) https://docs.python.org/3/library/typing.html#typing.List

   (309) https://docs.python.org/3/library/stdtypes.html#str

   (310) https://docs.python.org/3/library/typing.html#typing.Tuple

   (311) https://docs.python.org/3/library/stdtypes.html#str

   (312) https://docs.python.org/3/library/typing.html#typing.Optional

   (313) https://docs.python.org/3/library/exceptions.html#BaseException

   (314) https://docs.python.org/3/library/typing.html#typing.Optional

   (315) https://docs.python.org/3/library/exceptions.html#BaseException

   (316) https://docs.python.org/3/library/stdtypes.html#str

   (317) https://docs.python.org/3/library/typing.html#typing.Callable

   (318) https://docs.python.org/3/library/typing.html#typing.Callable

   (319) https://docs.python.org/3/library/typing.html#typing.Callable

   (320) https://docs.python.org/3/library/functions.html#int

   (321) https://docs.python.org/3/library/exceptions.html#Exception

   (322)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (323) https://docs.python.org/3/library/typing.html#typing.Union

   (324) https://docs.python.org/3/library/typing.html#typing.Type

   (325) https://docs.python.org/3/library/exceptions.html#Exception

   (326) https://docs.python.org/3/library/functions.html#int

   (327) https://docs.python.org/3/library/typing.html#typing.Callable

   (328) https://docs.python.org/3/library/stdtypes.html#dict

   (329) https://docs.python.org/3/library/stdtypes.html#str

   (330) https://docs.python.org/3/library/typing.html#typing.Any

   (331) https://docs.python.org/3/library/typing.html#typing.Callable

   (332) https://docs.python.org/3/library/typing.html#typing.Optional

   (333) https://docs.python.org/3/library/stdtypes.html#str

   (334) https://docs.python.org/3/library/typing.html#typing.Optional

   (335) https://docs.python.org/3/library/functions.html#int

   (336) https://docs.python.org/3/library/functions.html#bool

   (337)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.InternalServerError

   (338) https://docs.python.org/3/library/exceptions.html#Exception

   (339)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (340) https://docs.python.org/3/library/typing.html#typing.Union

   (341)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (342) https://docs.python.org/3/library/stdtypes.html#str

   (343) https://docs.python.org/3/library/stdtypes.html#bytes

   (344) https://docs.python.org/3/library/typing.html#typing.Dict

   (345) https://docs.python.org/3/library/stdtypes.html#str

   (346) https://docs.python.org/3/library/typing.html#typing.Any

   (347) https://docs.python.org/3/library/typing.html#typing.Iterator

   (348) https://docs.python.org/3/library/stdtypes.html#str

   (349) https://docs.python.org/3/library/typing.html#typing.Iterator

   (350) https://docs.python.org/3/library/stdtypes.html#bytes

   (351) https://docs.python.org/3/library/typing.html#typing.Tuple

   (352) https://docs.python.org/3/library/typing.html#typing.Union

   (353) https://docs.python.org/3/library/stdtypes.html#str

   (354) https://docs.python.org/3/library/stdtypes.html#bytes

   (355) https://docs.python.org/3/library/typing.html#typing.Dict

   (356) https://docs.python.org/3/library/stdtypes.html#str

   (357) https://docs.python.org/3/library/typing.html#typing.Any

   (358) https://docs.python.org/3/library/typing.html#typing.Iterator

   (359) https://docs.python.org/3/library/stdtypes.html#str

   (360) https://docs.python.org/3/library/typing.html#typing.Iterator

   (361) https://docs.python.org/3/library/stdtypes.html#bytes

   (362) https://docs.python.org/3/library/typing.html#typing.Union

   (363) https://docs.python.org/3/library/typing.html#typing.Dict

   (364) https://docs.python.org/3/library/stdtypes.html#str

   (365) https://docs.python.org/3/library/typing.html#typing.Union

   (366) https://docs.python.org/3/library/stdtypes.html#str

   (367) https://docs.python.org/3/library/typing.html#typing.List

   (368) https://docs.python.org/3/library/stdtypes.html#str

   (369) https://docs.python.org/3/library/typing.html#typing.Tuple

   (370) https://docs.python.org/3/library/stdtypes.html#str

   (371) https://docs.python.org/3/library/typing.html#typing.List

   (372) https://docs.python.org/3/library/typing.html#typing.Tuple

   (373) https://docs.python.org/3/library/stdtypes.html#str

   (374) https://docs.python.org/3/library/typing.html#typing.Union

   (375) https://docs.python.org/3/library/stdtypes.html#str

   (376) https://docs.python.org/3/library/typing.html#typing.List

   (377) https://docs.python.org/3/library/stdtypes.html#str

   (378) https://docs.python.org/3/library/typing.html#typing.Tuple

   (379) https://docs.python.org/3/library/stdtypes.html#str

   (380) https://docs.python.org/3/library/typing.html#typing.Tuple

   (381) https://docs.python.org/3/library/typing.html#typing.Union

   (382) https://docs.python.org/3/library/stdtypes.html#str

   (383) https://docs.python.org/3/library/stdtypes.html#bytes

   (384) https://docs.python.org/3/library/typing.html#typing.Dict

   (385) https://docs.python.org/3/library/stdtypes.html#str

   (386) https://docs.python.org/3/library/typing.html#typing.Any

   (387) https://docs.python.org/3/library/typing.html#typing.Iterator

   (388) https://docs.python.org/3/library/stdtypes.html#str

   (389) https://docs.python.org/3/library/typing.html#typing.Iterator

   (390) https://docs.python.org/3/library/stdtypes.html#bytes

   (391) https://docs.python.org/3/library/functions.html#int

   (392) https://docs.python.org/3/library/typing.html#typing.Tuple

   (393) https://docs.python.org/3/library/typing.html#typing.Union

   (394) https://docs.python.org/3/library/stdtypes.html#str

   (395) https://docs.python.org/3/library/stdtypes.html#bytes

   (396) https://docs.python.org/3/library/typing.html#typing.Dict

   (397) https://docs.python.org/3/library/stdtypes.html#str

   (398) https://docs.python.org/3/library/typing.html#typing.Any

   (399) https://docs.python.org/3/library/typing.html#typing.Iterator

   (400) https://docs.python.org/3/library/stdtypes.html#str

   (401) https://docs.python.org/3/library/typing.html#typing.Iterator

   (402) https://docs.python.org/3/library/stdtypes.html#bytes

   (403) https://docs.python.org/3/library/functions.html#int

   (404) https://docs.python.org/3/library/typing.html#typing.Union

   (405) https://docs.python.org/3/library/typing.html#typing.Dict

   (406) https://docs.python.org/3/library/stdtypes.html#str

   (407) https://docs.python.org/3/library/typing.html#typing.Union

   (408) https://docs.python.org/3/library/stdtypes.html#str

   (409) https://docs.python.org/3/library/typing.html#typing.List

   (410) https://docs.python.org/3/library/stdtypes.html#str

   (411) https://docs.python.org/3/library/typing.html#typing.Tuple

   (412) https://docs.python.org/3/library/stdtypes.html#str

   (413) https://docs.python.org/3/library/typing.html#typing.List

   (414) https://docs.python.org/3/library/typing.html#typing.Tuple

   (415) https://docs.python.org/3/library/stdtypes.html#str

   (416) https://docs.python.org/3/library/typing.html#typing.Union

   (417) https://docs.python.org/3/library/stdtypes.html#str

   (418) https://docs.python.org/3/library/typing.html#typing.List

   (419) https://docs.python.org/3/library/stdtypes.html#str

   (420) https://docs.python.org/3/library/typing.html#typing.Tuple

   (421) https://docs.python.org/3/library/stdtypes.html#str

   (422) https://docs.python.org/3/library/exceptions.html#Exception

   (423) https://docs.python.org/3/library/stdtypes.html#str

   (424) https://docs.python.org/3/library/stdtypes.html#dict

   (425) https://docs.python.org/3/library/stdtypes.html#str

   (426) https://docs.python.org/3/library/exceptions.html#Exception

   (427) https://docs.python.org/3/library/typing.html#typing.Union

   (428)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (429) https://docs.python.org/3/library/stdtypes.html#str

   (430) https://docs.python.org/3/library/stdtypes.html#bytes

   (431) https://docs.python.org/3/library/typing.html#typing.Dict

   (432) https://docs.python.org/3/library/stdtypes.html#str

   (433) https://docs.python.org/3/library/typing.html#typing.Any

   (434) https://docs.python.org/3/library/typing.html#typing.Iterator

   (435) https://docs.python.org/3/library/stdtypes.html#str

   (436) https://docs.python.org/3/library/typing.html#typing.Iterator

   (437) https://docs.python.org/3/library/stdtypes.html#bytes

   (438) https://docs.python.org/3/library/typing.html#typing.Tuple

   (439) https://docs.python.org/3/library/typing.html#typing.Union

   (440) https://docs.python.org/3/library/stdtypes.html#str

   (441) https://docs.python.org/3/library/stdtypes.html#bytes

   (442) https://docs.python.org/3/library/typing.html#typing.Dict

   (443) https://docs.python.org/3/library/stdtypes.html#str

   (444) https://docs.python.org/3/library/typing.html#typing.Any

   (445) https://docs.python.org/3/library/typing.html#typing.Iterator

   (446) https://docs.python.org/3/library/stdtypes.html#str

   (447) https://docs.python.org/3/library/typing.html#typing.Iterator

   (448) https://docs.python.org/3/library/stdtypes.html#bytes

   (449) https://docs.python.org/3/library/typing.html#typing.Union

   (450) https://docs.python.org/3/library/typing.html#typing.Dict

   (451) https://docs.python.org/3/library/stdtypes.html#str

   (452) https://docs.python.org/3/library/typing.html#typing.Union

   (453) https://docs.python.org/3/library/stdtypes.html#str

   (454) https://docs.python.org/3/library/typing.html#typing.List

   (455) https://docs.python.org/3/library/stdtypes.html#str

   (456) https://docs.python.org/3/library/typing.html#typing.Tuple

   (457) https://docs.python.org/3/library/stdtypes.html#str

   (458) https://docs.python.org/3/library/typing.html#typing.List

   (459) https://docs.python.org/3/library/typing.html#typing.Tuple

   (460) https://docs.python.org/3/library/stdtypes.html#str

   (461) https://docs.python.org/3/library/typing.html#typing.Union

   (462) https://docs.python.org/3/library/stdtypes.html#str

   (463) https://docs.python.org/3/library/typing.html#typing.List

   (464) https://docs.python.org/3/library/stdtypes.html#str

   (465) https://docs.python.org/3/library/typing.html#typing.Tuple

   (466) https://docs.python.org/3/library/stdtypes.html#str

   (467) https://docs.python.org/3/library/typing.html#typing.Tuple

   (468) https://docs.python.org/3/library/typing.html#typing.Union

   (469) https://docs.python.org/3/library/stdtypes.html#str

   (470) https://docs.python.org/3/library/stdtypes.html#bytes

   (471) https://docs.python.org/3/library/typing.html#typing.Dict

   (472) https://docs.python.org/3/library/stdtypes.html#str

   (473) https://docs.python.org/3/library/typing.html#typing.Any

   (474) https://docs.python.org/3/library/typing.html#typing.Iterator

   (475) https://docs.python.org/3/library/stdtypes.html#str

   (476) https://docs.python.org/3/library/typing.html#typing.Iterator

   (477) https://docs.python.org/3/library/stdtypes.html#bytes

   (478) https://docs.python.org/3/library/functions.html#int

   (479) https://docs.python.org/3/library/typing.html#typing.Tuple

   (480) https://docs.python.org/3/library/typing.html#typing.Union

   (481) https://docs.python.org/3/library/stdtypes.html#str

   (482) https://docs.python.org/3/library/stdtypes.html#bytes

   (483) https://docs.python.org/3/library/typing.html#typing.Dict

   (484) https://docs.python.org/3/library/stdtypes.html#str

   (485) https://docs.python.org/3/library/typing.html#typing.Any

   (486) https://docs.python.org/3/library/typing.html#typing.Iterator

   (487) https://docs.python.org/3/library/stdtypes.html#str

   (488) https://docs.python.org/3/library/typing.html#typing.Iterator

   (489) https://docs.python.org/3/library/stdtypes.html#bytes

   (490) https://docs.python.org/3/library/functions.html#int

   (491) https://docs.python.org/3/library/typing.html#typing.Union

   (492) https://docs.python.org/3/library/typing.html#typing.Dict

   (493) https://docs.python.org/3/library/stdtypes.html#str

   (494) https://docs.python.org/3/library/typing.html#typing.Union

   (495) https://docs.python.org/3/library/stdtypes.html#str

   (496) https://docs.python.org/3/library/typing.html#typing.List

   (497) https://docs.python.org/3/library/stdtypes.html#str

   (498) https://docs.python.org/3/library/typing.html#typing.Tuple

   (499) https://docs.python.org/3/library/stdtypes.html#str

   (500) https://docs.python.org/3/library/typing.html#typing.List

   (501) https://docs.python.org/3/library/typing.html#typing.Tuple

   (502) https://docs.python.org/3/library/stdtypes.html#str

   (503) https://docs.python.org/3/library/typing.html#typing.Union

   (504) https://docs.python.org/3/library/stdtypes.html#str

   (505) https://docs.python.org/3/library/typing.html#typing.List

   (506) https://docs.python.org/3/library/stdtypes.html#str

   (507) https://docs.python.org/3/library/typing.html#typing.Tuple

   (508) https://docs.python.org/3/library/stdtypes.html#str

   (509) https://docs.python.org/3/library/functions.html#bool

   (510) https://docs.python.org/3/library/stdtypes.html#str

   (511) https://docs.python.org/3/library/stdtypes.html#dict

   (512) https://docs.python.org/3/library/typing.html#typing.ValuesView

   (513)
https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.FileSystemLoader

   (514)
https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.FileSystemLoader

   (515) https://docs.python.org/3/library/stdtypes.html#dict

   (516) https://docs.python.org/3/library/typing.html#typing.Union

   (517) https://docs.python.org/3/library/typing.html#typing.Tuple

   (518) https://docs.python.org/3/library/functions.html#type

   (519) https://docs.python.org/3/library/exceptions.html#BaseException

   (520)
https://docs.python.org/3/library/types.html#types.TracebackType

   (521) https://docs.python.org/3/library/typing.html#typing.Tuple

   (522) https://docs.python.org/3/library/logging.html#logging.Logger

   (523) https://docs.python.org/3/library/logging.html#logging.Logger

   (524) https://docs.python.org/3/library/functions.html#bool

   (525) https://docs.python.org/3/library/typing.html#typing.Union

   (526) https://docs.python.org/3/library/stdtypes.html#str

   (527) https://docs.python.org/3/library/stdtypes.html#bytes

   (528) https://docs.python.org/3/library/typing.html#typing.Dict

   (529) https://docs.python.org/3/library/stdtypes.html#str

   (530) https://docs.python.org/3/library/typing.html#typing.Any

   (531) https://docs.python.org/3/library/typing.html#typing.Iterator

   (532) https://docs.python.org/3/library/stdtypes.html#str

   (533) https://docs.python.org/3/library/typing.html#typing.Iterator

   (534) https://docs.python.org/3/library/stdtypes.html#bytes

   (535) https://docs.python.org/3/library/typing.html#typing.Tuple

   (536) https://docs.python.org/3/library/typing.html#typing.Union

   (537) https://docs.python.org/3/library/stdtypes.html#str

   (538) https://docs.python.org/3/library/stdtypes.html#bytes

   (539) https://docs.python.org/3/library/typing.html#typing.Dict

   (540) https://docs.python.org/3/library/stdtypes.html#str

   (541) https://docs.python.org/3/library/typing.html#typing.Any

   (542) https://docs.python.org/3/library/typing.html#typing.Iterator

   (543) https://docs.python.org/3/library/stdtypes.html#str

   (544) https://docs.python.org/3/library/typing.html#typing.Iterator

   (545) https://docs.python.org/3/library/stdtypes.html#bytes

   (546) https://docs.python.org/3/library/typing.html#typing.Union

   (547) https://docs.python.org/3/library/typing.html#typing.Dict

   (548) https://docs.python.org/3/library/stdtypes.html#str

   (549) https://docs.python.org/3/library/typing.html#typing.Union

   (550) https://docs.python.org/3/library/stdtypes.html#str

   (551) https://docs.python.org/3/library/typing.html#typing.List

   (552) https://docs.python.org/3/library/stdtypes.html#str

   (553) https://docs.python.org/3/library/typing.html#typing.Tuple

   (554) https://docs.python.org/3/library/stdtypes.html#str

   (555) https://docs.python.org/3/library/typing.html#typing.List

   (556) https://docs.python.org/3/library/typing.html#typing.Tuple

   (557) https://docs.python.org/3/library/stdtypes.html#str

   (558) https://docs.python.org/3/library/typing.html#typing.Union

   (559) https://docs.python.org/3/library/stdtypes.html#str

   (560) https://docs.python.org/3/library/typing.html#typing.List

   (561) https://docs.python.org/3/library/stdtypes.html#str

   (562) https://docs.python.org/3/library/typing.html#typing.Tuple

   (563) https://docs.python.org/3/library/stdtypes.html#str

   (564) https://docs.python.org/3/library/typing.html#typing.Tuple

   (565) https://docs.python.org/3/library/typing.html#typing.Union

   (566) https://docs.python.org/3/library/stdtypes.html#str

   (567) https://docs.python.org/3/library/stdtypes.html#bytes

   (568) https://docs.python.org/3/library/typing.html#typing.Dict

   (569) https://docs.python.org/3/library/stdtypes.html#str

   (570) https://docs.python.org/3/library/typing.html#typing.Any

   (571) https://docs.python.org/3/library/typing.html#typing.Iterator

   (572) https://docs.python.org/3/library/stdtypes.html#str

   (573) https://docs.python.org/3/library/typing.html#typing.Iterator

   (574) https://docs.python.org/3/library/stdtypes.html#bytes

   (575) https://docs.python.org/3/library/functions.html#int

   (576) https://docs.python.org/3/library/typing.html#typing.Tuple

   (577) https://docs.python.org/3/library/typing.html#typing.Union

   (578) https://docs.python.org/3/library/stdtypes.html#str

   (579) https://docs.python.org/3/library/stdtypes.html#bytes

   (580) https://docs.python.org/3/library/typing.html#typing.Dict

   (581) https://docs.python.org/3/library/stdtypes.html#str

   (582) https://docs.python.org/3/library/typing.html#typing.Any

   (583) https://docs.python.org/3/library/typing.html#typing.Iterator

   (584) https://docs.python.org/3/library/stdtypes.html#str

   (585) https://docs.python.org/3/library/typing.html#typing.Iterator

   (586) https://docs.python.org/3/library/stdtypes.html#bytes

   (587) https://docs.python.org/3/library/functions.html#int

   (588) https://docs.python.org/3/library/typing.html#typing.Union

   (589) https://docs.python.org/3/library/typing.html#typing.Dict

   (590) https://docs.python.org/3/library/stdtypes.html#str

   (591) https://docs.python.org/3/library/typing.html#typing.Union

   (592) https://docs.python.org/3/library/stdtypes.html#str

   (593) https://docs.python.org/3/library/typing.html#typing.List

   (594) https://docs.python.org/3/library/stdtypes.html#str

   (595) https://docs.python.org/3/library/typing.html#typing.Tuple

   (596) https://docs.python.org/3/library/stdtypes.html#str

   (597) https://docs.python.org/3/library/typing.html#typing.List

   (598) https://docs.python.org/3/library/typing.html#typing.Tuple

   (599) https://docs.python.org/3/library/stdtypes.html#str

   (600) https://docs.python.org/3/library/typing.html#typing.Union

   (601) https://docs.python.org/3/library/stdtypes.html#str

   (602) https://docs.python.org/3/library/typing.html#typing.List

   (603) https://docs.python.org/3/library/stdtypes.html#str

   (604) https://docs.python.org/3/library/typing.html#typing.Tuple

   (605) https://docs.python.org/3/library/stdtypes.html#str

   (606)
https://werkzeug.palletsprojects.com/en/2.1.x/wrappers/#werkzeug.wrappers.Response

   (607) https://docs.python.org/3/library/functions.html#callable

   (608) https://docs.python.org/3/library/stdtypes.html#dict

   (609) https://docs.python.org/3/library/stdtypes.html#str

   (610) https://docs.python.org/3/library/stdtypes.html#str

   (611) https://docs.python.org/3/library/stdtypes.html#str

   (612) https://docs.python.org/3/library/typing.html#typing.IO

   (613) https://docs.python.org/3/library/stdtypes.html#str

   (614) https://docs.python.org/3/library/stdtypes.html#str

   (615) https://docs.python.org/3/library/typing.html#typing.IO

   (616) https://docs.python.org/3/library/stdtypes.html#str

   (617) https://docs.python.org/3/library/typing.html#typing.Any

   (618) https://docs.python.org/3/library/typing.html#typing.Callable

   (619)
https://docs.python.org/3/library/datetime.html#datetime.timedelta

   (620) https://docs.python.org/3/library/stdtypes.html#str

   (621) https://docs.python.org/3/library/typing.html#typing.Any

   (622) https://docs.python.org/3/library/typing.html#typing.Callable

   (623) https://docs.python.org/3/library/typing.html#typing.Optional

   (624) https://docs.python.org/3/library/typing.html#typing.Union

   (625) https://docs.python.org/3/library/stdtypes.html#str

   (626) https://docs.python.org/3/library/stdtypes.html#bytes

   (627) https://docs.python.org/3/library/typing.html#typing.Dict

   (628) https://docs.python.org/3/library/stdtypes.html#str

   (629) https://docs.python.org/3/library/typing.html#typing.Any

   (630) https://docs.python.org/3/library/typing.html#typing.Iterator

   (631) https://docs.python.org/3/library/stdtypes.html#str

   (632) https://docs.python.org/3/library/typing.html#typing.Iterator

   (633) https://docs.python.org/3/library/stdtypes.html#bytes

   (634) https://docs.python.org/3/library/typing.html#typing.Tuple

   (635) https://docs.python.org/3/library/typing.html#typing.Union

   (636) https://docs.python.org/3/library/stdtypes.html#str

   (637) https://docs.python.org/3/library/stdtypes.html#bytes

   (638) https://docs.python.org/3/library/typing.html#typing.Dict

   (639) https://docs.python.org/3/library/stdtypes.html#str

   (640) https://docs.python.org/3/library/typing.html#typing.Any

   (641) https://docs.python.org/3/library/typing.html#typing.Iterator

   (642) https://docs.python.org/3/library/stdtypes.html#str

   (643) https://docs.python.org/3/library/typing.html#typing.Iterator

   (644) https://docs.python.org/3/library/stdtypes.html#bytes

   (645) https://docs.python.org/3/library/typing.html#typing.Union

   (646) https://docs.python.org/3/library/typing.html#typing.Dict

   (647) https://docs.python.org/3/library/stdtypes.html#str

   (648) https://docs.python.org/3/library/typing.html#typing.Union

   (649) https://docs.python.org/3/library/stdtypes.html#str

   (650) https://docs.python.org/3/library/typing.html#typing.List

   (651) https://docs.python.org/3/library/stdtypes.html#str

   (652) https://docs.python.org/3/library/typing.html#typing.Tuple

   (653) https://docs.python.org/3/library/stdtypes.html#str

   (654) https://docs.python.org/3/library/typing.html#typing.List

   (655) https://docs.python.org/3/library/typing.html#typing.Tuple

   (656) https://docs.python.org/3/library/stdtypes.html#str

   (657) https://docs.python.org/3/library/typing.html#typing.Union

   (658) https://docs.python.org/3/library/stdtypes.html#str

   (659) https://docs.python.org/3/library/typing.html#typing.List

   (660) https://docs.python.org/3/library/stdtypes.html#str

   (661) https://docs.python.org/3/library/typing.html#typing.Tuple

   (662) https://docs.python.org/3/library/stdtypes.html#str

   (663) https://docs.python.org/3/library/typing.html#typing.Tuple

   (664) https://docs.python.org/3/library/typing.html#typing.Union

   (665) https://docs.python.org/3/library/stdtypes.html#str

   (666) https://docs.python.org/3/library/stdtypes.html#bytes

   (667) https://docs.python.org/3/library/typing.html#typing.Dict

   (668) https://docs.python.org/3/library/stdtypes.html#str

   (669) https://docs.python.org/3/library/typing.html#typing.Any

   (670) https://docs.python.org/3/library/typing.html#typing.Iterator

   (671) https://docs.python.org/3/library/stdtypes.html#str

   (672) https://docs.python.org/3/library/typing.html#typing.Iterator

   (673) https://docs.python.org/3/library/stdtypes.html#bytes

   (674) https://docs.python.org/3/library/functions.html#int

   (675) https://docs.python.org/3/library/typing.html#typing.Tuple

   (676) https://docs.python.org/3/library/typing.html#typing.Union

   (677) https://docs.python.org/3/library/stdtypes.html#str

   (678) https://docs.python.org/3/library/stdtypes.html#bytes

   (679) https://docs.python.org/3/library/typing.html#typing.Dict

   (680) https://docs.python.org/3/library/stdtypes.html#str

   (681) https://docs.python.org/3/library/typing.html#typing.Any

   (682) https://docs.python.org/3/library/typing.html#typing.Iterator

   (683) https://docs.python.org/3/library/stdtypes.html#str

   (684) https://docs.python.org/3/library/typing.html#typing.Iterator

   (685) https://docs.python.org/3/library/stdtypes.html#bytes

   (686) https://docs.python.org/3/library/functions.html#int

   (687) https://docs.python.org/3/library/typing.html#typing.Union

   (688) https://docs.python.org/3/library/typing.html#typing.Dict

   (689) https://docs.python.org/3/library/stdtypes.html#str

   (690) https://docs.python.org/3/library/typing.html#typing.Union

   (691) https://docs.python.org/3/library/stdtypes.html#str

   (692) https://docs.python.org/3/library/typing.html#typing.List

   (693) https://docs.python.org/3/library/stdtypes.html#str

   (694) https://docs.python.org/3/library/typing.html#typing.Tuple

   (695) https://docs.python.org/3/library/stdtypes.html#str

   (696) https://docs.python.org/3/library/typing.html#typing.List

   (697) https://docs.python.org/3/library/typing.html#typing.Tuple

   (698) https://docs.python.org/3/library/stdtypes.html#str

   (699) https://docs.python.org/3/library/typing.html#typing.Union

   (700) https://docs.python.org/3/library/stdtypes.html#str

   (701) https://docs.python.org/3/library/typing.html#typing.List

   (702) https://docs.python.org/3/library/stdtypes.html#str

   (703) https://docs.python.org/3/library/typing.html#typing.Tuple

   (704) https://docs.python.org/3/library/stdtypes.html#str

   (705) https://docs.python.org/3/library/functions.html#bool

   (706) https://docs.python.org/3/library/functions.html#bool

   (707) https://docs.python.org/3/library/stdtypes.html#str

   (708) https://docs.python.org/3/library/typing.html#typing.Any

   (709) https://docs.python.org/3/library/typing.html#typing.Callable

   (710) https://docs.python.org/3/library/typing.html#typing.Any

   (711) https://docs.python.org/3/library/typing.html#typing.Union

   (712) https://docs.python.org/3/library/typing.html#typing.Type

   (713) https://docs.python.org/3/library/exceptions.html#Exception

   (714) https://docs.python.org/3/library/functions.html#int

   (715) https://docs.python.org/3/library/stdtypes.html#dict

   (716) https://docs.python.org/3/library/stdtypes.html#str

   (717) https://docs.python.org/3/library/typing.html#typing.Any

   (718)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Rule

   (719) https://docs.python.org/3/library/typing.html#typing.Callable

   (720) https://docs.python.org/3/library/typing.html#typing.Optional

   (721) https://docs.python.org/3/library/stdtypes.html#str

   (722) https://docs.python.org/3/library/typing.html#typing.Optional

   (723) https://docs.python.org/3/library/functions.html#int

   (724) https://docs.python.org/3/library/typing.html#typing.Optional

   (725) https://docs.python.org/3/library/functions.html#bool

   (726) https://docs.python.org/3/library/functions.html#bool

   (727) https://docs.python.org/3/library/typing.html#typing.Any

   (728)
https://werkzeug.palletsprojects.com/en/2.1.x/serving/#werkzeug.serving.run_simple

   (729) https://docs.python.org/3/library/stdtypes.html#str

   (730) https://docs.python.org/3/library/functions.html#bool

   (731)
https://docs.python.org/3/library/datetime.html#datetime.timedelta

   (732) https://docs.python.org/3/library/stdtypes.html#str

   (733) https://docs.python.org/3/library/typing.html#typing.Callable

   (734) https://docs.python.org/3/library/typing.html#typing.Callable

   (735) https://docs.python.org/3/library/stdtypes.html#str

   (736) https://docs.python.org/3/library/typing.html#typing.Optional

   (737) https://docs.python.org/3/library/exceptions.html#BaseException

   (738) https://docs.python.org/3/library/functions.html#bool

   (739) https://docs.python.org/3/library/stdtypes.html#str

   (740) https://docs.python.org/3/library/stdtypes.html#str

   (741) https://docs.python.org/3/library/typing.html#typing.Callable

   (742) https://docs.python.org/3/library/typing.html#typing.Optional

   (743) https://docs.python.org/3/library/exceptions.html#BaseException

   (744) https://docs.python.org/3/library/typing.html#typing.Callable

   (745) https://docs.python.org/3/library/typing.html#typing.Optional

   (746) https://docs.python.org/3/library/exceptions.html#BaseException

   (747) https://docs.python.org/3/library/typing.html#typing.Callable

   (748) https://docs.python.org/3/library/typing.html#typing.Optional

   (749) https://docs.python.org/3/library/exceptions.html#BaseException

   (750) https://docs.python.org/3/library/typing.html#typing.Callable

   (751) https://docs.python.org/3/library/typing.html#typing.Optional

   (752) https://docs.python.org/3/library/exceptions.html#BaseException

   (753) https://docs.python.org/3/library/typing.html#typing.Optional

   (754) https://docs.python.org/3/library/stdtypes.html#str

   (755) https://docs.python.org/3/library/typing.html#typing.Callable

   (756) https://docs.python.org/3/library/typing.html#typing.Callable

   (757) https://docs.python.org/3/library/typing.html#typing.Any

   (758) https://docs.python.org/3/library/typing.html#typing.Callable

   (759) https://docs.python.org/3/library/typing.html#typing.Any

   (760) https://docs.python.org/3/library/typing.html#typing.Optional

   (761) https://docs.python.org/3/library/stdtypes.html#str

   (762) https://docs.python.org/3/library/typing.html#typing.Callable

   (763) https://docs.python.org/3/library/typing.html#typing.Callable

   (764) https://docs.python.org/3/library/typing.html#typing.Any

   (765) https://docs.python.org/3/library/typing.html#typing.Callable

   (766) https://docs.python.org/3/library/typing.html#typing.Any

   (767) https://docs.python.org/3/library/typing.html#typing.Optional

   (768) https://docs.python.org/3/library/stdtypes.html#str

   (769) https://docs.python.org/3/library/typing.html#typing.Callable

   (770) https://docs.python.org/3/library/typing.html#typing.Callable

   (771) https://docs.python.org/3/library/functions.html#bool

   (772) https://docs.python.org/3/library/typing.html#typing.Callable

   (773) https://docs.python.org/3/library/functions.html#bool

   (774) https://docs.python.org/3/library/functions.html#bool

   (775) https://docs.python.org/3/library/typing.html#typing.Any

   (776)
https://click.palletsprojects.com/en/8.1.x/api/#click.testing.CliRunner

   (777) https://docs.python.org/3/library/functions.html#bool

   (778) https://docs.python.org/3/library/typing.html#typing.Any

   (779)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.EnvironBuilder

   (780) https://docs.python.org/3/library/typing.html#typing.Any

   (781)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.EnvironBuilder

   (782) https://docs.python.org/3/library/typing.html#typing.Any

   (783)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.EnvironBuilder

   (784) https://docs.python.org/3/library/exceptions.html#Exception

   (785) https://docs.python.org/3/library/functions.html#bool

   (786) https://docs.python.org/3/library/stdtypes.html#dict

   (787) https://docs.python.org/3/library/exceptions.html#Exception

   (788) https://docs.python.org/3/library/stdtypes.html#str

   (789) https://docs.python.org/3/library/stdtypes.html#dict

   (790) https://docs.python.org/3/library/stdtypes.html#str

   (791) https://docs.python.org/3/library/typing.html#typing.Callable

   (792) https://docs.python.org/3/library/stdtypes.html#str

   (793) https://docs.python.org/3/library/stdtypes.html#dict

   (794) https://docs.python.org/3/library/typing.html#typing.Callable

   (795) https://docs.python.org/3/library/stdtypes.html#str

   (796) https://docs.python.org/3/library/stdtypes.html#dict

   (797)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Map

   (798)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Map

   (799)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Rule

   (800) https://docs.python.org/3/library/typing.html#typing.Callable

   (801) https://docs.python.org/3/library/typing.html#typing.Optional

   (802) https://docs.python.org/3/library/stdtypes.html#str

   (803) https://docs.python.org/3/library/typing.html#typing.Optional

   (804) https://docs.python.org/3/library/stdtypes.html#dict

   (805) https://docs.python.org/3/library/typing.html#typing.Callable

   (806) https://docs.python.org/3/library/typing.html#typing.Optional

   (807) https://docs.python.org/3/library/stdtypes.html#str

   (808) https://docs.python.org/3/library/typing.html#typing.Optional

   (809) https://docs.python.org/3/library/stdtypes.html#dict

   (810) https://docs.python.org/3/library/stdtypes.html#str

   (811) https://docs.python.org/3/library/stdtypes.html#dict

   (812) https://docs.python.org/3/library/typing.html#typing.Callable

   (813) https://docs.python.org/3/library/typing.html#typing.Any

2.1.2 Blueprint Objects
-----------------------

 -- Class: flask.Blueprint (name, import_name, static_folder=None,
          static_url_path=None, template_folder=None, url_prefix=None,
          subdomain=None, url_defaults=None, root_path=None,
          cli_group=<object object>)

     Represents a blueprint, a collection of routes and other
     app-related functions that can be registered on a real application
     later.

     A blueprint is an object that allows defining application functions
     without requiring an application object ahead of time.  It uses the
     same decorators as Flask, but defers the need for an
     application by recording them for later registration.

     Decorating a function with a blueprint creates a deferred function
     that is called with BlueprintSetupState when the
     blueprint is registered on an application.

     See Modular Applications with Blueprints for more
     information.

     Parameters:

        * ‘name’ (str(1)) – The name of the blueprint.  Will be
          prepended to each endpoint name.

        * ‘import_name’ (str(2)) – The name of the blueprint package,
          usually ‘__name__’.  This helps locate the ‘root_path’ for the
          blueprint.

        * ‘static_folder’ (Optional(3)‘[’Union(4)‘[’str(5)‘,
          ’os.PathLike(6)‘]’‘]’) – A folder with static files that
          should be served by the blueprint’s static route.  The path is
          relative to the blueprint’s root path.  Blueprint static files
          are disabled by default.

        * ‘static_url_path’ (Optional(7)‘[’str(8)‘]’) – The url to serve
          static files from.  Defaults to ‘static_folder’.  If the
          blueprint does not have a ‘url_prefix’, the app’s static route
          will take precedence, and the blueprint’s static files won’t
          be accessible.

        * ‘template_folder’ (Optional(9)‘[’str(10)‘]’) – A folder with
          templates that should be added to the app’s template search
          path.  The path is relative to the blueprint’s root path.
          Blueprint templates are disabled by default.  Blueprint
          templates have a lower precedence than those in the app’s
          templates folder.

        * ‘url_prefix’ (Optional(11)‘[’str(12)‘]’) – A path to prepend
          to all of the blueprint’s URLs, to make them distinct from the
          rest of the app’s routes.

        * ‘subdomain’ (Optional(13)‘[’str(14)‘]’) – A subdomain that
          blueprint routes will match on by default.

        * ‘url_defaults’ (Optional(15)‘[’dict(16)‘]’) – A dict of
          default values that blueprint routes will receive by default.

        * ‘root_path’ (Optional(17)‘[’str(18)‘]’) – By default, the
          blueprint will automatically set this based on ‘import_name’.
          In certain situations this automatic detection can fail, so
          the path can be specified manually instead.

        * ‘cli_group’ (Optional(19)‘[’str(20)‘]’) –

     Changed in version 1.1.0: Blueprints have a ‘cli’ group to register
     nested CLI commands.  The ‘cli_group’ parameter controls the name
     of the group under the ‘flask’ command.

     New in version 0.7.

      -- Method: add_app_template_filter (f, name=None)

          Register a custom template filter, available application wide.
          Like Flask.add_template_filter() but for a
          blueprint.  Works exactly like the
          app_template_filter() decorator.

          Parameters:

             * ‘name’ (Optional(21)‘[’str(22)‘]’) – the optional name of
               the filter, otherwise the function name will be used.

             * ‘f’ (Callable(23)‘[’‘[’‘...’‘]’‘, ’Any(24)‘]’) –

          Return type: None

      -- Method: add_app_template_global (f, name=None)

          Register a custom template global, available application wide.
          Like Flask.add_template_global() but for a
          blueprint.  Works exactly like the
          app_template_global() decorator.

          New in version 0.10.

          Parameters:

             * ‘name’ (Optional(25)‘[’str(26)‘]’) – the optional name of
               the global, otherwise the function name will be used.

             * ‘f’ (Callable(27)‘[’‘[’‘...’‘]’‘, ’Any(28)‘]’) –

          Return type: None

      -- Method: add_app_template_test (f, name=None)

          Register a custom template test, available application wide.
          Like Flask.add_template_test() but for a
          blueprint.  Works exactly like the app_template_test()
          decorator.

          New in version 0.10.

          Parameters:

             * ‘name’ (Optional(29)‘[’str(30)‘]’) – the optional name of
               the test, otherwise the function name will be used.

             * ‘f’ (Callable(31)‘[’‘[’‘...’‘]’‘, ’bool(32)‘]’) –

          Return type: None

      -- Method: add_url_rule (rule, endpoint=None, view_func=None,
               provide_automatic_options=None, **options)

          Like Flask.add_url_rule() but for a blueprint.  The
          endpoint for the url_for() function is prefixed
          with the name of the blueprint.

          Parameters:

             * ‘rule’ (str(33)) –

             * ‘endpoint’ (Optional(34)‘[’str(35)‘]’) –

             * ‘view_func’ (Optional(36)‘[’Callable(37)‘]’) –

             * ‘provide_automatic_options’ (Optional(38)‘[’bool(39)‘]’)
               –

             * ‘options’ (Any(40)) –

          Return type: None

      -- Method: after_app_request (f)

          Like Flask.after_request() but for a blueprint.
          Such a function is executed after each request, even if
          outside of the blueprint.

          Parameters: ‘f’ (Callable(41)‘[’‘[’Response.‘]’‘,
          ’Response.‘]’) –

          Return type: Callable(42)[[see Response], see
          Response]

      -- Method: after_request (f)

          Register a function to run after each request to this object.

          The function is called with the response object, and must
          return a response object.  This allows the functions to modify
          or replace the response before it is sent.

          If a function raises an exception, any remaining
          ‘after_request’ functions will not be called.  Therefore, this
          should not be used for actions that must execute, such as to
          close resources.  Use teardown_request() for that.

          Parameters: ‘f’ (Callable(43)‘[’‘[’Response.‘]’‘,
          ’Response.‘]’) –

          Return type: Callable(44)[[see Response], see
          Response]

      -- Attribute: after_request_funcs: t.Dict[AppOrBlueprintKey,
               t.List[AfterRequestCallable]]

          A data structure of functions to call at the end of each
          request, in the format ‘{scope: [functions]}’.  The ‘scope’
          key is the name of a blueprint the functions are active for,
          or ‘None’ for all requests.

          To register a function, use the after_request().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: app_context_processor (f)

          Like Flask.context_processor() but for a
          blueprint.  Such a function is executed each request, even if
          outside of the blueprint.

          Parameters: ‘f’ (Callable(45)‘[’‘[’‘]’‘, ’Dict(46)‘[’str(47)‘,
          ’Any(48)‘]’‘]’) –

          Return type: Callable(49)[[], Dict(50)[str(51), Any(52)]]

      -- Method: app_errorhandler (code)

          Like Flask.errorhandler() but for a blueprint.
          This handler is used for all requests, even if outside of the
          blueprint.

          Parameters: ‘code’ (Union(53)‘[’Type(54)‘[’Exception(55)‘]’‘,
          ’int(56)‘]’) –

          Return type: Callable(57)

      -- Method: app_template_filter (name=None)

          Register a custom template filter, available application wide.
          Like Flask.template_filter() but for a blueprint.

          Parameters: ‘name’ (Optional(58)‘[’str(59)‘]’) – the optional
          name of the filter, otherwise the function name will be used.

          Return type: Callable(60)[[Callable(61)[[…], Any(62)]],
          Callable(63)[[…], Any(64)]]

      -- Method: app_template_global (name=None)

          Register a custom template global, available application wide.
          Like Flask.template_global() but for a blueprint.

          New in version 0.10.

          Parameters: ‘name’ (Optional(65)‘[’str(66)‘]’) – the optional
          name of the global, otherwise the function name will be used.

          Return type: Callable(67)[[Callable(68)[[…], Any(69)]],
          Callable(70)[[…], Any(71)]]

      -- Method: app_template_test (name=None)

          Register a custom template test, available application wide.
          Like Flask.template_test() but for a blueprint.

          New in version 0.10.

          Parameters: ‘name’ (Optional(72)‘[’str(73)‘]’) – the optional
          name of the test, otherwise the function name will be used.

          Return type: Callable(74)[[Callable(75)[[…], bool(76)]],
          Callable(77)[[…], bool(78)]]

      -- Method: app_url_defaults (f)

          Same as url_defaults() but application wide.

          Parameters: ‘f’ (Callable(79)‘[’‘[’str(80)‘, ’dict(81)‘]’‘,
          ’‘None’‘]’) –

          Return type: Callable(82)[[str(83), dict(84)], None]

      -- Method: app_url_value_preprocessor (f)

          Same as url_value_preprocessor() but application
          wide.

          Parameters: ‘f’ (Callable(85)‘[’‘[’Optional(86)‘[’str(87)‘]’‘,
          ’Optional(88)‘[’dict(89)‘]’‘]’‘, ’‘None’‘]’) –

          Return type: Callable(90)[[Optional(91)[str(92)],
          Optional(93)[dict(94)]], None]

      -- Method: before_app_first_request (f)

          Like Flask.before_first_request().  Such a function
          is executed before the first request to the application.

          Parameters: ‘f’ (Callable(95)‘[’‘[’‘]’‘, ’‘None’‘]’) –

          Return type: Callable(96)[[], None]

      -- Method: before_app_request (f)

          Like Flask.before_request().  Such a function is
          executed before each request, even if outside of a blueprint.

          Parameters: ‘f’ (Callable(97)‘[’‘[’‘]’‘,
          ’Optional(98)‘[’Union(99)‘[’Response.‘, ’str(100)‘,
          ’bytes(101)‘, ’Dict(102)‘[’str(103)‘, ’Any(104)‘]’‘,
          ’Iterator(105)‘[’str(106)‘]’‘,
          ’Iterator(107)‘[’bytes(108)‘]’‘,
          ’Tuple(109)‘[’Union(110)‘[’Response.‘, ’str(111)‘,
          ’bytes(112)‘, ’Dict(113)‘[’str(114)‘, ’Any(115)‘]’‘,
          ’Iterator(116)‘[’str(117)‘]’‘,
          ’Iterator(118)‘[’bytes(119)‘]’‘]’‘, ’Union(120)‘[’‘Headers’‘,
          ’Dict(121)‘[’str(122)‘, ’Union(123)‘[’str(124)‘,
          ’List(125)‘[’str(126)‘]’‘, ’Tuple(127)‘[’str(128)‘,
          ’‘...’‘]’‘]’‘]’‘, ’List(129)‘[’Tuple(130)‘[’str(131)‘,
          ’Union(132)‘[’str(133)‘, ’List(134)‘[’str(135)‘]’‘,
          ’Tuple(136)‘[’str(137)‘, ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘,
          ’Tuple(138)‘[’Union(139)‘[’Response.‘, ’str(140)‘,
          ’bytes(141)‘, ’Dict(142)‘[’str(143)‘, ’Any(144)‘]’‘,
          ’Iterator(145)‘[’str(146)‘]’‘,
          ’Iterator(147)‘[’bytes(148)‘]’‘]’‘, ’int(149)‘]’‘,
          ’Tuple(150)‘[’Union(151)‘[’Response.‘, ’str(152)‘,
          ’bytes(153)‘, ’Dict(154)‘[’str(155)‘, ’Any(156)‘]’‘,
          ’Iterator(157)‘[’str(158)‘]’‘,
          ’Iterator(159)‘[’bytes(160)‘]’‘]’‘, ’int(161)‘,
          ’Union(162)‘[’‘Headers’‘, ’Dict(163)‘[’str(164)‘,
          ’Union(165)‘[’str(166)‘, ’List(167)‘[’str(168)‘]’‘,
          ’Tuple(169)‘[’str(170)‘, ’‘...’‘]’‘]’‘]’‘,
          ’List(171)‘[’Tuple(172)‘[’str(173)‘, ’Union(174)‘[’str(175)‘,
          ’List(176)‘[’str(177)‘]’‘, ’Tuple(178)‘[’str(179)‘,
          ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘, ’‘WSGIApplication’‘]’‘]’‘]’) –

          Return type: Callable(180)[[], Optional(181)[Union(182)[see
          Response, str(183), bytes(184), Dict(185)[str(186),
          Any(187)], Iterator(188)[str(189)], Iterator(190)[bytes(191)],
          Tuple(192)[Union(193)[see Response, str(194),
          bytes(195), Dict(196)[str(197), Any(198)],
          Iterator(199)[str(200)], Iterator(201)[bytes(202)]],
          Union(203)[Headers, Dict(204)[str(205), Union(206)[str(207),
          List(208)[str(209)], Tuple(210)[str(211), …]]],
          List(212)[Tuple(213)[str(214), Union(215)[str(216),
          List(217)[str(218)], Tuple(219)[str(220), …]]]]]],
          Tuple(221)[Union(222)[see Response, str(223),
          bytes(224), Dict(225)[str(226), Any(227)],
          Iterator(228)[str(229)], Iterator(230)[bytes(231)]],
          int(232)], Tuple(233)[Union(234)[see Response, str(235),
          bytes(236), Dict(237)[str(238), Any(239)],
          Iterator(240)[str(241)], Iterator(242)[bytes(243)]], int(244),
          Union(245)[Headers, Dict(246)[str(247), Union(248)[str(249),
          List(250)[str(251)], Tuple(252)[str(253), …]]],
          List(254)[Tuple(255)[str(256), Union(257)[str(258),
          List(259)[str(260)], Tuple(261)[str(262), …]]]]]],
          WSGIApplication]]]

      -- Method: before_request (f)

          Register a function to run before each request.

          For example, this can be used to open a database connection,
          or to load the logged in user from the session.

               @app.before_request
               def load_user():
                   if "user_id" in session:
                       g.user = db.session.get(session["user_id"])

          The function will be called without any arguments.  If it
          returns a non-‘None’ value, the value is handled as if it was
          the return value from the view, and further request handling
          is stopped.

          Parameters: ‘f’ (Callable(263)‘[’‘[’‘]’‘,
          ’Optional(264)‘[’Union(265)‘[’Response.‘,
          ’str(266)‘, ’bytes(267)‘, ’Dict(268)‘[’str(269)‘,
          ’Any(270)‘]’‘, ’Iterator(271)‘[’str(272)‘]’‘,
          ’Iterator(273)‘[’bytes(274)‘]’‘,
          ’Tuple(275)‘[’Union(276)‘[’Response.‘, ’str(277)‘,
          ’bytes(278)‘, ’Dict(279)‘[’str(280)‘, ’Any(281)‘]’‘,
          ’Iterator(282)‘[’str(283)‘]’‘,
          ’Iterator(284)‘[’bytes(285)‘]’‘]’‘, ’Union(286)‘[’‘Headers’‘,
          ’Dict(287)‘[’str(288)‘, ’Union(289)‘[’str(290)‘,
          ’List(291)‘[’str(292)‘]’‘, ’Tuple(293)‘[’str(294)‘,
          ’‘...’‘]’‘]’‘]’‘, ’List(295)‘[’Tuple(296)‘[’str(297)‘,
          ’Union(298)‘[’str(299)‘, ’List(300)‘[’str(301)‘]’‘,
          ’Tuple(302)‘[’str(303)‘, ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘,
          ’Tuple(304)‘[’Union(305)‘[’Response.‘, ’str(306)‘,
          ’bytes(307)‘, ’Dict(308)‘[’str(309)‘, ’Any(310)‘]’‘,
          ’Iterator(311)‘[’str(312)‘]’‘,
          ’Iterator(313)‘[’bytes(314)‘]’‘]’‘, ’int(315)‘]’‘,
          ’Tuple(316)‘[’Union(317)‘[’Response.‘, ’str(318)‘,
          ’bytes(319)‘, ’Dict(320)‘[’str(321)‘, ’Any(322)‘]’‘,
          ’Iterator(323)‘[’str(324)‘]’‘,
          ’Iterator(325)‘[’bytes(326)‘]’‘]’‘, ’int(327)‘,
          ’Union(328)‘[’‘Headers’‘, ’Dict(329)‘[’str(330)‘,
          ’Union(331)‘[’str(332)‘, ’List(333)‘[’str(334)‘]’‘,
          ’Tuple(335)‘[’str(336)‘, ’‘...’‘]’‘]’‘]’‘,
          ’List(337)‘[’Tuple(338)‘[’str(339)‘, ’Union(340)‘[’str(341)‘,
          ’List(342)‘[’str(343)‘]’‘, ’Tuple(344)‘[’str(345)‘,
          ’‘...’‘]’‘]’‘]’‘]’‘]’‘]’‘, ’‘WSGIApplication’‘]’‘]’‘]’) –

          Return type: Callable(346)[[], Optional(347)[Union(348)[see
          Response, str(349), bytes(350), Dict(351)[str(352),
          Any(353)], Iterator(354)[str(355)], Iterator(356)[bytes(357)],
          Tuple(358)[Union(359)[see Response, str(360),
          bytes(361), Dict(362)[str(363), Any(364)],
          Iterator(365)[str(366)], Iterator(367)[bytes(368)]],
          Union(369)[Headers, Dict(370)[str(371), Union(372)[str(373),
          List(374)[str(375)], Tuple(376)[str(377), …]]],
          List(378)[Tuple(379)[str(380), Union(381)[str(382),
          List(383)[str(384)], Tuple(385)[str(386), …]]]]]],
          Tuple(387)[Union(388)[see Response, str(389),
          bytes(390), Dict(391)[str(392), Any(393)],
          Iterator(394)[str(395)], Iterator(396)[bytes(397)]],
          int(398)], Tuple(399)[Union(400)[see Response, str(401),
          bytes(402), Dict(403)[str(404), Any(405)],
          Iterator(406)[str(407)], Iterator(408)[bytes(409)]], int(410),
          Union(411)[Headers, Dict(412)[str(413), Union(414)[str(415),
          List(416)[str(417)], Tuple(418)[str(419), …]]],
          List(420)[Tuple(421)[str(422), Union(423)[str(424),
          List(425)[str(426)], Tuple(427)[str(428), …]]]]]],
          WSGIApplication]]]

      -- Attribute: before_request_funcs: t.Dict[AppOrBlueprintKey,
               t.List[BeforeRequestCallable]]

          A data structure of functions to call at the beginning of each
          request, in the format ‘{scope: [functions]}’.  The ‘scope’
          key is the name of a blueprint the functions are active for,
          or ‘None’ for all requests.

          To register a function, use the before_request().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Attribute: cli

          The Click command group for registering CLI commands for this
          object.  The commands are available from the ‘flask’ command
          once the application has been discovered and blueprints have
          been registered.

      -- Method: context_processor (f)

          Registers a template context processor function.

          Parameters: ‘f’ (Callable(429)‘[’‘[’‘]’‘,
          ’Dict(430)‘[’str(431)‘, ’Any(432)‘]’‘]’) –

          Return type: Callable(433)[[], Dict(434)[str(435), Any(436)]]

      -- Method: delete (rule, **options)

          Shortcut for route() with ‘methods=["DELETE"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(437)) –

             * ‘options’ (Any(438)) –

          Return type: Callable(439)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: endpoint (endpoint)

          Decorate a view function to register it for the given
          endpoint.  Used if a rule is added without a ‘view_func’ with
          add_url_rule().

               app.add_url_rule("/ex", endpoint="example")

               @app.endpoint("example")
               def example():
                   ...

          Parameters: ‘endpoint’ (str(440)) – The endpoint name to
          associate with the view function.

          Return type: Callable(441)

      -- Attribute: error_handler_spec: t.Dict[AppOrBlueprintKey,
               t.Dict[t.Optional[int(442)],
               t.Dict[t.Type[Exception(443)], 'ErrorHandlerCallable']]]

          A data structure of registered error handlers, in the format
          ‘{scope: {code: {class: handler}}}`’.  The ‘scope’ key is the
          name of a blueprint the handlers are active for, or ‘None’ for
          all requests.  The ‘code’ key is the HTTP status code for
          ‘HTTPException’, or ‘None’ for other exceptions.  The
          innermost dictionary maps exception classes to handler
          functions.

          To register an error handler, use the errorhandler()
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: errorhandler (code_or_exception)

          Register a function to handle errors by code or exception
          class.

          A decorator that is used to register a function given an error
          code.  Example:

               @app.errorhandler(404)
               def page_not_found(error):
                   return 'This page does not exist', 404

          You can also register handlers for arbitrary exceptions:

               @app.errorhandler(DatabaseError)
               def special_exception_handler(error):
                   return 'Database connection failed', 500

          New in version 0.7: Use register_error_handler().
          instead of modifying error_handler_spec directly,
          for application wide error handlers.

          New in version 0.7: One can now additionally also register
          custom exception types that do not necessarily have to be a
          subclass of the HTTPException(444) class.

          Parameters: ‘code_or_exception’
          (Union(445)‘[’Type(446)‘[’Exception(447)‘]’‘, ’int(448)‘]’) –
          the code as integer for the handler, or an arbitrary exception

          Return type: Callable(449)[[ErrorHandlerCallable],
          ErrorHandlerCallable]

      -- Method: get (rule, **options)

          Shortcut for route() with ‘methods=["GET"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(450)) –

             * ‘options’ (Any(451)) –

          Return type: Callable(452)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: get_send_file_max_age (filename)

          Used by send_file() to determine the ‘max_age’
          cache value for a given file path if it wasn’t passed.

          By default, this returns SEND_FILE_MAX_AGE_DEFAULT.
          from the configuration of current_app.  This
          defaults to ‘None’, which tells the browser to use conditional
          requests instead of a timed cache, which is usually
          preferable.

          Changed in version 2.0: The default configuration is ‘None’
          instead of 12 hours.

          New in version 0.9.

          Parameters: ‘filename’ (Optional(453)‘[’str(454)‘]’) –

          Return type: Optional(455)[int(456)]

      -- Property: has_static_folder: bool(457)

          ‘True’ if static_folder is set.

          New in version 0.5.

      -- Attribute: import_name

          The name of the package or module that this object belongs to.
          Do not change this once it is set by the constructor.

      -- Property: jinja_loader:
               Optional[jinja2.loaders.FileSystemLoader(458)]

          The Jinja loader for this object’s templates.  By default this
          is a class jinja2.loaders.FileSystemLoader(459) to
          template_folder if it is set.

          New in version 0.5.

      -- Attribute: json_decoder:
               Optional[Type[json.decoder.JSONDecoder]] = None

          Blueprint local JSON decoder class to use.  Set to ‘None’ to
          use the app’s json_decoder.

      -- Attribute: json_encoder:
               Optional[Type[json.encoder.JSONEncoder]] = None

          Blueprint local JSON encoder class to use.  Set to ‘None’ to
          use the app’s json_encoder.

      -- Method: make_setup_state (app, options,
               first_registration=False)

          Creates an instance of BlueprintSetupState().
          object that is later passed to the register callback
          functions.  Subclasses can override this to return a subclass
          of the setup state.

          Parameters:

             * ‘app’ (see Flask) –

             * ‘options’ (dict(460)) –

             * ‘first_registration’ (bool(461)) –

          Return type: flask.blueprints.BlueprintSetupState.

      -- Method: open_resource (resource, mode='rb')

          Open a resource file relative to root_path for
          reading.

          For example, if the file ‘schema.sql’ is next to the file
          ‘app.py’ where the ‘Flask’ app is defined, it can be opened
          with:

               with app.open_resource("schema.sql") as f:
                   conn.executescript(f.read())

          Parameters:

             * ‘resource’ (str(462)) – Path to the resource relative to
               root_path.

             * ‘mode’ (str(463)) – Open the file in this mode.  Only
               reading is supported, valid values are “r” (or “rt”) and
               “rb”.

          Return type: IO(464)

      -- Method: patch (rule, **options)

          Shortcut for route() with ‘methods=["PATCH"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(465)) –

             * ‘options’ (Any(466)) –

          Return type: Callable(467)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: post (rule, **options)

          Shortcut for route() with ‘methods=["POST"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(468)) –

             * ‘options’ (Any(469)) –

          Return type: Callable(470)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: put (rule, **options)

          Shortcut for route() with ‘methods=["PUT"]’.

          New in version 2.0.

          Parameters:

             * ‘rule’ (str(471)) –

             * ‘options’ (Any(472)) –

          Return type: Callable(473)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: record (func)

          Registers a function that is called when the blueprint is
          registered on the application.  This function is called with
          the state as argument as returned by the
          make_setup_state() method.

          Parameters: ‘func’ (Callable(474)) –

          Return type: None

      -- Method: record_once (func)

          Works like record() but wraps the function in
          another function that will ensure the function is only called
          once.  If the blueprint is registered a second time on the
          application, the function passed is not called.

          Parameters: ‘func’ (Callable(475)) –

          Return type: None

      -- Method: register (app, options)

          Called by Flask.register_blueprint() to register
          all views and callbacks registered on the blueprint with the
          application.  Creates a BlueprintSetupState and
          calls each record() callback with it.

          Parameters:

             * ‘app’ (see Flask) – The application this blueprint
               is being registered with.

             * ‘options’ (dict(476)) – Keyword arguments forwarded from
               register_blueprint().

          Return type: None

          Changed in version 2.0.1: Nested blueprints are registered
          with their dotted name.  This allows different blueprints with
          the same name to be nested at different locations.

          Changed in version 2.0.1: The ‘name’ option can be used to
          change the (pre-dotted) name the blueprint is registered with.
          This allows the same blueprint to be registered multiple times
          with unique names for ‘url_for’.

          Changed in version 2.0.1: Registering the same blueprint with
          the same name multiple times is deprecated and will become an
          error in Flask 2.1.

      -- Method: register_blueprint (blueprint, **options)

          Register a Blueprint on this blueprint.  Keyword
          arguments passed to this method will override the defaults set
          on the blueprint.

          Changed in version 2.0.1: The ‘name’ option can be used to
          change the (pre-dotted) name the blueprint is registered with.
          This allows the same blueprint to be registered multiple times
          with unique names for ‘url_for’.

          New in version 2.0.

          Parameters:

             * ‘blueprint’ (see flask.blueprints.Blueprint) –

             * ‘options’ (Any(477)) –

          Return type: None

      -- Method: register_error_handler (code_or_exception, f)

          Alternative error attach function to the errorhandler()
          decorator that is more straightforward to use for non
          decorator usage.

          New in version 0.7.

          Parameters:

             * ‘code_or_exception’
               (Union(478)‘[’Type(479)‘[’Exception(480)‘]’‘,
               ’int(481)‘]’) –

             * ‘f’ (‘ErrorHandlerCallable’) –

          Return type: None

      -- Attribute: root_path

          Absolute path to the package on the filesystem.  Used to look
          up resources contained in the package.

      -- Method: route (rule, **options)

          Decorate a view function to register it with the given URL
          rule and options.  Calls add_url_rule(), which has
          more details about the implementation.

               @app.route("/")
               def index():
                   return "Hello, World!"

          See URL Route Registrations.

          The endpoint name for the route defaults to the name of the
          view function if the ‘endpoint’ parameter isn’t passed.

          The ‘methods’ parameter defaults to ‘["GET"]’.  ‘HEAD’ and
          ‘OPTIONS’ are added automatically.

          Parameters:

             * ‘rule’ (str(482)) – The URL rule string.

             * ‘options’ (Any(483)) – Extra options passed to the
               Rule(484) object.

          Return type: Callable(485)[[flask.scaffold.F],
          flask.scaffold.F]

      -- Method: send_static_file (filename)

          The view function used to serve files from
          static_folder.  A route is automatically registered for
          this view at static_url_path if
          static_folder is set.

          New in version 0.5.

          Parameters: ‘filename’ (str(486)) –

          Return type: Response.

      -- Property: static_folder: Optional[str(487)]

          The absolute path to the configured static folder.  ‘None’ if
          no static folder is set.

      -- Property: static_url_path: Optional[str(488)]

          The URL prefix that the static route will be accessible from.

          If it was not configured during init, it is derived from
          static_folder.

      -- Method: teardown_app_request (f)

          Like Flask.teardown_request() but for a blueprint.
          Such a function is executed when tearing down each request,
          even if outside of the blueprint.

          Parameters: ‘f’
          (Callable(489)‘[’‘[’Optional(490)‘[’BaseException(491)‘]’‘]’‘,
          ’‘None’‘]’) –

          Return type:
          Callable(492)[[Optional(493)[BaseException(494)]], None]

      -- Method: teardown_request (f)

          Register a function to be run at the end of each request,
          regardless of whether there was an exception or not.  These
          functions are executed when the request context is popped,
          even if not an actual request was performed.

          Example:

               ctx = app.test_request_context()
               ctx.push()
               ...
               ctx.pop()

          When ‘ctx.pop()’ is executed in the above example, the
          teardown functions are called just before the request context
          moves from the stack of active contexts.  This becomes
          relevant if you are using such constructs in tests.

          Teardown functions must avoid raising exceptions.  If they
          execute code that might fail they will have to surround the
          execution of that code with try/except statements and log any
          errors.

          When a teardown function was called because of an exception it
          will be passed an error object.

          The return values of teardown functions are ignored.

               Debug Note: In debug mode Flask will not tear down a
               request on an exception immediately.  Instead it will
               keep it alive so that the interactive debugger can still
               access it.  This behavior can be controlled by the
               ‘PRESERVE_CONTEXT_ON_EXCEPTION’ configuration variable.

          Parameters: ‘f’
          (Callable(495)‘[’‘[’Optional(496)‘[’BaseException(497)‘]’‘]’‘,
          ’‘None’‘]’) –

          Return type:
          Callable(498)[[Optional(499)[BaseException(500)]], None]

      -- Attribute: teardown_request_funcs: t.Dict[AppOrBlueprintKey,
               t.List[TeardownCallable]]

          A data structure of functions to call at the end of each
          request even if an exception is raised, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the teardown_request().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Attribute: template_context_processors:
               t.Dict[AppOrBlueprintKey,
               t.List[TemplateContextProcessorCallable]]

          A data structure of functions to call to pass extra context
          values when rendering templates, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the context_processor()
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Attribute: template_folder

          The path to the templates folder, relative to root_path,
          to add to the template loader.  ‘None’ if templates
          should not be added.

      -- Attribute: url_default_functions: t.Dict[AppOrBlueprintKey,
               t.List[URLDefaultCallable]]

          A data structure of functions to call to modify the keyword
          arguments when generating URLs, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the url_defaults().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Method: url_defaults (f)

          Callback function for URL defaults for all view functions of
          the application.  It’s called with the endpoint and values and
          should update the values passed in place.

          Parameters: ‘f’ (Callable(501)‘[’‘[’str(502)‘, ’dict(503)‘]’‘,
          ’‘None’‘]’) –

          Return type: Callable(504)[[str(505), dict(506)], None]

      -- Method: url_value_preprocessor (f)

          Register a URL value preprocessor function for all view
          functions in the application.  These functions will be called
          before the before_request() functions.

          The function can modify the values captured from the matched
          url before they are passed to the view.  For example, this can
          be used to pop a common language code value and place it in
          ‘g’ rather than pass it to every view.

          The function is passed the endpoint name and values dict.  The
          return value is ignored.

          Parameters: ‘f’
          (Callable(507)‘[’‘[’Optional(508)‘[’str(509)‘]’‘,
          ’Optional(510)‘[’dict(511)‘]’‘]’‘, ’‘None’‘]’) –

          Return type: Callable(512)[[Optional(513)[str(514)],
          Optional(515)[dict(516)]], None]

      -- Attribute: url_value_preprocessors: t.Dict[AppOrBlueprintKey,
               t.List[URLValuePreprocessorCallable]]

          A data structure of functions to call to modify the keyword
          arguments passed to the view function, in the format ‘{scope:
          [functions]}’.  The ‘scope’ key is the name of a blueprint the
          functions are active for, or ‘None’ for all requests.

          To register a function, use the
          url_value_preprocessor() decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

      -- Attribute: view_functions: t.Dict[str(517), t.Callable]

          A dictionary mapping endpoint names to view functions.

          To register a view function, use the route().
          decorator.

          This data structure is internal.  It should not be modified
          directly and its format may change at any time.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#str

   (2) https://docs.python.org/3/library/stdtypes.html#str

   (3) https://docs.python.org/3/library/typing.html#typing.Optional

   (4) https://docs.python.org/3/library/typing.html#typing.Union

   (5) https://docs.python.org/3/library/stdtypes.html#str

   (6) https://docs.python.org/3/library/os.html#os.PathLike

   (7) https://docs.python.org/3/library/typing.html#typing.Optional

   (8) https://docs.python.org/3/library/stdtypes.html#str

   (9) https://docs.python.org/3/library/typing.html#typing.Optional

   (10) https://docs.python.org/3/library/stdtypes.html#str

   (11) https://docs.python.org/3/library/typing.html#typing.Optional

   (12) https://docs.python.org/3/library/stdtypes.html#str

   (13) https://docs.python.org/3/library/typing.html#typing.Optional

   (14) https://docs.python.org/3/library/stdtypes.html#str

   (15) https://docs.python.org/3/library/typing.html#typing.Optional

   (16) https://docs.python.org/3/library/stdtypes.html#dict

   (17) https://docs.python.org/3/library/typing.html#typing.Optional

   (18) https://docs.python.org/3/library/stdtypes.html#str

   (19) https://docs.python.org/3/library/typing.html#typing.Optional

   (20) https://docs.python.org/3/library/stdtypes.html#str

   (21) https://docs.python.org/3/library/typing.html#typing.Optional

   (22) https://docs.python.org/3/library/stdtypes.html#str

   (23) https://docs.python.org/3/library/typing.html#typing.Callable

   (24) https://docs.python.org/3/library/typing.html#typing.Any

   (25) https://docs.python.org/3/library/typing.html#typing.Optional

   (26) https://docs.python.org/3/library/stdtypes.html#str

   (27) https://docs.python.org/3/library/typing.html#typing.Callable

   (28) https://docs.python.org/3/library/typing.html#typing.Any

   (29) https://docs.python.org/3/library/typing.html#typing.Optional

   (30) https://docs.python.org/3/library/stdtypes.html#str

   (31) https://docs.python.org/3/library/typing.html#typing.Callable

   (32) https://docs.python.org/3/library/functions.html#bool

   (33) https://docs.python.org/3/library/stdtypes.html#str

   (34) https://docs.python.org/3/library/typing.html#typing.Optional

   (35) https://docs.python.org/3/library/stdtypes.html#str

   (36) https://docs.python.org/3/library/typing.html#typing.Optional

   (37) https://docs.python.org/3/library/typing.html#typing.Callable

   (38) https://docs.python.org/3/library/typing.html#typing.Optional

   (39) https://docs.python.org/3/library/functions.html#bool

   (40) https://docs.python.org/3/library/typing.html#typing.Any

   (41) https://docs.python.org/3/library/typing.html#typing.Callable

   (42) https://docs.python.org/3/library/typing.html#typing.Callable

   (43) https://docs.python.org/3/library/typing.html#typing.Callable

   (44) https://docs.python.org/3/library/typing.html#typing.Callable

   (45) https://docs.python.org/3/library/typing.html#typing.Callable

   (46) https://docs.python.org/3/library/typing.html#typing.Dict

   (47) https://docs.python.org/3/library/stdtypes.html#str

   (48) https://docs.python.org/3/library/typing.html#typing.Any

   (49) https://docs.python.org/3/library/typing.html#typing.Callable

   (50) https://docs.python.org/3/library/typing.html#typing.Dict

   (51) https://docs.python.org/3/library/stdtypes.html#str

   (52) https://docs.python.org/3/library/typing.html#typing.Any

   (53) https://docs.python.org/3/library/typing.html#typing.Union

   (54) https://docs.python.org/3/library/typing.html#typing.Type

   (55) https://docs.python.org/3/library/exceptions.html#Exception

   (56) https://docs.python.org/3/library/functions.html#int

   (57) https://docs.python.org/3/library/typing.html#typing.Callable

   (58) https://docs.python.org/3/library/typing.html#typing.Optional

   (59) https://docs.python.org/3/library/stdtypes.html#str

   (60) https://docs.python.org/3/library/typing.html#typing.Callable

   (61) https://docs.python.org/3/library/typing.html#typing.Callable

   (62) https://docs.python.org/3/library/typing.html#typing.Any

   (63) https://docs.python.org/3/library/typing.html#typing.Callable

   (64) https://docs.python.org/3/library/typing.html#typing.Any

   (65) https://docs.python.org/3/library/typing.html#typing.Optional

   (66) https://docs.python.org/3/library/stdtypes.html#str

   (67) https://docs.python.org/3/library/typing.html#typing.Callable

   (68) https://docs.python.org/3/library/typing.html#typing.Callable

   (69) https://docs.python.org/3/library/typing.html#typing.Any

   (70) https://docs.python.org/3/library/typing.html#typing.Callable

   (71) https://docs.python.org/3/library/typing.html#typing.Any

   (72) https://docs.python.org/3/library/typing.html#typing.Optional

   (73) https://docs.python.org/3/library/stdtypes.html#str

   (74) https://docs.python.org/3/library/typing.html#typing.Callable

   (75) https://docs.python.org/3/library/typing.html#typing.Callable

   (76) https://docs.python.org/3/library/functions.html#bool

   (77) https://docs.python.org/3/library/typing.html#typing.Callable

   (78) https://docs.python.org/3/library/functions.html#bool

   (79) https://docs.python.org/3/library/typing.html#typing.Callable

   (80) https://docs.python.org/3/library/stdtypes.html#str

   (81) https://docs.python.org/3/library/stdtypes.html#dict

   (82) https://docs.python.org/3/library/typing.html#typing.Callable

   (83) https://docs.python.org/3/library/stdtypes.html#str

   (84) https://docs.python.org/3/library/stdtypes.html#dict

   (85) https://docs.python.org/3/library/typing.html#typing.Callable

   (86) https://docs.python.org/3/library/typing.html#typing.Optional

   (87) https://docs.python.org/3/library/stdtypes.html#str

   (88) https://docs.python.org/3/library/typing.html#typing.Optional

   (89) https://docs.python.org/3/library/stdtypes.html#dict

   (90) https://docs.python.org/3/library/typing.html#typing.Callable

   (91) https://docs.python.org/3/library/typing.html#typing.Optional

   (92) https://docs.python.org/3/library/stdtypes.html#str

   (93) https://docs.python.org/3/library/typing.html#typing.Optional

   (94) https://docs.python.org/3/library/stdtypes.html#dict

   (95) https://docs.python.org/3/library/typing.html#typing.Callable

   (96) https://docs.python.org/3/library/typing.html#typing.Callable

   (97) https://docs.python.org/3/library/typing.html#typing.Callable

   (98) https://docs.python.org/3/library/typing.html#typing.Optional

   (99) https://docs.python.org/3/library/typing.html#typing.Union

   (100) https://docs.python.org/3/library/stdtypes.html#str

   (101) https://docs.python.org/3/library/stdtypes.html#bytes

   (102) https://docs.python.org/3/library/typing.html#typing.Dict

   (103) https://docs.python.org/3/library/stdtypes.html#str

   (104) https://docs.python.org/3/library/typing.html#typing.Any

   (105) https://docs.python.org/3/library/typing.html#typing.Iterator

   (106) https://docs.python.org/3/library/stdtypes.html#str

   (107) https://docs.python.org/3/library/typing.html#typing.Iterator

   (108) https://docs.python.org/3/library/stdtypes.html#bytes

   (109) https://docs.python.org/3/library/typing.html#typing.Tuple

   (110) https://docs.python.org/3/library/typing.html#typing.Union

   (111) https://docs.python.org/3/library/stdtypes.html#str

   (112) https://docs.python.org/3/library/stdtypes.html#bytes

   (113) https://docs.python.org/3/library/typing.html#typing.Dict

   (114) https://docs.python.org/3/library/stdtypes.html#str

   (115) https://docs.python.org/3/library/typing.html#typing.Any

   (116) https://docs.python.org/3/library/typing.html#typing.Iterator

   (117) https://docs.python.org/3/library/stdtypes.html#str

   (118) https://docs.python.org/3/library/typing.html#typing.Iterator

   (119) https://docs.python.org/3/library/stdtypes.html#bytes

   (120) https://docs.python.org/3/library/typing.html#typing.Union

   (121) https://docs.python.org/3/library/typing.html#typing.Dict

   (122) https://docs.python.org/3/library/stdtypes.html#str

   (123) https://docs.python.org/3/library/typing.html#typing.Union

   (124) https://docs.python.org/3/library/stdtypes.html#str

   (125) https://docs.python.org/3/library/typing.html#typing.List

   (126) https://docs.python.org/3/library/stdtypes.html#str

   (127) https://docs.python.org/3/library/typing.html#typing.Tuple

   (128) https://docs.python.org/3/library/stdtypes.html#str

   (129) https://docs.python.org/3/library/typing.html#typing.List

   (130) https://docs.python.org/3/library/typing.html#typing.Tuple

   (131) https://docs.python.org/3/library/stdtypes.html#str

   (132) https://docs.python.org/3/library/typing.html#typing.Union

   (133) https://docs.python.org/3/library/stdtypes.html#str

   (134) https://docs.python.org/3/library/typing.html#typing.List

   (135) https://docs.python.org/3/library/stdtypes.html#str

   (136) https://docs.python.org/3/library/typing.html#typing.Tuple

   (137) https://docs.python.org/3/library/stdtypes.html#str

   (138) https://docs.python.org/3/library/typing.html#typing.Tuple

   (139) https://docs.python.org/3/library/typing.html#typing.Union

   (140) https://docs.python.org/3/library/stdtypes.html#str

   (141) https://docs.python.org/3/library/stdtypes.html#bytes

   (142) https://docs.python.org/3/library/typing.html#typing.Dict

   (143) https://docs.python.org/3/library/stdtypes.html#str

   (144) https://docs.python.org/3/library/typing.html#typing.Any

   (145) https://docs.python.org/3/library/typing.html#typing.Iterator

   (146) https://docs.python.org/3/library/stdtypes.html#str

   (147) https://docs.python.org/3/library/typing.html#typing.Iterator

   (148) https://docs.python.org/3/library/stdtypes.html#bytes

   (149) https://docs.python.org/3/library/functions.html#int

   (150) https://docs.python.org/3/library/typing.html#typing.Tuple

   (151) https://docs.python.org/3/library/typing.html#typing.Union

   (152) https://docs.python.org/3/library/stdtypes.html#str

   (153) https://docs.python.org/3/library/stdtypes.html#bytes

   (154) https://docs.python.org/3/library/typing.html#typing.Dict

   (155) https://docs.python.org/3/library/stdtypes.html#str

   (156) https://docs.python.org/3/library/typing.html#typing.Any

   (157) https://docs.python.org/3/library/typing.html#typing.Iterator

   (158) https://docs.python.org/3/library/stdtypes.html#str

   (159) https://docs.python.org/3/library/typing.html#typing.Iterator

   (160) https://docs.python.org/3/library/stdtypes.html#bytes

   (161) https://docs.python.org/3/library/functions.html#int

   (162) https://docs.python.org/3/library/typing.html#typing.Union

   (163) https://docs.python.org/3/library/typing.html#typing.Dict

   (164) https://docs.python.org/3/library/stdtypes.html#str

   (165) https://docs.python.org/3/library/typing.html#typing.Union

   (166) https://docs.python.org/3/library/stdtypes.html#str

   (167) https://docs.python.org/3/library/typing.html#typing.List

   (168) https://docs.python.org/3/library/stdtypes.html#str

   (169) https://docs.python.org/3/library/typing.html#typing.Tuple

   (170) https://docs.python.org/3/library/stdtypes.html#str

   (171) https://docs.python.org/3/library/typing.html#typing.List

   (172) https://docs.python.org/3/library/typing.html#typing.Tuple

   (173) https://docs.python.org/3/library/stdtypes.html#str

   (174) https://docs.python.org/3/library/typing.html#typing.Union

   (175) https://docs.python.org/3/library/stdtypes.html#str

   (176) https://docs.python.org/3/library/typing.html#typing.List

   (177) https://docs.python.org/3/library/stdtypes.html#str

   (178) https://docs.python.org/3/library/typing.html#typing.Tuple

   (179) https://docs.python.org/3/library/stdtypes.html#str

   (180) https://docs.python.org/3/library/typing.html#typing.Callable

   (181) https://docs.python.org/3/library/typing.html#typing.Optional

   (182) https://docs.python.org/3/library/typing.html#typing.Union

   (183) https://docs.python.org/3/library/stdtypes.html#str

   (184) https://docs.python.org/3/library/stdtypes.html#bytes

   (185) https://docs.python.org/3/library/typing.html#typing.Dict

   (186) https://docs.python.org/3/library/stdtypes.html#str

   (187) https://docs.python.org/3/library/typing.html#typing.Any

   (188) https://docs.python.org/3/library/typing.html#typing.Iterator

   (189) https://docs.python.org/3/library/stdtypes.html#str

   (190) https://docs.python.org/3/library/typing.html#typing.Iterator

   (191) https://docs.python.org/3/library/stdtypes.html#bytes

   (192) https://docs.python.org/3/library/typing.html#typing.Tuple

   (193) https://docs.python.org/3/library/typing.html#typing.Union

   (194) https://docs.python.org/3/library/stdtypes.html#str

   (195) https://docs.python.org/3/library/stdtypes.html#bytes

   (196) https://docs.python.org/3/library/typing.html#typing.Dict

   (197) https://docs.python.org/3/library/stdtypes.html#str

   (198) https://docs.python.org/3/library/typing.html#typing.Any

   (199) https://docs.python.org/3/library/typing.html#typing.Iterator

   (200) https://docs.python.org/3/library/stdtypes.html#str

   (201) https://docs.python.org/3/library/typing.html#typing.Iterator

   (202) https://docs.python.org/3/library/stdtypes.html#bytes

   (203) https://docs.python.org/3/library/typing.html#typing.Union

   (204) https://docs.python.org/3/library/typing.html#typing.Dict

   (205) https://docs.python.org/3/library/stdtypes.html#str

   (206) https://docs.python.org/3/library/typing.html#typing.Union

   (207) https://docs.python.org/3/library/stdtypes.html#str

   (208) https://docs.python.org/3/library/typing.html#typing.List

   (209) https://docs.python.org/3/library/stdtypes.html#str

   (210) https://docs.python.org/3/library/typing.html#typing.Tuple

   (211) https://docs.python.org/3/library/stdtypes.html#str

   (212) https://docs.python.org/3/library/typing.html#typing.List

   (213) https://docs.python.org/3/library/typing.html#typing.Tuple

   (214) https://docs.python.org/3/library/stdtypes.html#str

   (215) https://docs.python.org/3/library/typing.html#typing.Union

   (216) https://docs.python.org/3/library/stdtypes.html#str

   (217) https://docs.python.org/3/library/typing.html#typing.List

   (218) https://docs.python.org/3/library/stdtypes.html#str

   (219) https://docs.python.org/3/library/typing.html#typing.Tuple

   (220) https://docs.python.org/3/library/stdtypes.html#str

   (221) https://docs.python.org/3/library/typing.html#typing.Tuple

   (222) https://docs.python.org/3/library/typing.html#typing.Union

   (223) https://docs.python.org/3/library/stdtypes.html#str

   (224) https://docs.python.org/3/library/stdtypes.html#bytes

   (225) https://docs.python.org/3/library/typing.html#typing.Dict

   (226) https://docs.python.org/3/library/stdtypes.html#str

   (227) https://docs.python.org/3/library/typing.html#typing.Any

   (228) https://docs.python.org/3/library/typing.html#typing.Iterator

   (229) https://docs.python.org/3/library/stdtypes.html#str

   (230) https://docs.python.org/3/library/typing.html#typing.Iterator

   (231) https://docs.python.org/3/library/stdtypes.html#bytes

   (232) https://docs.python.org/3/library/functions.html#int

   (233) https://docs.python.org/3/library/typing.html#typing.Tuple

   (234) https://docs.python.org/3/library/typing.html#typing.Union

   (235) https://docs.python.org/3/library/stdtypes.html#str

   (236) https://docs.python.org/3/library/stdtypes.html#bytes

   (237) https://docs.python.org/3/library/typing.html#typing.Dict

   (238) https://docs.python.org/3/library/stdtypes.html#str

   (239) https://docs.python.org/3/library/typing.html#typing.Any

   (240) https://docs.python.org/3/library/typing.html#typing.Iterator

   (241) https://docs.python.org/3/library/stdtypes.html#str

   (242) https://docs.python.org/3/library/typing.html#typing.Iterator

   (243) https://docs.python.org/3/library/stdtypes.html#bytes

   (244) https://docs.python.org/3/library/functions.html#int

   (245) https://docs.python.org/3/library/typing.html#typing.Union

   (246) https://docs.python.org/3/library/typing.html#typing.Dict

   (247) https://docs.python.org/3/library/stdtypes.html#str

   (248) https://docs.python.org/3/library/typing.html#typing.Union

   (249) https://docs.python.org/3/library/stdtypes.html#str

   (250) https://docs.python.org/3/library/typing.html#typing.List

   (251) https://docs.python.org/3/library/stdtypes.html#str

   (252) https://docs.python.org/3/library/typing.html#typing.Tuple

   (253) https://docs.python.org/3/library/stdtypes.html#str

   (254) https://docs.python.org/3/library/typing.html#typing.List

   (255) https://docs.python.org/3/library/typing.html#typing.Tuple

   (256) https://docs.python.org/3/library/stdtypes.html#str

   (257) https://docs.python.org/3/library/typing.html#typing.Union

   (258) https://docs.python.org/3/library/stdtypes.html#str

   (259) https://docs.python.org/3/library/typing.html#typing.List

   (260) https://docs.python.org/3/library/stdtypes.html#str

   (261) https://docs.python.org/3/library/typing.html#typing.Tuple

   (262) https://docs.python.org/3/library/stdtypes.html#str

   (263) https://docs.python.org/3/library/typing.html#typing.Callable

   (264) https://docs.python.org/3/library/typing.html#typing.Optional

   (265) https://docs.python.org/3/library/typing.html#typing.Union

   (266) https://docs.python.org/3/library/stdtypes.html#str

   (267) https://docs.python.org/3/library/stdtypes.html#bytes

   (268) https://docs.python.org/3/library/typing.html#typing.Dict

   (269) https://docs.python.org/3/library/stdtypes.html#str

   (270) https://docs.python.org/3/library/typing.html#typing.Any

   (271) https://docs.python.org/3/library/typing.html#typing.Iterator

   (272) https://docs.python.org/3/library/stdtypes.html#str

   (273) https://docs.python.org/3/library/typing.html#typing.Iterator

   (274) https://docs.python.org/3/library/stdtypes.html#bytes

   (275) https://docs.python.org/3/library/typing.html#typing.Tuple

   (276) https://docs.python.org/3/library/typing.html#typing.Union

   (277) https://docs.python.org/3/library/stdtypes.html#str

   (278) https://docs.python.org/3/library/stdtypes.html#bytes

   (279) https://docs.python.org/3/library/typing.html#typing.Dict

   (280) https://docs.python.org/3/library/stdtypes.html#str

   (281) https://docs.python.org/3/library/typing.html#typing.Any

   (282) https://docs.python.org/3/library/typing.html#typing.Iterator

   (283) https://docs.python.org/3/library/stdtypes.html#str

   (284) https://docs.python.org/3/library/typing.html#typing.Iterator

   (285) https://docs.python.org/3/library/stdtypes.html#bytes

   (286) https://docs.python.org/3/library/typing.html#typing.Union

   (287) https://docs.python.org/3/library/typing.html#typing.Dict

   (288) https://docs.python.org/3/library/stdtypes.html#str

   (289) https://docs.python.org/3/library/typing.html#typing.Union

   (290) https://docs.python.org/3/library/stdtypes.html#str

   (291) https://docs.python.org/3/library/typing.html#typing.List

   (292) https://docs.python.org/3/library/stdtypes.html#str

   (293) https://docs.python.org/3/library/typing.html#typing.Tuple

   (294) https://docs.python.org/3/library/stdtypes.html#str

   (295) https://docs.python.org/3/library/typing.html#typing.List

   (296) https://docs.python.org/3/library/typing.html#typing.Tuple

   (297) https://docs.python.org/3/library/stdtypes.html#str

   (298) https://docs.python.org/3/library/typing.html#typing.Union

   (299) https://docs.python.org/3/library/stdtypes.html#str

   (300) https://docs.python.org/3/library/typing.html#typing.List

   (301) https://docs.python.org/3/library/stdtypes.html#str

   (302) https://docs.python.org/3/library/typing.html#typing.Tuple

   (303) https://docs.python.org/3/library/stdtypes.html#str

   (304) https://docs.python.org/3/library/typing.html#typing.Tuple

   (305) https://docs.python.org/3/library/typing.html#typing.Union

   (306) https://docs.python.org/3/library/stdtypes.html#str

   (307) https://docs.python.org/3/library/stdtypes.html#bytes

   (308) https://docs.python.org/3/library/typing.html#typing.Dict

   (309) https://docs.python.org/3/library/stdtypes.html#str

   (310) https://docs.python.org/3/library/typing.html#typing.Any

   (311) https://docs.python.org/3/library/typing.html#typing.Iterator

   (312) https://docs.python.org/3/library/stdtypes.html#str

   (313) https://docs.python.org/3/library/typing.html#typing.Iterator

   (314) https://docs.python.org/3/library/stdtypes.html#bytes

   (315) https://docs.python.org/3/library/functions.html#int

   (316) https://docs.python.org/3/library/typing.html#typing.Tuple

   (317) https://docs.python.org/3/library/typing.html#typing.Union

   (318) https://docs.python.org/3/library/stdtypes.html#str

   (319) https://docs.python.org/3/library/stdtypes.html#bytes

   (320) https://docs.python.org/3/library/typing.html#typing.Dict

   (321) https://docs.python.org/3/library/stdtypes.html#str

   (322) https://docs.python.org/3/library/typing.html#typing.Any

   (323) https://docs.python.org/3/library/typing.html#typing.Iterator

   (324) https://docs.python.org/3/library/stdtypes.html#str

   (325) https://docs.python.org/3/library/typing.html#typing.Iterator

   (326) https://docs.python.org/3/library/stdtypes.html#bytes

   (327) https://docs.python.org/3/library/functions.html#int

   (328) https://docs.python.org/3/library/typing.html#typing.Union

   (329) https://docs.python.org/3/library/typing.html#typing.Dict

   (330) https://docs.python.org/3/library/stdtypes.html#str

   (331) https://docs.python.org/3/library/typing.html#typing.Union

   (332) https://docs.python.org/3/library/stdtypes.html#str

   (333) https://docs.python.org/3/library/typing.html#typing.List

   (334) https://docs.python.org/3/library/stdtypes.html#str

   (335) https://docs.python.org/3/library/typing.html#typing.Tuple

   (336) https://docs.python.org/3/library/stdtypes.html#str

   (337) https://docs.python.org/3/library/typing.html#typing.List

   (338) https://docs.python.org/3/library/typing.html#typing.Tuple

   (339) https://docs.python.org/3/library/stdtypes.html#str

   (340) https://docs.python.org/3/library/typing.html#typing.Union

   (341) https://docs.python.org/3/library/stdtypes.html#str

   (342) https://docs.python.org/3/library/typing.html#typing.List

   (343) https://docs.python.org/3/library/stdtypes.html#str

   (344) https://docs.python.org/3/library/typing.html#typing.Tuple

   (345) https://docs.python.org/3/library/stdtypes.html#str

   (346) https://docs.python.org/3/library/typing.html#typing.Callable

   (347) https://docs.python.org/3/library/typing.html#typing.Optional

   (348) https://docs.python.org/3/library/typing.html#typing.Union

   (349) https://docs.python.org/3/library/stdtypes.html#str

   (350) https://docs.python.org/3/library/stdtypes.html#bytes

   (351) https://docs.python.org/3/library/typing.html#typing.Dict

   (352) https://docs.python.org/3/library/stdtypes.html#str

   (353) https://docs.python.org/3/library/typing.html#typing.Any

   (354) https://docs.python.org/3/library/typing.html#typing.Iterator

   (355) https://docs.python.org/3/library/stdtypes.html#str

   (356) https://docs.python.org/3/library/typing.html#typing.Iterator

   (357) https://docs.python.org/3/library/stdtypes.html#bytes

   (358) https://docs.python.org/3/library/typing.html#typing.Tuple

   (359) https://docs.python.org/3/library/typing.html#typing.Union

   (360) https://docs.python.org/3/library/stdtypes.html#str

   (361) https://docs.python.org/3/library/stdtypes.html#bytes

   (362) https://docs.python.org/3/library/typing.html#typing.Dict

   (363) https://docs.python.org/3/library/stdtypes.html#str

   (364) https://docs.python.org/3/library/typing.html#typing.Any

   (365) https://docs.python.org/3/library/typing.html#typing.Iterator

   (366) https://docs.python.org/3/library/stdtypes.html#str

   (367) https://docs.python.org/3/library/typing.html#typing.Iterator

   (368) https://docs.python.org/3/library/stdtypes.html#bytes

   (369) https://docs.python.org/3/library/typing.html#typing.Union

   (370) https://docs.python.org/3/library/typing.html#typing.Dict

   (371) https://docs.python.org/3/library/stdtypes.html#str

   (372) https://docs.python.org/3/library/typing.html#typing.Union

   (373) https://docs.python.org/3/library/stdtypes.html#str

   (374) https://docs.python.org/3/library/typing.html#typing.List

   (375) https://docs.python.org/3/library/stdtypes.html#str

   (376) https://docs.python.org/3/library/typing.html#typing.Tuple

   (377) https://docs.python.org/3/library/stdtypes.html#str

   (378) https://docs.python.org/3/library/typing.html#typing.List

   (379) https://docs.python.org/3/library/typing.html#typing.Tuple

   (380) https://docs.python.org/3/library/stdtypes.html#str

   (381) https://docs.python.org/3/library/typing.html#typing.Union

   (382) https://docs.python.org/3/library/stdtypes.html#str

   (383) https://docs.python.org/3/library/typing.html#typing.List

   (384) https://docs.python.org/3/library/stdtypes.html#str

   (385) https://docs.python.org/3/library/typing.html#typing.Tuple

   (386) https://docs.python.org/3/library/stdtypes.html#str

   (387) https://docs.python.org/3/library/typing.html#typing.Tuple

   (388) https://docs.python.org/3/library/typing.html#typing.Union

   (389) https://docs.python.org/3/library/stdtypes.html#str

   (390) https://docs.python.org/3/library/stdtypes.html#bytes

   (391) https://docs.python.org/3/library/typing.html#typing.Dict

   (392) https://docs.python.org/3/library/stdtypes.html#str

   (393) https://docs.python.org/3/library/typing.html#typing.Any

   (394) https://docs.python.org/3/library/typing.html#typing.Iterator

   (395) https://docs.python.org/3/library/stdtypes.html#str

   (396) https://docs.python.org/3/library/typing.html#typing.Iterator

   (397) https://docs.python.org/3/library/stdtypes.html#bytes

   (398) https://docs.python.org/3/library/functions.html#int

   (399) https://docs.python.org/3/library/typing.html#typing.Tuple

   (400) https://docs.python.org/3/library/typing.html#typing.Union

   (401) https://docs.python.org/3/library/stdtypes.html#str

   (402) https://docs.python.org/3/library/stdtypes.html#bytes

   (403) https://docs.python.org/3/library/typing.html#typing.Dict

   (404) https://docs.python.org/3/library/stdtypes.html#str

   (405) https://docs.python.org/3/library/typing.html#typing.Any

   (406) https://docs.python.org/3/library/typing.html#typing.Iterator

   (407) https://docs.python.org/3/library/stdtypes.html#str

   (408) https://docs.python.org/3/library/typing.html#typing.Iterator

   (409) https://docs.python.org/3/library/stdtypes.html#bytes

   (410) https://docs.python.org/3/library/functions.html#int

   (411) https://docs.python.org/3/library/typing.html#typing.Union

   (412) https://docs.python.org/3/library/typing.html#typing.Dict

   (413) https://docs.python.org/3/library/stdtypes.html#str

   (414) https://docs.python.org/3/library/typing.html#typing.Union

   (415) https://docs.python.org/3/library/stdtypes.html#str

   (416) https://docs.python.org/3/library/typing.html#typing.List

   (417) https://docs.python.org/3/library/stdtypes.html#str

   (418) https://docs.python.org/3/library/typing.html#typing.Tuple

   (419) https://docs.python.org/3/library/stdtypes.html#str

   (420) https://docs.python.org/3/library/typing.html#typing.List

   (421) https://docs.python.org/3/library/typing.html#typing.Tuple

   (422) https://docs.python.org/3/library/stdtypes.html#str

   (423) https://docs.python.org/3/library/typing.html#typing.Union

   (424) https://docs.python.org/3/library/stdtypes.html#str

   (425) https://docs.python.org/3/library/typing.html#typing.List

   (426) https://docs.python.org/3/library/stdtypes.html#str

   (427) https://docs.python.org/3/library/typing.html#typing.Tuple

   (428) https://docs.python.org/3/library/stdtypes.html#str

   (429) https://docs.python.org/3/library/typing.html#typing.Callable

   (430) https://docs.python.org/3/library/typing.html#typing.Dict

   (431) https://docs.python.org/3/library/stdtypes.html#str

   (432) https://docs.python.org/3/library/typing.html#typing.Any

   (433) https://docs.python.org/3/library/typing.html#typing.Callable

   (434) https://docs.python.org/3/library/typing.html#typing.Dict

   (435) https://docs.python.org/3/library/stdtypes.html#str

   (436) https://docs.python.org/3/library/typing.html#typing.Any

   (437) https://docs.python.org/3/library/stdtypes.html#str

   (438) https://docs.python.org/3/library/typing.html#typing.Any

   (439) https://docs.python.org/3/library/typing.html#typing.Callable

   (440) https://docs.python.org/3/library/stdtypes.html#str

   (441) https://docs.python.org/3/library/typing.html#typing.Callable

   (442) https://docs.python.org/3/library/functions.html#int

   (443) https://docs.python.org/3/library/exceptions.html#Exception

   (444)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (445) https://docs.python.org/3/library/typing.html#typing.Union

   (446) https://docs.python.org/3/library/typing.html#typing.Type

   (447) https://docs.python.org/3/library/exceptions.html#Exception

   (448) https://docs.python.org/3/library/functions.html#int

   (449) https://docs.python.org/3/library/typing.html#typing.Callable

   (450) https://docs.python.org/3/library/stdtypes.html#str

   (451) https://docs.python.org/3/library/typing.html#typing.Any

   (452) https://docs.python.org/3/library/typing.html#typing.Callable

   (453) https://docs.python.org/3/library/typing.html#typing.Optional

   (454) https://docs.python.org/3/library/stdtypes.html#str

   (455) https://docs.python.org/3/library/typing.html#typing.Optional

   (456) https://docs.python.org/3/library/functions.html#int

   (457) https://docs.python.org/3/library/functions.html#bool

   (458)
https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.FileSystemLoader

   (459)
https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.FileSystemLoader

   (460) https://docs.python.org/3/library/stdtypes.html#dict

   (461) https://docs.python.org/3/library/functions.html#bool

   (462) https://docs.python.org/3/library/stdtypes.html#str

   (463) https://docs.python.org/3/library/stdtypes.html#str

   (464) https://docs.python.org/3/library/typing.html#typing.IO

   (465) https://docs.python.org/3/library/stdtypes.html#str

   (466) https://docs.python.org/3/library/typing.html#typing.Any

   (467) https://docs.python.org/3/library/typing.html#typing.Callable

   (468) https://docs.python.org/3/library/stdtypes.html#str

   (469) https://docs.python.org/3/library/typing.html#typing.Any

   (470) https://docs.python.org/3/library/typing.html#typing.Callable

   (471) https://docs.python.org/3/library/stdtypes.html#str

   (472) https://docs.python.org/3/library/typing.html#typing.Any

   (473) https://docs.python.org/3/library/typing.html#typing.Callable

   (474) https://docs.python.org/3/library/typing.html#typing.Callable

   (475) https://docs.python.org/3/library/typing.html#typing.Callable

   (476) https://docs.python.org/3/library/stdtypes.html#dict

   (477) https://docs.python.org/3/library/typing.html#typing.Any

   (478) https://docs.python.org/3/library/typing.html#typing.Union

   (479) https://docs.python.org/3/library/typing.html#typing.Type

   (480) https://docs.python.org/3/library/exceptions.html#Exception

   (481) https://docs.python.org/3/library/functions.html#int

   (482) https://docs.python.org/3/library/stdtypes.html#str

   (483) https://docs.python.org/3/library/typing.html#typing.Any

   (484)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Rule

   (485) https://docs.python.org/3/library/typing.html#typing.Callable

   (486) https://docs.python.org/3/library/stdtypes.html#str

   (487) https://docs.python.org/3/library/stdtypes.html#str

   (488) https://docs.python.org/3/library/stdtypes.html#str

   (489) https://docs.python.org/3/library/typing.html#typing.Callable

   (490) https://docs.python.org/3/library/typing.html#typing.Optional

   (491) https://docs.python.org/3/library/exceptions.html#BaseException

   (492) https://docs.python.org/3/library/typing.html#typing.Callable

   (493) https://docs.python.org/3/library/typing.html#typing.Optional

   (494) https://docs.python.org/3/library/exceptions.html#BaseException

   (495) https://docs.python.org/3/library/typing.html#typing.Callable

   (496) https://docs.python.org/3/library/typing.html#typing.Optional

   (497) https://docs.python.org/3/library/exceptions.html#BaseException

   (498) https://docs.python.org/3/library/typing.html#typing.Callable

   (499) https://docs.python.org/3/library/typing.html#typing.Optional

   (500) https://docs.python.org/3/library/exceptions.html#BaseException

   (501) https://docs.python.org/3/library/typing.html#typing.Callable

   (502) https://docs.python.org/3/library/stdtypes.html#str

   (503) https://docs.python.org/3/library/stdtypes.html#dict

   (504) https://docs.python.org/3/library/typing.html#typing.Callable

   (505) https://docs.python.org/3/library/stdtypes.html#str

   (506) https://docs.python.org/3/library/stdtypes.html#dict

   (507) https://docs.python.org/3/library/typing.html#typing.Callable

   (508) https://docs.python.org/3/library/typing.html#typing.Optional

   (509) https://docs.python.org/3/library/stdtypes.html#str

   (510) https://docs.python.org/3/library/typing.html#typing.Optional

   (511) https://docs.python.org/3/library/stdtypes.html#dict

   (512) https://docs.python.org/3/library/typing.html#typing.Callable

   (513) https://docs.python.org/3/library/typing.html#typing.Optional

   (514) https://docs.python.org/3/library/stdtypes.html#str

   (515) https://docs.python.org/3/library/typing.html#typing.Optional

   (516) https://docs.python.org/3/library/stdtypes.html#dict

   (517) https://docs.python.org/3/library/stdtypes.html#str

2.1.3 Incoming Request Data
---------------------------

 -- Class: flask.Request (environ, populate_request=True, shallow=False)

     The request object used by default in Flask.  Remembers the matched
     endpoint and view arguments.

     It is what ends up as request.  If you want to replace
     the request object used you can subclass this and set
     request_class to your subclass.

     The request object is a Request(1) subclass and provides all of the
     attributes Werkzeug defines plus a few Flask specific ones.

     Parameters:

        * ‘environ’ (‘WSGIEnvironment’) –

        * ‘populate_request’ (bool(2)) –

        * ‘shallow’ (bool(3)) –

     Return type: None

      -- Property: accept_charsets:
               werkzeug.datastructures.CharsetAccept(4)

          List of charsets this client supports as CharsetAccept(5)
          object.

      -- Property: accept_encodings: werkzeug.datastructures.Accept(6)

          List of encodings this client accepts.  Encodings in a HTTP
          term are compression encodings such as gzip.  For charsets
          have a look at ‘accept_charset’.

      -- Property: accept_languages:
               werkzeug.datastructures.LanguageAccept(7)

          List of languages this client accepts as LanguageAccept(8)
          object.

      -- Property: accept_mimetypes:
               werkzeug.datastructures.MIMEAccept(9)

          List of mimetypes this client supports as MIMEAccept(10)
          object.

      -- Attribute: access_control_request_headers

          Sent with a preflight request to indicate which headers will
          be sent with the cross origin request.  Set
          ‘access_control_allow_headers’ on the response to indicate
          which headers are allowed.

      -- Attribute: access_control_request_method

          Sent with a preflight request to indicate which method will be
          used for the cross origin request.  Set
          ‘access_control_allow_methods’ on the response to indicate
          which methods are allowed.

      -- Property: access_route: List[str(11)]

          If a forwarded header exists this is a list of all ip
          addresses from the client ip to the last proxy server.

      -- Method: classmethod application (f)

          Decorate a function as responder that accepts the request as
          the last argument.  This works like the ‘responder()’
          decorator but the function is passed the request object as the
          last argument and the request object will be closed
          automatically:

               @Request.application
               def my_wsgi_app(request):
                   return Response('Hello World!')

          As of Werkzeug 0.14 HTTP exceptions are automatically caught
          and converted to responses instead of failing.

          Parameters: ‘f’ (Callable(12)‘[’‘[’Request.‘]’‘,
          ’‘WSGIApplication’‘]’) – the WSGI callable to decorate

          Returns: a new WSGI callable

          Return type: WSGIApplication

      -- Property: args: MultiDict[str(13), str(14)]

          The parsed URL parameters (the part in the URL after the
          question mark).

          By default an ImmutableMultiDict(15) is returned from this
          function.  This can be changed by setting
          parameter_storage_class to a different type.  This might
          be necessary if the order of the form data is important.

      -- Property: authorization:
               Optional[werkzeug.datastructures.Authorization(16)]

          The ‘Authorization’ object in parsed form.

      -- Property: base_url: str(17)

          Like url but without the query string.

      -- Property: blueprint: Optional[str(18)]

          The registered name of the current blueprint.

          This will be ‘None’ if the endpoint is not part of a
          blueprint, or if URL matching failed or has not been performed
          yet.

          This does not necessarily match the name the blueprint was
          created with.  It may have been nested, or registered with a
          different name.

      -- Property: blueprints: List[str(19)]

          The registered names of the current blueprint upwards through
          parent blueprints.

          This will be an empty list if there is no current blueprint,
          or if URL matching failed.

          New in version 2.0.1.

      -- Property: cache_control:
               werkzeug.datastructures.RequestCacheControl(20)

          A RequestCacheControl(21) object for the incoming cache
          control headers.

      -- Method: close ()

          Closes associated resources of this request object.  This
          closes all file handles explicitly.  You can also use the
          request object in a with statement which will automatically
          close it.

          New in version 0.9.

          Return type: None

      -- Attribute: content_encoding

          The Content-Encoding entity-header field is used as a modifier
          to the media-type.  When present, its value indicates what
          additional content codings have been applied to the
          entity-body, and thus what decoding mechanisms must be applied
          in order to obtain the media-type referenced by the
          Content-Type header field.

          New in version 0.9.

      -- Property: content_length: Optional[int(22)]

          The Content-Length entity-header field indicates the size of
          the entity-body in bytes or, in the case of the HEAD method,
          the size of the entity-body that would have been sent had the
          request been a GET.

      -- Attribute: content_md5

          The Content-MD5 entity-header field, as defined in RFC 1864,
          is an MD5 digest of the entity-body for the purpose of
          providing an end-to-end message integrity check (MIC) of the
          entity-body.  (Note: a MIC is good for detecting accidental
          modification of the entity-body in transit, but is not proof
          against malicious attacks.)

          New in version 0.9.

      -- Attribute: content_type

          The Content-Type entity-header field indicates the media type
          of the entity-body sent to the recipient or, in the case of
          the HEAD method, the media type that would have been sent had
          the request been a GET.

      -- Property: cookies: ImmutableMultiDict[str(23), str(24)]

          A dict(25) with the contents of all cookies transmitted with
          the request.

      -- Property: data: bytes(26)

          Contains the incoming request data as string in case it came
          with a mimetype Werkzeug does not handle.

      -- Attribute: date

          The Date general-header field represents the date and time at
          which the message was originated, having the same semantics as
          orig-date in RFC 822.

          Changed in version 2.0: The datetime object is timezone-aware.

      -- Attribute: dict_storage_class

          alias of werkzeug.datastructures.ImmutableMultiDict(27)

      -- Property: endpoint: Optional[str(28)]

          The endpoint that matched the request URL.

          This will be ‘None’ if matching failed or has not been
          performed yet.

          This in combination with view_args can be used to
          reconstruct the same URL or a modified URL.

      -- Attribute: environ: WSGIEnvironment

          The WSGI environment containing HTTP headers and information
          from the WSGI server.

      -- Property: files: ImmutableMultiDict[str(29), FileStorage]

          MultiDict(30) object containing all uploaded files.  Each key
          in files is the name from the ‘<input type="file"
          name="">’.  Each value in files is a Werkzeug
          FileStorage(31) object.

          It basically behaves like a standard file object you know from
          Python, with the difference that it also has a save()(32)
          function that can store the file on the filesystem.

          Note that files will only contain data if the
          request method was POST, PUT or PATCH and the ‘<form>’ that
          posted to the request had ‘enctype="multipart/form-data"’.  It
          will be empty otherwise.

          See the MultiDict(33) / FileStorage(34) documentation for more
          details about the used data structure.

      -- Property: form: ImmutableMultiDict[str(35), str(36)]

          The form parameters.  By default an ImmutableMultiDict(37) is
          returned from this function.  This can be changed by setting
          parameter_storage_class to a different type.  This
          might be necessary if the order of the form data is important.

          Please keep in mind that file uploads will not end up here,
          but instead in the files attribute.

          Changed in version 0.9: Previous to Werkzeug 0.9 this would
          only contain form data for POST and PUT requests.

      -- Attribute: form_data_parser_class

          alias of werkzeug.formparser.FormDataParser(38)

      -- Method: classmethod from_values (*args, **kwargs)

          Create a new request object based on the values provided.  If
          environ is given missing values are filled from there.  This
          method is useful for small scripts when you need to simulate a
          request from an URL. Do not use this method for unittesting,
          there is a full featured client object (‘Client’) that allows
          to create multipart requests, support for cookies etc.

          This accepts the same options as the EnvironBuilder(39).

          Changed in version 0.5: This method now accepts the same
          arguments as EnvironBuilder(40).  Because of this the
          ‘environ’ parameter is now called ‘environ_overrides’.

          Returns: request object

          Parameters:

             * ‘args’ (Any(41)) –

             * ‘kwargs’ (Any(42)) –

          Return type: werkzeug.wrappers.request.Request(43)

      -- Property: full_path: str(44)

          Requested path, including the query string.

      -- Method: get_data (cache=True, as_text=False,
               parse_form_data=False)

          This reads the buffered incoming data from the client into one
          bytes object.  By default this is cached but that behavior can
          be changed by setting ‘cache’ to ‘False’.

          Usually it’s a bad idea to call this method without checking
          the content length first as a client could send dozens of
          megabytes or more to cause memory problems on the server.

          Note that if the form data was already parsed this method will
          not return anything as form data parsing does not cache the
          data like this method does.  To implicitly invoke form data
          parsing function set ‘parse_form_data’ to ‘True’.  When this
          is done the return value of this method will be an empty
          string if the form parser handles the data.  This generally is
          not necessary as if the whole data is cached (which is the
          default) the form parser will used the cached data to parse
          the form data.  Please be generally aware of checking the
          content length first in any case before calling this method to
          avoid exhausting server memory.

          If ‘as_text’ is set to ‘True’ the return value will be a
          decoded string.

          New in version 0.9.

          Parameters:

             * ‘cache’ (bool(45)) –

             * ‘as_text’ (bool(46)) –

             * ‘parse_form_data’ (bool(47)) –

          Return type: Union(48)[bytes(49), str(50)]

      -- Method: get_json (force=False, silent=False, cache=True)

          Parse data as JSON.

          If the mimetype does not indicate JSON (‘application/json’,
          see is_json), or parsing fails, see
          on_json_loading_failed() is called and its return value
          is used as the return value.  By default this raises a 400 Bad
          Request error.

          Parameters:

             * ‘force’ (bool(51)) – Ignore the mimetype and always try
               to parse JSON.

             * ‘silent’ (bool(52)) – Silence mimetype and parsing
               errors, and return ‘None’ instead.

             * ‘cache’ (bool(53)) – Store the parsed JSON to return for
               subsequent calls.

          Return type: Optional(54)[Any(55)]

          Changed in version 2.1: Raise a 400 error if the content type
          is incorrect.

      -- Attribute: headers

          The headers received with the request.

      -- Property: host: str(56)

          The host name the request was made to, including the port if
          it’s non-standard.  Validated with ‘trusted_hosts’.

      -- Property: host_url: str(57)

          The request URL scheme and host only.

      -- Property: if_match: werkzeug.datastructures.ETags(58)

          An object containing all the etags in the ‘If-Match’ header.

          Return type: ETags(59)

      -- Property: if_modified_since: Optional[datetime.datetime(60)]

          The parsed ‘If-Modified-Since’ header as a datetime object.

          Changed in version 2.0: The datetime object is timezone-aware.

      -- Property: if_none_match: werkzeug.datastructures.ETags(61)

          An object containing all the etags in the ‘If-None-Match’
          header.

          Return type: ETags(62)

      -- Property: if_range: werkzeug.datastructures.IfRange(63)

          The parsed ‘If-Range’ header.

          Changed in version 2.0: ‘IfRange.date’ is timezone-aware.

          New in version 0.7.

      -- Property: if_unmodified_since: Optional[datetime.datetime(64)]

          The parsed ‘If-Unmodified-Since’ header as a datetime object.

          Changed in version 2.0: The datetime object is timezone-aware.

      -- Attribute: input_stream

          The WSGI input stream.

          In general it’s a bad idea to use this one because you can
          easily read past the boundary.  Use the stream.
          instead.

      -- Property: is_json: bool(65)

          Check if the mimetype indicates JSON data, either
          ‘application/json’ or ‘application/*+json’.

      -- Attribute: is_multiprocess

          boolean that is ‘True’ if the application is served by a WSGI
          server that spawns multiple processes.

      -- Attribute: is_multithread

          boolean that is ‘True’ if the application is served by a
          multithreaded WSGI server.

      -- Attribute: is_run_once

          boolean that is ‘True’ if the application will be executed
          only once in a process lifetime.  This is the case for CGI for
          example, but it’s not guaranteed that the execution only
          happens one time.

      -- Property: is_secure: bool(66)

          ‘True’ if the request was made with a secure protocol (HTTPS
          or WSS).

      -- Property: json: Optional[Any]

          The parsed JSON data if mimetype indicates JSON
          (‘application/json’, see is_json).

          Calls get_json() with default arguments.

          If the request content type is not ‘application/json’, this
          will raise a 400 Bad Request error.

          Changed in version 2.1: Raise a 400 error if the content type
          is incorrect.

      -- Attribute: list_storage_class

          alias of werkzeug.datastructures.ImmutableList(67)

      -- Method: make_form_data_parser ()

          Creates the form data parser.  Instantiates the
          form_data_parser_class with some parameters.

          New in version 0.8.

          Return type: werkzeug.formparser.FormDataParser(68)

      -- Property: max_content_length: Optional[int(69)]

          Read-only view of the ‘MAX_CONTENT_LENGTH’ config key.

      -- Attribute: max_forwards

          The Max-Forwards request-header field provides a mechanism
          with the TRACE and OPTIONS methods to limit the number of
          proxies or gateways that can forward the request to the next
          inbound server.

      -- Attribute: method

          The method the request was made with, such as ‘GET’.

      -- Property: mimetype: str(70)

          Like content_type, but without parameters (eg,
          without charset, type etc.)  and always lowercase.  For
          example if the content type is ‘text/HTML; charset=utf-8’ the
          mimetype would be ‘'text/html'’.

      -- Property: mimetype_params: Dict[str(71), str(72)]

          The mimetype parameters as dict.  For example if the content
          type is ‘text/html; charset=utf-8’ the params would be
          ‘{'charset': 'utf-8'}’.

      -- Method: on_json_loading_failed (e)

          Called if get_json() fails and isn’t silenced.

          If this method returns a value, it is used as the return value
          for get_json().  The default implementation raises
          BadRequest(73).

          Parameters: ‘e’ (Optional(74)‘[’ValueError(75)‘]’) – If
          parsing failed, this is the exception.  It will be ‘None’ if
          the content type wasn’t ‘application/json’.

          Return type: Any(76)

      -- Attribute: origin

          The host that the request originated from.  Set
          ‘access_control_allow_origin’ on the response to indicate
          which origins are allowed.

      -- Attribute: parameter_storage_class

          alias of werkzeug.datastructures.ImmutableMultiDict(77)

      -- Attribute: path

          The path part of the URL after root_path.  This is
          the path used for routing within the application.

      -- Property: pragma: werkzeug.datastructures.HeaderSet(78)

          The Pragma general-header field is used to include
          implementation-specific directives that might apply to any
          recipient along the request/response chain.  All pragma
          directives specify optional behavior from the viewpoint of the
          protocol; however, some systems MAY require that behavior be
          consistent with the directives.

      -- Attribute: query_string

          The part of the URL after the “?”.  This is the raw value, use
          args for the parsed values.

      -- Property: range: Optional[werkzeug.datastructures.Range(79)]

          The parsed ‘Range’ header.

          New in version 0.7.

          Return type: Range(80)

      -- Attribute: referrer

          The Referer[sic] request-header field allows the client to
          specify, for the server’s benefit, the address (URI) of the
          resource from which the Request-URI was obtained (the
          “referrer”, although the header field is misspelled).

      -- Attribute: remote_addr

          The address of the client sending the request.

      -- Attribute: remote_user

          If the server supports user authentication, and the script is
          protected, this attribute contains the username the user has
          authenticated as.

      -- Attribute: root_path

          The prefix that the application is mounted under, without a
          trailing slash.  path comes after this.

      -- Property: root_url: str(81)

          The request URL scheme, host, and root path.  This is the root
          that the application is accessed from.

      -- Attribute: routing_exception: Optional[Exception(82)] = None

          If matching the URL failed, this is the exception that will be
          raised / was raised as part of the request handling.  This is
          usually a NotFound(83) exception or something similar.

      -- Attribute: scheme

          The URL scheme of the protocol the request used, such as
          ‘https’ or ‘wss’.

      -- Property: script_root: str(84)

          Alias for ‘self.root_path’.  ‘environ["SCRIPT_ROOT"]’ without
          a trailing slash.

      -- Attribute: server

          The address of the server.  ‘(host, port)’, ‘(path, None)’ for
          unix sockets, or ‘None’ if not known.

      -- Attribute: shallow: bool(85)

          Set when creating the request object.  If ‘True’, reading from
          the request body will cause a ‘RuntimeException’.  Useful to
          prevent modifying the stream from middleware.

      -- Property: stream: IO[bytes(86)]

          If the incoming form data was not encoded with a known
          mimetype the data is stored unmodified in this stream for
          consumption.  Most of the time it is a better idea to use
          data which will give you that data as a string.
          The stream only returns the data once.

          Unlike input_stream this stream is properly
          guarded that you can’t accidentally read past the length of
          the input.  Werkzeug will internally always refer to this
          stream to read data which makes it possible to wrap this
          object with a stream that does filtering.

          Changed in version 0.9: This stream is now always available
          but might be consumed by the form parser later on.  Previously
          the stream was only set if no parsing happened.

      -- Property: url: str(87)

          The full request URL with the scheme, host, root path, path,
          and query string.

      -- Property: url_charset: str(88)

          The charset that is assumed for URLs.  Defaults to the value
          of ‘charset’.

          New in version 0.6.

      -- Property: url_root: str(89)

          Alias for root_url.  The URL with scheme, host, and
          root path.  For example, ‘https://example.com/app/’.

      -- Attribute: url_rule: Optional[Rule] = None

          The internal URL rule that matched the request.  This can be
          useful to inspect which methods are allowed for the URL from a
          before/after handler (‘request.url_rule.methods’) etc.  Though
          if the request’s method was invalid for the URL rule, the
          valid list is available in ‘routing_exception.valid_methods’
          instead (an attribute of the Werkzeug exception
          MethodNotAllowed(90)) because the request was never internally
          bound.

          New in version 0.6.

      -- Property: user_agent: werkzeug.user_agent.UserAgent(91)

          The user agent.  Use ‘user_agent.string’ to get the header
          value.  Set user_agent_class to a subclass of
          UserAgent(92) to provide parsing for the other properties or
          other extended data.

          Changed in version 2.0: The built in parser is deprecated and
          will be removed in Werkzeug 2.1.  A ‘UserAgent’ subclass must
          be set to parse data from the string.

      -- Attribute: user_agent_class

          alias of werkzeug.user_agent.UserAgent(93)

      -- Property: values: CombinedMultiDict[str(94), str(95)]

          A werkzeug.datastructures.CombinedMultiDict(96) that combines
          args and form.

          For GET requests, only ‘args’ are present, not ‘form’.

          Changed in version 2.0: For GET requests, only ‘args’ are
          present, not ‘form’.

      -- Attribute: view_args: Optional[Dict[str(97), Any]] = None

          A dict of view arguments that matched the request.  If an
          exception happened when matching, this will be ‘None’.

      -- Property: want_form_data_parsed: bool(98)

          ‘True’ if the request method carries content.  By default this
          is true if a ‘Content-Type’ is sent.

          New in version 0.8.

 -- Attribute: flask.request

     To access incoming request data, you can use the global ‘request’
     object.  Flask parses incoming request data for you and gives you
     access to it through that global object.  Internally Flask makes
     sure that you always get the correct data for the active thread if
     you are in a multithreaded environment.

     This is a proxy.  See Notes On Proxies for more
     information.

     The request object is an instance of a Request.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/wrappers/#werkzeug.wrappers.Request

   (2) https://docs.python.org/3/library/functions.html#bool

   (3) https://docs.python.org/3/library/functions.html#bool

   (4)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.CharsetAccept

   (5)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.CharsetAccept

   (6)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.Accept

   (7)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.LanguageAccept

   (8)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.LanguageAccept

   (9)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.MIMEAccept

   (10)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.MIMEAccept

   (11) https://docs.python.org/3/library/stdtypes.html#str

   (12) https://docs.python.org/3/library/typing.html#typing.Callable

   (13) https://docs.python.org/3/library/stdtypes.html#str

   (14) https://docs.python.org/3/library/stdtypes.html#str

   (15)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ImmutableMultiDict

   (16)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.Authorization

   (17) https://docs.python.org/3/library/stdtypes.html#str

   (18) https://docs.python.org/3/library/stdtypes.html#str

   (19) https://docs.python.org/3/library/stdtypes.html#str

   (20)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.RequestCacheControl

   (21)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.RequestCacheControl

   (22) https://docs.python.org/3/library/functions.html#int

   (23) https://docs.python.org/3/library/stdtypes.html#str

   (24) https://docs.python.org/3/library/stdtypes.html#str

   (25) https://docs.python.org/3/library/stdtypes.html#dict

   (26) https://docs.python.org/3/library/stdtypes.html#bytes

   (27)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ImmutableMultiDict

   (28) https://docs.python.org/3/library/stdtypes.html#str

   (29) https://docs.python.org/3/library/stdtypes.html#str

   (30)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.MultiDict

   (31)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.FileStorage

   (32)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.FileStorage.save

   (33)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.MultiDict

   (34)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.FileStorage

   (35) https://docs.python.org/3/library/stdtypes.html#str

   (36) https://docs.python.org/3/library/stdtypes.html#str

   (37)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ImmutableMultiDict

   (38)
https://werkzeug.palletsprojects.com/en/2.1.x/http/#werkzeug.formparser.FormDataParser

   (39)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.EnvironBuilder

   (40)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.EnvironBuilder

   (41) https://docs.python.org/3/library/typing.html#typing.Any

   (42) https://docs.python.org/3/library/typing.html#typing.Any

   (43)
https://werkzeug.palletsprojects.com/en/2.1.x/wrappers/#werkzeug.wrappers.Request

   (44) https://docs.python.org/3/library/stdtypes.html#str

   (45) https://docs.python.org/3/library/functions.html#bool

   (46) https://docs.python.org/3/library/functions.html#bool

   (47) https://docs.python.org/3/library/functions.html#bool

   (48) https://docs.python.org/3/library/typing.html#typing.Union

   (49) https://docs.python.org/3/library/stdtypes.html#bytes

   (50) https://docs.python.org/3/library/stdtypes.html#str

   (51) https://docs.python.org/3/library/functions.html#bool

   (52) https://docs.python.org/3/library/functions.html#bool

   (53) https://docs.python.org/3/library/functions.html#bool

   (54) https://docs.python.org/3/library/typing.html#typing.Optional

   (55) https://docs.python.org/3/library/typing.html#typing.Any

   (56) https://docs.python.org/3/library/stdtypes.html#str

   (57) https://docs.python.org/3/library/stdtypes.html#str

   (58)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ETags

   (59)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ETags

   (60)
https://docs.python.org/3/library/datetime.html#datetime.datetime

   (61)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ETags

   (62)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ETags

   (63)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.IfRange

   (64)
https://docs.python.org/3/library/datetime.html#datetime.datetime

   (65) https://docs.python.org/3/library/functions.html#bool

   (66) https://docs.python.org/3/library/functions.html#bool

   (67)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ImmutableList

   (68)
https://werkzeug.palletsprojects.com/en/2.1.x/http/#werkzeug.formparser.FormDataParser

   (69) https://docs.python.org/3/library/functions.html#int

   (70) https://docs.python.org/3/library/stdtypes.html#str

   (71) https://docs.python.org/3/library/stdtypes.html#str

   (72) https://docs.python.org/3/library/stdtypes.html#str

   (73)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.BadRequest

   (74) https://docs.python.org/3/library/typing.html#typing.Optional

   (75) https://docs.python.org/3/library/exceptions.html#ValueError

   (76) https://docs.python.org/3/library/typing.html#typing.Any

   (77)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ImmutableMultiDict

   (78)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.HeaderSet

   (79)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.Range

   (80)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.Range

   (81) https://docs.python.org/3/library/stdtypes.html#str

   (82) https://docs.python.org/3/library/exceptions.html#Exception

   (83)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.NotFound

   (84) https://docs.python.org/3/library/stdtypes.html#str

   (85) https://docs.python.org/3/library/functions.html#bool

   (86) https://docs.python.org/3/library/stdtypes.html#bytes

   (87) https://docs.python.org/3/library/stdtypes.html#str

   (88) https://docs.python.org/3/library/stdtypes.html#str

   (89) https://docs.python.org/3/library/stdtypes.html#str

   (90)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.MethodNotAllowed

   (91)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.user_agent.UserAgent

   (92)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.user_agent.UserAgent

   (93)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.user_agent.UserAgent

   (94) https://docs.python.org/3/library/stdtypes.html#str

   (95) https://docs.python.org/3/library/stdtypes.html#str

   (96)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.CombinedMultiDict

   (97) https://docs.python.org/3/library/stdtypes.html#str

   (98) https://docs.python.org/3/library/functions.html#bool

2.1.4 Response Objects
----------------------

 -- Class: flask.Response (response=None, status=None, headers=None,
          mimetype=None, content_type=None, direct_passthrough=False)

     The response object that is used by default in Flask.  Works like
     the response object from Werkzeug but is set to have an HTML
     mimetype by default.  Quite often you don’t have to create this
     object yourself because make_response() will take care
     of that for you.

     If you want to replace the response object used you can subclass
     this and set response_class to your subclass.

     Changed in version 1.0: JSON support is added to the response, like
     the request.  This is useful when testing to get the test client
     response data as JSON.

     Changed in version 1.0: Added max_cookie_size.

     Parameters:

        * ‘response’ (Union(1)‘[’Iterable(2)‘[’str(3)‘]’‘,
          ’Iterable(4)‘[’bytes(5)‘]’‘]’) –

        * ‘status’ (Optional(6)‘[’Union(7)‘[’int(8)‘, ’str(9)‘,
          ’http.HTTPStatus(10)‘]’‘]’) –

        * ‘headers’ (werkzeug.datastructures.Headers(11)) –

        * ‘mimetype’ (Optional(12)‘[’str(13)‘]’) –

        * ‘content_type’ (Optional(14)‘[’str(15)‘]’) –

        * ‘direct_passthrough’ (bool(16)) –

     Return type: None

      -- Attribute: accept_ranges

          The ‘Accept-Ranges’ header.  Even though the name would
          indicate that multiple values are supported, it must be one
          string token only.

          The values ‘'bytes'’ and ‘'none'’ are common.

          New in version 0.7.

      -- Property: access_control_allow_credentials: bool(17)

          Whether credentials can be shared by the browser to JavaScript
          code.  As part of the preflight request it indicates whether
          credentials can be used on the cross origin request.

      -- Attribute: access_control_allow_headers

          Which headers can be sent with the cross origin request.

      -- Attribute: access_control_allow_methods

          Which methods can be used for the cross origin request.

      -- Attribute: access_control_allow_origin

          The origin or ‘*’ for any origin that may make cross origin
          requests.

      -- Attribute: access_control_expose_headers

          Which headers can be shared by the browser to JavaScript code.

      -- Attribute: access_control_max_age

          The maximum age in seconds the access control settings can be
          cached for.

      -- Method: add_etag (overwrite=False, weak=False)

          Add an etag for the current response if there is none yet.

          Changed in version 2.0: SHA-1 is used to generate the value.
          MD5 may not be available in some environments.

          Parameters:

             * ‘overwrite’ (bool(18)) –

             * ‘weak’ (bool(19)) –

          Return type: None

      -- Attribute: age

          The Age response-header field conveys the sender’s estimate of
          the amount of time since the response (or its revalidation)
          was generated at the origin server.

          Age values are non-negative decimal integers, representing
          time in seconds.

      -- Property: allow: werkzeug.datastructures.HeaderSet(20)

          The Allow entity-header field lists the set of methods
          supported by the resource identified by the Request-URI. The
          purpose of this field is strictly to inform the recipient of
          valid methods associated with the resource.  An Allow header
          field MUST be present in a 405 (Method Not Allowed) response.

      -- Property: cache_control:
               werkzeug.datastructures.ResponseCacheControl(21)

          The Cache-Control general-header field is used to specify
          directives that MUST be obeyed by all caching mechanisms along
          the request/response chain.

      -- Method: calculate_content_length ()

          Returns the content length if available or ‘None’ otherwise.

          Return type: Optional(22)[int(23)]

      -- Method: call_on_close (func)

          Adds a function to the internal list of functions that should
          be called as part of closing down the response.  Since 0.7
          this function also returns the function that was passed so
          that this can be used as a decorator.

          New in version 0.6.

          Parameters: ‘func’ (Callable(24)‘[’‘[’‘]’‘, ’Any(25)‘]’) –

          Return type: Callable(26)[[], Any(27)]

      -- Method: close ()

          Close the wrapped response if possible.  You can also use the
          object in a with statement which will automatically close it.

          New in version 0.9: Can now be used in a with statement.

          Return type: None

      -- Attribute: content_encoding

          The Content-Encoding entity-header field is used as a modifier
          to the media-type.  When present, its value indicates what
          additional content codings have been applied to the
          entity-body, and thus what decoding mechanisms must be applied
          in order to obtain the media-type referenced by the
          Content-Type header field.

      -- Property: content_language:
               werkzeug.datastructures.HeaderSet(28)

          The Content-Language entity-header field describes the natural
          language(s) of the intended audience for the enclosed entity.
          Note that this might not be equivalent to all the languages
          used within the entity-body.

      -- Attribute: content_length

          The Content-Length entity-header field indicates the size of
          the entity-body, in decimal number of OCTETs, sent to the
          recipient or, in the case of the HEAD method, the size of the
          entity-body that would have been sent had the request been a
          GET.

      -- Attribute: content_location

          The Content-Location entity-header field MAY be used to supply
          the resource location for the entity enclosed in the message
          when that entity is accessible from a location separate from
          the requested resource’s URI.

      -- Attribute: content_md5

          The Content-MD5 entity-header field, as defined in RFC 1864,
          is an MD5 digest of the entity-body for the purpose of
          providing an end-to-end message integrity check (MIC) of the
          entity-body.  (Note: a MIC is good for detecting accidental
          modification of the entity-body in transit, but is not proof
          against malicious attacks.)

      -- Property: content_range:
               werkzeug.datastructures.ContentRange(29)

          The ‘Content-Range’ header as a ContentRange(30) object.
          Available even if the header is not set.

          New in version 0.7.

      -- Property: content_security_policy:
               werkzeug.datastructures.ContentSecurityPolicy

          The ‘Content-Security-Policy’ header as a
          ‘ContentSecurityPolicy’ object.  Available even if the header
          is not set.

          The Content-Security-Policy header adds an additional layer of
          security to help detect and mitigate certain types of attacks.

      -- Property: content_security_policy_report_only:
               werkzeug.datastructures.ContentSecurityPolicy

          The ‘Content-Security-policy-report-only’ header as a
          ‘ContentSecurityPolicy’ object.  Available even if the header
          is not set.

          The Content-Security-Policy-Report-Only header adds a csp
          policy that is not enforced but is reported thereby helping
          detect certain types of attacks.

      -- Attribute: content_type

          The Content-Type entity-header field indicates the media type
          of the entity-body sent to the recipient or, in the case of
          the HEAD method, the media type that would have been sent had
          the request been a GET.

      -- Attribute: cross_origin_embedder_policy

          Prevents a document from loading any cross-origin resources
          that do not explicitly grant the document permission.  Values
          must be a member of the ‘werkzeug.http.COEP’ enum.

      -- Attribute: cross_origin_opener_policy

          Allows control over sharing of browsing context group with
          cross-origin documents.  Values must be a member of the
          ‘werkzeug.http.COOP’ enum.

      -- Property: data: Union[bytes(31), str(32)]

          A descriptor that calls get_data() and
          set_data().

      -- Attribute: date

          The Date general-header field represents the date and time at
          which the message was originated, having the same semantics as
          orig-date in RFC 822.

          Changed in version 2.0: The datetime object is timezone-aware.

      -- Method: delete_cookie (key, path='/', domain=None,
               secure=False, httponly=False, samesite=None)

          Delete a cookie.  Fails silently if key doesn’t exist.

          Parameters:

             * ‘key’ (str(33)) – the key (name) of the cookie to be
               deleted.

             * ‘path’ (str(34)) – if the cookie that should be deleted
               was limited to a path, the path has to be defined here.

             * ‘domain’ (Optional(35)‘[’str(36)‘]’) – if the cookie that
               should be deleted was limited to a domain, that domain
               has to be defined here.

             * ‘secure’ (bool(37)) – If ‘True’, the cookie will only be
               available via HTTPS.

             * ‘httponly’ (bool(38)) – Disallow JavaScript access to the
               cookie.

             * ‘samesite’ (Optional(39)‘[’str(40)‘]’) – Limit the scope
               of the cookie to only be attached to requests that are
               “same-site”.

          Return type: None

      -- Attribute: direct_passthrough

          Pass the response body directly through as the WSGI iterable.
          This can be used when the body is a binary file or other
          iterator of bytes, to skip some unnecessary checks.  Use
          send_file()(41) instead of setting this manually.

      -- Attribute: expires

          The Expires entity-header field gives the date/time after
          which the response is considered stale.  A stale cache entry
          may not normally be returned by a cache.

          Changed in version 2.0: The datetime object is timezone-aware.

      -- Method: classmethod force_type (response, environ=None)

          Enforce that the WSGI response is a response object of the
          current type.  Werkzeug will use the Response.
          internally in many situations like the exceptions.  If you
          call ‘get_response()’ on an exception you will get back a
          regular Response object, even if you are using a
          custom subclass.

          This method can enforce a given response type, and it will
          also convert arbitrary WSGI callables into response objects if
          an environ is provided:

               # convert a Werkzeug response object into an instance of the
               # MyResponseClass subclass.
               response = MyResponseClass.force_type(response)

               # convert any WSGI application into a response object
               response = MyResponseClass.force_type(response, environ)

          This is especially useful if you want to post-process
          responses in the main dispatcher and use functionality
          provided by your subclass.

          Keep in mind that this will modify response objects in place
          if possible!

          Parameters:

             * ‘response’ (see Response) – a response object or
               wsgi application.

             * ‘environ’ (Optional(42)‘[’‘WSGIEnvironment’‘]’) – a WSGI
               environment object.

          Returns: a response object.

          Return type: Response.

      -- Method: freeze ()

          Make the response object ready to be pickled.  Does the
          following:

             * Buffer the response into a list, ignoring
               ‘implicity_sequence_conversion’ and
               direct_passthrough.

             * Set the ‘Content-Length’ header.

             * Generate an ‘ETag’ header if one is not already set.

          Changed in version 2.1: Removed the ‘no_etag’ parameter.

          Changed in version 2.0: An ‘ETag’ header is added, the
          ‘no_etag’ parameter is deprecated and will be removed in
          Werkzeug 2.1.

          Changed in version 0.6: The ‘Content-Length’ header is set.

          Return type: None

      -- Method: classmethod from_app (app, environ, buffered=False)

          Create a new response object from an application output.  This
          works best if you pass it an application that returns a
          generator all the time.  Sometimes applications may use the
          ‘write()’ callable returned by the ‘start_response’ function.
          This tries to resolve such edge cases automatically.  But if
          you don’t get the expected output you should set ‘buffered’ to
          ‘True’ which enforces buffering.

          Parameters:

             * ‘app’ (‘WSGIApplication’) – the WSGI application to
               execute.

             * ‘environ’ (‘WSGIEnvironment’) – the WSGI environment to
               execute against.

             * ‘buffered’ (bool(43)) – set to ‘True’ to enforce
               buffering.

          Returns: a response object.

          Return type: Response.

      -- Method: get_app_iter (environ)

          Returns the application iterator for the given environ.
          Depending on the request method and the current status code
          the return value might be an empty response rather than the
          one from the response.

          If the request method is ‘HEAD’ or the status code is in a
          range where the HTTP specification requires an empty response,
          an empty iterable is returned.

          New in version 0.6.

          Parameters: ‘environ’ (‘WSGIEnvironment’) – the WSGI
          environment of the request.

          Returns: a response iterable.

          Return type: Iterable(44)[bytes(45)]

      -- Method: get_data (as_text=False)

          The string representation of the response body.  Whenever you
          call this property the response iterable is encoded and
          flattened.  This can lead to unwanted behavior if you stream
          big data.

          This behavior can be disabled by setting
          ‘implicit_sequence_conversion’ to ‘False’.

          If ‘as_text’ is set to ‘True’ the return value will be a
          decoded string.

          New in version 0.9.

          Parameters: ‘as_text’ (bool(46)) –

          Return type: Union(47)[bytes(48), str(49)]

      -- Method: get_etag ()

          Return a tuple in the form ‘(etag, is_weak)’.  If there is no
          ETag the return value is ‘(None, None)’.

          Return type: Union(50)[Tuple(51)[str(52), bool(53)],
          Tuple(54)[None, None]]

      -- Method: get_json (force=False, silent=False)

          Parse data as JSON. Useful during testing.

          If the mimetype does not indicate JSON (‘application/json’,
          see is_json), this returns ‘None’.

          Unlike Request.get_json(), the result is not
          cached.

          Parameters:

             * ‘force’ (bool(55)) – Ignore the mimetype and always try
               to parse JSON.

             * ‘silent’ (bool(56)) – Silence parsing errors and return
               ‘None’ instead.

          Return type: Optional(57)[Any(58)]

      -- Method: get_wsgi_headers (environ)

          This is automatically called right before the response is
          started and returns headers modified for the given
          environment.  It returns a copy of the headers from the
          response with some modifications applied if necessary.

          For example the location header (if present) is joined with
          the root URL of the environment.  Also the content length is
          automatically set to zero here for certain status codes.

          Changed in version 0.6: Previously that function was called
          ‘fix_headers’ and modified the response object in place.  Also
          since 0.6, IRIs in location and content-location headers are
          handled properly.

          Also starting with 0.6, Werkzeug will attempt to set the
          content length if it is able to figure it out on its own.
          This is the case if all the strings in the response iterable
          are already encoded and the iterable is buffered.

          Parameters: ‘environ’ (‘WSGIEnvironment’) – the WSGI
          environment of the request.

          Returns: returns a new Headers(59) object.

          Return type: werkzeug.datastructures.Headers(60)

      -- Method: get_wsgi_response (environ)

          Returns the final WSGI response as tuple.  The first item in
          the tuple is the application iterator, the second the status
          and the third the list of headers.  The response returned is
          created specially for the given environment.  For example if
          the request method in the WSGI environment is ‘'HEAD'’ the
          response will be empty and only the headers and status code
          will be present.

          New in version 0.6.

          Parameters: ‘environ’ (‘WSGIEnvironment’) – the WSGI
          environment of the request.

          Returns: an ‘(app_iter, status, headers)’ tuple.

          Return type: Tuple(61)[Iterable(62)[bytes(63)], str(64),
          List(65)[Tuple(66)[str(67), str(68)]]]

      -- Property: is_json: bool(69)

          Check if the mimetype indicates JSON data, either
          ‘application/json’ or ‘application/*+json’.

      -- Property: is_sequence: bool(70)

          If the iterator is buffered, this property will be ‘True’.  A
          response object will consider an iterator to be buffered if
          the response attribute is a list or tuple.

          New in version 0.6.

      -- Property: is_streamed: bool(71)

          If the response is streamed (the response is not an iterable
          with a length information) this property is ‘True’.  In this
          case streamed means that there is no information about the
          number of iterations.  This is usually ‘True’ if a generator
          is passed to the response object.

          This is useful for checking before applying some sort of post
          filtering that should not take place for streamed responses.

      -- Method: iter_encoded ()

          Iter the response encoded with the encoding of the response.
          If the response object is invoked as WSGI application the
          return value of this method is used as application iterator
          unless direct_passthrough was activated.

          Return type: Iterator(72)[bytes(73)]

      -- Property: json: Optional[Any]

          The parsed JSON data if mimetype indicates JSON
          (‘application/json’, see is_json).

          Calls get_json() with default arguments.

      -- Attribute: last_modified

          The Last-Modified entity-header field indicates the date and
          time at which the origin server believes the variant was last
          modified.

          Changed in version 2.0: The datetime object is timezone-aware.

      -- Attribute: location

          The Location response-header field is used to redirect the
          recipient to a location other than the Request-URI for
          completion of the request or identification of a new resource.

      -- Method: make_conditional (request_or_environ,
               accept_ranges=False, complete_length=None)

          Make the response conditional to the request.  This method
          works best if an etag was defined for the response already.
          The ‘add_etag’ method can be used to do that.  If called
          without etag just the date header is set.

          This does nothing if the request method in the request or
          environ is anything but GET or HEAD.

          For optimal performance when handling range requests, it’s
          recommended that your response data object implements
          ‘seekable’, ‘seek’ and ‘tell’ methods as described by
          io.IOBase(74).  Objects returned by ‘wrap_file()’
          automatically implement those methods.

          It does not remove the body of the response because that’s
          something the ‘__call__()’ function does for us automatically.

          Returns self so that you can do ‘return
          resp.make_conditional(req)’ but modifies the object in-place.

          Parameters:

             * ‘request_or_environ’ (Union(75)‘[’‘WSGIEnvironment’‘,
               ’Request.‘]’) – a request object or WSGI
               environment to be used to make the response conditional
               against.

             * ‘accept_ranges’ (Union(76)‘[’bool(77)‘, ’str(78)‘]’) –
               This parameter dictates the value of ‘Accept-Ranges’
               header.  If ‘False’ (default), the header is not set.  If
               ‘True’, it will be set to ‘"bytes"’.  If ‘None’, it will
               be set to ‘"none"’.  If it’s a string, it will use this
               value.

             * ‘complete_length’ (Optional(79)‘[’int(80)‘]’) – Will be
               used only in valid Range Requests.  It will set
               ‘Content-Range’ complete length value and compute
               ‘Content-Length’ real value.  This parameter is mandatory
               for successful Range Requests completion.

          Raises: RequestedRangeNotSatisfiable(81) if ‘Range’ header
          could not be parsed or satisfied.

          Return type: Response.

          Changed in version 2.0: Range processing is skipped if length
          is 0 instead of raising a 416 Range Not Satisfiable error.

      -- Method: make_sequence ()

          Converts the response iterator in a list.  By default this
          happens automatically if required.  If
          ‘implicit_sequence_conversion’ is disabled, this method is not
          automatically called and some properties might raise
          exceptions.  This also encodes all the items.

          New in version 0.6.

          Return type: None

      -- Property: max_cookie_size: int(82)

          Read-only view of the MAX_COOKIE_SIZE config key.

          See ‘max_cookie_size’ in Werkzeug’s docs.

      -- Property: mimetype: Optional[str(83)]

          The mimetype (content type without charset etc.)

      -- Property: mimetype_params: Dict[str(84), str(85)]

          The mimetype parameters as dict.  For example if the content
          type is ‘text/html; charset=utf-8’ the params would be
          ‘{'charset': 'utf-8'}’.

          New in version 0.5.

      -- Property: retry_after: Optional[datetime.datetime(86)]

          The Retry-After response-header field can be used with a 503
          (Service Unavailable) response to indicate how long the
          service is expected to be unavailable to the requesting
          client.

          Time in seconds until expiration or date.

          Changed in version 2.0: The datetime object is timezone-aware.

      -- Method: set_cookie (key, value='', max_age=None, expires=None,
               path='/', domain=None, secure=False, httponly=False,
               samesite=None)

          Sets a cookie.

          A warning is raised if the size of the cookie header exceeds
          max_cookie_size, but the header will still be set.

          Parameters:

             * ‘key’ (str(87)) – the key (name) of the cookie to be set.

             * ‘value’ (str(88)) – the value of the cookie.

             * ‘max_age’
               (Optional(89)‘[’Union(90)‘[’datetime.timedelta(91)‘,
               ’int(92)‘]’‘]’) – should be a number of seconds, or
               ‘None’ (default) if the cookie should last only as long
               as the client’s browser session.

             * ‘expires’ (Optional(93)‘[’Union(94)‘[’str(95)‘,
               ’datetime.datetime(96)‘, ’int(97)‘, ’float(98)‘]’‘]’) –
               should be a ‘datetime’ object or UNIX timestamp.

             * ‘path’ (Optional(99)‘[’str(100)‘]’) – limits the cookie
               to a given path, per default it will span the whole
               domain.

             * ‘domain’ (Optional(101)‘[’str(102)‘]’) – if you want to
               set a cross-domain cookie.  For example,
               ‘domain=".example.com"’ will set a cookie that is
               readable by the domain ‘www.example.com’,
               ‘foo.example.com’ etc.  Otherwise, a cookie will only be
               readable by the domain that set it.

             * ‘secure’ (bool(103)) – If ‘True’, the cookie will only be
               available via HTTPS.

             * ‘httponly’ (bool(104)) – Disallow JavaScript access to
               the cookie.

             * ‘samesite’ (Optional(105)‘[’str(106)‘]’) – Limit the
               scope of the cookie to only be attached to requests that
               are “same-site”.

          Return type: None

      -- Method: set_data (value)

          Sets a new string as response.  The value must be a string or
          bytes.  If a string is set it’s encoded to the charset of the
          response (utf-8 by default).

          New in version 0.9.

          Parameters: ‘value’ (Union(107)‘[’bytes(108)‘, ’str(109)‘]’) –

          Return type: None

      -- Method: set_etag (etag, weak=False)

          Set the etag, and override the old one if there was one.

          Parameters:

             * ‘etag’ (str(110)) –

             * ‘weak’ (bool(111)) –

          Return type: None

      -- Property: status: str(112)

          The HTTP status code as a string.

      -- Property: status_code: int(113)

          The HTTP status code as a number.

      -- Property: stream: werkzeug.wrappers.response.ResponseStream

          The response iterable as write-only stream.

      -- Property: vary: werkzeug.datastructures.HeaderSet(114)

          The Vary field value indicates the set of request-header
          fields that fully determines, while the response is fresh,
          whether a cache is permitted to use the response to reply to a
          subsequent request without revalidation.

      -- Property: www_authenticate:
               werkzeug.datastructures.WWWAuthenticate(115)

          The ‘WWW-Authenticate’ header in a parsed form.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/typing.html#typing.Union

   (2) https://docs.python.org/3/library/typing.html#typing.Iterable

   (3) https://docs.python.org/3/library/stdtypes.html#str

   (4) https://docs.python.org/3/library/typing.html#typing.Iterable

   (5) https://docs.python.org/3/library/stdtypes.html#bytes

   (6) https://docs.python.org/3/library/typing.html#typing.Optional

   (7) https://docs.python.org/3/library/typing.html#typing.Union

   (8) https://docs.python.org/3/library/functions.html#int

   (9) https://docs.python.org/3/library/stdtypes.html#str

   (10) https://docs.python.org/3/library/http.html#http.HTTPStatus

   (11)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.Headers

   (12) https://docs.python.org/3/library/typing.html#typing.Optional

   (13) https://docs.python.org/3/library/stdtypes.html#str

   (14) https://docs.python.org/3/library/typing.html#typing.Optional

   (15) https://docs.python.org/3/library/stdtypes.html#str

   (16) https://docs.python.org/3/library/functions.html#bool

   (17) https://docs.python.org/3/library/functions.html#bool

   (18) https://docs.python.org/3/library/functions.html#bool

   (19) https://docs.python.org/3/library/functions.html#bool

   (20)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.HeaderSet

   (21)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ResponseCacheControl

   (22) https://docs.python.org/3/library/typing.html#typing.Optional

   (23) https://docs.python.org/3/library/functions.html#int

   (24) https://docs.python.org/3/library/typing.html#typing.Callable

   (25) https://docs.python.org/3/library/typing.html#typing.Any

   (26) https://docs.python.org/3/library/typing.html#typing.Callable

   (27) https://docs.python.org/3/library/typing.html#typing.Any

   (28)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.HeaderSet

   (29)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ContentRange

   (30)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.ContentRange

   (31) https://docs.python.org/3/library/stdtypes.html#bytes

   (32) https://docs.python.org/3/library/stdtypes.html#str

   (33) https://docs.python.org/3/library/stdtypes.html#str

   (34) https://docs.python.org/3/library/stdtypes.html#str

   (35) https://docs.python.org/3/library/typing.html#typing.Optional

   (36) https://docs.python.org/3/library/stdtypes.html#str

   (37) https://docs.python.org/3/library/functions.html#bool

   (38) https://docs.python.org/3/library/functions.html#bool

   (39) https://docs.python.org/3/library/typing.html#typing.Optional

   (40) https://docs.python.org/3/library/stdtypes.html#str

   (41)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.utils.send_file

   (42) https://docs.python.org/3/library/typing.html#typing.Optional

   (43) https://docs.python.org/3/library/functions.html#bool

   (44) https://docs.python.org/3/library/typing.html#typing.Iterable

   (45) https://docs.python.org/3/library/stdtypes.html#bytes

   (46) https://docs.python.org/3/library/functions.html#bool

   (47) https://docs.python.org/3/library/typing.html#typing.Union

   (48) https://docs.python.org/3/library/stdtypes.html#bytes

   (49) https://docs.python.org/3/library/stdtypes.html#str

   (50) https://docs.python.org/3/library/typing.html#typing.Union

   (51) https://docs.python.org/3/library/typing.html#typing.Tuple

   (52) https://docs.python.org/3/library/stdtypes.html#str

   (53) https://docs.python.org/3/library/functions.html#bool

   (54) https://docs.python.org/3/library/typing.html#typing.Tuple

   (55) https://docs.python.org/3/library/functions.html#bool

   (56) https://docs.python.org/3/library/functions.html#bool

   (57) https://docs.python.org/3/library/typing.html#typing.Optional

   (58) https://docs.python.org/3/library/typing.html#typing.Any

   (59)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.Headers

   (60)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.Headers

   (61) https://docs.python.org/3/library/typing.html#typing.Tuple

   (62) https://docs.python.org/3/library/typing.html#typing.Iterable

   (63) https://docs.python.org/3/library/stdtypes.html#bytes

   (64) https://docs.python.org/3/library/stdtypes.html#str

   (65) https://docs.python.org/3/library/typing.html#typing.List

   (66) https://docs.python.org/3/library/typing.html#typing.Tuple

   (67) https://docs.python.org/3/library/stdtypes.html#str

   (68) https://docs.python.org/3/library/stdtypes.html#str

   (69) https://docs.python.org/3/library/functions.html#bool

   (70) https://docs.python.org/3/library/functions.html#bool

   (71) https://docs.python.org/3/library/functions.html#bool

   (72) https://docs.python.org/3/library/typing.html#typing.Iterator

   (73) https://docs.python.org/3/library/stdtypes.html#bytes

   (74) https://docs.python.org/3/library/io.html#io.IOBase

   (75) https://docs.python.org/3/library/typing.html#typing.Union

   (76) https://docs.python.org/3/library/typing.html#typing.Union

   (77) https://docs.python.org/3/library/functions.html#bool

   (78) https://docs.python.org/3/library/stdtypes.html#str

   (79) https://docs.python.org/3/library/typing.html#typing.Optional

   (80) https://docs.python.org/3/library/functions.html#int

   (81)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.RequestedRangeNotSatisfiable

   (82) https://docs.python.org/3/library/functions.html#int

   (83) https://docs.python.org/3/library/stdtypes.html#str

   (84) https://docs.python.org/3/library/stdtypes.html#str

   (85) https://docs.python.org/3/library/stdtypes.html#str

   (86)
https://docs.python.org/3/library/datetime.html#datetime.datetime

   (87) https://docs.python.org/3/library/stdtypes.html#str

   (88) https://docs.python.org/3/library/stdtypes.html#str

   (89) https://docs.python.org/3/library/typing.html#typing.Optional

   (90) https://docs.python.org/3/library/typing.html#typing.Union

   (91)
https://docs.python.org/3/library/datetime.html#datetime.timedelta

   (92) https://docs.python.org/3/library/functions.html#int

   (93) https://docs.python.org/3/library/typing.html#typing.Optional

   (94) https://docs.python.org/3/library/typing.html#typing.Union

   (95) https://docs.python.org/3/library/stdtypes.html#str

   (96)
https://docs.python.org/3/library/datetime.html#datetime.datetime

   (97) https://docs.python.org/3/library/functions.html#int

   (98) https://docs.python.org/3/library/functions.html#float

   (99) https://docs.python.org/3/library/typing.html#typing.Optional

   (100) https://docs.python.org/3/library/stdtypes.html#str

   (101) https://docs.python.org/3/library/typing.html#typing.Optional

   (102) https://docs.python.org/3/library/stdtypes.html#str

   (103) https://docs.python.org/3/library/functions.html#bool

   (104) https://docs.python.org/3/library/functions.html#bool

   (105) https://docs.python.org/3/library/typing.html#typing.Optional

   (106) https://docs.python.org/3/library/stdtypes.html#str

   (107) https://docs.python.org/3/library/typing.html#typing.Union

   (108) https://docs.python.org/3/library/stdtypes.html#bytes

   (109) https://docs.python.org/3/library/stdtypes.html#str

   (110) https://docs.python.org/3/library/stdtypes.html#str

   (111) https://docs.python.org/3/library/functions.html#bool

   (112) https://docs.python.org/3/library/stdtypes.html#str

   (113) https://docs.python.org/3/library/functions.html#int

   (114)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.HeaderSet

   (115)
https://werkzeug.palletsprojects.com/en/2.1.x/datastructures/#werkzeug.datastructures.WWWAuthenticate

2.1.5 Sessions
--------------

If you have set Flask.secret_key. (or configured it from
SECRET_KEY) you can use sessions in Flask applications.  A
session makes it possible to remember information from one request to
another.  The way Flask does this is by using a signed cookie.  The user
can look at the session contents, but can’t modify it unless they know
the secret key, so make sure to set that to something complex and
unguessable.

To access the current session you can use the session object:

 -- Class: flask.session

     The session object works pretty much like an ordinary dict, with
     the difference that it keeps track of modifications.

     This is a proxy.  See Notes On Proxies for more
     information.

     The following attributes are interesting:

      -- Attribute: new

          ‘True’ if the session is new, ‘False’ otherwise.

      -- Attribute: modified

          ‘True’ if the session object detected a modification.  Be
          advised that modifications on mutable structures are not
          picked up automatically, in that situation you have to
          explicitly set the attribute to ‘True’ yourself.  Here an
          example:

               # this change is not picked up because a mutable object (here
               # a list) is changed.
               session['objects'].append(42)
               # so mark it as modified yourself
               session.modified = True

      -- Attribute: permanent

          If set to ‘True’ the session lives for
          permanent_session_lifetime seconds.  The default is 31
          days.  If set to ‘False’ (which is the default) the session
          will be deleted when the user closes the browser.

2.1.6 Session Interface
-----------------------

New in version 0.8.

The session interface provides a simple way to replace the session
implementation that Flask is using.

 -- Class: flask.sessions.SessionInterface

     The basic interface you have to implement in order to replace the
     default session interface which uses werkzeug’s securecookie
     implementation.  The only methods you have to implement are
     open_session() and save_session(), the others have
     useful defaults which you don’t need to change.

     The session object returned by the open_session().
     method has to provide a dictionary like interface plus the
     properties and methods from the SessionMixin.  We
     recommend just subclassing a dict and adding that mixin:

          class Session(dict, SessionMixin):
              pass

     If open_session() returns ‘None’ Flask will call into
     make_null_session() to create a session that acts as
     replacement if the session support cannot work because some
     requirement is not fulfilled.  The default NullSession.
     class that is created will complain that the secret key was not
     set.

     To replace the session interface on an application all you have to
     do is to assign flask.Flask.session_interface.:

          app = Flask(__name__)
          app.session_interface = MySessionInterface()

     Multiple requests with the same session may be sent and handled
     concurrently.  When implementing a new session interface, consider
     whether reads or writes to the backing store must be synchronized.
     There is no guarantee on the order in which the session for each
     request is opened or saved, it will occur in the order that
     requests begin and end processing.

     New in version 0.8.

      -- Method: get_cookie_domain (app)

          Returns the domain that should be set for the session cookie.

          Uses ‘SESSION_COOKIE_DOMAIN’ if it is configured, otherwise
          falls back to detecting the domain based on ‘SERVER_NAME’.

          Once detected (or if not set at all), ‘SESSION_COOKIE_DOMAIN’
          is updated to avoid re-running the logic.

          Parameters: ‘app’ (see Flask) –

          Return type: Optional(1)[str(2)]

      -- Method: get_cookie_httponly (app)

          Returns True if the session cookie should be httponly.  This
          currently just returns the value of the
          ‘SESSION_COOKIE_HTTPONLY’ config var.

          Parameters: ‘app’ (see Flask) –

          Return type: bool(3)

      -- Method: get_cookie_name (app)

          Returns the name of the session cookie.

          Uses ‘app.session_cookie_name’ which is set to
          ‘SESSION_COOKIE_NAME’

          Parameters: ‘app’ (see Flask) –

          Return type: str(4)

      -- Method: get_cookie_path (app)

          Returns the path for which the cookie should be valid.  The
          default implementation uses the value from the
          ‘SESSION_COOKIE_PATH’ config var if it’s set, and falls back
          to ‘APPLICATION_ROOT’ or uses ‘/’ if it’s ‘None’.

          Parameters: ‘app’ (see Flask) –

          Return type: str(5)

      -- Method: get_cookie_samesite (app)

          Return ‘'Strict'’ or ‘'Lax'’ if the cookie should use the
          ‘SameSite’ attribute.  This currently just returns the value
          of the SESSION_COOKIE_SAMESITE setting.

          Parameters: ‘app’ (see Flask) –

          Return type: str(6)

      -- Method: get_cookie_secure (app)

          Returns True if the cookie should be secure.  This currently
          just returns the value of the ‘SESSION_COOKIE_SECURE’ setting.

          Parameters: ‘app’ (see Flask) –

          Return type: bool(7)

      -- Method: get_expiration_time (app, session)

          A helper method that returns an expiration date for the
          session or ‘None’ if the session is linked to the browser
          session.  The default implementation returns now + the
          permanent session lifetime configured on the application.

          Parameters:

             * ‘app’ (see Flask) –

             * ‘session’ (see flask.sessions.SessionMixin) –

          Return type: Optional(8)[datetime.datetime(9)]

      -- Method: is_null_session (obj)

          Checks if a given object is a null session.  Null sessions are
          not asked to be saved.

          This checks if the object is an instance of
          null_session_class by default.

          Parameters: ‘obj’ (object(10)) –

          Return type: bool(11)

      -- Method: make_null_session (app)

          Creates a null session which acts as a replacement object if
          the real session support could not be loaded due to a
          configuration error.  This mainly aids the user experience
          because the job of the null session is to still support lookup
          without complaining but modifications are answered with a
          helpful error message of what failed.

          This creates an instance of null_session_class by
          default.

          Parameters: ‘app’ (see Flask) –

          Return type: flask.sessions.NullSession.

      -- Attribute: null_session_class

          make_null_session() will look here for the class
          that should be created when a null session is requested.
          Likewise the is_null_session() method will perform
          a typecheck against this type.

          alias of flask.sessions.NullSession.

      -- Method: open_session (app, request)

          This is called at the beginning of each request, after pushing
          the request context, before matching the URL.

          This must return an object which implements a dictionary-like
          interface as well as the SessionMixin interface.

          This will return ‘None’ to indicate that loading failed in
          some way that is not immediately an error.  The request
          context will fall back to using make_null_session()
          in this case.

          Parameters:

             * ‘app’ (see Flask) –

             * ‘request’ (see Request) –

          Return type: Optional(12)[see flask.sessions.SessionMixin]

      -- Attribute: pickle_based = False

          A flag that indicates if the session interface is pickle
          based.  This can be used by Flask extensions to make a
          decision in regards to how to deal with the session object.

          New in version 0.10.

      -- Method: save_session (app, session, response)

          This is called at the end of each request, after generating a
          response, before removing the request context.  It is skipped
          if is_null_session() returns ‘True’.

          Parameters:

             * ‘app’ (see Flask) –

             * ‘session’ (see flask.sessions.SessionMixin) –

             * ‘response’ (see Response) –

          Return type: None

      -- Method: should_set_cookie (app, session)

          Used by session backends to determine if a ‘Set-Cookie’ header
          should be set for this session cookie for this response.  If
          the session has been modified, the cookie is set.  If the
          session is permanent and the ‘SESSION_REFRESH_EACH_REQUEST’
          config is true, the cookie is always set.

          This check is usually skipped if the session was deleted.

          New in version 0.11.

          Parameters:

             * ‘app’ (see Flask) –

             * ‘session’ (see flask.sessions.SessionMixin) –

          Return type: bool(13)

 -- Class: flask.sessions.SecureCookieSessionInterface

     The default session interface that stores sessions in signed
     cookies through the ‘itsdangerous’ module.

      -- Method: static digest_method (string=b'')

          the hash function to use for the signature.  The default is
          sha1

      -- Attribute: key_derivation = 'hmac'

          the name of the itsdangerous supported key derivation.  The
          default is hmac.

      -- Method: open_session (app, request)

          This is called at the beginning of each request, after pushing
          the request context, before matching the URL.

          This must return an object which implements a dictionary-like
          interface as well as the SessionMixin interface.

          This will return ‘None’ to indicate that loading failed in
          some way that is not immediately an error.  The request
          context will fall back to using ‘make_null_session()’ in this
          case.

          Parameters:

             * ‘app’ (see Flask) –

             * ‘request’ (see Request) –

          Return type: Optional(14)[see
          flask.sessions.SecureCookieSession]

      -- Attribute: salt = 'cookie-session'

          the salt that should be applied on top of the secret key for
          the signing of cookie based sessions.

      -- Method: save_session (app, session, response)

          This is called at the end of each request, after generating a
          response, before removing the request context.  It is skipped
          if ‘is_null_session()’ returns ‘True’.

          Parameters:

             * ‘app’ (see Flask) –

             * ‘session’ (see flask.sessions.SessionMixin) –

             * ‘response’ (see Response) –

          Return type: None

      -- Attribute: serializer = <flask.json.tag.TaggedJSONSerializer
               object>

          A python serializer for the payload.  The default is a compact
          JSON derived serializer with support for some extra Python
          types such as datetime objects or tuples.

      -- Attribute: session_class

          alias of flask.sessions.SecureCookieSession.

 -- Class: flask.sessions.SecureCookieSession (initial=None)

     Base class for sessions based on signed cookies.

     This session backend will set the modified and
     accessed attributes.  It cannot reliably track whether a
     session is new (vs.  empty), so ‘new’ remains hard coded to
     ‘False’.

     Parameters: ‘initial’ (Any(15)) –

     Return type: None

      -- Attribute: accessed = False

          header, which allows caching proxies to cache different pages
          for different users.

      -- Method: get (key, default=None)

          Return the value for key if key is in the dictionary, else
          default.

          Parameters:

             * ‘key’ (str(16)) –

             * ‘default’ (Optional(17)‘[’Any(18)‘]’) –

          Return type: Any(19)

      -- Attribute: modified = False

          When data is changed, this is set to ‘True’.  Only the session
          dictionary itself is tracked; if the session contains mutable
          data (for example a nested dict) then this must be set to
          ‘True’ manually when modifying that data.  The session cookie
          will only be written to the response if this is ‘True’.

      -- Method: setdefault (key, default=None)

          Insert key with a value of default if key is not in the
          dictionary.

          Return the value for key if key is in the dictionary, else
          default.

          Parameters:

             * ‘key’ (str(20)) –

             * ‘default’ (Optional(21)‘[’Any(22)‘]’) –

          Return type: Any(23)

 -- Class: flask.sessions.NullSession (initial=None)

     Class used to generate nicer error messages if sessions are not
     available.  Will still allow read-only access to the empty session
     but fail on setting.

     Parameters: ‘initial’ (Any(24)) –

     Return type: None

      -- Method: clear () -> None. Remove all items from D.

          Parameters:

             * ‘args’ (Any(25)) –

             * ‘kwargs’ (Any(26)) –

          Return type: te.NoReturn

      -- Method: pop (k[, d]) -> v, remove specified key and return the
               corresponding value.

          If key is not found, d is returned if given, otherwise
          KeyError is raised

          Parameters:

             * ‘args’ (Any(27)) –

             * ‘kwargs’ (Any(28)) –

          Return type: te.NoReturn

      -- Method: popitem (*args, **kwargs)

          Remove and return a (key, value) pair as a 2-tuple.

          Pairs are returned in LIFO (last-in, first-out) order.  Raises
          KeyError if the dict is empty.

          Parameters:

             * ‘args’ (Any(29)) –

             * ‘kwargs’ (Any(30)) –

          Return type: te.NoReturn

      -- Method: setdefault (*args, **kwargs)

          Insert key with a value of default if key is not in the
          dictionary.

          Return the value for key if key is in the dictionary, else
          default.

          Parameters:

             * ‘args’ (Any(31)) –

             * ‘kwargs’ (Any(32)) –

          Return type: te.NoReturn

      -- Method: update ([E], **F) -> None. Update D from dict/iterable
               E and F.

          If E is present and has a .keys() method, then does: for k in
          E: D[k] = E[k] If E is present and lacks a .keys() method,
          then does: for k, v in E: D[k] = v In either case, this is
          followed by: for k in F: D[k] = F[k]

          Parameters:

             * ‘args’ (Any(33)) –

             * ‘kwargs’ (Any(34)) –

          Return type: te.NoReturn

 -- Class: flask.sessions.SessionMixin

     Expands a basic dictionary with session attributes.

      -- Attribute: accessed = True

          Some implementations can detect when session data is read or
          written and set this when that happens.  The mixin default is
          hard coded to ‘True’.

      -- Attribute: modified = True

          Some implementations can detect changes to the session and set
          this when that happens.  The mixin default is hard coded to
          ‘True’.

      -- Property: permanent: bool(35)

          This reflects the ‘'_permanent'’ key in the dict.

     Notice: The ‘PERMANENT_SESSION_LIFETIME’ config key can also be an
     integer starting with Flask 0.8.  Either catch this down yourself
     or use the permanent_session_lifetime attribute on the
     app which converts the result to an integer automatically.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/typing.html#typing.Optional

   (2) https://docs.python.org/3/library/stdtypes.html#str

   (3) https://docs.python.org/3/library/functions.html#bool

   (4) https://docs.python.org/3/library/stdtypes.html#str

   (5) https://docs.python.org/3/library/stdtypes.html#str

   (6) https://docs.python.org/3/library/stdtypes.html#str

   (7) https://docs.python.org/3/library/functions.html#bool

   (8) https://docs.python.org/3/library/typing.html#typing.Optional

   (9) https://docs.python.org/3/library/datetime.html#datetime.datetime

   (10) https://docs.python.org/3/library/functions.html#object

   (11) https://docs.python.org/3/library/functions.html#bool

   (12) https://docs.python.org/3/library/typing.html#typing.Optional

   (13) https://docs.python.org/3/library/functions.html#bool

   (14) https://docs.python.org/3/library/typing.html#typing.Optional

   (15) https://docs.python.org/3/library/typing.html#typing.Any

   (16) https://docs.python.org/3/library/stdtypes.html#str

   (17) https://docs.python.org/3/library/typing.html#typing.Optional

   (18) https://docs.python.org/3/library/typing.html#typing.Any

   (19) https://docs.python.org/3/library/typing.html#typing.Any

   (20) https://docs.python.org/3/library/stdtypes.html#str

   (21) https://docs.python.org/3/library/typing.html#typing.Optional

   (22) https://docs.python.org/3/library/typing.html#typing.Any

   (23) https://docs.python.org/3/library/typing.html#typing.Any

   (24) https://docs.python.org/3/library/typing.html#typing.Any

   (25) https://docs.python.org/3/library/typing.html#typing.Any

   (26) https://docs.python.org/3/library/typing.html#typing.Any

   (27) https://docs.python.org/3/library/typing.html#typing.Any

   (28) https://docs.python.org/3/library/typing.html#typing.Any

   (29) https://docs.python.org/3/library/typing.html#typing.Any

   (30) https://docs.python.org/3/library/typing.html#typing.Any

   (31) https://docs.python.org/3/library/typing.html#typing.Any

   (32) https://docs.python.org/3/library/typing.html#typing.Any

   (33) https://docs.python.org/3/library/typing.html#typing.Any

   (34) https://docs.python.org/3/library/typing.html#typing.Any

   (35) https://docs.python.org/3/library/functions.html#bool

2.1.7 Test Client
-----------------

 -- Class: flask.testing.FlaskClient (*args, **kwargs)

     Works like a regular Werkzeug test client but has some knowledge
     about how Flask works to defer the cleanup of the request context
     stack to the end of a ‘with’ body when used in a ‘with’ statement.
     For general information about how to use this class refer to
     werkzeug.test.Client(1).

     Changed in version 0.12: ‘app.test_client()’ includes preset
     default environment, which can be set after instantiation of the
     ‘app.test_client()’ object in ‘client.environ_base’.

     Basic usage is outlined in the Testing Flask Applications
     chapter.

     Parameters:

        * ‘args’ (Any(2)) –

        * ‘kwargs’ (Any(3)) –

     Return type: None

      -- Method: open (*args, buffered=False, follow_redirects=False,
               **kwargs)

          Generate an environ dict from the given arguments, make a
          request to the application using it, and return the response.

          Parameters:

             * ‘args’ (Any(4)) – Passed to ‘EnvironBuilder’ to create
               the environ for the request.  If a single arg is passed,
               it can be an existing ‘EnvironBuilder’ or an environ
               dict.

             * ‘buffered’ (bool(5)) – Convert the iterator returned by
               the app into a list.  If the iterator has a ‘close()’
               method, it is called automatically.

             * ‘follow_redirects’ (bool(6)) – Make additional requests
               to follow HTTP redirects until a non-redirect status is
               returned.  ‘TestResponse.history’ lists the intermediate
               responses.

             * ‘kwargs’ (Any(7)) –

          Return type: TestResponse

          Changed in version 2.1: Removed the ‘as_tuple’ parameter.

          Changed in version 2.0: ‘as_tuple’ is deprecated and will be
          removed in Werkzeug 2.1.  Use ‘TestResponse.request’ and
          ‘request.environ’ instead.

          Changed in version 2.0: The request input stream is closed
          when calling ‘response.close()’.  Input streams for redirects
          are automatically closed.

          Changed in version 0.5: If a dict is provided as file in the
          dict for the ‘data’ parameter the content type has to be
          called ‘content_type’ instead of ‘mimetype’.  This change was
          made for consistency with ‘werkzeug.FileWrapper’.

          Changed in version 0.5: Added the ‘follow_redirects’
          parameter.

      -- Method: session_transaction (*args, **kwargs)

          When used in combination with a ‘with’ statement this opens a
          session transaction.  This can be used to modify the session
          that the test client uses.  Once the ‘with’ block is left the
          session is stored back.

               with client.session_transaction() as session:
                   session['value'] = 42

          Internally this is implemented by going through a temporary
          test request context and since session handling could depend
          on request variables this function accepts the same arguments
          as test_request_context() which are directly passed
          through.

          Parameters:

             * ‘args’ (Any(8)) –

             * ‘kwargs’ (Any(9)) –

          Return type: Generator(10)[see flask.sessions.SessionMixin,
          None, None]

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.Client

   (2) https://docs.python.org/3/library/typing.html#typing.Any

   (3) https://docs.python.org/3/library/typing.html#typing.Any

   (4) https://docs.python.org/3/library/typing.html#typing.Any

   (5) https://docs.python.org/3/library/functions.html#bool

   (6) https://docs.python.org/3/library/functions.html#bool

   (7) https://docs.python.org/3/library/typing.html#typing.Any

   (8) https://docs.python.org/3/library/typing.html#typing.Any

   (9) https://docs.python.org/3/library/typing.html#typing.Any

   (10) https://docs.python.org/3/library/typing.html#typing.Generator

2.1.8 Test CLI Runner
---------------------

 -- Class: flask.testing.FlaskCliRunner (app, **kwargs)

     A CliRunner(1) for testing a Flask app’s CLI commands.  Typically
     created using test_cli_runner().  See Running Commands with the CLI Runner.

     Parameters:

        * ‘app’ (see Flask) –

        * ‘kwargs’ (Any(2)) –

     Return type: None

      -- Method: invoke (cli=None, args=None, **kwargs)

          Invokes a CLI command in an isolated environment.  See
          CliRunner.invoke(3) for full method documentation.  See
          Running Commands with the CLI Runner for examples.

          If the ‘obj’ argument is not given, passes an instance of
          ScriptInfo that knows how to load the Flask app
          being tested.

          Parameters:

             * ‘cli’ (Optional(4)‘[’Any(5)‘]’) – Command object to
               invoke.  Default is the app’s ‘cli’ group.

             * ‘args’ (Optional(6)‘[’Any(7)‘]’) – List of strings to
               invoke the command with.

             * ‘kwargs’ (Any(8)) –

          Returns: a Result(9) object.

          Return type: Any(10)

   ---------- Footnotes ----------

   (1)
https://click.palletsprojects.com/en/8.1.x/api/#click.testing.CliRunner

   (2) https://docs.python.org/3/library/typing.html#typing.Any

   (3)
https://click.palletsprojects.com/en/8.1.x/api/#click.testing.CliRunner.invoke

   (4) https://docs.python.org/3/library/typing.html#typing.Optional

   (5) https://docs.python.org/3/library/typing.html#typing.Any

   (6) https://docs.python.org/3/library/typing.html#typing.Optional

   (7) https://docs.python.org/3/library/typing.html#typing.Any

   (8) https://docs.python.org/3/library/typing.html#typing.Any

   (9)
https://click.palletsprojects.com/en/8.1.x/api/#click.testing.Result

   (10) https://docs.python.org/3/library/typing.html#typing.Any

2.1.9 Application Globals
-------------------------

To share data that is valid for one request only from one function to
another, a global variable is not good enough because it would break in
threaded environments.  Flask provides you with a special object that
ensures it is only valid for the active request and that will return
different values for each request.  In a nutshell: it does the right
thing, like it does for request and session.

 -- Data: flask.g

     A namespace object that can store data during an application context.
     This is an instance of
     Flask.app_ctx_globals_class, which defaults to
     ctx._AppCtxGlobals.

     This is a good place to store resources during a request.  For
     example, a ‘before_request’ function could load a user object from
     a session id, then set ‘g.user’ to be used in the view function.

     This is a proxy.  See Notes On Proxies for more
     information.

     Changed in version 0.10: Bound to the application context instead
     of the request context.

 -- Class: flask.ctx._AppCtxGlobals

     A plain object.  Used as a namespace for storing data during an
     application context.

     Creating an app context automatically creates this object, which is
     made available as the ‘g’ proxy.

      -- Describe: 'key' in g

          Check whether an attribute is present.

          New in version 0.10.

      -- Describe: iter(g)

          Return an iterator over the attribute names.

          New in version 0.10.

      -- Method: get (name, default=None)

          Get an attribute by name, or a default value.  Like
          dict.get()(1).

          Parameters:

             * ‘name’ (str(2)) – Name of attribute to get.

             * ‘default’ (Optional(3)‘[’Any(4)‘]’) – Value to return if
               the attribute is not present.

          Return type: Any(5)

          New in version 0.10.

      -- Method: pop (name, default=<object object>)

          Get and remove an attribute by name.  Like dict.pop()(6).

          Parameters:

             * ‘name’ (str(7)) – Name of attribute to pop.

             * ‘default’ (Any(8)) – Value to return if the attribute is
               not present, instead of raising a ‘KeyError’.

          Return type: Any(9)

          New in version 0.11.

      -- Method: setdefault (name, default=None)

          Get the value of an attribute if it is present, otherwise set
          and return a default value.  Like dict.setdefault()(10).

          Parameters:

             * ‘name’ (str(11)) – Name of attribute to get.

             * ‘default’ (Optional(12)‘[’Any(13)‘]’) – Value to set and
               return if the attribute is not present.

          Return type: Any(14)

          New in version 0.11.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#dict.get

   (2) https://docs.python.org/3/library/stdtypes.html#str

   (3) https://docs.python.org/3/library/typing.html#typing.Optional

   (4) https://docs.python.org/3/library/typing.html#typing.Any

   (5) https://docs.python.org/3/library/typing.html#typing.Any

   (6) https://docs.python.org/3/library/stdtypes.html#dict.pop

   (7) https://docs.python.org/3/library/stdtypes.html#str

   (8) https://docs.python.org/3/library/typing.html#typing.Any

   (9) https://docs.python.org/3/library/typing.html#typing.Any

   (10) https://docs.python.org/3/library/stdtypes.html#dict.setdefault

   (11) https://docs.python.org/3/library/stdtypes.html#str

   (12) https://docs.python.org/3/library/typing.html#typing.Optional

   (13) https://docs.python.org/3/library/typing.html#typing.Any

   (14) https://docs.python.org/3/library/typing.html#typing.Any

2.1.10 Useful Functions and Classes
-----------------------------------

 -- Data: flask.current_app

     A proxy to the application handling the current request.  This is
     useful to access the application without needing to import it, or
     if it can’t be imported, such as when using the application factory
     pattern or in blueprints and extensions.

     This is only available when an application context is
     pushed.  This happens automatically during requests and CLI
     commands.  It can be controlled manually with app_context().

     This is a proxy.  See Notes On Proxies for more
     information.

 -- Function: flask.has_request_context ()

     If you have code that wants to test if a request context is there
     or not this function can be used.  For instance, you may want to
     take advantage of request information if the request object is
     available, but fail silently if it is unavailable.

          class User(db.Model):

              def __init__(self, username, remote_addr=None):
                  self.username = username
                  if remote_addr is None and has_request_context():
                      remote_addr = request.remote_addr
                  self.remote_addr = remote_addr

     Alternatively you can also just test any of the context bound
     objects (such as request or g) for truthness:

          class User(db.Model):

              def __init__(self, username, remote_addr=None):
                  self.username = username
                  if remote_addr is None and request:
                      remote_addr = request.remote_addr
                  self.remote_addr = remote_addr

     New in version 0.7.

     Return type: bool(1)

 -- Function: flask.copy_current_request_context (f)

     A helper function that decorates a function to retain the current
     request context.  This is useful when working with greenlets.  The
     moment the function is decorated a copy of the request context is
     created and then pushed when the function is called.  The current
     session is also included in the copied request context.

     Example:

          import gevent
          from flask import copy_current_request_context

          @app.route('/')
          def index():
              @copy_current_request_context
              def do_some_work():
                  # do some work here, it can access flask.request or
                  # flask.session like you would otherwise in the view function.
                  ...
              gevent.spawn(do_some_work)
              return 'Regular response'

     New in version 0.10.

     Parameters: ‘f’ (Callable(2)) –

     Return type: Callable(3)

 -- Function: flask.has_app_context ()

     Works like has_request_context() but for the
     application context.  You can also just do a boolean check on the
     current_app object instead.

     New in version 0.9.

     Return type: bool(4)

 -- Function: flask.url_for (endpoint, **values)

     Generates a URL to the given endpoint with the method provided.

     Variable arguments that are unknown to the target endpoint are
     appended to the generated URL as query arguments.  If the value of
     a query argument is ‘None’, the whole pair is skipped.  In case
     blueprints are active you can shortcut references to the same
     blueprint by prefixing the local endpoint with a dot (‘.’).

     This will reference the index function local to the current
     blueprint:

          url_for('.index')

     See URL Building.

     Configuration values ‘APPLICATION_ROOT’ and ‘SERVER_NAME’ are only
     used when generating URLs outside of a request context.

     To integrate applications, see Flask has a hook to intercept
     URL build errors through Flask.url_build_error_handlers.
     The ‘url_for’ function results in a ‘BuildError’ when the current
     app does not have a URL for the given endpoint and values.  When it
     does, the current_app calls its
     url_build_error_handlers if it is not ‘None’, which can
     return a string to use as the result of ‘url_for’ (instead of
     ‘url_for’’s default to raise the ‘BuildError’ exception) or
     re-raise the exception.  An example:

          def external_url_handler(error, endpoint, values):
              "Looks up an external URL when `url_for` cannot build a URL."
              # This is an example of hooking the build_error_handler.
              # Here, lookup_url is some utility function you've built
              # which looks up the endpoint in some external URL registry.
              url = lookup_url(endpoint, **values)
              if url is None:
                  # External lookup did not have a URL.
                  # Re-raise the BuildError, in context of original traceback.
                  exc_type, exc_value, tb = sys.exc_info()
                  if exc_value is error:
                      raise exc_type(exc_value).with_traceback(tb)
                  else:
                      raise error
              # url_for will use this result, instead of raising BuildError.
              return url

          app.url_build_error_handlers.append(external_url_handler)

     Here, ‘error’ is the instance of ‘BuildError’, and ‘endpoint’ and
     ‘values’ are the arguments passed into ‘url_for’.  Note that this
     is for building URLs outside the current application, and not for
     handling 404 NotFound errors.

     New in version 0.10: The ‘_scheme’ parameter was added.

     New in version 0.9: The ‘_anchor’ and ‘_method’ parameters were
     added.

     New in version 0.9: Calls ‘Flask.handle_build_error()’ on
     ‘BuildError’.

     Parameters:

        * ‘endpoint’ (str(5)) – the endpoint of the URL (name of the
          function)

        * ‘values’ (Any(6)) – the variable arguments of the URL rule

        * ‘_external’ – if set to ‘True’, an absolute URL is generated.
          Server address can be changed via ‘SERVER_NAME’ configuration
          variable which falls back to the ‘Host’ header, then to the IP
          and port of the request.

        * ‘_scheme’ – a string specifying the desired URL scheme.  The
          ‘_external’ parameter must be set to ‘True’ or a ValueError(7)
          is raised.  The default behavior uses the same scheme as the
          current request, or PREFERRED_URL_SCHEME if no
          request context is available.  This also can be set to an
          empty string to build protocol-relative URLs.

        * ‘_anchor’ – if provided this is added as anchor to the URL.

        * ‘_method’ – if provided this explicitly specifies an HTTP
          method.

     Return type: str(8)

 -- Function: flask.abort (status, *args, **kwargs)

     Raises an ‘HTTPException’ for the given status code or WSGI
     application.

     If a status code is given, it will be looked up in the list of
     exceptions and will raise that exception.  If passed a WSGI
     application, it will wrap it in a proxy WSGI exception and raise
     that:

          abort(404)  # 404 Not Found
          abort(Response('Hello World'))

     Parameters:

        * ‘status’ (Union(9)‘[’int(10)‘, ’Response.‘]’) –

        * ‘args’ (Any(11)) –

        * ‘kwargs’ (Any(12)) –

     Return type: te.NoReturn

 -- Function: flask.redirect (location, code=302, Response=None)

     Returns a response object (a WSGI application) that, if called,
     redirects the client to the target location.  Supported codes are
     301, 302, 303, 305, 307, and 308.  300 is not supported because
     it’s not a real redirect and 304 because it’s the answer for a
     request with a request with defined If-Modified-Since headers.

     New in version 0.10: The class used for the Response object can now
     be passed in.

     New in version 0.6: The location can now be a unicode string that
     is encoded using the ‘iri_to_uri()’ function.

     Parameters:

        * ‘location’ (str(13)) – the location the response should
          redirect to.

        * ‘code’ (int(14)) – the redirect status code.  defaults to 302.

        * ‘Response’ (‘class’) – a Response class to use when
          instantiating a response.  The default is
          werkzeug.wrappers.Response(15) if unspecified.

     Return type: Response.

 -- Function: flask.make_response (*args)

     Sometimes it is necessary to set additional headers in a view.
     Because views do not have to return response objects but can return
     a value that is converted into a response object by Flask itself,
     it becomes tricky to add headers to it.  This function can be
     called instead of using a return and you will get a response object
     which you can use to attach headers.

     If view looked like this and you want to add a new header:

          def index():
              return render_template('index.html', foo=42)

     You can now do something like this:

          def index():
              response = make_response(render_template('index.html', foo=42))
              response.headers['X-Parachutes'] = 'parachutes are cool'
              return response

     This function accepts the very same arguments you can return from a
     view function.  This for example creates a response with a 404
     error code:

          response = make_response(render_template('not_found.html'), 404)

     The other use case of this function is to force the return value of
     a view function into a response which is helpful with view
     decorators:

          response = make_response(view_function())
          response.headers['X-Parachutes'] = 'parachutes are cool'

     Internally this function does the following things:

        - if no arguments are passed, it creates a new response argument

        - if one argument is passed, see flask.Flask.make_response()
          is invoked with it.

        - if more than one argument is passed, the arguments are passed
          to the flask.Flask.make_response() function as
          tuple.

     New in version 0.6.

     Parameters: ‘args’ (Any(16)) –

     Return type: Response.

 -- Function: flask.after_this_request (f)

     Executes a function after this request.  This is useful to modify
     response objects.  The function is passed the response object and
     has to return the same or a new one.

     Example:

          @app.route('/')
          def index():
              @after_this_request
              def add_header(response):
                  response.headers['X-Foo'] = 'Parachute'
                  return response
              return 'Hello World!'

     This is more useful if a function other than the view function
     wants to modify a response.  For instance think of a decorator that
     wants to add some headers without converting the return value into
     a response object.

     New in version 0.9.

     Parameters: ‘f’ (Callable(17)‘[’‘[’Response.‘]’‘, ’
     Response.‘]’) –

     Return type: Callable(18)[[see Response], see Response]

 -- Function: flask.send_file (path_or_file, mimetype=None,
          as_attachment=False, download_name=None,
          attachment_filename=None, conditional=True, etag=True,
          add_etags=None, last_modified=None, max_age=None,
          cache_timeout=None)

     Send the contents of a file to the client.

     The first argument can be a file path or a file-like object.  Paths
     are preferred in most cases because Werkzeug can manage the file
     and get extra information from the path.  Passing a file-like
     object requires that the file is opened in binary mode, and is
     mostly useful when building a file in memory with io.BytesIO(19).

     Never pass file paths provided by a user.  The path is assumed to
     be trusted, so a user could craft a path to access a file you
     didn’t intend.  Use send_from_directory() to safely
     serve user-requested paths from within a directory.

     If the WSGI server sets a ‘file_wrapper’ in ‘environ’, it is used,
     otherwise Werkzeug’s built-in wrapper is used.  Alternatively, if
     the HTTP server supports ‘X-Sendfile’, configuring Flask with
     ‘USE_X_SENDFILE = True’ will tell the server to send the given
     path, which is much more efficient than reading it in Python.

     Parameters:

        * ‘path_or_file’ (Union(20)‘[’os.PathLike(21)‘, ’str(22)‘,
          ’BinaryIO(23)‘]’) – The path to the file to send, relative to
          the current working directory if a relative path is given.
          Alternatively, a file-like object opened in binary mode.  Make
          sure the file pointer is seeked to the start of the data.

        * ‘mimetype’ (Optional(24)‘[’str(25)‘]’) – The MIME type to send
          for the file.  If not provided, it will try to detect it from
          the file name.

        * ‘as_attachment’ (bool(26)) – Indicate to a browser that it
          should offer to save the file instead of displaying it.

        * ‘download_name’ (Optional(27)‘[’str(28)‘]’) – The default name
          browsers will use when saving the file.  Defaults to the
          passed file name.

        * ‘conditional’ (bool(29)) – Enable conditional and range
          responses based on request headers.  Requires passing a file
          path and ‘environ’.

        * ‘etag’ (Union(30)‘[’bool(31)‘, ’str(32)‘]’) – Calculate an
          ETag for the file, which requires passing a file path.  Can
          also be a string to use instead.

        * ‘last_modified’
          (Optional(33)‘[’Union(34)‘[’datetime.datetime(35)‘, ’int(36)‘,
          ’float(37)‘]’‘]’) – The last modified time to send for the
          file, in seconds.  If not provided, it will try to detect it
          from the file path.

        * ‘max_age’ (Optional(38)‘[’Union(39)‘[’int(40)‘,
          ’Callable(41)‘[’‘[’Optional(42)‘[’str(43)‘]’‘]’‘,
          ’Optional(44)‘[’int(45)‘]’‘]’‘]’‘]’) – How long the client
          should cache the file, in seconds.  If set, ‘Cache-Control’
          will be ‘public’, otherwise it will be ‘no-cache’ to prefer
          conditional caching.

        * ‘attachment_filename’ (Optional(46)‘[’str(47)‘]’) –

        * ‘add_etags’ (Optional(48)‘[’bool(49)‘]’) –

        * ‘cache_timeout’ (Optional(50)‘[’int(51)‘]’) –

     Changed in version 2.0: ‘download_name’ replaces the
     ‘attachment_filename’ parameter.  If ‘as_attachment=False’, it is
     passed with ‘Content-Disposition: inline’ instead.

     Changed in version 2.0: ‘max_age’ replaces the ‘cache_timeout’
     parameter.  ‘conditional’ is enabled and ‘max_age’ is not set by
     default.

     Changed in version 2.0: ‘etag’ replaces the ‘add_etags’ parameter.
     It can be a string to use instead of generating one.

     Changed in version 2.0: Passing a file-like object that inherits
     from TextIOBase(52) will raise a ValueError(53) rather than sending
     an empty file.

     New in version 2.0: Moved the implementation to Werkzeug.  This is
     now a wrapper to pass some Flask-specific arguments.

     Changed in version 1.1: ‘filename’ may be a PathLike(54) object.

     Changed in version 1.1: Passing a BytesIO(55) object supports range
     requests.

     Changed in version 1.0.3: Filenames are encoded with ASCII instead
     of Latin-1 for broader compatibility with WSGI servers.

     Changed in version 1.0: UTF-8 filenames as specified in RFC
     2231(56) are supported.

     Changed in version 0.12: The filename is no longer automatically
     inferred from file objects.  If you want to use automatic MIME and
     etag support, pass a filename via ‘filename_or_fp’ or
     ‘attachment_filename’.

     Changed in version 0.12: ‘attachment_filename’ is preferred over
     ‘filename’ for MIME detection.

     Changed in version 0.9: ‘cache_timeout’ defaults to
     Flask.get_send_file_max_age().

     Changed in version 0.7: MIME guessing and etag support for
     file-like objects was deprecated because it was unreliable.  Pass a
     filename if you are able to, otherwise attach an etag yourself.

     Changed in version 0.5: The ‘add_etags’, ‘cache_timeout’ and
     ‘conditional’ parameters were added.  The default behavior is to
     add etags.

     New in version 0.2.

 -- Function: flask.send_from_directory (directory, path, filename=None,
          **kwargs)

     Send a file from within a directory using send_file().

          @app.route("/uploads/<path:name>")
          def download_file(name):
              return send_from_directory(
                  app.config['UPLOAD_FOLDER'], name, as_attachment=True
              )

     This is a secure way to serve files from a folder, such as static
     files or uploads.  Uses safe_join()(57) to ensure the path coming
     from the client is not maliciously crafted to point outside the
     specified directory.

     If the final path does not point to an existing regular file,
     raises a 404 NotFound(58) error.

     Parameters:

        * ‘directory’ (Union(59)‘[’os.PathLike(60)‘, ’str(61)‘]’) – The
          directory that ‘path’ must be located under.

        * ‘path’ (Union(62)‘[’os.PathLike(63)‘, ’str(64)‘]’) – The path
          to the file to send, relative to ‘directory’.

        * ‘kwargs’ (Any(65)) – Arguments to pass to send_file().

        * ‘filename’ (Optional(66)‘[’str(67)‘]’) –

     Return type: Response.

     Changed in version 2.0: ‘path’ replaces the ‘filename’ parameter.

     New in version 2.0: Moved the implementation to Werkzeug.  This is
     now a wrapper to pass some Flask-specific arguments.

     New in version 0.5.

 -- Function: flask.escape ()

     Replace the characters ‘&’, ‘<’, ‘>’, ‘'’, and ‘"’ in the string
     with HTML-safe sequences.  Use this if you need to display text
     that might contain such characters in HTML.

     If the object has an ‘__html__’ method, it is called and the return
     value is assumed to already be safe for HTML.

     Parameters: ‘s’ – An object to be converted to a string and
     escaped.

     Returns: A Markup string with the escaped text.

 -- Class: flask.Markup (base='', encoding=None, errors='strict')

     A string that is ready to be safely inserted into an HTML or XML
     document, either because it was escaped or because it was marked
     safe.

     Passing an object to the constructor converts it to text and wraps
     it to mark it safe without escaping.  To escape the text, use the
     escape() class method instead.

          >>> Markup("Hello, <em>World</em>!")
          Markup('Hello, <em>World</em>!')
          >>> Markup(42)
          Markup('42')
          >>> Markup.escape("Hello, <em>World</em>!")
          Markup('Hello &lt;em&gt;World&lt;/em&gt;!')

     This implements the ‘__html__()’ interface that some frameworks
     use.  Passing an object that implements ‘__html__()’ will wrap the
     output of that method, marking it safe.

          >>> class Foo:
          ...     def __html__(self):
          ...         return '<a href="/foo">foo</a>'
          ...
          >>> Markup(Foo())
          Markup('<a href="/foo">foo</a>')

     This is a subclass of str(68).  It has the same methods, but
     escapes their arguments and returns a ‘Markup’ instance.

          >>> Markup("<em>%s</em>") % ("foo & bar",)
          Markup('<em>foo &amp; bar</em>')
          >>> Markup("<em>Hello</em> ") + "<foo>"
          Markup('<em>Hello</em> &lt;foo&gt;')

     Parameters:

        * ‘base’ (Any(69)) –

        * ‘encoding’ (Optional(70)‘[’str(71)‘]’) –

        * ‘errors’ (str(72)) –

     Return type: Markup.

      -- Method: classmethod escape (s)

          Escape a string.  Calls escape() and ensures that
          for subclasses the correct type is returned.

          Parameters: ‘s’ (Any(73)) –

          Return type: markupsafe.Markup.

      -- Method: striptags ()

          unescape() the markup, remove tags, and normalize
          whitespace to single spaces.

               >>> Markup("Main &raquo;        <em>About</em>").striptags()
               'Main » About'

          Return type: str(74)

      -- Method: unescape ()

          Convert escaped markup back into a text string.  This replaces
          HTML entities with the characters they represent.

               >>> Markup("Main &raquo; <em>About</em>").unescape()
               'Main » <em>About</em>'

          Return type: str(75)

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/functions.html#bool

   (2) https://docs.python.org/3/library/typing.html#typing.Callable

   (3) https://docs.python.org/3/library/typing.html#typing.Callable

   (4) https://docs.python.org/3/library/functions.html#bool

   (5) https://docs.python.org/3/library/stdtypes.html#str

   (6) https://docs.python.org/3/library/typing.html#typing.Any

   (7) https://docs.python.org/3/library/exceptions.html#ValueError

   (8) https://docs.python.org/3/library/stdtypes.html#str

   (9) https://docs.python.org/3/library/typing.html#typing.Union

   (10) https://docs.python.org/3/library/functions.html#int

   (11) https://docs.python.org/3/library/typing.html#typing.Any

   (12) https://docs.python.org/3/library/typing.html#typing.Any

   (13) https://docs.python.org/3/library/stdtypes.html#str

   (14) https://docs.python.org/3/library/functions.html#int

   (15)
https://werkzeug.palletsprojects.com/en/2.1.x/wrappers/#werkzeug.wrappers.Response

   (16) https://docs.python.org/3/library/typing.html#typing.Any

   (17) https://docs.python.org/3/library/typing.html#typing.Callable

   (18) https://docs.python.org/3/library/typing.html#typing.Callable

   (19) https://docs.python.org/3/library/io.html#io.BytesIO

   (20) https://docs.python.org/3/library/typing.html#typing.Union

   (21) https://docs.python.org/3/library/os.html#os.PathLike

   (22) https://docs.python.org/3/library/stdtypes.html#str

   (23) https://docs.python.org/3/library/typing.html#typing.BinaryIO

   (24) https://docs.python.org/3/library/typing.html#typing.Optional

   (25) https://docs.python.org/3/library/stdtypes.html#str

   (26) https://docs.python.org/3/library/functions.html#bool

   (27) https://docs.python.org/3/library/typing.html#typing.Optional

   (28) https://docs.python.org/3/library/stdtypes.html#str

   (29) https://docs.python.org/3/library/functions.html#bool

   (30) https://docs.python.org/3/library/typing.html#typing.Union

   (31) https://docs.python.org/3/library/functions.html#bool

   (32) https://docs.python.org/3/library/stdtypes.html#str

   (33) https://docs.python.org/3/library/typing.html#typing.Optional

   (34) https://docs.python.org/3/library/typing.html#typing.Union

   (35)
https://docs.python.org/3/library/datetime.html#datetime.datetime

   (36) https://docs.python.org/3/library/functions.html#int

   (37) https://docs.python.org/3/library/functions.html#float

   (38) https://docs.python.org/3/library/typing.html#typing.Optional

   (39) https://docs.python.org/3/library/typing.html#typing.Union

   (40) https://docs.python.org/3/library/functions.html#int

   (41) https://docs.python.org/3/library/typing.html#typing.Callable

   (42) https://docs.python.org/3/library/typing.html#typing.Optional

   (43) https://docs.python.org/3/library/stdtypes.html#str

   (44) https://docs.python.org/3/library/typing.html#typing.Optional

   (45) https://docs.python.org/3/library/functions.html#int

   (46) https://docs.python.org/3/library/typing.html#typing.Optional

   (47) https://docs.python.org/3/library/stdtypes.html#str

   (48) https://docs.python.org/3/library/typing.html#typing.Optional

   (49) https://docs.python.org/3/library/functions.html#bool

   (50) https://docs.python.org/3/library/typing.html#typing.Optional

   (51) https://docs.python.org/3/library/functions.html#int

   (52) https://docs.python.org/3/library/io.html#io.TextIOBase

   (53) https://docs.python.org/3/library/exceptions.html#ValueError

   (54) https://docs.python.org/3/library/os.html#os.PathLike

   (55) https://docs.python.org/3/library/io.html#io.BytesIO

   (56) https://datatracker.ietf.org/doc/html/rfc2231.html

   (57)
https://werkzeug.palletsprojects.com/en/2.1.x/utils/#werkzeug.security.safe_join

   (58)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.NotFound

   (59) https://docs.python.org/3/library/typing.html#typing.Union

   (60) https://docs.python.org/3/library/os.html#os.PathLike

   (61) https://docs.python.org/3/library/stdtypes.html#str

   (62) https://docs.python.org/3/library/typing.html#typing.Union

   (63) https://docs.python.org/3/library/os.html#os.PathLike

   (64) https://docs.python.org/3/library/stdtypes.html#str

   (65) https://docs.python.org/3/library/typing.html#typing.Any

   (66) https://docs.python.org/3/library/typing.html#typing.Optional

   (67) https://docs.python.org/3/library/stdtypes.html#str

   (68) https://docs.python.org/3/library/stdtypes.html#str

   (69) https://docs.python.org/3/library/typing.html#typing.Any

   (70) https://docs.python.org/3/library/typing.html#typing.Optional

   (71) https://docs.python.org/3/library/stdtypes.html#str

   (72) https://docs.python.org/3/library/stdtypes.html#str

   (73) https://docs.python.org/3/library/typing.html#typing.Any

   (74) https://docs.python.org/3/library/stdtypes.html#str

   (75) https://docs.python.org/3/library/stdtypes.html#str

2.1.11 Message Flashing
-----------------------

 -- Function: flask.flash (message, category='message')

     Flashes a message to the next request.  In order to remove the
     flashed message from the session and to display it to the user, the
     template has to call get_flashed_messages().

     Changed in version 0.3: ‘category’ parameter added.

     Parameters:

        * ‘message’ (str(1)) – the message to be flashed.

        * ‘category’ (str(2)) – the category for the message.  The
          following values are recommended: ‘'message'’ for any kind of
          message, ‘'error'’ for errors, ‘'info'’ for information
          messages and ‘'warning'’ for warnings.  However any kind of
          string can be used as category.

     Return type: None

 -- Function: flask.get_flashed_messages (with_categories=False,
          category_filter=())

     Pulls all flashed messages from the session and returns them.
     Further calls in the same request to the function will return the
     same messages.  By default just the messages are returned, but when
     ‘with_categories’ is set to ‘True’, the return value will be a list
     of tuples in the form ‘(category, message)’ instead.

     Filter the flashed messages to one or more categories by providing
     those categories in ‘category_filter’.  This allows rendering
     categories in separate html blocks.  The ‘with_categories’ and
     ‘category_filter’ arguments are distinct:

        * ‘with_categories’ controls whether categories are returned
          with message text (‘True’ gives a tuple, where ‘False’ gives
          just the message text).

        * ‘category_filter’ filters the messages down to only those
          matching the provided categories.

     See Message Flashing for examples.

     Changed in version 0.9: ‘category_filter’ parameter added.

     Changed in version 0.3: ‘with_categories’ parameter added.

     Parameters:

        * ‘with_categories’ (bool(3)) – set to ‘True’ to also receive
          categories.

        * ‘category_filter’ (Iterable(4)‘[’str(5)‘]’) – filter of
          categories to limit return values.  Only categories in the
          list will be returned.

     Return type: Union(6)[List(7)[str(8)], List(9)[Tuple(10)[str(11),
     str(12)]]]

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#str

   (2) https://docs.python.org/3/library/stdtypes.html#str

   (3) https://docs.python.org/3/library/functions.html#bool

   (4) https://docs.python.org/3/library/typing.html#typing.Iterable

   (5) https://docs.python.org/3/library/stdtypes.html#str

   (6) https://docs.python.org/3/library/typing.html#typing.Union

   (7) https://docs.python.org/3/library/typing.html#typing.List

   (8) https://docs.python.org/3/library/stdtypes.html#str

   (9) https://docs.python.org/3/library/typing.html#typing.List

   (10) https://docs.python.org/3/library/typing.html#typing.Tuple

   (11) https://docs.python.org/3/library/stdtypes.html#str

   (12) https://docs.python.org/3/library/stdtypes.html#str

2.1.12 JSON Support
-------------------

Flask uses the built-in json(1) module for handling JSON. It will use
the current blueprint’s or application’s JSON encoder and decoder for
easier customization.  By default it handles some extra data types:

   - datetime.datetime(2) and datetime.date(3) are serialized to RFC
     822(4) strings.  This is the same as the HTTP date format.

   - uuid.UUID(5) is serialized to a string.

   - ‘dataclasses.dataclass’ is passed to dataclasses.asdict()(6).

   - Markup. (or any object with a ‘__html__’ method) will
     call the ‘__html__’ method to get a string.

Jinja’s ‘|tojson’ filter is configured to use Flask’s dumps()
function.  The filter marks the output with ‘|safe’ automatically.
Use the filter to render data inside ‘<script>’ tags.

     <script>
         const names = {{ names|tosjon }};
         renderChart(names, {{ axis_data|tojson }});
     </script>

 -- Function: flask.json.jsonify (*args, **kwargs)

     Serialize data to JSON and wrap it in a Response with
     the ‘application/json’ mimetype.

     Uses dumps() to serialize the data, but ‘args’ and
     ‘kwargs’ are treated as data rather than arguments to
     json.dumps()(7).

       1. Single argument: Treated as a single value.

       2. Multiple arguments: Treated as a list of values.  ‘jsonify(1,
          2, 3)’ is the same as ‘jsonify([1, 2, 3])’.

       3. Keyword arguments: Treated as a dict of values.
          ‘jsonify(data=data, errors=errors)’ is the same as
          ‘jsonify({"data": data, "errors": errors})’.

       4. Passing both arguments and keyword arguments is not allowed as
          it’s not clear what should happen.

          from flask import jsonify

          @app.route("/users/me")
          def get_current_user():
              return jsonify(
                  username=g.user.username,
                  email=g.user.email,
                  id=g.user.id,
              )

     Will return a JSON response like this:

          {
            "username": "admin",
            "email": "admin@localhost",
            "id": 42
          }

     The default output omits indents and spaces after separators.  In
     debug mode or if JSONIFY_PRETTYPRINT_REGULAR is ‘True’,
     the output will be formatted to be easier to read.

     Changed in version 2.0.2: decimal.Decimal(8) is supported by
     converting to a string.

     Changed in version 0.11: Added support for serializing top-level
     arrays.  This introduces a security risk in ancient browsers.  See
     JSON Security.

     New in version 0.2.

     Parameters:

        * ‘args’ (Any(9)) –

        * ‘kwargs’ (Any(10)) –

     Return type: Response.

 -- Function: flask.json.dumps (obj, app=None, **kwargs)

     Serialize an object to a string of JSON.

     Takes the same arguments as the built-in json.dumps()(11), with
     some defaults from application configuration.

     Parameters:

        * ‘obj’ (Any(12)) – Object to serialize to JSON.

        * ‘app’ (Optional(13)‘[’Flask.‘]’) – Use this app’s
          config instead of the active app context or defaults.

        * ‘kwargs’ (Any(14)) – Extra arguments passed to
          json.dumps()(15).

     Return type: str(16)

     Changed in version 2.0.2: decimal.Decimal(17) is supported by
     converting to a string.

     Changed in version 2.0: ‘encoding’ is deprecated and will be
     removed in Flask 2.1.

     Changed in version 1.0.3: ‘app’ can be passed directly, rather than
     requiring an app context for configuration.

 -- Function: flask.json.dump (obj, fp, app=None, **kwargs)

     Serialize an object to JSON written to a file object.

     Takes the same arguments as the built-in json.dump()(18), with some
     defaults from application configuration.

     Parameters:

        * ‘obj’ (Any(19)) – Object to serialize to JSON.

        * ‘fp’ (IO(20)‘[’str(21)‘]’) – File object to write JSON to.

        * ‘app’ (Optional(22)‘[’Flask.‘]’) – Use this app’s
          config instead of the active app context or defaults.

        * ‘kwargs’ (Any(23)) – Extra arguments passed to
          json.dump()(24).

     Return type: None

     Changed in version 2.0: Writing to a binary file, and the
     ‘encoding’ argument, is deprecated and will be removed in Flask
     2.1.

 -- Function: flask.json.loads (s, app=None, **kwargs)

     Deserialize an object from a string of JSON.

     Takes the same arguments as the built-in json.loads()(25), with
     some defaults from application configuration.

     Parameters:

        * ‘s’ (Union(26)‘[’str(27)‘, ’bytes(28)‘]’) – JSON string to
          deserialize.

        * ‘app’ (Optional(29)‘[’Flask.‘]’) – Use this app’s
          config instead of the active app context or defaults.

        * ‘kwargs’ (Any(30)) – Extra arguments passed to
          json.loads()(31).

     Return type: Any(32)

     Changed in version 2.0: ‘encoding’ is deprecated and will be
     removed in Flask 2.1.  The data must be a string or UTF-8 bytes.

     Changed in version 1.0.3: ‘app’ can be passed directly, rather than
     requiring an app context for configuration.

 -- Function: flask.json.load (fp, app=None, **kwargs)

     Deserialize an object from JSON read from a file object.

     Takes the same arguments as the built-in json.load()(33), with some
     defaults from application configuration.

     Parameters:

        * ‘fp’ (IO(34)‘[’str(35)‘]’) – File object to read JSON from.

        * ‘app’ (Optional(36)‘[’Flask.‘]’) – Use this app’s
          config instead of the active app context or defaults.

        * ‘kwargs’ (Any(37)) – Extra arguments passed to
          json.load()(38).

     Return type: Any(39)

     Changed in version 2.0: ‘encoding’ is deprecated and will be
     removed in Flask 2.1.  The file must be text mode, or binary mode
     with UTF-8 bytes.

 -- Class: flask.json.JSONEncoder (*, skipkeys=False, ensure_ascii=True,
          check_circular=True, allow_nan=True, sort_keys=False,
          indent=None, separators=None, default=None)

     The default JSON encoder.  Handles extra types compared to the
     built-in json.JSONEncoder(40).

        - datetime.datetime(41) and datetime.date(42) are serialized to
          RFC 822(43) strings.  This is the same as the HTTP date
          format.

        - uuid.UUID(44) is serialized to a string.

        - ‘dataclasses.dataclass’ is passed to dataclasses.asdict()(45).

        - Markup. (or any object with a ‘__html__’ method)
          will call the ‘__html__’ method to get a string.

     Assign a subclass of this to flask.Flask.json_encoder.
     or flask.Blueprint.json_encoder to override the
     default.

      -- Method: default (o)

          Convert ‘o’ to a JSON serializable type.  See
          json.JSONEncoder.default()(46).  Python does not support
          overriding how basic types like ‘str’ or ‘list’ are
          serialized, they are handled before this method.

          Parameters: ‘o’ (Any(47)) –

          Return type: Any(48)

 -- Class: flask.json.JSONDecoder (*, object_hook=None,
          parse_float=None, parse_int=None, parse_constant=None,
          strict=True, object_pairs_hook=None)

     The default JSON decoder.

     This does not change any behavior from the built-in
     json.JSONDecoder(49).

     Assign a subclass of this to flask.Flask.json_decoder.
     or flask.Blueprint.json_decoder to override the
     default.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/json.html#module-json

   (2) https://docs.python.org/3/library/datetime.html#datetime.datetime

   (3) https://docs.python.org/3/library/datetime.html#datetime.date

   (4) https://datatracker.ietf.org/doc/html/rfc822.html

   (5) https://docs.python.org/3/library/uuid.html#uuid.UUID

   (6)
https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict

   (7) https://docs.python.org/3/library/json.html#json.dumps

   (8) https://docs.python.org/3/library/decimal.html#decimal.Decimal

   (9) https://docs.python.org/3/library/typing.html#typing.Any

   (10) https://docs.python.org/3/library/typing.html#typing.Any

   (11) https://docs.python.org/3/library/json.html#json.dumps

   (12) https://docs.python.org/3/library/typing.html#typing.Any

   (13) https://docs.python.org/3/library/typing.html#typing.Optional

   (14) https://docs.python.org/3/library/typing.html#typing.Any

   (15) https://docs.python.org/3/library/json.html#json.dumps

   (16) https://docs.python.org/3/library/stdtypes.html#str

   (17) https://docs.python.org/3/library/decimal.html#decimal.Decimal

   (18) https://docs.python.org/3/library/json.html#json.dump

   (19) https://docs.python.org/3/library/typing.html#typing.Any

   (20) https://docs.python.org/3/library/typing.html#typing.IO

   (21) https://docs.python.org/3/library/stdtypes.html#str

   (22) https://docs.python.org/3/library/typing.html#typing.Optional

   (23) https://docs.python.org/3/library/typing.html#typing.Any

   (24) https://docs.python.org/3/library/json.html#json.dump

   (25) https://docs.python.org/3/library/json.html#json.loads

   (26) https://docs.python.org/3/library/typing.html#typing.Union

   (27) https://docs.python.org/3/library/stdtypes.html#str

   (28) https://docs.python.org/3/library/stdtypes.html#bytes

   (29) https://docs.python.org/3/library/typing.html#typing.Optional

   (30) https://docs.python.org/3/library/typing.html#typing.Any

   (31) https://docs.python.org/3/library/json.html#json.loads

   (32) https://docs.python.org/3/library/typing.html#typing.Any

   (33) https://docs.python.org/3/library/json.html#json.load

   (34) https://docs.python.org/3/library/typing.html#typing.IO

   (35) https://docs.python.org/3/library/stdtypes.html#str

   (36) https://docs.python.org/3/library/typing.html#typing.Optional

   (37) https://docs.python.org/3/library/typing.html#typing.Any

   (38) https://docs.python.org/3/library/json.html#json.load

   (39) https://docs.python.org/3/library/typing.html#typing.Any

   (40) https://docs.python.org/3/library/json.html#json.JSONEncoder

   (41)
https://docs.python.org/3/library/datetime.html#datetime.datetime

   (42) https://docs.python.org/3/library/datetime.html#datetime.date

   (43) https://datatracker.ietf.org/doc/html/rfc822.html

   (44) https://docs.python.org/3/library/uuid.html#uuid.UUID

   (45)
https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict

   (46)
https://docs.python.org/3/library/json.html#json.JSONEncoder.default

   (47) https://docs.python.org/3/library/typing.html#typing.Any

   (48) https://docs.python.org/3/library/typing.html#typing.Any

   (49) https://docs.python.org/3/library/json.html#json.JSONDecoder

2.1.12.1 Tagged JSON
....................

A compact representation for lossless serialization of non-standard JSON
types.  SecureCookieSessionInterface uses this to serialize
the session data, but it may be useful in other places.  It can be
extended to support other types.

 -- Class: flask.json.tag.TaggedJSONSerializer

     Serializer that uses a tag system to compactly represent objects
     that are not JSON types.  Passed as the intermediate serializer to
     ‘itsdangerous.Serializer’.

     The following extra types are supported:

        * dict(1)

        * tuple(2)

        * bytes(3)

        * Markup.

        * UUID(4)

        * datetime(5)

     Return type: None

      -- Attribute: default_tags = [<class 'flask.json.tag.TagDict'>,
               <class 'flask.json.tag.PassDict'>, <class
               'flask.json.tag.TagTuple'>, <class
               'flask.json.tag.PassList'>, <class
               'flask.json.tag.TagBytes'>, <class
               'flask.json.tag.TagMarkup'>, <class
               'flask.json.tag.TagUUID'>, <class
               'flask.json.tag.TagDateTime'>]

          Tag classes to bind when creating the serializer.  Other tags
          can be added later using register().

      -- Method: dumps (value)

          Tag the value and dump it to a compact JSON string.

          Parameters: ‘value’ (Any(6)) –

          Return type: str(7)

      -- Method: loads (value)

          Load data from a JSON string and deserialized any tagged
          objects.

          Parameters: ‘value’ (str(8)) –

          Return type: Any(9)

      -- Method: register (tag_class, force=False, index=None)

          Register a new tag with this serializer.

          Parameters:

             * ‘tag_class’ (Type(10)‘[’flask.json.tag.JSONTag.
               ‘]’) – tag class to register.  Will be instantiated
               with this serializer instance.

             * ‘force’ (bool(11)) – overwrite an existing tag.  If false
               (default), a KeyError(12) is raised.

             * ‘index’ (Optional(13)‘[’int(14)‘]’) – index to insert the
               new tag in the tag order.  Useful when the new tag is a
               special case of an existing tag.  If ‘None’ (default),
               the tag is appended to the end of the order.

          Raises: KeyError(15) – if the tag key is already registered
          and ‘force’ is not true.

          Return type: None

      -- Method: tag (value)

          Convert a value to a tagged representation if necessary.

          Parameters: ‘value’ (Any(16)) –

          Return type: Dict(17)[str(18), Any(19)]

      -- Method: untag (value)

          Convert a tagged representation back to the original type.

          Parameters: ‘value’ (Dict(20)‘[’str(21)‘, ’Any(22)‘]’) –

          Return type: Any(23)

 -- Class: flask.json.tag.JSONTag (serializer)

     Base class for defining type tags for TaggedJSONSerializer.

     Parameters: ‘serializer’ (see TaggedJSONSerializer) –

     Return type: None

      -- Method: check (value)

          Check if the given value should be tagged by this tag.

          Parameters: ‘value’ (Any(24)) –

          Return type: bool(25)

      -- Attribute: key: Optional[str(26)] = None

          The tag to mark the serialized object with.  If ‘None’, this
          tag is only used as an intermediate step during tagging.

      -- Method: tag (value)

          Convert the value to a valid JSON type and add the tag
          structure around it.

          Parameters: ‘value’ (Any(27)) –

          Return type: Any(28)

      -- Method: to_json (value)

          Convert the Python object to an object that is a valid JSON
          type.  The tag will be added later.

          Parameters: ‘value’ (Any(29)) –

          Return type: Any(30)

      -- Method: to_python (value)

          Convert the JSON representation back to the correct type.  The
          tag will already be removed.

          Parameters: ‘value’ (Any(31)) –

          Return type: Any(32)

Let’s see an example that adds support for OrderedDict(33).  Dicts don’t
have an order in JSON, so to handle this we will dump the items as a
list of ‘[key, value]’ pairs.  Subclass JSONTag and give it
the new key ‘' od'’ to identify the type.  The session serializer
processes dicts first, so insert the new tag at the front of the order
since ‘OrderedDict’ must be processed before ‘dict’.

     from flask.json.tag import JSONTag

     class TagOrderedDict(JSONTag):
         __slots__ = ('serializer',)
         key = ' od'

         def check(self, value):
             return isinstance(value, OrderedDict)

         def to_json(self, value):
             return [[k, self.serializer.tag(v)] for k, v in iteritems(value)]

         def to_python(self, value):
             return OrderedDict(value)

     app.session_interface.serializer.register(TagOrderedDict, index=0)

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#dict

   (2) https://docs.python.org/3/library/stdtypes.html#tuple

   (3) https://docs.python.org/3/library/stdtypes.html#bytes

   (4) https://docs.python.org/3/library/uuid.html#uuid.UUID

   (5) https://docs.python.org/3/library/datetime.html#datetime.datetime

   (6) https://docs.python.org/3/library/typing.html#typing.Any

   (7) https://docs.python.org/3/library/stdtypes.html#str

   (8) https://docs.python.org/3/library/stdtypes.html#str

   (9) https://docs.python.org/3/library/typing.html#typing.Any

   (10) https://docs.python.org/3/library/typing.html#typing.Type

   (11) https://docs.python.org/3/library/functions.html#bool

   (12) https://docs.python.org/3/library/exceptions.html#KeyError

   (13) https://docs.python.org/3/library/typing.html#typing.Optional

   (14) https://docs.python.org/3/library/functions.html#int

   (15) https://docs.python.org/3/library/exceptions.html#KeyError

   (16) https://docs.python.org/3/library/typing.html#typing.Any

   (17) https://docs.python.org/3/library/typing.html#typing.Dict

   (18) https://docs.python.org/3/library/stdtypes.html#str

   (19) https://docs.python.org/3/library/typing.html#typing.Any

   (20) https://docs.python.org/3/library/typing.html#typing.Dict

   (21) https://docs.python.org/3/library/stdtypes.html#str

   (22) https://docs.python.org/3/library/typing.html#typing.Any

   (23) https://docs.python.org/3/library/typing.html#typing.Any

   (24) https://docs.python.org/3/library/typing.html#typing.Any

   (25) https://docs.python.org/3/library/functions.html#bool

   (26) https://docs.python.org/3/library/stdtypes.html#str

   (27) https://docs.python.org/3/library/typing.html#typing.Any

   (28) https://docs.python.org/3/library/typing.html#typing.Any

   (29) https://docs.python.org/3/library/typing.html#typing.Any

   (30) https://docs.python.org/3/library/typing.html#typing.Any

   (31) https://docs.python.org/3/library/typing.html#typing.Any

   (32) https://docs.python.org/3/library/typing.html#typing.Any

   (33)
https://docs.python.org/3/library/collections.html#collections.OrderedDict

2.1.13 Template Rendering
-------------------------

 -- Function: flask.render_template (template_name_or_list, **context)

     Renders a template from the template folder with the given context.

     Parameters:

        * ‘template_name_or_list’ (Union(1)‘[’str(2)‘,
          ’jinja2.environment.Template(3)‘,
          ’List(4)‘[’Union(5)‘[’str(6)‘,
          ’jinja2.environment.Template(7)‘]’‘]’‘]’) – the name of the
          template to be rendered, or an iterable with template names
          the first one existing will be rendered

        * ‘context’ (Any(8)) – the variables that should be available in
          the context of the template.

     Return type: str(9)

 -- Function: flask.render_template_string (source, **context)

     Renders a template from the given template source string with the
     given context.  Template variables will be autoescaped.

     Parameters:

        * ‘source’ (str(10)) – the source code of the template to be
          rendered

        * ‘context’ (Any(11)) – the variables that should be available
          in the context of the template.

     Return type: str(12)

 -- Function: flask.get_template_attribute (template_name, attribute)

     Loads a macro (or variable) a template exports.  This can be used
     to invoke a macro from within Python code.  If you for example have
     a template named ‘_cider.html’ with the following contents:

          {% macro hello(name) %}Hello {{ name }}!{% endmacro %}

     You can access this from Python code like this:

          hello = get_template_attribute('_cider.html', 'hello')
          return hello('World')

     New in version 0.2.

     Parameters:

        * ‘template_name’ (str(13)) – the name of the template

        * ‘attribute’ (str(14)) – the name of the variable of macro to
          access

     Return type: Any(15)

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/typing.html#typing.Union

   (2) https://docs.python.org/3/library/stdtypes.html#str

   (3) https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Template

   (4) https://docs.python.org/3/library/typing.html#typing.List

   (5) https://docs.python.org/3/library/typing.html#typing.Union

   (6) https://docs.python.org/3/library/stdtypes.html#str

   (7) https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Template

   (8) https://docs.python.org/3/library/typing.html#typing.Any

   (9) https://docs.python.org/3/library/stdtypes.html#str

   (10) https://docs.python.org/3/library/stdtypes.html#str

   (11) https://docs.python.org/3/library/typing.html#typing.Any

   (12) https://docs.python.org/3/library/stdtypes.html#str

   (13) https://docs.python.org/3/library/stdtypes.html#str

   (14) https://docs.python.org/3/library/stdtypes.html#str

   (15) https://docs.python.org/3/library/typing.html#typing.Any

2.1.14 Configuration
--------------------

 -- Class: flask.Config (root_path, defaults=None)

     Works exactly like a dict but provides ways to fill it from files
     or special dictionaries.  There are two common patterns to populate
     the config.

     Either you can fill the config from a config file:

          app.config.from_pyfile('yourconfig.cfg')

     Or alternatively you can define the configuration options in the
     module that calls from_object() or provide an import
     path to a module that should be loaded.  It is also possible to
     tell it to use the same module and with that provide the
     configuration values just before the call:

          DEBUG = True
          SECRET_KEY = 'development key'
          app.config.from_object(__name__)

     In both cases (loading from any Python file or loading from
     modules), only uppercase keys are added to the config.  This makes
     it possible to use lowercase values in the config file for
     temporary values that are not added to the config or to define the
     config keys in the same file that implements the application.

     Probably the most interesting way to load configurations is from an
     environment variable pointing to a file:

          app.config.from_envvar('YOURAPPLICATION_SETTINGS')

     In this case before launching the application you have to set this
     environment variable to the file you want to use.  On Linux and OS
     X use the export statement:

          export YOURAPPLICATION_SETTINGS='/path/to/config/file'

     On windows use ‘set’ instead.

     Parameters:

        * ‘root_path’ (str(1)) – path to which files are read relative
          from.  When the config object is created by the application,
          this is the application’s root_path.

        * ‘defaults’ (Optional(2)‘[’dict(3)‘]’) – an optional dictionary
          of default values

     Return type: None

      -- Method: from_envvar (variable_name, silent=False)

          Loads a configuration from an environment variable pointing to
          a configuration file.  This is basically just a shortcut with
          nicer error messages for this line of code:

               app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])

          Parameters:

             * ‘variable_name’ (str(4)) – name of the environment
               variable

             * ‘silent’ (bool(5)) – set to ‘True’ if you want silent
               failure for missing files.

          Returns: ‘True’ if the file was loaded successfully.

          Return type: bool(6)

      -- Method: from_file (filename, load, silent=False)

          Update the values in the config from a file that is loaded
          using the ‘load’ parameter.  The loaded data is passed to the
          from_mapping() method.

               import json
               app.config.from_file("config.json", load=json.load)

               import toml
               app.config.from_file("config.toml", load=toml.load)

          Parameters:

             * ‘filename’ (str(7)) – The path to the data file.  This
               can be an absolute path or relative to the config root
               path.

             * ‘load’ (‘Callable[[Reader], Mapping]’ where ‘Reader’
               implements a ‘read’ method.)  – A callable that takes a
               file handle and returns a mapping of loaded data from the
               file.

             * ‘silent’ (bool(8)) – Ignore the file if it doesn’t exist.

          Returns: ‘True’ if the file was loaded successfully.

          Return type: bool(9)

          New in version 2.0.

      -- Method: from_mapping (mapping=None, **kwargs)

          Updates the config like ‘update()’ ignoring items with
          non-upper keys.  :return: Always returns ‘True’.

          New in version 0.11.

          Parameters:

             * ‘mapping’ (Optional(10)‘[’Mapping(11)‘[’str(12)‘,
               ’Any(13)‘]’‘]’) –

             * ‘kwargs’ (Any(14)) –

          Return type: bool(15)

      -- Method: from_object (obj)

          Updates the values from the given object.  An object can be of
          one of the following two types:

             - a string: in this case the object with that name will be
               imported

             - an actual object reference: that object is used directly

          Objects are usually either modules or classes.
          from_object() loads only the uppercase attributes of the
          module/class.  A ‘dict’ object will not work with
          from_object() because the keys of a ‘dict’ are not
          attributes of the ‘dict’ class.

          Example of module-based configuration:

               app.config.from_object('yourapplication.default_config')
               from yourapplication import default_config
               app.config.from_object(default_config)

          Nothing is done to the object before loading.  If the object
          is a class and has ‘@property’ attributes, it needs to be
          instantiated before being passed to this method.

          You should not use this function to load the actual
          configuration but rather configuration defaults.  The actual
          config should be loaded with from_pyfile() and
          ideally from a location not within the package because the
          package might be installed system wide.

          See Development / Production for an example of
          class-based configuration using from_object().

          Parameters: ‘obj’ (Union(16)‘[’object(17)‘, ’str(18)‘]’) – an
          import name or object

          Return type: None

      -- Method: from_prefixed_env (prefix='FLASK', *, loads=<function
               loads>)

          Load any environment variables that start with ‘FLASK_’,
          dropping the prefix from the env key for the config key.
          Values are passed through a loading function to attempt to
          convert them to more specific types than strings.

          Keys are loaded in sorted()(19) order.

          The default loading function attempts to parse values as any
          valid JSON type, including dicts and lists.

          Specific items in nested dicts can be set by separating the
          keys with double underscores (‘__’).  If an intermediate key
          doesn’t exist, it will be initialized to an empty dict.

          Parameters:

             * ‘prefix’ (str(20)) – Load env vars that start with this
               prefix, separated with an underscore (‘_’).

             * ‘loads’ (Callable(21)‘[’‘[’str(22)‘]’‘, ’Any(23)‘]’) –
               Pass each string value to this function and use the
               returned value as the config value.  If any error is
               raised it is ignored and the value remains a string.  The
               default is json.loads().

          Return type: bool(24)

          New in version 2.1.

      -- Method: from_pyfile (filename, silent=False)

          Updates the values in the config from a Python file.  This
          function behaves as if the file was imported as module with
          the from_object() function.

          Parameters:

             * ‘filename’ (str(25)) – the filename of the config.  This
               can either be an absolute filename or a filename relative
               to the root path.

             * ‘silent’ (bool(26)) – set to ‘True’ if you want silent
               failure for missing files.

          Returns: ‘True’ if the file was loaded successfully.

          Return type: bool(27)

          New in version 0.7: ‘silent’ parameter.

      -- Method: get_namespace (namespace, lowercase=True,
               trim_namespace=True)

          Returns a dictionary containing a subset of configuration
          options that match the specified namespace/prefix.  Example
          usage:

               app.config['IMAGE_STORE_TYPE'] = 'fs'
               app.config['IMAGE_STORE_PATH'] = '/var/app/images'
               app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
               image_store_config = app.config.get_namespace('IMAGE_STORE_')

          The resulting dictionary ‘image_store_config’ would look like:

               {
                   'type': 'fs',
                   'path': '/var/app/images',
                   'base_url': 'http://img.website.com'
               }

          This is often useful when configuration options map directly
          to keyword arguments in functions or class constructors.

          Parameters:

             * ‘namespace’ (str(28)) – a configuration namespace

             * ‘lowercase’ (bool(29)) – a flag indicating if the keys of
               the resulting dictionary should be lowercase

             * ‘trim_namespace’ (bool(30)) – a flag indicating if the
               keys of the resulting dictionary should not include the
               namespace

          Return type: Dict(31)[str(32), Any(33)]

          New in version 0.11.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#str

   (2) https://docs.python.org/3/library/typing.html#typing.Optional

   (3) https://docs.python.org/3/library/stdtypes.html#dict

   (4) https://docs.python.org/3/library/stdtypes.html#str

   (5) https://docs.python.org/3/library/functions.html#bool

   (6) https://docs.python.org/3/library/functions.html#bool

   (7) https://docs.python.org/3/library/stdtypes.html#str

   (8) https://docs.python.org/3/library/functions.html#bool

   (9) https://docs.python.org/3/library/functions.html#bool

   (10) https://docs.python.org/3/library/typing.html#typing.Optional

   (11) https://docs.python.org/3/library/typing.html#typing.Mapping

   (12) https://docs.python.org/3/library/stdtypes.html#str

   (13) https://docs.python.org/3/library/typing.html#typing.Any

   (14) https://docs.python.org/3/library/typing.html#typing.Any

   (15) https://docs.python.org/3/library/functions.html#bool

   (16) https://docs.python.org/3/library/typing.html#typing.Union

   (17) https://docs.python.org/3/library/functions.html#object

   (18) https://docs.python.org/3/library/stdtypes.html#str

   (19) https://docs.python.org/3/library/functions.html#sorted

   (20) https://docs.python.org/3/library/stdtypes.html#str

   (21) https://docs.python.org/3/library/typing.html#typing.Callable

   (22) https://docs.python.org/3/library/stdtypes.html#str

   (23) https://docs.python.org/3/library/typing.html#typing.Any

   (24) https://docs.python.org/3/library/functions.html#bool

   (25) https://docs.python.org/3/library/stdtypes.html#str

   (26) https://docs.python.org/3/library/functions.html#bool

   (27) https://docs.python.org/3/library/functions.html#bool

   (28) https://docs.python.org/3/library/stdtypes.html#str

   (29) https://docs.python.org/3/library/functions.html#bool

   (30) https://docs.python.org/3/library/functions.html#bool

   (31) https://docs.python.org/3/library/typing.html#typing.Dict

   (32) https://docs.python.org/3/library/stdtypes.html#str

   (33) https://docs.python.org/3/library/typing.html#typing.Any

2.1.15 Stream Helpers
---------------------

 -- Function: flask.stream_with_context (generator_or_function)

     Request contexts disappear when the response is started on the
     server.  This is done for efficiency reasons and to make it less
     likely to encounter memory leaks with badly written WSGI
     middlewares.  The downside is that if you are using streamed
     responses, the generator cannot access request bound information
     any more.

     This function however can help you keep the context around for
     longer:

          from flask import stream_with_context, request, Response

          @app.route('/stream')
          def streamed_response():
              @stream_with_context
              def generate():
                  yield 'Hello '
                  yield request.args['name']
                  yield '!'
              return Response(generate())

     Alternatively it can also be used around a specific generator:

          from flask import stream_with_context, request, Response

          @app.route('/stream')
          def streamed_response():
              def generate():
                  yield 'Hello '
                  yield request.args['name']
                  yield '!'
              return Response(stream_with_context(generate()))

     New in version 0.9.

     Parameters: ‘generator_or_function’ (Union(1)‘[’Iterator(2)‘,
     ’Callable(3)‘[’‘[’‘...’‘]’‘, ’Iterator(4)‘]’‘]’) –

     Return type: Iterator(5)

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/typing.html#typing.Union

   (2) https://docs.python.org/3/library/typing.html#typing.Iterator

   (3) https://docs.python.org/3/library/typing.html#typing.Callable

   (4) https://docs.python.org/3/library/typing.html#typing.Iterator

   (5) https://docs.python.org/3/library/typing.html#typing.Iterator

2.1.16 Useful Internals
-----------------------

 -- Class: flask.ctx.RequestContext (app, environ, request=None,
          session=None)

     The request context contains all request relevant information.  It
     is created at the beginning of the request and pushed to the
     ‘_request_ctx_stack’ and removed at the end of it.  It will create
     the URL adapter and request object for the WSGI environment
     provided.

     Do not attempt to use this class directly, instead use
     test_request_context() and request_context() to
     create this object.

     When the request context is popped, it will evaluate all the
     functions registered on the application for teardown execution
     (see teardown_request()).

     The request context is automatically popped at the end of the
     request for you.  In debug mode the request context is kept around
     if exceptions happen so that interactive debuggers have a chance to
     introspect the data.  With 0.4 this can also be forced for requests
     that did not fail and outside of ‘DEBUG’ mode.  By setting
     ‘'flask._preserve_context'’ to ‘True’ on the WSGI environment the
     context will not pop itself at the end of the request.  This is
     used by the test_client() for example to implement the
     deferred cleanup functionality.

     You might find this helpful for unittests where you need the
     information from the context local around for a little longer.
     Make sure to properly ‘pop()’ the stack yourself in that situation,
     otherwise your unittests will leak memory.

     Parameters:

        * ‘app’ (see Flask) –

        * ‘environ’ (dict(1)) –

        * ‘request’ (Optional(2)‘[’Request.‘]’) –

        * ‘session’ (Optional(3)‘[’SessionMixin.‘]’) –

     Return type: None

      -- Method: copy ()

          Creates a copy of this request context with the same request
          object.  This can be used to move a request context to a
          different greenlet.  Because the actual request object is the
          same this cannot be used to move a request context to a
          different thread unless access to the request object is
          locked.

          Changed in version 1.1: The current session object is used
          instead of reloading the original data.  This prevents
          ‘flask.session’ pointing to an out-of-date object.

          New in version 0.10.

          Return type: flask.ctx.RequestContext.

      -- Method: match_request ()

          Can be overridden by a subclass to hook into the matching of
          the request.

          Return type: None

      -- Method: pop (exc=<object object>)

          Pops the request context and unbinds it by doing that.  This
          will also trigger the execution of functions registered by the
          teardown_request() decorator.

          Changed in version 0.9: Added the ‘exc’ argument.

          Parameters: ‘exc’ (Optional(4)‘[’BaseException(5)‘]’) –

          Return type: None

      -- Method: push ()

          Binds the request context to the current context.

          Return type: None

 -- Data: flask._request_ctx_stack

     The internal LocalStack(6) that holds RequestContext.
     instances.  Typically, the request and session
     proxies should be accessed instead of the stack.  It may be
     useful to access the stack in extension code.

     The following attributes are always present on each layer of the
     stack:

     ‘app’

          the active Flask application.

     ‘url_adapter’

          the URL adapter that was used to match the request.

     ‘request’

          the current request object.

     ‘session’

          the active session object.

     ‘g’

          an object with all the attributes of the flask.g.
          object.

     ‘flashes’

          an internal cache for the flashed messages.

     Example usage:

          from flask import _request_ctx_stack

          def get_session():
              ctx = _request_ctx_stack.top
              if ctx is not None:
                  return ctx.session

 -- Class: flask.ctx.AppContext (app)

     The application context binds an application object implicitly to
     the current thread or greenlet, similar to how the
     RequestContext binds request information.  The application
     context is also implicitly created if a request context is created
     but the application is not on top of the individual application
     context.

     Parameters: ‘app’ (see Flask) –

     Return type: None

      -- Method: pop (exc=<object object>)

          Pops the app context.

          Parameters: ‘exc’ (Optional(7)‘[’BaseException(8)‘]’) –

          Return type: None

      -- Method: push ()

          Binds the app context to the current context.

          Return type: None

 -- Data: flask._app_ctx_stack

     The internal LocalStack(9) that holds AppContext.
     instances.  Typically, the current_app and g.
     proxies should be accessed instead of the stack.  Extensions can
     access the contexts on the stack as a namespace to store data.

     New in version 0.9.

 -- Class: flask.blueprints.BlueprintSetupState (blueprint, app,
          options, first_registration)

     Temporary holder object for registering a blueprint with the
     application.  An instance of this class is created by the
     make_setup_state() method and later passed to all register
     callback functions.

     Parameters:

        * ‘blueprint’ (see Blueprint) –

        * ‘app’ (see Flask) –

        * ‘options’ (Any(10)) –

        * ‘first_registration’ (bool(11)) –

     Return type: None

      -- Method: add_url_rule (rule, endpoint=None, view_func=None,
               **options)

          A helper method to register a rule (and optionally a view
          function) to the application.  The endpoint is automatically
          prefixed with the blueprint’s name.

          Parameters:

             * ‘rule’ (str(12)) –

             * ‘endpoint’ (Optional(13)‘[’str(14)‘]’) –

             * ‘view_func’ (Optional(15)‘[’Callable(16)‘]’) –

             * ‘options’ (Any(17)) –

          Return type: None

      -- Attribute: app

          a reference to the current application

      -- Attribute: blueprint

          a reference to the blueprint that created this setup state.

      -- Attribute: first_registration

          as blueprints can be registered multiple times with the
          application and not everything wants to be registered multiple
          times on it, this attribute can be used to figure out if the
          blueprint was registered in the past already.

      -- Attribute: options

          a dictionary with all options that were passed to the
          register_blueprint() method.

      -- Attribute: subdomain

          The subdomain that the blueprint should be active for, ‘None’
          otherwise.

      -- Attribute: url_defaults

          A dictionary with URL defaults that is added to each and every
          URL that was defined with the blueprint.

      -- Attribute: url_prefix

          The prefix that should be used for all URLs defined on the
          blueprint.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#dict

   (2) https://docs.python.org/3/library/typing.html#typing.Optional

   (3) https://docs.python.org/3/library/typing.html#typing.Optional

   (4) https://docs.python.org/3/library/typing.html#typing.Optional

   (5) https://docs.python.org/3/library/exceptions.html#BaseException

   (6)
https://werkzeug.palletsprojects.com/en/2.1.x/local/#werkzeug.local.LocalStack

   (7) https://docs.python.org/3/library/typing.html#typing.Optional

   (8) https://docs.python.org/3/library/exceptions.html#BaseException

   (9)
https://werkzeug.palletsprojects.com/en/2.1.x/local/#werkzeug.local.LocalStack

   (10) https://docs.python.org/3/library/typing.html#typing.Any

   (11) https://docs.python.org/3/library/functions.html#bool

   (12) https://docs.python.org/3/library/stdtypes.html#str

   (13) https://docs.python.org/3/library/typing.html#typing.Optional

   (14) https://docs.python.org/3/library/stdtypes.html#str

   (15) https://docs.python.org/3/library/typing.html#typing.Optional

   (16) https://docs.python.org/3/library/typing.html#typing.Callable

   (17) https://docs.python.org/3/library/typing.html#typing.Any

2.1.17 Signals
--------------

New in version 0.6.

 -- Data: signals.signals_available

     ‘True’ if the signaling system is available.  This is the case when
     blinker(1) is installed.

The following signals exist in Flask:

 -- Data: flask.template_rendered

     This signal is sent when a template was successfully rendered.  The
     signal is invoked with the instance of the template as ‘template’
     and the context as dictionary (named ‘context’).

     Example subscriber:

          def log_template_renders(sender, template, context, **extra):
              sender.logger.debug('Rendering template "%s" with context %s',
                                  template.name or 'string template',
                                  context)

          from flask import template_rendered
          template_rendered.connect(log_template_renders, app)

 -- Data: flask.before_render_template

     This signal is sent before template rendering process.  The signal
     is invoked with the instance of the template as ‘template’ and the
     context as dictionary (named ‘context’).

     Example subscriber:

          def log_template_renders(sender, template, context, **extra):
              sender.logger.debug('Rendering template "%s" with context %s',
                                  template.name or 'string template',
                                  context)

          from flask import before_render_template
          before_render_template.connect(log_template_renders, app)

 -- Data: flask.request_started

     This signal is sent when the request context is set up, before any
     request processing happens.  Because the request context is already
     bound, the subscriber can access the request with the standard
     global proxies such as request.

     Example subscriber:

          def log_request(sender, **extra):
              sender.logger.debug('Request context is set up')

          from flask import request_started
          request_started.connect(log_request, app)

 -- Data: flask.request_finished

     This signal is sent right before the response is sent to the
     client.  It is passed the response to be sent named ‘response’.

     Example subscriber:

          def log_response(sender, response, **extra):
              sender.logger.debug('Request context is about to close down.  '
                                  'Response: %s', response)

          from flask import request_finished
          request_finished.connect(log_response, app)

 -- Data: flask.got_request_exception

     This signal is sent when an unhandled exception happens during
     request processing, including when debugging.  The exception is
     passed to the subscriber as ‘exception’.

     This signal is not sent for HTTPException(2), or other exceptions
     that have error handlers registered, unless the exception was
     raised from an error handler.

     This example shows how to do some extra logging if a theoretical
     ‘SecurityException’ was raised:

          from flask import got_request_exception

          def log_security_exception(sender, exception, **extra):
              if not isinstance(exception, SecurityException):
                  return

              security_logger.exception(
                  f"SecurityException at {request.url!r}",
                  exc_info=exception,
              )

          got_request_exception.connect(log_security_exception, app)

 -- Data: flask.request_tearing_down

     This signal is sent when the request is tearing down.  This is
     always called, even if an exception is caused.  Currently functions
     listening to this signal are called after the regular teardown
     handlers, but this is not something you can rely on.

     Example subscriber:

          def close_db_connection(sender, **extra):
              session.close()

          from flask import request_tearing_down
          request_tearing_down.connect(close_db_connection, app)

     As of Flask 0.9, this will also be passed an ‘exc’ keyword argument
     that has a reference to the exception that caused the teardown if
     there was one.

 -- Data: flask.appcontext_tearing_down

     This signal is sent when the app context is tearing down.  This is
     always called, even if an exception is caused.  Currently functions
     listening to this signal are called after the regular teardown
     handlers, but this is not something you can rely on.

     Example subscriber:

          def close_db_connection(sender, **extra):
              session.close()

          from flask import appcontext_tearing_down
          appcontext_tearing_down.connect(close_db_connection, app)

     This will also be passed an ‘exc’ keyword argument that has a
     reference to the exception that caused the teardown if there was
     one.

 -- Data: flask.appcontext_pushed

     This signal is sent when an application context is pushed.  The
     sender is the application.  This is usually useful for unittests in
     order to temporarily hook in information.  For instance it can be
     used to set a resource early onto the ‘g’ object.

     Example usage:

          from contextlib import contextmanager
          from flask import appcontext_pushed

          @contextmanager
          def user_set(app, user):
              def handler(sender, **kwargs):
                  g.user = user
              with appcontext_pushed.connected_to(handler, app):
                  yield

     And in the testcode:

          def test_user_me(self):
              with user_set(app, 'john'):
                  c = app.test_client()
                  resp = c.get('/users/me')
                  assert resp.data == 'username=john'

     New in version 0.10.

 -- Data: flask.appcontext_popped

     This signal is sent when an application context is popped.  The
     sender is the application.  This usually falls in line with the
     appcontext_tearing_down signal.

     New in version 0.10.

 -- Data: flask.message_flashed

     This signal is sent when the application is flashing a message.
     The messages is sent as ‘message’ keyword argument and the category
     as ‘category’.

     Example subscriber:

          recorded = []
          def record(sender, message, category, **extra):
              recorded.append((message, category))

          from flask import message_flashed
          message_flashed.connect(record, app)

     New in version 0.10.

 -- Class: signals.Namespace

     An alias for blinker.base.Namespace(3) if blinker is available,
     otherwise a dummy class that creates fake signals.  This class is
     available for Flask extensions that want to provide the same
     fallback system as Flask itself.

      -- Method: signal (name, doc=None)

          Creates a new signal for this namespace if blinker is
          available, otherwise returns a fake signal that has a send
          method that will do nothing but will fail with a
          RuntimeError(4) for all other operations, including
          connecting.

   ---------- Footnotes ----------

   (1) https://pypi.org/project/blinker/

   (2)
https://werkzeug.palletsprojects.com/en/2.1.x/exceptions/#werkzeug.exceptions.HTTPException

   (3)
https://pythonhosted.org/blinker/index.html#blinker.base.Namespace

   (4) https://docs.python.org/3/library/exceptions.html#RuntimeError

2.1.18 Class-Based Views
------------------------

New in version 0.7.

 -- Class: flask.views.View

     Alternative way to use view functions.  A subclass has to implement
     dispatch_request() which is called with the view
     arguments from the URL routing system.  If methods is
     provided the methods do not have to be passed to the
     add_url_rule() method explicitly:

          class MyView(View):
              methods = ['GET']

              def dispatch_request(self, name):
                  return f"Hello {name}!"

          app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))

     When you want to decorate a pluggable view you will have to either
     do that when the view function is created (by wrapping the return
     value of as_view()) or you can use the
     decorators attribute:

          class SecretView(View):
              methods = ['GET']
              decorators = [superuser_required]

              def dispatch_request(self):
                  ...

     The decorators stored in the decorators list are applied one after
     another when the view function is created.  Note that you can `not'
     use the class based decorators since those would decorate the view
     class and not the generated view function!

      -- Method: classmethod as_view (name, *class_args, **class_kwargs)

          Converts the class into an actual view function that can be
          used with the routing system.  Internally this generates a
          function on the fly which will instantiate the View
          on each request and call the dispatch_request()
          method on it.

          The arguments passed to as_view() are forwarded to
          the constructor of the class.

          Parameters:

             * ‘name’ (str(1)) –

             * ‘class_args’ (Any(2)) –

             * ‘class_kwargs’ (Any(3)) –

          Return type: Callable(4)

      -- Attribute: decorators: List[Callable] = []

          The canonical way to decorate class-based views is to decorate
          the return value of as_view().  However since this moves parts
          of the logic from the class declaration to the place where
          it’s hooked into the routing system.

          You can place one or more decorators in this list and whenever
          the view function is created the result is automatically
          decorated.

          New in version 0.8.

      -- Method: dispatch_request ()

          Subclasses have to override this method to implement the
          actual view function code.  This method is called with all the
          arguments from the URL rule.

          Return type: Union(5)[see Response, str(6), bytes(7),
          Dict(8)[str(9), Any(10)], Iterator(11)[str(12)],
          Iterator(13)[bytes(14)], Tuple(15)[Union(16)[see Response,
          str(17), bytes(18), Dict(19)[str(20), Any(21)],
          Iterator(22)[str(23)], Iterator(24)[bytes(25)]],
          Union(26)[Headers, Dict(27)[str(28), Union(29)[str(30),
          List(31)[str(32)], Tuple(33)[str(34), …]]],
          List(35)[Tuple(36)[str(37), Union(38)[str(39),
          List(40)[str(41)], Tuple(42)[str(43), …]]]]]],
          Tuple(44)[Union(45)[see Response, str(46), bytes(47),
          Dict(48)[str(49), Any(50)], Iterator(51)[str(52)],
          Iterator(53)[bytes(54)]], int(55)], Tuple(56)[Union(57)[see
          Response, str(58), bytes(59), Dict(60)[str(61), Any(62)],
          Iterator(63)[str(64)], Iterator(65)[bytes(66)]], int(67),
          Union(68)[Headers, Dict(69)[str(70), Union(71)[str(72),
          List(73)[str(74)], Tuple(75)[str(76), …]]],
          List(77)[Tuple(78)[str(79), Union(80)[str(81),
          List(82)[str(83)], Tuple(84)[str(85), …]]]]]],
          WSGIApplication]

      -- Attribute: methods: Optional[List[str(86)]] = None

          A list of methods this view can handle.

      -- Attribute: provide_automatic_options: Optional[bool(87)] = None

          Setting this disables or force-enables the automatic options
          handling.

 -- Class: flask.views.MethodView

     A class-based view that dispatches request methods to the
     corresponding class methods.  For example, if you implement a ‘get’
     method, it will be used to handle ‘GET’ requests.

          class CounterAPI(MethodView):
              def get(self):
                  return session.get('counter', 0)

              def post(self):
                  session['counter'] = session.get('counter', 0) + 1
                  return 'OK'

          app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))

      -- Method: dispatch_request (*args, **kwargs)

          Subclasses have to override this method to implement the
          actual view function code.  This method is called with all the
          arguments from the URL rule.

          Parameters:

             * ‘args’ (Any(88)) –

             * ‘kwargs’ (Any(89)) –

          Return type: Union(90)[see Response, str(91), bytes(92),
          Dict(93)[str(94), Any(95)], Iterator(96)[str(97)],
          Iterator(98)[bytes(99)], Tuple(100)[Union(101)[see Response,
          str(102), bytes(103), Dict(104)[str(105), Any(106)],
          Iterator(107)[str(108)], Iterator(109)[bytes(110)]],
          Union(111)[Headers, Dict(112)[str(113), Union(114)[str(115),
          List(116)[str(117)], Tuple(118)[str(119), …]]],
          List(120)[Tuple(121)[str(122), Union(123)[str(124),
          List(125)[str(126)], Tuple(127)[str(128), …]]]]]],
          Tuple(129)[Union(130)[see Response, str(131),
          bytes(132), Dict(133)[str(134), Any(135)],
          Iterator(136)[str(137)], Iterator(138)[bytes(139)]],
          int(140)], Tuple(141)[Union(142)[see Response, str(143),
          bytes(144), Dict(145)[str(146), Any(147)],
          Iterator(148)[str(149)], Iterator(150)[bytes(151)]], int(152),
          Union(153)[Headers, Dict(154)[str(155), Union(156)[str(157),
          List(158)[str(159)], Tuple(160)[str(161), …]]],
          List(162)[Tuple(163)[str(164), Union(165)[str(166),
          List(167)[str(168)], Tuple(169)[str(170), …]]]]]],
          WSGIApplication]

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/stdtypes.html#str

   (2) https://docs.python.org/3/library/typing.html#typing.Any

   (3) https://docs.python.org/3/library/typing.html#typing.Any

   (4) https://docs.python.org/3/library/typing.html#typing.Callable

   (5) https://docs.python.org/3/library/typing.html#typing.Union

   (6) https://docs.python.org/3/library/stdtypes.html#str

   (7) https://docs.python.org/3/library/stdtypes.html#bytes

   (8) https://docs.python.org/3/library/typing.html#typing.Dict

   (9) https://docs.python.org/3/library/stdtypes.html#str

   (10) https://docs.python.org/3/library/typing.html#typing.Any

   (11) https://docs.python.org/3/library/typing.html#typing.Iterator

   (12) https://docs.python.org/3/library/stdtypes.html#str

   (13) https://docs.python.org/3/library/typing.html#typing.Iterator

   (14) https://docs.python.org/3/library/stdtypes.html#bytes

   (15) https://docs.python.org/3/library/typing.html#typing.Tuple

   (16) https://docs.python.org/3/library/typing.html#typing.Union

   (17) https://docs.python.org/3/library/stdtypes.html#str

   (18) https://docs.python.org/3/library/stdtypes.html#bytes

   (19) https://docs.python.org/3/library/typing.html#typing.Dict

   (20) https://docs.python.org/3/library/stdtypes.html#str

   (21) https://docs.python.org/3/library/typing.html#typing.Any

   (22) https://docs.python.org/3/library/typing.html#typing.Iterator

   (23) https://docs.python.org/3/library/stdtypes.html#str

   (24) https://docs.python.org/3/library/typing.html#typing.Iterator

   (25) https://docs.python.org/3/library/stdtypes.html#bytes

   (26) https://docs.python.org/3/library/typing.html#typing.Union

   (27) https://docs.python.org/3/library/typing.html#typing.Dict

   (28) https://docs.python.org/3/library/stdtypes.html#str

   (29) https://docs.python.org/3/library/typing.html#typing.Union

   (30) https://docs.python.org/3/library/stdtypes.html#str

   (31) https://docs.python.org/3/library/typing.html#typing.List

   (32) https://docs.python.org/3/library/stdtypes.html#str

   (33) https://docs.python.org/3/library/typing.html#typing.Tuple

   (34) https://docs.python.org/3/library/stdtypes.html#str

   (35) https://docs.python.org/3/library/typing.html#typing.List

   (36) https://docs.python.org/3/library/typing.html#typing.Tuple

   (37) https://docs.python.org/3/library/stdtypes.html#str

   (38) https://docs.python.org/3/library/typing.html#typing.Union

   (39) https://docs.python.org/3/library/stdtypes.html#str

   (40) https://docs.python.org/3/library/typing.html#typing.List

   (41) https://docs.python.org/3/library/stdtypes.html#str

   (42) https://docs.python.org/3/library/typing.html#typing.Tuple

   (43) https://docs.python.org/3/library/stdtypes.html#str

   (44) https://docs.python.org/3/library/typing.html#typing.Tuple

   (45) https://docs.python.org/3/library/typing.html#typing.Union

   (46) https://docs.python.org/3/library/stdtypes.html#str

   (47) https://docs.python.org/3/library/stdtypes.html#bytes

   (48) https://docs.python.org/3/library/typing.html#typing.Dict

   (49) https://docs.python.org/3/library/stdtypes.html#str

   (50) https://docs.python.org/3/library/typing.html#typing.Any

   (51) https://docs.python.org/3/library/typing.html#typing.Iterator

   (52) https://docs.python.org/3/library/stdtypes.html#str

   (53) https://docs.python.org/3/library/typing.html#typing.Iterator

   (54) https://docs.python.org/3/library/stdtypes.html#bytes

   (55) https://docs.python.org/3/library/functions.html#int

   (56) https://docs.python.org/3/library/typing.html#typing.Tuple

   (57) https://docs.python.org/3/library/typing.html#typing.Union

   (58) https://docs.python.org/3/library/stdtypes.html#str

   (59) https://docs.python.org/3/library/stdtypes.html#bytes

   (60) https://docs.python.org/3/library/typing.html#typing.Dict

   (61) https://docs.python.org/3/library/stdtypes.html#str

   (62) https://docs.python.org/3/library/typing.html#typing.Any

   (63) https://docs.python.org/3/library/typing.html#typing.Iterator

   (64) https://docs.python.org/3/library/stdtypes.html#str

   (65) https://docs.python.org/3/library/typing.html#typing.Iterator

   (66) https://docs.python.org/3/library/stdtypes.html#bytes

   (67) https://docs.python.org/3/library/functions.html#int

   (68) https://docs.python.org/3/library/typing.html#typing.Union

   (69) https://docs.python.org/3/library/typing.html#typing.Dict

   (70) https://docs.python.org/3/library/stdtypes.html#str

   (71) https://docs.python.org/3/library/typing.html#typing.Union

   (72) https://docs.python.org/3/library/stdtypes.html#str

   (73) https://docs.python.org/3/library/typing.html#typing.List

   (74) https://docs.python.org/3/library/stdtypes.html#str

   (75) https://docs.python.org/3/library/typing.html#typing.Tuple

   (76) https://docs.python.org/3/library/stdtypes.html#str

   (77) https://docs.python.org/3/library/typing.html#typing.List

   (78) https://docs.python.org/3/library/typing.html#typing.Tuple

   (79) https://docs.python.org/3/library/stdtypes.html#str

   (80) https://docs.python.org/3/library/typing.html#typing.Union

   (81) https://docs.python.org/3/library/stdtypes.html#str

   (82) https://docs.python.org/3/library/typing.html#typing.List

   (83) https://docs.python.org/3/library/stdtypes.html#str

   (84) https://docs.python.org/3/library/typing.html#typing.Tuple

   (85) https://docs.python.org/3/library/stdtypes.html#str

   (86) https://docs.python.org/3/library/stdtypes.html#str

   (87) https://docs.python.org/3/library/functions.html#bool

   (88) https://docs.python.org/3/library/typing.html#typing.Any

   (89) https://docs.python.org/3/library/typing.html#typing.Any

   (90) https://docs.python.org/3/library/typing.html#typing.Union

   (91) https://docs.python.org/3/library/stdtypes.html#str

   (92) https://docs.python.org/3/library/stdtypes.html#bytes

   (93) https://docs.python.org/3/library/typing.html#typing.Dict

   (94) https://docs.python.org/3/library/stdtypes.html#str

   (95) https://docs.python.org/3/library/typing.html#typing.Any

   (96) https://docs.python.org/3/library/typing.html#typing.Iterator

   (97) https://docs.python.org/3/library/stdtypes.html#str

   (98) https://docs.python.org/3/library/typing.html#typing.Iterator

   (99) https://docs.python.org/3/library/stdtypes.html#bytes

   (100) https://docs.python.org/3/library/typing.html#typing.Tuple

   (101) https://docs.python.org/3/library/typing.html#typing.Union

   (102) https://docs.python.org/3/library/stdtypes.html#str

   (103) https://docs.python.org/3/library/stdtypes.html#bytes

   (104) https://docs.python.org/3/library/typing.html#typing.Dict

   (105) https://docs.python.org/3/library/stdtypes.html#str

   (106) https://docs.python.org/3/library/typing.html#typing.Any

   (107) https://docs.python.org/3/library/typing.html#typing.Iterator

   (108) https://docs.python.org/3/library/stdtypes.html#str

   (109) https://docs.python.org/3/library/typing.html#typing.Iterator

   (110) https://docs.python.org/3/library/stdtypes.html#bytes

   (111) https://docs.python.org/3/library/typing.html#typing.Union

   (112) https://docs.python.org/3/library/typing.html#typing.Dict

   (113) https://docs.python.org/3/library/stdtypes.html#str

   (114) https://docs.python.org/3/library/typing.html#typing.Union

   (115) https://docs.python.org/3/library/stdtypes.html#str

   (116) https://docs.python.org/3/library/typing.html#typing.List

   (117) https://docs.python.org/3/library/stdtypes.html#str

   (118) https://docs.python.org/3/library/typing.html#typing.Tuple

   (119) https://docs.python.org/3/library/stdtypes.html#str

   (120) https://docs.python.org/3/library/typing.html#typing.List

   (121) https://docs.python.org/3/library/typing.html#typing.Tuple

   (122) https://docs.python.org/3/library/stdtypes.html#str

   (123) https://docs.python.org/3/library/typing.html#typing.Union

   (124) https://docs.python.org/3/library/stdtypes.html#str

   (125) https://docs.python.org/3/library/typing.html#typing.List

   (126) https://docs.python.org/3/library/stdtypes.html#str

   (127) https://docs.python.org/3/library/typing.html#typing.Tuple

   (128) https://docs.python.org/3/library/stdtypes.html#str

   (129) https://docs.python.org/3/library/typing.html#typing.Tuple

   (130) https://docs.python.org/3/library/typing.html#typing.Union

   (131) https://docs.python.org/3/library/stdtypes.html#str

   (132) https://docs.python.org/3/library/stdtypes.html#bytes

   (133) https://docs.python.org/3/library/typing.html#typing.Dict

   (134) https://docs.python.org/3/library/stdtypes.html#str

   (135) https://docs.python.org/3/library/typing.html#typing.Any

   (136) https://docs.python.org/3/library/typing.html#typing.Iterator

   (137) https://docs.python.org/3/library/stdtypes.html#str

   (138) https://docs.python.org/3/library/typing.html#typing.Iterator

   (139) https://docs.python.org/3/library/stdtypes.html#bytes

   (140) https://docs.python.org/3/library/functions.html#int

   (141) https://docs.python.org/3/library/typing.html#typing.Tuple

   (142) https://docs.python.org/3/library/typing.html#typing.Union

   (143) https://docs.python.org/3/library/stdtypes.html#str

   (144) https://docs.python.org/3/library/stdtypes.html#bytes

   (145) https://docs.python.org/3/library/typing.html#typing.Dict

   (146) https://docs.python.org/3/library/stdtypes.html#str

   (147) https://docs.python.org/3/library/typing.html#typing.Any

   (148) https://docs.python.org/3/library/typing.html#typing.Iterator

   (149) https://docs.python.org/3/library/stdtypes.html#str

   (150) https://docs.python.org/3/library/typing.html#typing.Iterator

   (151) https://docs.python.org/3/library/stdtypes.html#bytes

   (152) https://docs.python.org/3/library/functions.html#int

   (153) https://docs.python.org/3/library/typing.html#typing.Union

   (154) https://docs.python.org/3/library/typing.html#typing.Dict

   (155) https://docs.python.org/3/library/stdtypes.html#str

   (156) https://docs.python.org/3/library/typing.html#typing.Union

   (157) https://docs.python.org/3/library/stdtypes.html#str

   (158) https://docs.python.org/3/library/typing.html#typing.List

   (159) https://docs.python.org/3/library/stdtypes.html#str

   (160) https://docs.python.org/3/library/typing.html#typing.Tuple

   (161) https://docs.python.org/3/library/stdtypes.html#str

   (162) https://docs.python.org/3/library/typing.html#typing.List

   (163) https://docs.python.org/3/library/typing.html#typing.Tuple

   (164) https://docs.python.org/3/library/stdtypes.html#str

   (165) https://docs.python.org/3/library/typing.html#typing.Union

   (166) https://docs.python.org/3/library/stdtypes.html#str

   (167) https://docs.python.org/3/library/typing.html#typing.List

   (168) https://docs.python.org/3/library/stdtypes.html#str

   (169) https://docs.python.org/3/library/typing.html#typing.Tuple

   (170) https://docs.python.org/3/library/stdtypes.html#str

2.1.19 URL Route Registrations
------------------------------

Generally there are three ways to define rules for the routing system:

  1. You can use the flask.Flask.route() decorator.

  2. You can use the flask.Flask.add_url_rule() function.

  3. You can directly access the underlying Werkzeug routing system
     which is exposed as flask.Flask.url_map.

Variable parts in the route can be specified with angular brackets
(‘/user/<username>’).  By default a variable part in the URL accepts any
string without a slash however a different converter can be specified as
well by using ‘<converter:name>’.

Variable parts are passed to the view function as keyword arguments.

The following converters are available:

‘string’        accepts any text without a slash (the default)

‘int’           accepts integers

‘float’         like ‘int’ but for floating point values

‘path’          like the default but also accepts slashes

‘any’           matches one of the items provided

‘uuid’          accepts UUID strings

Custom converters can be defined using flask.Flask.url_map.

Here are some examples:

     @app.route('/')
     def index():
         pass

     @app.route('/<username>')
     def show_user(username):
         pass

     @app.route('/post/<int:post_id>')
     def show_post(post_id):
         pass

An important detail to keep in mind is how Flask deals with trailing
slashes.  The idea is to keep each URL unique so the following rules
apply:

  1. If a rule ends with a slash and is requested without a slash by the
     user, the user is automatically redirected to the same page with a
     trailing slash attached.

  2. If a rule does not end with a trailing slash and the user requests
     the page with a trailing slash, a 404 not found is raised.

This is consistent with how web servers deal with static files.  This
also makes it possible to use relative link targets safely.

You can also define multiple rules for the same function.  They have to
be unique however.  Defaults can also be specified.  Here for example is
a definition for a URL that accepts an optional page:

     @app.route('/users/', defaults={'page': 1})
     @app.route('/users/page/<int:page>')
     def show_users(page):
         pass

This specifies that ‘/users/’ will be the URL for page one and
‘/users/page/N’ will be the URL for page ‘N’.

If a URL contains a default value, it will be redirected to its simpler
form with a 301 redirect.  In the above example, ‘/users/page/1’ will be
redirected to ‘/users/’.  If your route handles ‘GET’ and ‘POST’
requests, make sure the default route only handles ‘GET’, as redirects
can’t preserve form data.

     @app.route('/region/', defaults={'id': 1})
     @app.route('/region/<int:id>', methods=['GET', 'POST'])
     def region(id):
        pass

Here are the parameters that route() and
add_url_rule() accept.  The only difference is that with the route
parameter the view function is defined with the decorator instead of the
‘view_func’ parameter.

‘rule’              the URL rule as string

‘endpoint’          the endpoint for the registered URL rule.  Flask itself assumes
                    that the name of the view function is the name of the endpoint
                    if not explicitly stated.

‘view_func’         the function to call when serving a request to the provided
                    endpoint.  If this is not provided one can specify the function
                    later by storing it in the view_functions dictionary
                    with the endpoint as key.

‘defaults’          A dictionary with defaults for this rule.  See the example above
                    for how defaults work.

‘subdomain’         specifies the rule for the subdomain in case subdomain matching
                    is in use.  If not specified the default subdomain is assumed.

‘**options’         the options to be forwarded to the underlying Rule(1) object.  A
                    change to Werkzeug is handling of method options.  methods is a
                    list of methods this rule should be limited to (‘GET’, ‘POST’
                    etc.).  By default a rule just listens for ‘GET’ (and implicitly
                    ‘HEAD’).  Starting with Flask 0.6, ‘OPTIONS’ is implicitly added
                    and handled by the standard request handling.  They have to be
                    specified as keyword arguments.

   ---------- Footnotes ----------

   (1)
https://werkzeug.palletsprojects.com/en/2.1.x/routing/#werkzeug.routing.Rule

2.1.20 View Function Options
----------------------------

For internal usage the view functions can have some attributes attached
to customize behavior the view function would normally not have control
over.  The following attributes can be provided optionally to either
override some defaults to add_url_rule() or general behavior:

   - ‘__name__’: The name of a function is by default used as endpoint.
     If endpoint is provided explicitly this value is used.
     Additionally this will be prefixed with the name of the blueprint
     by default which cannot be customized from the function itself.

   - ‘methods’: If methods are not provided when the URL rule is added,
     Flask will look on the view function object itself if a ‘methods’
     attribute exists.  If it does, it will pull the information for the
     methods from there.

   - ‘provide_automatic_options’: if this attribute is set Flask will
     either force enable or disable the automatic implementation of the
     HTTP ‘OPTIONS’ response.  This can be useful when working with
     decorators that want to customize the ‘OPTIONS’ response on a
     per-view basis.

   - ‘required_methods’: if this attribute is set, Flask will always add
     these methods when registering a URL rule even if the methods were
     explicitly overridden in the ‘route()’ call.

Full example:

     def index():
         if request.method == 'OPTIONS':
             # custom options handling here
             ...
         return 'Hello World!'
     index.provide_automatic_options = False
     index.methods = ['GET', 'OPTIONS']

     app.add_url_rule('/', index)

New in version 0.8: The ‘provide_automatic_options’ functionality was
added.

2.1.21 Command Line Interface
-----------------------------

 -- Class: flask.cli.FlaskGroup (add_default_commands=True,
          create_app=None, add_version_option=True, load_dotenv=True,
          set_debug_flag=True, **extra)

     Special subclass of the AppGroup group that supports
     loading more commands from the configured Flask app.  Normally a
     developer does not have to interface with this class but there are
     some very advanced use cases for which it makes sense to create an
     instance of this.  see Custom Scripts.

     Parameters:

        * ‘add_default_commands’ – if this is True then the default run
          and shell commands will be added.

        * ‘add_version_option’ – adds the ‘--version’ option.

        * ‘create_app’ – an optional callback that is passed the script
          info and returns the loaded app.

        * ‘load_dotenv’ – Load the nearest ‘.env’ and ‘.flaskenv’ files
          to set environment variables.  Will also change the working
          directory to the directory containing the first file found.

        * ‘set_debug_flag’ – Set the app’s debug flag based on the
          active environment

     Changed in version 1.0: If installed, python-dotenv will be used to
     load environment variables from ‘.env’ and ‘.flaskenv’ files.

      -- Method: get_command (ctx, name)

          Given a context and a command name, this returns a ‘Command’
          object if it exists or returns ‘None’.

      -- Method: list_commands (ctx)

          Returns a list of subcommand names in the order they should
          appear.

      -- Method: main (*args, **kwargs)

          This is the way to invoke a script with all the bells and
          whistles as a command line application.  This will always
          terminate the application after a call.  If this is not
          wanted, ‘SystemExit’ needs to be caught.

          This method is also available by directly calling the instance
          of a ‘Command’.

          Parameters:

             * ‘args’ – the arguments that should be used for parsing.
               If not provided, ‘sys.argv[1:]’ is used.

             * ‘prog_name’ – the program name that should be used.  By
               default the program name is constructed by taking the
               file name from ‘sys.argv[0]’.

             * ‘complete_var’ – the environment variable that controls
               the bash completion support.  The default is
               ‘"_<prog_name>_COMPLETE"’ with prog_name in uppercase.

             * ‘standalone_mode’ – the default behavior is to invoke the
               script in standalone mode.  Click will then handle
               exceptions and convert them into error messages and the
               function will never return but shut down the interpreter.
               If this is set to ‘False’ they will be propagated to the
               caller and the return value of this function is the
               return value of ‘invoke()’.

             * ‘windows_expand_args’ – Expand glob patterns, user dir,
               and env vars in command line args on Windows.

             * ‘extra’ – extra keyword arguments are forwarded to the
               context constructor.  See ‘Context’ for more information.

          Changed in version 8.0.1: Added the ‘windows_expand_args’
          parameter to allow disabling command line arg expansion on
          Windows.

          Changed in version 8.0: When taking arguments from ‘sys.argv’
          on Windows, glob patterns, user dir, and env vars are
          expanded.

          Changed in version 3.0: Added the ‘standalone_mode’ parameter.

 -- Class: flask.cli.AppGroup (name=None, commands=None, **attrs)

     This works similar to a regular click Group(1) but it changes the
     behavior of the command() decorator so that it
     automatically wraps the functions in with_appcontext().

     Not to be confused with FlaskGroup.

     Parameters:

        * ‘name’ (Optional(2)‘[’str(3)‘]’) –

        * ‘commands’ (Optional(4)‘[’Union(5)‘[’Dict(6)‘[’str(7)‘,
          ’click.core.Command(8)‘]’‘,
          ’Sequence(9)‘[’click.core.Command(10)‘]’‘]’‘]’) –

        * ‘attrs’ (Any(11)) –

     Return type: None

      -- Method: command (*args, **kwargs)

          This works exactly like the method of the same name on a
          regular click.Group(12) but it wraps callbacks in
          with_appcontext() unless it’s disabled by passing
          ‘with_appcontext=False’.

      -- Method: group (*args, **kwargs)

          This works exactly like the method of the same name on a
          regular click.Group(13) but it defaults the group class to
          AppGroup.

 -- Class: flask.cli.ScriptInfo (app_import_path=None, create_app=None,
          set_debug_flag=True)

     Helper object to deal with Flask applications.  This is usually not
     necessary to interface with as it’s used internally in the
     dispatching to click.  In future versions of Flask this object will
     most likely play a bigger role.  Typically it’s created
     automatically by the FlaskGroup but you can also
     manually create it and pass it onwards as click object.

      -- Attribute: app_import_path

          Optionally the import path for the Flask application.

      -- Attribute: create_app

          Optionally a function that is passed the script info to create
          the instance of the application.

      -- Attribute: data

          A dictionary with arbitrary data that can be associated with
          this script info.

      -- Method: load_app ()

          Loads the Flask app (if not yet loaded) and returns it.
          Calling this multiple times will just result in the already
          loaded app to be returned.

 -- Function: flask.cli.load_dotenv (path=None)

     Load “dotenv” files in order of precedence to set environment
     variables.

     If an env var is already set it is not overwritten, so earlier
     files in the list are preferred over later files.

     This is a no-op if python-dotenv(14) is not installed.

     Parameters: ‘path’ – Load the file at this location instead of
     searching.

     Returns: ‘True’ if a file was loaded.

     Changed in version 2.0: When loading the env files, set the default
     encoding to UTF-8.

     Changed in version 1.1.0: Returns ‘False’ when python-dotenv is not
     installed, or when the given path isn’t a file.

     New in version 1.0.

 -- Function: flask.cli.with_appcontext (f)

     Wraps a callback so that it’s guaranteed to be executed with the
     script’s application context.  If callbacks are registered directly
     to the ‘app.cli’ object then they are wrapped with this function by
     default unless it’s disabled.

 -- Function: flask.cli.pass_script_info (f)

     Marks a function so that an instance of ScriptInfo is
     passed as first argument to the click callback.

     Parameters: ‘f’ (‘click.decorators.F’) –

     Return type: click.decorators.F

 -- Data: flask.cli.run_command = <Command run>

     Run a local development server.

     This server is for development purposes only.  It does not provide
     the stability, security, or performance of production WSGI servers.

     The reloader and debugger are enabled by default if
     FLASK_ENV=development or FLASK_DEBUG=1.

     Parameters:

        * ‘args’ (Any(15)) –

        * ‘kwargs’ (Any(16)) –

     Return type: Any(17)

 -- Data: flask.cli.shell_command = <Command shell>

     Run an interactive Python shell in the context of a given Flask
     application.  The application will populate the default namespace
     of this shell according to its configuration.

     This is useful for executing small snippets of management code
     without having to manually configure the application.

     Parameters:

        * ‘args’ (Any(18)) –

        * ‘kwargs’ (Any(19)) –

     Return type: Any(20)

   ---------- Footnotes ----------

   (1) https://click.palletsprojects.com/en/8.1.x/api/#click.Group

   (2) https://docs.python.org/3/library/typing.html#typing.Optional

   (3) https://docs.python.org/3/library/stdtypes.html#str

   (4) https://docs.python.org/3/library/typing.html#typing.Optional

   (5) https://docs.python.org/3/library/typing.html#typing.Union

   (6) https://docs.python.org/3/library/typing.html#typing.Dict

   (7) https://docs.python.org/3/library/stdtypes.html#str

   (8) https://click.palletsprojects.com/en/8.1.x/api/#click.Command

   (9) https://docs.python.org/3/library/typing.html#typing.Sequence

   (10) https://click.palletsprojects.com/en/8.1.x/api/#click.Command

   (11) https://docs.python.org/3/library/typing.html#typing.Any

   (12) https://click.palletsprojects.com/en/8.1.x/api/#click.Group

   (13) https://click.palletsprojects.com/en/8.1.x/api/#click.Group

   (14) https://github.com/theskumar/python-dotenv#readme

   (15) https://docs.python.org/3/library/typing.html#typing.Any

   (16) https://docs.python.org/3/library/typing.html#typing.Any

   (17) https://docs.python.org/3/library/typing.html#typing.Any

   (18) https://docs.python.org/3/library/typing.html#typing.Any

   (19) https://docs.python.org/3/library/typing.html#typing.Any

   (20) https://docs.python.org/3/library/typing.html#typing.Any

3 Additional Notes
******************

Design notes, legal information and changelog are here for the
interested.

3.1 Design Decisions in Flask
=============================

If you are curious why Flask does certain things the way it does and not
differently, this section is for you.  This should give you an idea
about some of the design decisions that may appear arbitrary and
surprising at first, especially in direct comparison with other
frameworks.

3.1.1 The Explicit Application Object
-------------------------------------

A Python web application based on WSGI has to have one central callable
object that implements the actual application.  In Flask this is an
instance of the Flask class.  Each Flask application has to
create an instance of this class itself and pass it the name of the
module, but why can’t Flask do that itself?

Without such an explicit application object the following code:

     from flask import Flask
     app = Flask(__name__)

     @app.route('/')
     def index():
         return 'Hello World!'

Would look like this instead:

     from hypothetical_flask import route

     @route('/')
     def index():
         return 'Hello World!'

There are three major reasons for this.  The most important one is that
implicit application objects require that there may only be one instance
at the time.  There are ways to fake multiple applications with a single
application object, like maintaining a stack of applications, but this
causes some problems I won’t outline here in detail.  Now the question
is: when does a microframework need more than one application at the
same time?  A good example for this is unit testing.  When you want to
test something it can be very helpful to create a minimal application to
test specific behavior.  When the application object is deleted
everything it allocated will be freed again.

Another thing that becomes possible when you have an explicit object
lying around in your code is that you can subclass the base class (see
Flask) to alter specific behavior.  This would not be possible
without hacks if the object were created ahead of time for you based on
a class that is not exposed to you.

But there is another very important reason why Flask depends on an
explicit instantiation of that class: the package name.  Whenever you
create a Flask instance you usually pass it ‘__name__’ as package name.
Flask depends on that information to properly load resources relative to
your module.  With Python’s outstanding support for reflection it can
then access the package to figure out where the templates and static
files are stored (see open_resource()).  Now obviously there
are frameworks around that do not need any configuration and will still
be able to load templates relative to your application module.  But they
have to use the current working directory for that, which is a very
unreliable way to determine where the application is.  The current
working directory is process-wide and if you are running multiple
applications in one process (which could happen in a webserver without
you knowing) the paths will be off.  Worse: many webservers do not set
the working directory to the directory of your application but to the
document root which does not have to be the same folder.

The third reason is “explicit is better than implicit”.  That object is
your WSGI application, you don’t have to remember anything else.  If you
want to apply a WSGI middleware, just wrap it and you’re done (though
there are better ways to do that so that you do not lose the reference
to the application object wsgi_app()).

Furthermore this design makes it possible to use a factory function to
create the application which is very helpful for unit testing and
similar things (see Application Factories).

3.1.2 The Routing System
------------------------

Flask uses the Werkzeug routing system which was designed to
automatically order routes by complexity.  This means that you can
declare routes in arbitrary order and they will still work as expected.
This is a requirement if you want to properly implement decorator based
routing since decorators could be fired in undefined order when the
application is split into multiple modules.

Another design decision with the Werkzeug routing system is that routes
in Werkzeug try to ensure that URLs are unique.  Werkzeug will go quite
far with that in that it will automatically redirect to a canonical URL
if a route is ambiguous.

3.1.3 One Template Engine
-------------------------

Flask decides on one template engine: Jinja2.  Why doesn’t Flask have a
pluggable template engine interface?  You can obviously use a different
template engine, but Flask will still configure Jinja2 for you.  While
that limitation that Jinja2 is `always' configured will probably go
away, the decision to bundle one template engine and use that will not.

Template engines are like programming languages and each of those
engines has a certain understanding about how things work.  On the
surface they all work the same: you tell the engine to evaluate a
template with a set of variables and take the return value as string.

But that’s about where similarities end.  Jinja2 for example has an
extensive filter system, a certain way to do template inheritance,
support for reusable blocks (macros) that can be used from inside
templates and also from Python code, supports iterative template
rendering, configurable syntax and more.  On the other hand an engine
like Genshi is based on XML stream evaluation, template inheritance by
taking the availability of XPath into account and more.  Mako on the
other hand treats templates similar to Python modules.

When it comes to connecting a template engine with an application or
framework there is more than just rendering templates.  For instance,
Flask uses Jinja2’s extensive autoescaping support.  Also it provides
ways to access macros from Jinja2 templates.

A template abstraction layer that would not take the unique features of
the template engines away is a science on its own and a too large
undertaking for a microframework like Flask.

Furthermore extensions can then easily depend on one template language
being present.  You can easily use your own templating language, but an
extension could still depend on Jinja itself.

3.1.4 Micro with Dependencies
-----------------------------

Why does Flask call itself a microframework and yet it depends on two
libraries (namely Werkzeug and Jinja2).  Why shouldn’t it?  If we look
over to the Ruby side of web development there we have a protocol very
similar to WSGI. Just that it’s called Rack there, but besides that it
looks very much like a WSGI rendition for Ruby.  But nearly all
applications in Ruby land do not work with Rack directly, but on top of
a library with the same name.  This Rack library has two equivalents in
Python: WebOb (formerly Paste) and Werkzeug.  Paste is still around but
from my understanding it’s sort of deprecated in favour of WebOb.  The
development of WebOb and Werkzeug started side by side with similar
ideas in mind: be a good implementation of WSGI for other applications
to take advantage.

Flask is a framework that takes advantage of the work already done by
Werkzeug to properly interface WSGI (which can be a complex task at
times).  Thanks to recent developments in the Python package
infrastructure, packages with dependencies are no longer an issue and
there are very few reasons against having libraries that depend on
others.

3.1.5 Thread Locals
-------------------

Flask uses thread local objects (context local objects in fact, they
support greenlet contexts as well) for request, session and an extra
object you can put your own things on (see g).  Why is that and
isn’t that a bad idea?

Yes it is usually not such a bright idea to use thread locals.  They
cause troubles for servers that are not based on the concept of threads
and make large applications harder to maintain.  However Flask is just
not designed for large applications or asynchronous servers.  Flask
wants to make it quick and easy to write a traditional web application.

Also see the Becoming Big section of the documentation for
some inspiration for larger applications based on Flask.

3.1.6 Async/await and ASGI support
----------------------------------

Flask supports ‘async’ coroutines for view functions by executing the
coroutine on a separate thread instead of using an event loop on the
main thread as an async-first (ASGI) framework would.  This is necessary
for Flask to remain backwards compatible with extensions and code built
before ‘async’ was introduced into Python.  This compromise introduces a
performance cost compared with the ASGI frameworks, due to the overhead
of the threads.

Due to how tied to WSGI Flask’s code is, it’s not clear if it’s possible
to make the ‘Flask’ class support ASGI and WSGI at the same time.  Work
is currently being done in Werkzeug to work with ASGI, which may
eventually enable support in Flask as well.

See Using async and await for more discussion.

3.1.7 What Flask is, What Flask is Not
--------------------------------------

Flask will never have a database layer.  It will not have a form library
or anything else in that direction.  Flask itself just bridges to
Werkzeug to implement a proper WSGI application and to Jinja2 to handle
templating.  It also binds to a few common standard library packages
such as logging.  Everything else is up for extensions.

Why is this the case?  Because people have different preferences and
requirements and Flask could not meet those if it would force any of
this into the core.  The majority of web applications will need a
template engine in some sort.  However not every application needs a SQL
database.

The idea of Flask is to build a good foundation for all applications.
Everything else is up to you or extensions.

3.2 HTML/XHTML FAQ
==================

The Flask documentation and example applications are using HTML5.  You
may notice that in many situations, when end tags are optional they are
not used, so that the HTML is cleaner and faster to load.  Because there
is much confusion about HTML and XHTML among developers, this document
tries to answer some of the major questions.

3.2.1 History of XHTML
----------------------

For a while, it appeared that HTML was about to be replaced by XHTML.
However, barely any websites on the Internet are actual XHTML (which is
HTML processed using XML rules).  There are a couple of major reasons
why this is the case.  One of them is Internet Explorer’s lack of proper
XHTML support.  The XHTML spec states that XHTML must be served with the
MIME type ‘application/xhtml+xml’, but Internet Explorer refuses to read
files with that MIME type.  While it is relatively easy to configure Web
servers to serve XHTML properly, few people do.  This is likely because
properly using XHTML can be quite painful.

One of the most important causes of pain is XML’s draconian (strict and
ruthless) error handling.  When an XML parsing error is encountered, the
browser is supposed to show the user an ugly error message, instead of
attempting to recover from the error and display what it can.  Most of
the (X)HTML generation on the web is based on non-XML template engines
(such as Jinja, the one used in Flask) which do not protect you from
accidentally creating invalid XHTML. There are XML based template
engines, such as Kid and the popular Genshi, but they often come with a
larger runtime overhead and are not as straightforward to use because
they have to obey XML rules.

The majority of users, however, assumed they were properly using XHTML.
They wrote an XHTML doctype at the top of the document and self-closed
all the necessary tags (‘<br>’ becomes ‘<br/>’ or ‘<br></br>’ in XHTML).
However, even if the document properly validates as XHTML, what really
determines XHTML/HTML processing in browsers is the MIME type, which as
said before is often not set properly.  So the valid XHTML was being
treated as invalid HTML.

XHTML also changed the way JavaScript is used.  To properly work with
XHTML, programmers have to use the namespaced DOM interface with the
XHTML namespace to query for HTML elements.

3.2.2 History of HTML5
----------------------

Development of the HTML5 specification was started in 2004 under the
name “Web Applications 1.0” by the Web Hypertext Application Technology
Working Group, or WHATWG (which was formed by the major browser vendors
Apple, Mozilla, and Opera) with the goal of writing a new and improved
HTML specification, based on existing browser behavior instead of
unrealistic and backwards-incompatible specifications.

For example, in HTML4 ‘<title/Hello/’ theoretically parses exactly the
same as ‘<title>Hello</title>’.  However, since people were using
XHTML-like tags along the lines of ‘<link />’, browser vendors
implemented the XHTML syntax over the syntax defined by the
specification.

In 2007, the specification was adopted as the basis of a new HTML
specification under the umbrella of the W3C, known as HTML5.  Currently,
it appears that XHTML is losing traction, as the XHTML 2 working group
has been disbanded and HTML5 is being implemented by all major browser
vendors.

3.2.3 HTML versus XHTML
-----------------------

The following table gives you a quick overview of features available in
HTML 4.01, XHTML 1.1 and HTML5.  (XHTML 1.0 is not included, as it was
superseded by XHTML 1.1 and the barely-used XHTML5.)

                                              HTML4.01       XHTML1.1       HTML5

-------------------------------------------------------------------------------------------

‘<tag/value/’ == ‘<tag>value</tag>’

                                              (1)

‘<br/>’ supported

                                                                            (2)

‘<script/>’ supported

should be served as ‘text/html’

                                                             (3)

should be served as ‘application/xhtml+xml’

strict error handling

inline SVG

inline MathML

‘<video>’ tag

‘<audio>’ tag

New semantic tags like ‘<article>’

   ---------- Footnotes ----------

   (1) This is an obscure feature inherited from SGML. It is usually not
supported by browsers, for reasons detailed above.

   (2) This is for compatibility with server code that generates XHTML
for tags such as ‘<br>’.  It should not be used in new code.

   (3) XHTML 1.0 is the last XHTML standard that allows to be served as
‘text/html’ for backwards compatibility reasons.

3.2.4 What does “strict” mean?
------------------------------

HTML5 has strictly defined parsing rules, but it also specifies exactly
how a browser should react to parsing errors - unlike XHTML, which
simply states parsing should abort.  Some people are confused by
apparently invalid syntax that still generates the expected results (for
example, missing end tags or unquoted attribute values).

Some of these work because of the lenient error handling most browsers
use when they encounter a markup error, others are actually specified.
The following constructs are optional in HTML5 by standard, but have to
be supported by browsers:

   - Wrapping the document in an ‘<html>’ tag

   - Wrapping header elements in ‘<head>’ or the body elements in
     ‘<body>’

   - Closing the ‘<p>’, ‘<li>’, ‘<dt>’, ‘<dd>’, ‘<tr>’, ‘<td>’, ‘<th>’,
     ‘<tbody>’, ‘<thead>’, or ‘<tfoot>’ tags.

   - Quoting attributes, so long as they contain no whitespace or
     special characters (like ‘<’, ‘>’, ‘'’, or ‘"’).

   - Requiring boolean attributes to have a value.

This means the following page in HTML5 is perfectly valid:

     <!doctype html>
     <title>Hello HTML5</title>
     <div class=header>
       <h1>Hello HTML5</h1>
       <p class=tagline>HTML5 is awesome
     </div>
     <ul class=nav>
       <li><a href=/index>Index</a>
       <li><a href=/downloads>Downloads</a>
       <li><a href=/about>About</a>
     </ul>
     <div class=body>
       <h2>HTML5 is probably the future</h2>
       <p>
         There might be some other things around but in terms of
         browser vendor support, HTML5 is hard to beat.
       <dl>
         <dt>Key 1
         <dd>Value 1
         <dt>Key 2
         <dd>Value 2
       </dl>
     </div>

3.2.5 New technologies in HTML5
-------------------------------

HTML5 adds many new features that make Web applications easier to write
and to use.

   - The ‘<audio>’ and ‘<video>’ tags provide a way to embed audio and
     video without complicated add-ons like QuickTime or Flash.

   - Semantic elements like ‘<article>’, ‘<header>’, ‘<nav>’, and
     ‘<time>’ that make content easier to understand.

   - The ‘<canvas>’ tag, which supports a powerful drawing API, reducing
     the need for server-generated images to present data graphically.

   - New form control types like ‘<input type="date">’ that allow user
     agents to make entering and validating values easier.

   - Advanced JavaScript APIs like Web Storage, Web Workers, Web
     Sockets, geolocation, and offline applications.

Many other features have been added, as well.  A good guide to new
features in HTML5 is Mark Pilgrim’s book, Dive Into HTML5(1).  Not all
of them are supported in browsers yet, however, so use caution.

   ---------- Footnotes ----------

   (1) https://diveintohtml5.info/

3.2.6 What should be used?
--------------------------

Currently, the answer is HTML5.  There are very few reasons to use XHTML
considering the latest developments in Web browsers.  To summarize the
reasons given above:

   - Internet Explorer has poor support for XHTML.

   - Many JavaScript libraries also do not support XHTML, due to the
     more complicated namespacing API it requires.

   - HTML5 adds several new features, including semantic tags and the
     long-awaited ‘<audio>’ and ‘<video>’ tags.

   - It has the support of most browser vendors behind it.

   - It is much easier to write, and more compact.

For most applications, it is undoubtedly better to use HTML5 than XHTML.

3.3 Security Considerations
===========================

Web applications usually face all kinds of security problems and it’s
very hard to get everything right.  Flask tries to solve a few of these
things for you, but there are a couple more you have to take care of
yourself.

3.3.1 Cross-Site Scripting (XSS)
--------------------------------

Cross site scripting is the concept of injecting arbitrary HTML (and
with it JavaScript) into the context of a website.  To remedy this,
developers have to properly escape text so that it cannot include
arbitrary HTML tags.  For more information on that have a look at the
Wikipedia article on Cross-Site Scripting(1).

Flask configures Jinja2 to automatically escape all values unless
explicitly told otherwise.  This should rule out all XSS problems caused
in templates, but there are still other places where you have to be
careful:

   - generating HTML without the help of Jinja2

   - calling Markup on data submitted by users

   - sending out HTML from uploaded files, never do that, use the
     ‘Content-Disposition: attachment’ header to prevent that problem.

   - sending out textfiles from uploaded files.  Some browsers are using
     content-type guessing based on the first few bytes so users could
     trick a browser to execute HTML.

Another thing that is very important are unquoted attributes.  While
Jinja2 can protect you from XSS issues by escaping HTML, there is one
thing it cannot protect you from: XSS by attribute injection.  To
counter this possible attack vector, be sure to always quote your
attributes with either double or single quotes when using Jinja
expressions in them:

     <input value="{{ value }}">

Why is this necessary?  Because if you would not be doing that, an
attacker could easily inject custom JavaScript handlers.  For example an
attacker could inject this piece of HTML+JavaScript:

     onmouseover=alert(document.cookie)

When the user would then move with the mouse over the input, the cookie
would be presented to the user in an alert window.  But instead of
showing the cookie to the user, a good attacker might also execute any
other JavaScript code.  In combination with CSS injections the attacker
might even make the element fill out the entire page so that the user
would just have to have the mouse anywhere on the page to trigger the
attack.

There is one class of XSS issues that Jinja’s escaping does not protect
against.  The ‘a’ tag’s ‘href’ attribute can contain a ‘javascript:’
URI, which the browser will execute when clicked if not secured
properly.

     <a href="{{ value }}">click here</a>
     <a href="javascript:alert('unsafe');">click here</a>

To prevent this, you’ll need to set the Content Security Policy (CSP)
response header.

   ---------- Footnotes ----------

   (1) https://en.wikipedia.org/wiki/Cross-site_scripting

3.3.2 Cross-Site Request Forgery (CSRF)
---------------------------------------

Another big problem is CSRF. This is a very complex topic and I won’t
outline it here in detail just mention what it is and how to
theoretically prevent it.

If your authentication information is stored in cookies, you have
implicit state management.  The state of “being logged in” is controlled
by a cookie, and that cookie is sent with each request to a page.
Unfortunately that includes requests triggered by 3rd party sites.  If
you don’t keep that in mind, some people might be able to trick your
application’s users with social engineering to do stupid things without
them knowing.

Say you have a specific URL that, when you sent ‘POST’ requests to will
delete a user’s profile (say ‘http://example.com/user/delete’).  If an
attacker now creates a page that sends a post request to that page with
some JavaScript they just have to trick some users to load that page and
their profiles will end up being deleted.

Imagine you were to run Facebook with millions of concurrent users and
someone would send out links to images of little kittens.  When users
would go to that page, their profiles would get deleted while they are
looking at images of fluffy cats.

How can you prevent that?  Basically for each request that modifies
content on the server you would have to either use a one-time token and
store that in the cookie `and' also transmit it with the form data.
After receiving the data on the server again, you would then have to
compare the two tokens and ensure they are equal.

Why does Flask not do that for you?  The ideal place for this to happen
is the form validation framework, which does not exist in Flask.

3.3.3 JSON Security
-------------------

In Flask 0.10 and lower, ‘jsonify()’ did not serialize top-level arrays
to JSON. This was because of a security vulnerability in ECMAScript 4.

ECMAScript 5 closed this vulnerability, so only extremely old browsers
are still vulnerable.  All of these browsers have other more serious
vulnerabilities(1), so this behavior was changed and ‘jsonify()’ now
supports serializing arrays.

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/248#issuecomment-59934857

3.3.4 Security Headers
----------------------

Browsers recognize various response headers in order to control
security.  We recommend reviewing each of the headers below for use in
your application.  The Flask-Talisman(1) extension can be used to manage
HTTPS and the security headers for you.

   ---------- Footnotes ----------

   (1) https://github.com/GoogleCloudPlatform/flask-talisman

3.3.4.1 HTTP Strict Transport Security (HSTS)
.............................................

Tells the browser to convert all HTTP requests to HTTPS, preventing
man-in-the-middle (MITM) attacks.

     response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'

   -
     ‘https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security’

3.3.4.2 Content Security Policy (CSP)
.....................................

Tell the browser where it can load various types of resource from.  This
header should be used whenever possible, but requires some work to
define the correct policy for your site.  A very strict policy would be:

     response.headers['Content-Security-Policy'] = "default-src 'self'"

   - ‘https://csp.withgoogle.com/docs/index.html’

   -
     ‘https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy’

3.3.4.3 X-Content-Type-Options
..............................

Forces the browser to honor the response content type instead of trying
to detect it, which can be abused to generate a cross-site scripting
(XSS) attack.

     response.headers['X-Content-Type-Options'] = 'nosniff'

   -
     ‘https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options’

3.3.4.4 X-Frame-Options
.......................

Prevents external sites from embedding your site in an ‘iframe’.  This
prevents a class of attacks where clicks in the outer frame can be
translated invisibly to clicks on your page’s elements.  This is also
known as “clickjacking”.

     response.headers['X-Frame-Options'] = 'SAMEORIGIN'

   -
     ‘https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options’

3.3.4.5 Set-Cookie options
..........................

These options can be added to a ‘Set-Cookie’ header to improve their
security.  Flask has configuration options to set these on the session
cookie.  They can be set on other cookies too.

   - ‘Secure’ limits cookies to HTTPS traffic only.

   - ‘HttpOnly’ protects the contents of cookies from being read with
     JavaScript.

   - ‘SameSite’ restricts how cookies are sent with requests from
     external sites.  Can be set to ‘'Lax'’ (recommended) or ‘'Strict'’.
     ‘Lax’ prevents sending cookies with CSRF-prone requests from
     external sites, such as submitting a form.  ‘Strict’ prevents
     sending cookies with all external requests, including following
     regular links.

     app.config.update(
         SESSION_COOKIE_SECURE=True,
         SESSION_COOKIE_HTTPONLY=True,
         SESSION_COOKIE_SAMESITE='Lax',
     )

     response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax')

Specifying ‘Expires’ or ‘Max-Age’ options, will remove the cookie after
the given time, or the current time plus the age, respectively.  If
neither option is set, the cookie will be removed when the browser is
closed.

     # cookie expires after 10 minutes
     response.set_cookie('snakes', '3', max_age=600)

For the session cookie, if session.permanent is set, then
PERMANENT_SESSION_LIFETIME is used to set the expiration.
Flask’s default cookie implementation validates that the cryptographic
signature is not older than this value.  Lowering this value may help
mitigate replay attacks, where intercepted cookies can be sent at a
later time.

     app.config.update(
         PERMANENT_SESSION_LIFETIME=600
     )

     @app.route('/login', methods=['POST'])
     def login():
         ...
         session.clear()
         session['user_id'] = user.id
         session.permanent = True
         ...

Use ‘itsdangerous.TimedSerializer’ to sign and validate other cookie
values (or any values that need secure signatures).

   - ‘https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies’

   -
     ‘https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie’

3.3.4.6 HTTP Public Key Pinning (HPKP)
......................................

This tells the browser to authenticate with the server using only the
specific certificate key to prevent MITM attacks.

     Warning: Be careful when enabling this, as it is very difficult to
     undo if you set up or upgrade your key incorrectly.

   -
     ‘https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning’

3.3.5 Copy/Paste to Terminal
----------------------------

Hidden characters such as the backspace character (‘\b’, ‘^H’) can cause
text to render differently in HTML than how it is interpreted if pasted
into a terminal(1).

For example, ‘import y\bose\bm\bi\bt\be\b’ renders as ‘import yosemite’
in HTML, but the backspaces are applied when pasted into a terminal, and
it becomes ‘import os’.

If you expect users to copy and paste untrusted code from your site,
such as from comments posted by users on a technical blog, consider
applying extra filtering, such as replacing all ‘\b’ characters.

     body = body.replace("\b", "")

Most modern terminals will warn about and remove hidden characters when
pasting, so this isn’t strictly necessary.  It’s also possible to craft
dangerous commands in other ways that aren’t possible to filter.
Depending on your site’s use case, it may be good to show a warning
about copying code in general.

   ---------- Footnotes ----------

   (1) https://security.stackexchange.com/q/39118

3.4 Flask Extension Development
===============================

Flask, being a microframework, often requires some repetitive steps to
get a third party library working.  Many such extensions are already
available on PyPI(1).

If you want to create your own Flask extension for something that does
not exist yet, this guide to extension development will help you get
your extension running in no time and to feel like users would expect
your extension to behave.

   ---------- Footnotes ----------

   (1) https://pypi.org/search/?c=Framework+%3A%3A+Flask

3.4.1 Anatomy of an Extension
-----------------------------

Extensions are all located in a package called ‘flask_something’ where
“something” is the name of the library you want to bridge.  So for
example if you plan to add support for a library named ‘simplexml’ to
Flask, you would name your extension’s package ‘flask_simplexml’.

The name of the actual extension (the human readable name) however would
be something like “Flask-SimpleXML”.  Make sure to include the name
“Flask” somewhere in that name and that you check the capitalization.
This is how users can then register dependencies to your extension in
their ‘setup.py’ files.

But what do extensions look like themselves?  An extension has to ensure
that it works with multiple Flask application instances at once.  This
is a requirement because many people will use patterns like the
Application Factories pattern to create their application as needed
to aid unittests and to support multiple configurations.  Because of
that it is crucial that your application supports that kind of behavior.

Most importantly the extension must be shipped with a ‘setup.py’ file
and registered on PyPI. Also the development checkout link should work
so that people can easily install the development version into their
virtualenv without having to download the library by hand.

Flask extensions must be licensed under a BSD, MIT or more liberal
license in order to be listed in the Flask Extension Registry.  Keep in
mind that the Flask Extension Registry is a moderated place and
libraries will be reviewed upfront if they behave as required.

3.4.2 “Hello Flaskext!”
-----------------------

So let’s get started with creating such a Flask extension.  The
extension we want to create here will provide very basic support for
SQLite3.

First we create the following folder structure:

     flask-sqlite3/
         flask_sqlite3.py
         LICENSE
         README

Here’s the contents of the most important files:

3.4.2.1 setup.py
................

The next file that is absolutely required is the ‘setup.py’ file which
is used to install your Flask extension.  The following contents are
something you can work with:

     """
     Flask-SQLite3
     -------------

     This is the description for that library
     """
     from setuptools import setup

     setup(
         name='Flask-SQLite3',
         version='1.0',
         url='http://example.com/flask-sqlite3/',
         license='BSD',
         author='Your Name',
         author_email='your-email@example.com',
         description='Very short description',
         long_description=__doc__,
         py_modules=['flask_sqlite3'],
         # if you would be using a package instead use packages instead
         # of py_modules:
         # packages=['flask_sqlite3'],
         zip_safe=False,
         include_package_data=True,
         platforms='any',
         install_requires=[
             'Flask'
         ],
         classifiers=[
             'Environment :: Web Environment',
             'Intended Audience :: Developers',
             'License :: OSI Approved :: BSD License',
             'Operating System :: OS Independent',
             'Programming Language :: Python',
             'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
             'Topic :: Software Development :: Libraries :: Python Modules'
         ]
     )

That’s a lot of code but you can really just copy/paste that from
existing extensions and adapt.

3.4.2.2 flask_sqlite3.py
........................

Now this is where your extension code goes.  But how exactly should such
an extension look like?  What are the best practices?  Continue reading
for some insight.

3.4.3 Initializing Extensions
-----------------------------

Many extensions will need some kind of initialization step.  For
example, consider an application that’s currently connecting to SQLite
like the documentation suggests (see Using SQLite 3 with Flask).
So how does the extension know the name of the application object?

Quite simple: you pass it to it.

There are two recommended ways for an extension to initialize:

initialization functions:

     If your extension is called ‘helloworld’ you might have a function
     called ‘init_helloworld(app[, extra_args])’ that initializes the
     extension for that application.  It could attach before / after
     handlers etc.

classes:

     Classes work mostly like initialization functions but can later be
     used to further change the behavior.

What to use depends on what you have in mind.  For the SQLite 3
extension we will use the class-based approach because it will provide
users with an object that handles opening and closing database
connections.

When designing your classes, it’s important to make them easily reusable
at the module level.  This means the object itself must not under any
circumstances store any application specific state and must be shareable
between different applications.

3.4.4 The Extension Code
------------------------

Here’s the contents of the ‘flask_sqlite3.py’ for copy/paste:

     import sqlite3
     from flask import current_app, _app_ctx_stack

     class SQLite3(object):
         def __init__(self, app=None):
             self.app = app
             if app is not None:
                 self.init_app(app)

         def init_app(self, app):
             app.config.setdefault('SQLITE3_DATABASE', ':memory:')
             app.teardown_appcontext(self.teardown)

         def connect(self):
             return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])

         def teardown(self, exception):
             ctx = _app_ctx_stack.top
             if hasattr(ctx, 'sqlite3_db'):
                 ctx.sqlite3_db.close()

         @property
         def connection(self):
             ctx = _app_ctx_stack.top
             if ctx is not None:
                 if not hasattr(ctx, 'sqlite3_db'):
                     ctx.sqlite3_db = self.connect()
                 return ctx.sqlite3_db

So here’s what these lines of code do:

  1. The ‘__init__’ method takes an optional app object and, if
     supplied, will call ‘init_app’.

  2. The ‘init_app’ method exists so that the ‘SQLite3’ object can be
     instantiated without requiring an app object.  This method supports
     the factory pattern for creating applications.  The ‘init_app’ will
     set the configuration for the database, defaulting to an in memory
     database if no configuration is supplied.  In addition, the
     ‘init_app’ method attaches the ‘teardown’ handler.

  3. Next, we define a ‘connect’ method that opens a database
     connection.

  4. Finally, we add a ‘connection’ property that on first access opens
     the database connection and stores it on the context.  This is also
     the recommended way to handling resources: fetch resources lazily
     the first time they are used.

     Note here that we’re attaching our database connection to the top
     application context via ‘_app_ctx_stack.top’.  Extensions should
     use the top context for storing their own information with a
     sufficiently complex name.

So why did we decide on a class-based approach here?  Because using our
extension looks something like this:

     from flask import Flask
     from flask_sqlite3 import SQLite3

     app = Flask(__name__)
     app.config.from_pyfile('the-config.cfg')
     db = SQLite3(app)

You can then use the database from views like this:

     @app.route('/')
     def show_all():
         cur = db.connection.cursor()
         cur.execute(...)

Likewise if you are outside of a request you can use the database by
pushing an app context:

     with app.app_context():
         cur = db.connection.cursor()
         cur.execute(...)

At the end of the ‘with’ block the teardown handles will be executed
automatically.

Additionally, the ‘init_app’ method is used to support the factory
pattern for creating apps:

     db = SQLite3()
     # Then later on.
     app = create_app('the-config.cfg')
     db.init_app(app)

Keep in mind that supporting this factory pattern for creating apps is
required for approved flask extensions (described below).

     Note on init_app: As you noticed, ‘init_app’ does not assign ‘app’
     to ‘self’.  This is intentional!  Class based Flask extensions must
     only store the application on the object when the application was
     passed to the constructor.  This tells the extension: I am not
     interested in using multiple applications.

     When the extension needs to find the current application and it
     does not have a reference to it, it must either use the
     current_app context local or change the API in a way that you
     can pass the application explicitly.

3.4.5 Using _app_ctx_stack
--------------------------

In the example above, before every request, a ‘sqlite3_db’ variable is
assigned to ‘_app_ctx_stack.top’.  In a view function, this variable is
accessible using the ‘connection’ property of ‘SQLite3’.  During the
teardown of a request, the ‘sqlite3_db’ connection is closed.  By using
this pattern, the `same' connection to the sqlite3 database is
accessible to anything that needs it for the duration of the request.

3.4.6 Learn from Others
-----------------------

This documentation only touches the bare minimum for extension
development.  If you want to learn more, it’s a very good idea to check
out existing extensions on the PyPI(1). If you feel lost there is still
the mailinglist(2) and the Discord server(3) to get some ideas for nice
looking APIs.  Especially if you do something nobody before you did, it
might be a very good idea to get some more input.  This not only
generates useful feedback on what people might want from an extension,
but also avoids having multiple developers working in isolation on
pretty much the same problem.

Remember: good API design is hard, so introduce your project on the
mailing list, and let other developers give you a helping hand with
designing the API.

The best Flask extensions are extensions that share common idioms for
the API. And this can only work if collaboration happens early.

   ---------- Footnotes ----------

   (1) https://pypi.org/search/?c=Framework+%3A%3A+Flask

   (2) https://mail.python.org/mailman/listinfo/flask

   (3) https://discord.gg/pallets

3.4.7 Approved Extensions
-------------------------

Flask previously had the concept of approved extensions.  These came
with some vetting of support and compatibility.  While this list became
too difficult to maintain over time, the guidelines are still relevant
to all extensions maintained and developed today, as they help the Flask
ecosystem remain consistent and compatible.

  0. An approved Flask extension requires a maintainer.  In the event an
     extension author would like to move beyond the project, the project
     should find a new maintainer and transfer access to the repository,
     documentation, PyPI, and any other services.  If no maintainer is
     available, give access to the Pallets core team.

  1. The naming scheme is `Flask-ExtensionName' or
     `ExtensionName-Flask'.  It must provide exactly one package or
     module named ‘flask_extension_name’.

  2. The extension must be BSD or MIT licensed.  It must be open source
     and publicly available.

  3. The extension’s API must have the following characteristics:

        - It must support multiple applications running in the same
          Python process.  Use ‘current_app’ instead of ‘self.app’,
          store configuration and state per application instance.

        - It must be possible to use the factory pattern for creating
          applications.  Use the ‘ext.init_app()’ pattern.

  4. From a clone of the repository, an extension with its dependencies
     must be installable with ‘pip install -e .’.

  5. It must ship a testing suite that can be invoked with ‘tox -e py’
     or ‘pytest’.  If not using ‘tox’, the test dependencies should be
     specified in a ‘requirements.txt’ file.  The tests must be part of
     the sdist distribution.

  6. The documentation must use the ‘flask’ theme from the Official
     Pallets Themes(1).  A link to the documentation or project website
     must be in the PyPI metadata or the readme.

  7. For maximum compatibility, the extension should support the same
     versions of Python that Flask supports.  3.7+ is recommended as of
     December 2021.  Use ‘python_requires=">= 3.7"’ in ‘setup.py’ to
     indicate supported versions.

   ---------- Footnotes ----------

   (1) https://pypi.org/project/Pallets-Sphinx-Themes/

3.5 How to contribute to Flask
==============================

Thank you for considering contributing to Flask!

3.5.1 Support questions
-----------------------

Please don’t use the issue tracker for this.  The issue tracker is a
tool to address bugs and feature requests in Flask itself.  Use one of
the following resources for questions about using Flask or issues with
your own code:

   - The ‘#questions’ channel on our Discord chat:
     ‘https://discord.gg/pallets’

   - The mailing list <flask@python.org> for long term discussion or
     larger issues.

   - Ask on Stack Overflow(1).  Search with Google first using:
     ‘site:stackoverflow.com flask {search term, exception message,
     etc.}’

   - Ask on our GitHub Discussions(2).

   ---------- Footnotes ----------

   (1) https://stackoverflow.com/questions/tagged/flask?tab=Frequent

   (2) https://github.com/pallets/flask/discussions

3.5.2 Reporting issues
----------------------

Include the following information in your post:

   - Describe what you expected to happen.

   - If possible, include a minimal reproducible example(1) to help us
     identify the issue.  This also helps check that the issue is not
     with your own code.

   - Describe what actually happened.  Include the full traceback if
     there was an exception.

   - List your Python and Flask versions.  If possible, check if this
     issue is already fixed in the latest releases or the latest code in
     the repository.

   ---------- Footnotes ----------

   (1) https://stackoverflow.com/help/minimal-reproducible-example

3.5.3 Submitting patches
------------------------

If there is not an open issue for what you want to submit, prefer
opening one for discussion before working on a PR. You can work on any
issue that doesn’t have an open PR linked to it or a maintainer assigned
to it.  These show up in the sidebar.  No need to ask if you can work on
an issue that interests you.

Include the following in your patch:

   - Use Black(1) to format your code.  This and other tools will run
     automatically if you install pre-commit(2) using the instructions
     below.

   - Include tests if your patch adds or changes code.  Make sure the
     test fails without your patch.

   - Update any relevant docs pages and docstrings.  Docs pages and
     docstrings should be wrapped at 72 characters.

   - Add an entry in ‘CHANGES.rst’.  Use the same style as other
     entries.  Also include ‘.. versionchanged::’ inline changelogs in
     relevant docstrings.

   ---------- Footnotes ----------

   (1) https://black.readthedocs.io

   (2) https://pre-commit.com

3.5.3.1 First time setup
........................

   - Download and install the latest version of git(1).

   - Configure git with your username(2) and email(3).

          $ git config --global user.name 'your name'
          $ git config --global user.email 'your email'

   - Make sure you have a GitHub account(4).

   - Fork Flask to your GitHub account by clicking the Fork(5) button.

   - Clone(6) the main repository locally.

          $ git clone https://github.com/pallets/flask
          $ cd flask

   - Add your fork as a remote to push your work to.  Replace
     ‘{username}’ with your username.  This names the remote “fork”, the
     default Pallets remote is “origin”.

          $ git remote add fork https://github.com/{username}/flask

   - Create a virtualenv.

     Linux/macOS

     Windows

          $ python3 -m venv env
          $ . env/bin/activate

          > py -3 -m venv env
          > env\Scripts\activate

   - Upgrade pip and setuptools.

          $ python -m pip install --upgrade pip setuptools

   - Install the development dependencies, then install Flask in
     editable mode.

          $ pip install -r requirements/dev.txt && pip install -e .

   - Install the pre-commit hooks.

          $ pre-commit install

   ---------- Footnotes ----------

   (1) https://git-scm.com/downloads

   (2)
https://docs.github.com/en/github/using-git/setting-your-username-in-git

   (3)
https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address

   (4) https://github.com/join

   (5) https://github.com/pallets/flask/fork

   (6)
https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#step-2-create-a-local-clone-of-your-fork

3.5.3.2 Start coding
....................

   - Create a branch to identify the issue you would like to work on.
     If you’re submitting a bug or documentation fix, branch off of the
     latest “.x” branch.

          $ git fetch origin
          $ git checkout -b your-branch-name origin/2.0.x

     If you’re submitting a feature addition or change, branch off of
     the “main” branch.

          $ git fetch origin
          $ git checkout -b your-branch-name origin/main

   - Using your favorite editor, make your changes, committing as you
     go(1).

   - Include tests that cover any code changes you make.  Make sure the
     test fails without your patch.  Run the tests as described below.

   - Push your commits to your fork on GitHub and create a pull
     request(2).  Link to the issue being addressed with ‘fixes #123’ in
     the pull request.

          $ git push --set-upstream fork your-branch-name

   ---------- Footnotes ----------

   (1)
https://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes

   (2)
https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request

3.5.3.3 Running the tests
.........................

Run the basic test suite with pytest.

     $ pytest

This runs the tests for the current environment, which is usually
sufficient.  CI will run the full suite when you submit your pull
request.  You can run the full test suite with tox if you don’t want to
wait.

     $ tox

3.5.3.4 Running test coverage
.............................

Generating a report of lines that do not have test coverage can indicate
where to start contributing.  Run ‘pytest’ using ‘coverage’ and generate
a report.

     $ pip install coverage
     $ coverage run -m pytest
     $ coverage html

Open ‘htmlcov/index.html’ in your browser to explore the report.

Read more about coverage(1).

   ---------- Footnotes ----------

   (1) https://coverage.readthedocs.io

3.5.3.5 Building the docs
.........................

Build the docs in the ‘docs’ directory using Sphinx.

     $ cd docs
     $ make html

Open ‘_build/html/index.html’ in your browser to view the docs.

Read more about Sphinx(1).

   ---------- Footnotes ----------

   (1) https://www.sphinx-doc.org/en/stable/

3.6 License
===========

3.6.1 BSD-3-Clause Source License
---------------------------------

The BSD-3-Clause license applies to all files in the Flask repository
and source distribution.  This includes Flask’s source code, the
examples, and tests, as well as the documentation.

Copyright 2010 Pallets

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

  2. 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.

  3. Neither the name of the copyright holder nor the names of its
     contributors may 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
HOLDER 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.

3.6.2 Artwork License
---------------------

This license applies to Flask’s logo.

Copyright 2010 Pallets

This logo or a modified version may be used by anyone to refer to the
Flask project, but does not indicate endorsement by the project.

Redistribution and use in source (SVG) and binary (renders in PNG, etc.)
forms, with or without modification, are permitted provided that the
following conditions are met:

  1. Redistributions of source code must retain the above copyright
     notice and this list of conditions.

  2. Neither the name of the copyright holder nor the names of its
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

We would appreciate that you make the image a link to
‘https://palletsprojects.com/p/flask/’ if you use it in a medium that
supports links.

3.7.1 Version 2.1.2
-------------------

Released 2022-04-28

   - Fix type annotation for ‘json.loads’, it accepts str or bytes.
     #4519(1)

   - The ‘--cert’ and ‘--key’ options on ‘flask run’ can be given in
     either order.  #4459(2)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/4519

   (2) https://github.com/pallets/flask/issues/4459

3.7.2 Version 2.1.1
-------------------

Released on 2022-03-30

   - Set the minimum required version of importlib_metadata to 3.6.0,
     which is required on Python < 3.10.  #4502(1)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/4502

3.7.3 Version 2.1.0
-------------------

Released 2022-03-28

   - Drop support for Python 3.6.  #4335(1)

   - Update Click dependency to >= 8.0.  #4008(2)

   - Remove previously deprecated code.  #4337(3)

        - The CLI does not pass ‘script_info’ to app factory functions.

        - ‘config.from_json’ is replaced by ‘config.from_file(name,
          load=json.load)’.

        - ‘json’ functions no longer take an ‘encoding’ parameter.

        - ‘safe_join’ is removed, use ‘werkzeug.utils.safe_join’
          instead.

        - ‘total_seconds’ is removed, use ‘timedelta.total_seconds’
          instead.

        - The same blueprint cannot be registered with the same name.
          Use ‘name=’ when registering to specify a unique name.

        - The test client’s ‘as_tuple’ parameter is removed.  Use
          ‘response.request.environ’ instead.  #4417(4)

   - Some parameters in ‘send_file’ and ‘send_from_directory’ were
     renamed in 2.0.  The deprecation period for the old names is
     extended to 2.2.  Be sure to test with deprecation warnings
     visible.

        - ‘attachment_filename’ is renamed to ‘download_name’.

        - ‘cache_timeout’ is renamed to ‘max_age’.

        - ‘add_etags’ is renamed to ‘etag’.

        - ‘filename’ is renamed to ‘path’.

   - The ‘RequestContext.g’ property is deprecated.  Use ‘g’ directly or
     ‘AppContext.g’ instead.  #3898(5)

   - ‘copy_current_request_context’ can decorate async functions.
     #4303(6)

   - The CLI uses ‘importlib.metadata’ instead of ‘setuptools’ to load
     command entry points.  #4419(7)

   - Overriding ‘FlaskClient.open’ will not cause an error on redirect.
     #3396(8)

   - Add an ‘--exclude-patterns’ option to the ‘flask run’ CLI command
     to specify patterns that will be ignored by the reloader.  #4188(9)

   - When using lazy loading (the default with the debugger), the Click
     context from the ‘flask run’ command remains available in the
     loader thread.  #4460(10)

   - Deleting the session cookie uses the ‘httponly’ flag.  #4485(11)

   - Relax typing for ‘errorhandler’ to allow the user to use more
     precise types and decorate the same function multiple times.
     #4095(12)#4295(13)#4297(14)

   - Fix typing for ‘__exit__’ methods for better compatibility with
     ‘ExitStack’.  #4474(15)

   - From Werkzeug, for redirect responses the ‘Location’ header URL
     will remain relative, and exclude the scheme and domain, by
     default.  #4496(16)

   - Add ‘Config.from_prefixed_env()’ to load config values from
     environment variables that start with ‘FLASK_’ or another prefix.
     This parses values as JSON by default, and allows setting keys in
     nested dicts.  #4479(17)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/4335

   (2) https://github.com/pallets/flask/pull/4008

   (3) https://github.com/pallets/flask/pull/4337

   (4) https://github.com/pallets/flask/pull/4417

   (5) https://github.com/pallets/flask/issues/3898

   (6) https://github.com/pallets/flask/pull/4303

   (7) https://github.com/pallets/flask/issues/4419

   (8) https://github.com/pallets/flask/issues/3396

   (9) https://github.com/pallets/flask/issues/4188

   (10) https://github.com/pallets/flask/issues/4460

   (11) https://github.com/pallets/flask/issues/4485

   (12) https://github.com/pallets/flask/issues/4095

   (13) https://github.com/pallets/flask/issues/4295

   (14) https://github.com/pallets/flask/issues/4297

   (15) https://github.com/pallets/flask/issues/4474

   (16) https://github.com/pallets/flask/pull/4496

   (17) https://github.com/pallets/flask/pull/4479

3.7.4 Version 2.0.3
-------------------

Released 2022-02-14

   - The test client’s ‘as_tuple’ parameter is deprecated and will be
     removed in Werkzeug 2.1.  It is now also deprecated in Flask, to be
     removed in Flask 2.1, while remaining compatible with both in
     2.0.x.  Use ‘response.request.environ’ instead.  #4341(1)

   - Fix type annotation for ‘errorhandler’ decorator.  #4295(2)

   - Revert a change to the CLI that caused it to hide ‘ImportError’
     tracebacks when importing the application.  #4307(3)

   - ‘app.json_encoder’ and ‘json_decoder’ are only passed to ‘dumps’
     and ‘loads’ if they have custom behavior.  This improves
     performance, mainly on PyPy.  #4349(4)

   - Clearer error message when ‘after_this_request’ is used outside a
     request context.  #4333(5)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/4341

   (2) https://github.com/pallets/flask/issues/4295

   (3) https://github.com/pallets/flask/issues/4307

   (4) https://github.com/pallets/flask/issues/4349

   (5) https://github.com/pallets/flask/issues/4333

3.7.5 Version 2.0.2
-------------------

Released 2021-10-04

   - Fix type annotation for ‘teardown_*’ methods.  #4093(1)

   - Fix type annotation for ‘before_request’ and ‘before_app_request’
     decorators.  #4104(2)

   - Fixed the issue where typing requires template global decorators to
     accept functions with no arguments.  #4098(3)

   - Support View and MethodView instances with async handlers.
     #4112(4)

   - Enhance typing of ‘app.errorhandler’ decorator.  #4095(5)

   - Fix registering a blueprint twice with differing names.  #4124(6)

   - Fix the type of ‘static_folder’ to accept ‘pathlib.Path’.  #4150(7)

   - ‘jsonify’ handles ‘decimal.Decimal’ by encoding to ‘str’.  #4157(8)

   - Correctly handle raising deferred errors in CLI lazy loading.
     #4096(9)

   - The CLI loader handles ‘**kwargs’ in a ‘create_app’ function.
     #4170(10)

   - Fix the order of ‘before_request’ and other callbacks that trigger
     before the view returns.  They are called from the app down to the
     closest nested blueprint.  #4229(11)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/4093

   (2) https://github.com/pallets/flask/issues/4104

   (3) https://github.com/pallets/flask/issues/4098

   (4) https://github.com/pallets/flask/issues/4112

   (5) https://github.com/pallets/flask/issues/4095

   (6) https://github.com/pallets/flask/issues/4124

   (7) https://github.com/pallets/flask/issues/4150

   (8) https://github.com/pallets/flask/issues/4157

   (9) https://github.com/pallets/flask/issues/4096

   (10) https://github.com/pallets/flask/issues/4170

   (11) https://github.com/pallets/flask/issues/4229

3.7.6 Version 2.0.1
-------------------

Released 2021-05-21

   - Re-add the ‘filename’ parameter in ‘send_from_directory’.  The
     ‘filename’ parameter has been renamed to ‘path’, the old name is
     deprecated.  #4019(1)

   - Mark top-level names as exported so type checking understands
     imports in user projects.  #4024(2)

   - Fix type annotation for ‘g’ and inform mypy that it is a namespace
     object that has arbitrary attributes.  #4020(3)

   - Fix some types that weren’t available in Python 3.6.0.  #4040(4)

   - Improve typing for ‘send_file’, ‘send_from_directory’, and
     ‘get_send_file_max_age’.  #4044(5), #4026(6)

   - Show an error when a blueprint name contains a dot.  The ‘.’ has
     special meaning, it is used to separate (nested) blueprint names
     and the endpoint name.  #4041(7)

   - Combine URL prefixes when nesting blueprints that were created with
     a ‘url_prefix’ value.  #4037(8)

   - Roll back a change to the order that URL matching was done.  The
     URL is again matched after the session is loaded, so the session is
     available in custom URL converters.  #4053(9)

   - Re-add deprecated ‘Config.from_json’, which was accidentally
     removed early.  #4078(10)

   - Improve typing for some functions using ‘Callable’ in their type
     signatures, focusing on decorator factories.  #4060(11)

   - Nested blueprints are registered with their dotted name.  This
     allows different blueprints with the same name to be nested at
     different locations.  #4069(12)

   - ‘register_blueprint’ takes a ‘name’ option to change the
     (pre-dotted) name the blueprint is registered with.  This allows
     the same blueprint to be registered multiple times with unique
     names for ‘url_for’.  Registering the same blueprint with the same
     name multiple times is deprecated.  #1091(13)

   - Improve typing for ‘stream_with_context’.  #4052(14)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/4019

   (2) https://github.com/pallets/flask/issues/4024

   (3) https://github.com/pallets/flask/issues/4020

   (4) https://github.com/pallets/flask/issues/4040

   (5) https://github.com/pallets/flask/issues/4044

   (6) https://github.com/pallets/flask/pull/4026

   (7) https://github.com/pallets/flask/issues/4041

   (8) https://github.com/pallets/flask/issues/4037

   (9) https://github.com/pallets/flask/issues/4053

   (10) https://github.com/pallets/flask/issues/4078

   (11) https://github.com/pallets/flask/issues/4060

   (12) https://github.com/pallets/flask/issues/4069

   (13) https://github.com/pallets/flask/issues/1091

   (14) https://github.com/pallets/flask/issues/4052

3.7.7 Version 2.0.0
-------------------

Released 2021-05-11

   - Drop support for Python 2 and 3.5.

   - Bump minimum versions of other Pallets projects: Werkzeug >= 2,
     Jinja2 >= 3, MarkupSafe >= 2, ItsDangerous >= 2, Click >= 8.  Be
     sure to check the change logs for each project.  For better
     compatibility with other applications (e.g.  Celery) that still
     require Click 7, there is no hard dependency on Click 8 yet, but
     using Click 7 will trigger a DeprecationWarning and Flask 2.1 will
     depend on Click 8.

   - JSON support no longer uses simplejson.  To use another JSON
     module, override ‘app.json_encoder’ and ‘json_decoder’.  #3555(1)

   - The ‘encoding’ option to JSON functions is deprecated.  #3562(2)

   - Passing ‘script_info’ to app factory functions is deprecated.  This
     was not portable outside the ‘flask’ command.  Use
     ‘click.get_current_context().obj’ if it’s needed.  #3552(3)

   - The CLI shows better error messages when the app failed to load
     when looking up commands.  #2741(4)

   - Add sessions.SessionInterface.get_cookie_name() to
     allow setting the session cookie name dynamically.  #3369(5)

   - Add Config.from_file() to load config using arbitrary
     file loaders, such as ‘toml.load’ or ‘json.load’.
     ‘Config.from_json()’ is deprecated in favor of this.  #3398(6)

   - The ‘flask run’ command will only defer errors on reload.  Errors
     present during the initial call will cause the server to exit with
     the traceback immediately.  #3431(7)

   - send_file() raises a ValueError(8) when passed an io(9)
     object in text mode.  Previously, it would respond with 200 OK and
     an empty file.  #3358(10)

   - When using ad-hoc certificates, check for the cryptography library
     instead of PyOpenSSL. #3492(11)

   - When specifying a factory function with ‘FLASK_APP’, keyword
     argument can be passed.  #3553(12)

   - When loading a ‘.env’ or ‘.flaskenv’ file, the current working
     directory is no longer changed to the location of the file.
     #3560(13)

   - When returning a ‘(response, headers)’ tuple from a view, the
     headers replace rather than extend existing headers on the
     response.  For example, this allows setting the ‘Content-Type’ for
     ‘jsonify()’.  Use ‘response.headers.extend()’ if extending is
     desired.  #3628(14)

   - The ‘Scaffold’ class provides a common API for the ‘Flask’ and
     ‘Blueprint’ classes.  ‘Blueprint’ information is stored in
     attributes just like ‘Flask’, rather than opaque lambda functions.
     This is intended to improve consistency and maintainability.
     #3215(15)

   - Include ‘samesite’ and ‘secure’ options when removing the session
     cookie.  #3726(16)

   - Support passing a ‘pathlib.Path’ to ‘static_folder’.  #3579(17)

   - ‘send_file’ and ‘send_from_directory’ are wrappers around the
     implementations in ‘werkzeug.utils’.  #3828(18)

   - Some ‘send_file’ parameters have been renamed, the old names are
     deprecated.  ‘attachment_filename’ is renamed to ‘download_name’.
     ‘cache_timeout’ is renamed to ‘max_age’.  ‘add_etags’ is renamed to
     ‘etag’.  #3828(19)#3883(20)

   - ‘send_file’ passes ‘download_name’ even if ‘as_attachment=False’ by
     using ‘Content-Disposition: inline’.  #3828(21)

   - ‘send_file’ sets ‘conditional=True’ and ‘max_age=None’ by default.
     ‘Cache-Control’ is set to ‘no-cache’ if ‘max_age’ is not set,
     otherwise ‘public’.  This tells browsers to validate conditional
     requests instead of using a timed cache.  #3828(22)

   - ‘helpers.safe_join’ is deprecated.  Use ‘werkzeug.utils.safe_join’
     instead.  #3828(23)

   - The request context does route matching before opening the session.
     This could allow a session interface to change behavior based on
     ‘request.endpoint’.  #3776(24)

   - Use Jinja’s implementation of the ‘|tojson’ filter.  #3881(25)

   - Add route decorators for common HTTP methods.  For example,
     ‘@app.post("/login")’ is a shortcut for ‘@app.route("/login",
     methods=["POST"])’.  #3907(26)

   - Support async views, error handlers, before and after request, and
     teardown functions.  #3412(27)

   - Support nesting blueprints.  #593(28)#1548(29), #3923(30)

   - Set the default encoding to “UTF-8” when loading ‘.env’ and
     ‘.flaskenv’ files to allow to use non-ASCII characters.  #3931(31)

   - ‘flask shell’ sets up tab and history completion like the default
     ‘python’ shell if ‘readline’ is installed.  #3941(32)

   - ‘helpers.total_seconds()’ is deprecated.  Use
     ‘timedelta.total_seconds()’ instead.  #3962(33)

   - Add type hinting.  #3973(34).

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/3555

   (2) https://github.com/pallets/flask/pull/3562

   (3) https://github.com/pallets/flask/issues/3552

   (4) https://github.com/pallets/flask/issues/2741

   (5) https://github.com/pallets/flask/pull/3369

   (6) https://github.com/pallets/flask/pull/3398

   (7) https://github.com/pallets/flask/issues/3431

   (8) https://docs.python.org/3/library/exceptions.html#ValueError

   (9) https://docs.python.org/3/library/io.html#module-io

   (10) https://github.com/pallets/flask/issues/3358

   (11) https://github.com/pallets/flask/pull/3492

   (12) https://github.com/pallets/flask/issues/3553

   (13) https://github.com/pallets/flask/pull/3560

   (14) https://github.com/pallets/flask/issues/3628

   (15) https://github.com/pallets/flask/issues/3215

   (16) https://github.com/pallets/flask/pull/3726

   (17) https://github.com/pallets/flask/pull/3579

   (18) https://github.com/pallets/flask/pull/3828

   (19) https://github.com/pallets/flask/pull/3828

   (20) https://github.com/pallets/flask/pull/3883

   (21) https://github.com/pallets/flask/pull/3828

   (22) https://github.com/pallets/flask/pull/3828

   (23) https://github.com/pallets/flask/pull/3828

   (24) https://github.com/pallets/flask/issues/3776

   (25) https://github.com/pallets/flask/issues/3881

   (26) https://github.com/pallets/flask/pull/3907

   (27) https://github.com/pallets/flask/pull/3412

   (28) https://github.com/pallets/flask/issues/593

   (29) https://github.com/pallets/flask/issues/1548

   (30) https://github.com/pallets/flask/pull/3923

   (31) https://github.com/pallets/flask/issues/3931

   (32) https://github.com/pallets/flask/issues/3941

   (33) https://github.com/pallets/flask/pull/3962

   (34) https://github.com/pallets/flask/pull/3973

3.7.8 Version 1.1.4
-------------------

Released 2021-05-13

   - Update ‘static_folder’ to use ‘_compat.fspath’ instead of
     ‘os.fspath’ to continue supporting Python < 3.6 #4050(1)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/4050

3.7.9 Version 1.1.3
-------------------

Released 2021-05-13

   - Set maximum versions of Werkzeug, Jinja, Click, and ItsDangerous.
     #4043(1)

   - Re-add support for passing a ‘pathlib.Path’ for ‘static_folder’.
     #3579(2)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/4043

   (2) https://github.com/pallets/flask/pull/3579

3.7.10 Version 1.1.2
--------------------

Released 2020-04-03

   - Work around an issue when running the ‘flask’ command with an
     external debugger on Windows.  #3297(1)

   - The static route will not catch all URLs if the ‘Flask’
     ‘static_folder’ argument ends with a slash.  #3452(2)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/3297

   (2) https://github.com/pallets/flask/issues/3452

3.7.11 Version 1.1.1
--------------------

Released 2019-07-08

   - The ‘flask.json_available’ flag was added back for compatibility
     with some extensions.  It will raise a deprecation warning when
     used, and will be removed in version 2.0.0.  #3288(1)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/3288

3.7.12 Version 1.1.0
--------------------

Released 2019-07-04

   - Bump minimum Werkzeug version to >= 0.15.

   - Drop support for Python 3.4.

   - Error handlers for ‘InternalServerError’ or ‘500’ will always be
     passed an instance of ‘InternalServerError’.  If they are invoked
     due to an unhandled exception, that original exception is now
     available as ‘e.original_exception’ rather than being passed
     directly to the handler.  The same is true if the handler is for
     the base ‘HTTPException’.  This makes error handler behavior more
     consistent.  #3266(1)

        - ‘Flask.finalize_request()’ is called for all unhandled
          exceptions even if there is no ‘500’ error handler.

   - Flask.logger takes the same name as Flask.name.
     (the value passed as ‘Flask(import_name)’.  This reverts 1.0’s
     behavior of always logging to ‘"flask.app"’, in order to support
     multiple apps in the same process.  A warning will be shown if old
     configuration is detected that needs to be moved.  #2866(2)

   - ‘flask.RequestContext.copy()’ includes the current session object
     in the request context copy.  This prevents ‘session’ pointing to
     an out-of-date object.  #2935(3)

   - Using built-in RequestContext, unprintable Unicode characters in
     Host header will result in a HTTP 400 response and not HTTP 500 as
     previously.  #2994(4)

   - send_file() supports PathLike(5) objects as described
     in PEP 0519, to support pathlib(6) in Python 3.  #3059(7)

   - send_file() supports BytesIO(8) partial content.
     #2957(9)

   - ‘open_resource()’ accepts the “rt” file mode.  This still does the
     same thing as “r”.  #3163(10)

   - The ‘MethodView.methods’ attribute set in a base class is used by
     subclasses.  #3138(11)

   - Flask.jinja_options is a ‘dict’ instead of an
     ‘ImmutableDict’ to allow easier configuration.  Changes must still
     be made before creating the environment.  #3190(12)

   - Flask’s ‘JSONMixin’ for the request and response wrappers was moved
     into Werkzeug.  Use Werkzeug’s version with Flask-specific support.
     This bumps the Werkzeug dependency to >= 0.15.  #3125(13)

   - The ‘flask’ command entry point is simplified to take advantage of
     Werkzeug 0.15’s better reloader support.  This bumps the Werkzeug
     dependency to >= 0.15.  #3022(14)

   - Support ‘static_url_path’ that ends with a forward slash.
     #3134(15)

   - Support empty ‘static_folder’ without requiring setting an empty
     ‘static_url_path’ as well.  #3124(16)

   - ‘jsonify()’ supports ‘dataclasses.dataclass’ objects.  #3195(17)

   - Allow customizing the Flask.url_map_class used for
     routing.  #3069(18)

   - The development server port can be set to 0, which tells the OS to
     pick an available port.  #2926(19)

   - The return value from cli.load_dotenv() is more
     consistent with the documentation.  It will return ‘False’ if
     python-dotenv is not installed, or if the given path isn’t a file.
     #2937(20)

   - Signaling support has a stub for the ‘connect_via’ method when the
     Blinker library is not installed.  #3208(21)

   - Add an ‘--extra-files’ option to the ‘flask run’ CLI command to
     specify extra files that will trigger the reloader on change.
     #2897(22)

   - Allow returning a dictionary from a view function.  Similar to how
     returning a string will produce a ‘text/html’ response, returning a
     dict will call ‘jsonify’ to produce a ‘application/json’ response.
     #3111(23)

   - Blueprints have a ‘cli’ Click group like ‘app.cli’.  CLI commands
     registered with a blueprint will be available as a group under the
     ‘flask’ command.  #1357(24).

   - When using the test client as a context manager (‘with client:’),
     all preserved request contexts are popped when the block exits,
     ensuring nested contexts are cleaned up correctly.  #3157(25)

   - Show a better error message when the view return type is not
     supported.  #3214(26)

   - ‘flask.testing.make_test_environ_builder()’ has been deprecated in
     favour of a new class ‘flask.testing.EnvironBuilder’.  #3232(27)

   - The ‘flask run’ command no longer fails if Python is not built with
     SSL support.  Using the ‘--cert’ option will show an appropriate
     error message.  #3211(28)

   - URL matching now occurs after the request context is pushed, rather
     than when it’s created.  This allows custom URL converters to
     access the app and request contexts, such as to query a database
     for an id.  #3088(29)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/3266

   (2) https://github.com/pallets/flask/issues/2866

   (3) https://github.com/pallets/flask/issues/2935

   (4) https://github.com/pallets/flask/pull/2994

   (5) https://docs.python.org/3/library/os.html#os.PathLike

   (6) https://docs.python.org/3/library/pathlib.html#module-pathlib

   (7) https://github.com/pallets/flask/pull/3059

   (8) https://docs.python.org/3/library/io.html#io.BytesIO

   (9) https://github.com/pallets/flask/issues/2957

   (10) https://github.com/pallets/flask/issues/3163

   (11) https://github.com/pallets/flask/issues/3138

   (12) https://github.com/pallets/flask/pull/3190

   (13) https://github.com/pallets/flask/issues/3125

   (14) https://github.com/pallets/flask/issues/3022

   (15) https://github.com/pallets/flask/issues/3134

   (16) https://github.com/pallets/flask/pull/3124

   (17) https://github.com/pallets/flask/pull/3195

   (18) https://github.com/pallets/flask/pull/3069

   (19) https://github.com/pallets/flask/issues/2926

   (20) https://github.com/pallets/flask/issues/2937

   (21) https://github.com/pallets/flask/pull/3208

   (22) https://github.com/pallets/flask/issues/2897

   (23) https://github.com/pallets/flask/pull/3111

   (24) https://github.com/pallets/flask/issues/1357

   (25) https://github.com/pallets/flask/pull/3157

   (26) https://github.com/pallets/flask/issues/3214

   (27) https://github.com/pallets/flask/pull/3232

   (28) https://github.com/pallets/flask/issues/3211

   (29) https://github.com/pallets/flask/issues/3088

3.7.13 Version 1.0.4
--------------------

Released 2019-07-04

   - The key information for ‘BadRequestKeyError’ is no longer cleared
     outside debug mode, so error handlers can still access it.  This
     requires upgrading to Werkzeug 0.15.5.  #3249(1)

   - ‘send_file’ url quotes the “:” and “/” characters for more
     compatible UTF-8 filename support in some browsers.  #3074(2)

   - Fixes for PEP451 import loaders and pytest 5.x.  #3275(3)

   - Show message about dotenv on stderr instead of stdout.  #3285(4)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/3249

   (2) https://github.com/pallets/flask/issues/3074

   (3) https://github.com/pallets/flask/issues/3275

   (4) https://github.com/pallets/flask/issues/3285

3.7.14 Version 1.0.3
--------------------

Released 2019-05-17

   - send_file() encodes filenames as ASCII instead of
     Latin-1 (ISO-8859-1).  This fixes compatibility with Gunicorn,
     which is stricter about header encodings than PEP 3333.  #2766(1)

   - Allow custom CLIs using ‘FlaskGroup’ to set the debug flag without
     it always being overwritten based on environment variables.
     #2765(2)

   - ‘flask --version’ outputs Werkzeug’s version and simplifies the
     Python version.  #2825(3)

   - send_file() handles an ‘attachment_filename’ that is a
     native Python 2 string (bytes) with UTF-8 coded bytes.  #2933(4)

   - A catch-all error handler registered for ‘HTTPException’ will not
     handle ‘RoutingException’, which is used internally during routing.
     This fixes the unexpected behavior that had been introduced in 1.0.
     #2986(5)

   - Passing the ‘json’ argument to ‘app.test_client’ does not push/pop
     an extra app context.  #2900(6)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/2766

   (2) https://github.com/pallets/flask/pull/2765

   (3) https://github.com/pallets/flask/pull/2825

   (4) https://github.com/pallets/flask/issues/2933

   (5) https://github.com/pallets/flask/pull/2986

   (6) https://github.com/pallets/flask/issues/2900

3.7.15 Version 1.0.2
--------------------

Released 2018-05-02

   - Fix more backwards compatibility issues with merging slashes
     between a blueprint prefix and route.  #2748(1)

   - Fix error with ‘flask routes’ command when there are no routes.
     #2751(2)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/2748

   (2) https://github.com/pallets/flask/issues/2751

3.7.16 Version 1.0.1
--------------------

Released 2018-04-29

   - Fix registering partials (with no ‘__name__’) as view functions.
     #2730(1)

   - Don’t treat lists returned from view functions the same as tuples.
     Only tuples are interpreted as response data.  #2736(2)

   - Extra slashes between a blueprint’s ‘url_prefix’ and a route URL
     are merged.  This fixes some backwards compatibility issues with
     the change in 1.0.  #2731(3), #2742(4)

   - Only trap ‘BadRequestKeyError’ errors in debug mode, not all
     ‘BadRequest’ errors.  This allows ‘abort(400)’ to continue working
     as expected.  #2735(5)

   - The ‘FLASK_SKIP_DOTENV’ environment variable can be set to ‘1’ to
     skip automatically loading dotenv files.  #2722(6)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/2730

   (2) https://github.com/pallets/flask/issues/2736

   (3) https://github.com/pallets/flask/issues/2731

   (4) https://github.com/pallets/flask/issues/2742

   (5) https://github.com/pallets/flask/issues/2735

   (6) https://github.com/pallets/flask/issues/2722

3.7.17 Version 1.0
------------------

Released 2018-04-26

   - Python 2.6 and 3.3 are no longer supported.

   - Bump minimum dependency versions to the latest stable versions:
     Werkzeug >= 0.14, Jinja >= 2.10, itsdangerous >= 0.24, Click >=
     5.1.  #2586(1)

   - Skip app.run when a Flask application is run from the
     command line.  This avoids some behavior that was confusing to
     debug.

   - Change the default for JSONIFY_PRETTYPRINT_REGULAR to
     ‘False’.  jsonify() returns a compact format by default,
     and an indented format in debug mode.  #2193(2)

   - Flask.__init__ accepts the ‘host_matching’ argument and
     sets it on url_map.  #1559(3)

   - Flask.__init__ accepts the ‘static_host’ argument and
     passes it as the ‘host’ argument when defining the static route.
     #1559(4)

   - send_file() supports Unicode in ‘attachment_filename’.
     #2223(5)

   - Pass ‘_scheme’ argument from url_for() to
     handle_url_build_error().  #2017(6)

   - add_url_rule() accepts the ‘provide_automatic_options’
     argument to disable adding the ‘OPTIONS’ method.  #1489(7)

   - MethodView subclasses inherit method handlers from base
     classes.  #1936(8)

   - Errors caused while opening the session at the beginning of the
     request are handled by the app’s error handlers.  #2254(9)

   - Blueprints gained json_encoder and json_decoder
     attributes to override the app’s encoder and decoder.
     #1898(10)

   - Flask.make_response() raises ‘TypeError’ instead of
     ‘ValueError’ for bad response types.  The error messages have been
     improved to describe why the type is invalid.  #2256(11)

   - Add ‘routes’ CLI command to output routes registered on the
     application.  #2259(12)

   - Show warning when session cookie domain is a bare hostname or an IP
     address, as these may not behave properly in some browsers, such as
     Chrome.  #2282(13)

   - Allow IP address as exact session cookie domain.  #2282(14)

   - ‘SESSION_COOKIE_DOMAIN’ is set if it is detected through
     ‘SERVER_NAME’.  #2282(15)

   - Auto-detect zero-argument app factory called ‘create_app’ or
     ‘make_app’ from ‘FLASK_APP’.  #2297(16)

   - Factory functions are not required to take a ‘script_info’
     parameter to work with the ‘flask’ command.  If they take a single
     parameter or a parameter named ‘script_info’, the ScriptInfo
     object will be passed.  #2319(17)

   - ‘FLASK_APP’ can be set to an app factory, with arguments if needed,
     for example ‘FLASK_APP=myproject.app:create_app('dev')’.  #2326(18)

   - ‘FLASK_APP’ can point to local packages that are not installed in
     editable mode, although ‘pip install -e’ is still preferred.
     #2414(19)

   - The View class attribute
     provide_automatic_options is set in as_view(), to
     be detected by add_url_rule().  #2316(20)

   - Error handling will try handlers registered for ‘blueprint, code’,
     ‘app, code’, ‘blueprint, exception’, ‘app, exception’.  #2314(21)

   - ‘Cookie’ is added to the response’s ‘Vary’ header if the session is
     accessed at all during the request (and not deleted).  #2288(22)

   - test_request_context() accepts ‘subdomain’ and
     ‘url_scheme’ arguments for use when building the base URL.
     #1621(23)

   - Set APPLICATION_ROOT to ‘'/'’ by default.  This was
     already the implicit default when it was set to ‘None’.

   - TRAP_BAD_REQUEST_ERRORS is enabled by default in debug
     mode.  ‘BadRequestKeyError’ has a message with the bad key in debug
     mode instead of the generic bad request message.  #2348(24)

   - Allow registering new tags with TaggedJSONSerializer to
     support storing other types in the session cookie.  #2352(25)

   - Only open the session if the request has not been pushed onto the
     context stack yet.  This allows stream_with_context().
     generators to access the same session that the containing view
     uses.  #2354(26)

   - Add ‘json’ keyword argument for the test client request methods.
     This will dump the given object as JSON and set the appropriate
     content type.  #2358(27)

   - Extract JSON handling to a mixin applied to both the Request
     and Response classes.  This adds the
     is_json() and get_json() methods to the response
     to make testing JSON response much easier.  #2358(28)

   - Removed error handler caching because it caused unexpected results
     for some exception inheritance hierarchies.  Register handlers
     explicitly for each exception if you want to avoid traversing the
     MRO. #2362(29)

   - Fix incorrect JSON encoding of aware, non-UTC datetimes.  #2374(30)

   - Template auto reloading will honor debug mode even even if
     jinja_env was already accessed.  #2373(31)

   - The following old deprecated code was removed.  #2385(32)

        - ‘flask.ext’ - import extensions directly by their name instead
          of through the ‘flask.ext’ namespace.  For example, ‘import
          flask.ext.sqlalchemy’ becomes ‘import flask_sqlalchemy’.

        - ‘Flask.init_jinja_globals’ - extend
          Flask.create_jinja_environment() instead.

        - ‘Flask.error_handlers’ - tracked by
          Flask.error_handler_spec, use Flask.errorhandler()
          to register handlers.

        - ‘Flask.request_globals_class’ - use
          Flask.app_ctx_globals_class instead.

        - ‘Flask.static_path’ - use Flask.static_url_path.
          instead.

        - ‘Request.module’ - use Request.blueprint instead.

   - The Request.json property is no longer deprecated.
     #1421(33)

   - Support passing a EnvironBuilder(34) or ‘dict’ to
     test_client.open(35).  #2412(36)

   - The ‘flask’ command and Flask.run() will load
     environment variables from ‘.env’ and ‘.flaskenv’ files if
     python-dotenv is installed.  #2416(37)

   - When passing a full URL to the test client, the scheme in the URL
     is used instead of PREFERRED_URL_SCHEME.  #2430(38)

   - Flask.logger has been simplified.  ‘LOGGER_NAME’ and
     ‘LOGGER_HANDLER_POLICY’ config was removed.  The logger is always
     named ‘flask.app’.  The level is only set on first access, it
     doesn’t check Flask.debug each time.  Only one format
     is used, not different ones depending on Flask.debug.
     No handlers are removed, and a handler is only added if no handlers
     are already configured.  #2436(39)

   - Blueprint view function names may not contain dots.  #2450(40)

   - Fix a ‘ValueError’ caused by invalid ‘Range’ requests in some
     cases.  #2526(41)

   - The development server uses threads by default.  #2529(42)

   - Loading config files with ‘silent=True’ will ignore ENOTDIR(43)
     errors.  #2581(44)

   - Pass ‘--cert’ and ‘--key’ options to ‘flask run’ to run the
     development server over HTTPS. #2606(45)

   - Added SESSION_COOKIE_SAMESITE to control the ‘SameSite’
     attribute on the session cookie.  #2607(46)

   - Added test_cli_runner() to create a Click runner that
     can invoke Flask CLI commands for testing.  #2636(47)

   - Subdomain matching is disabled by default and setting
     SERVER_NAME does not implicitly enable it.  It can be enabled
     by passing ‘subdomain_matching=True’ to the ‘Flask’ constructor.
     #2635(48)

   - A single trailing slash is stripped from the blueprint ‘url_prefix’
     when it is registered with the app.  #2629(49)

   - Request.get_json() doesn’t cache the result if parsing
     fails when ‘silent’ is true.  #2651(50)

   - Request.get_json() no longer accepts arbitrary
     encodings.  Incoming JSON should be encoded using UTF-8 per RFC
     8259(51), but Flask will autodetect UTF-8, -16, or -32.  #2691(52)

   - Added MAX_COOKIE_SIZE and
     Response.max_cookie_size to control when Werkzeug warns about
     large cookies that browsers may ignore.  #2693(53)

   - Updated documentation theme to make docs look better in small
     windows.  #2709(54)

   - Rewrote the tutorial docs and example project to take a more
     structured approach to help new users avoid common pitfalls.
     #2676(55)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/2586

   (2) https://github.com/pallets/flask/pull/2193

   (3) https://github.com/pallets/flask/issues/1559

   (4) https://github.com/pallets/flask/issues/1559

   (5) https://github.com/pallets/flask/pull/2223

   (6) https://github.com/pallets/flask/pull/2017

   (7) https://github.com/pallets/flask/pull/1489

   (8) https://github.com/pallets/flask/pull/1936

   (9) https://github.com/pallets/flask/pull/2254

   (10) https://github.com/pallets/flask/pull/1898

   (11) https://github.com/pallets/flask/pull/2256

   (12) https://github.com/pallets/flask/pull/2259

   (13) https://github.com/pallets/flask/pull/2282

   (14) https://github.com/pallets/flask/pull/2282

   (15) https://github.com/pallets/flask/pull/2282

   (16) https://github.com/pallets/flask/pull/2297

   (17) https://github.com/pallets/flask/pull/2319

   (18) https://github.com/pallets/flask/pull/2326

   (19) https://github.com/pallets/flask/pull/2414

   (20) https://github.com/pallets/flask/pull/2316

   (21) https://github.com/pallets/flask/pull/2314

   (22) https://github.com/pallets/flask/pull/2288

   (23) https://github.com/pallets/flask/pull/1621

   (24) https://github.com/pallets/flask/pull/2348

   (25) https://github.com/pallets/flask/pull/2352

   (26) https://github.com/pallets/flask/pull/2354

   (27) https://github.com/pallets/flask/pull/2358

   (28) https://github.com/pallets/flask/pull/2358

   (29) https://github.com/pallets/flask/pull/2362

   (30) https://github.com/pallets/flask/pull/2374

   (31) https://github.com/pallets/flask/pull/2373

   (32) https://github.com/pallets/flask/issues/2385

   (33) https://github.com/pallets/flask/issues/1421

   (34)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.EnvironBuilder

   (35)
https://werkzeug.palletsprojects.com/en/2.1.x/test/#werkzeug.test.Client.open

   (36) https://github.com/pallets/flask/pull/2412

   (37) https://github.com/pallets/flask/pull/2416

   (38) https://github.com/pallets/flask/pull/2430

   (39) https://github.com/pallets/flask/pull/2436

   (40) https://github.com/pallets/flask/pull/2450

   (41) https://github.com/pallets/flask/issues/2526

   (42) https://github.com/pallets/flask/pull/2529

   (43) https://docs.python.org/3/library/errno.html#errno.ENOTDIR

   (44) https://github.com/pallets/flask/pull/2581

   (45) https://github.com/pallets/flask/pull/2606

   (46) https://github.com/pallets/flask/pull/2607

   (47) https://github.com/pallets/flask/pull/2636

   (48) https://github.com/pallets/flask/pull/2635

   (49) https://github.com/pallets/flask/pull/2629

   (50) https://github.com/pallets/flask/issues/2651

   (51) https://datatracker.ietf.org/doc/html/rfc8259.html

   (52) https://github.com/pallets/flask/pull/2691

   (53) https://github.com/pallets/flask/pull/2693

   (54) https://github.com/pallets/flask/pull/2709

   (55) https://github.com/pallets/flask/pull/2676

3.7.18 Version 0.12.5
---------------------

Released 2020-02-10

   - Pin Werkzeug to < 1.0.0.  #3497(1)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/3497

3.7.19 Version 0.12.4
---------------------

Released 2018-04-29

   - Repackage 0.12.3 to fix package layout issue.  #2728(1)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/2728

3.7.20 Version 0.12.3
---------------------

Released 2018-04-26

   - Request.get_json() no longer accepts arbitrary
     encodings.  Incoming JSON should be encoded using UTF-8 per RFC
     8259(1), but Flask will autodetect UTF-8, -16, or -32.  #2692(2)

   - Fix a Python warning about imports when using ‘python -m flask’.
     #2666(3)

   - Fix a ‘ValueError’ caused by invalid ‘Range’ requests in some
     cases.

   ---------- Footnotes ----------

   (1) https://datatracker.ietf.org/doc/html/rfc8259.html

   (2) https://github.com/pallets/flask/issues/2692

   (3) https://github.com/pallets/flask/issues/2666

3.7.21 Version 0.12.2
---------------------

Released 2017-05-16

   - Fix a bug in ‘safe_join’ on Windows.

3.7.22 Version 0.12.1
---------------------

Released 2017-03-31

   - Prevent ‘flask run’ from showing a ‘NoAppException’ when an
     ‘ImportError’ occurs within the imported application module.

   - Fix encoding behavior of ‘app.config.from_pyfile’ for Python 3.
     #2118(1)

   - Use the ‘SERVER_NAME’ config if it is present as default values for
     ‘app.run’.  #2109(2), #2152(3)

   - Call ‘ctx.auto_pop’ with the exception object instead of ‘None’, in
     the event that a ‘BaseException’ such as ‘KeyboardInterrupt’ is
     raised in a request handler.

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/2118

   (2) https://github.com/pallets/flask/issues/2109

   (3) https://github.com/pallets/flask/pull/2152

3.7.23 Version 0.12
-------------------

Released 2016-12-21, codename Punsch

   - The cli command now responds to ‘--version’.

   - Mimetype guessing and ETag generation for file-like objects in
     ‘send_file’ has been removed.  #104(1), :pr‘1849‘

   - Mimetype guessing in ‘send_file’ now fails loudly and doesn’t fall
     back to ‘application/octet-stream’.  #1988(2)

   - Make ‘flask.safe_join’ able to join multiple paths like
     ‘os.path.join’ #1730(3)

   - Revert a behavior change that made the dev server crash instead of
     returning an Internal Server Error.  #2006(4)

   - Correctly invoke response handlers for both regular request
     dispatching as well as error handlers.

   - Disable logger propagation by default for the app logger.

   - Add support for range requests in ‘send_file’.

   - ‘app.test_client’ includes preset default environment, which can
     now be directly set, instead of per ‘client.get’.

   - Fix crash when running under PyPy3.  #1814(5)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/issues/104

   (2) https://github.com/pallets/flask/pull/1988

   (3) https://github.com/pallets/flask/pull/1730

   (4) https://github.com/pallets/flask/pull/2006

   (5) https://github.com/pallets/flask/pull/1814

3.7.24 Version 0.11.1
---------------------

Released 2016-06-07

   - Fixed a bug that prevented ‘FLASK_APP=foobar/__init__.py’ from
     working.  #1872(1)

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/1872

3.7.25 Version 0.11
-------------------

Released 2016-05-29, codename Absinthe

   - Added support to serializing top-level arrays to ‘flask.jsonify()’.
     This introduces a security risk in ancient browsers.

   - Added before_render_template signal.

   - Added ‘**kwargs’ to ‘flask.Test.test_client()’ to support passing
     additional keyword arguments to the constructor of
     flask.Flask.test_client_class.

   - Added ‘SESSION_REFRESH_EACH_REQUEST’ config key that controls the
     set-cookie behavior.  If set to ‘True’ a permanent session will be
     refreshed each request and get their lifetime extended, if set to
     ‘False’ it will only be modified if the session actually modifies.
     Non permanent sessions are not affected by this and will always
     expire if the browser window closes.

   - Made Flask support custom JSON mimetypes for incoming data.

   - Added support for returning tuples in the form ‘(response,
     headers)’ from a view function.

   - Added ‘flask.Config.from_json()’.

   - Added flask.Flask.config_class.

   - Added flask.Config.get_namespace().

   - Templates are no longer automatically reloaded outside of debug
     mode.  This can be configured with the new ‘TEMPLATES_AUTO_RELOAD’
     config key.

   - Added a workaround for a limitation in Python 3.3’s namespace
     loader.

   - Added support for explicit root paths when using Python 3.3’s
     namespace packages.

   - Added ‘flask’ and the ‘flask.cli’ module to start the local debug
     server through the click CLI system.  This is recommended over the
     old ‘flask.run()’ method as it works faster and more reliable due
     to a different design and also replaces ‘Flask-Script’.

   - Error handlers that match specific classes are now checked first,
     thereby allowing catching exceptions that are subclasses of HTTP
     exceptions (in ‘werkzeug.exceptions’).  This makes it possible for
     an extension author to create exceptions that will by default
     result in the HTTP error of their choosing, but may be caught with
     a custom error handler if desired.

   - Added flask.Config.from_mapping().

   - Flask will now log by default even if debug is disabled.  The log
     format is now hardcoded but the default log handling can be
     disabled through the ‘LOGGER_HANDLER_POLICY’ configuration key.

   - Removed deprecated module functionality.

   - Added the ‘EXPLAIN_TEMPLATE_LOADING’ config flag which when enabled
     will instruct Flask to explain how it locates templates.  This
     should help users debug when the wrong templates are loaded.

   - Enforce blueprint handling in the order they were registered for
     template loading.

   - Ported test suite to py.test.

   - Deprecated ‘request.json’ in favour of ‘request.get_json()’.

   - Add “pretty” and “compressed” separators definitions in jsonify()
     method.  Reduces JSON response size when
     ‘JSONIFY_PRETTYPRINT_REGULAR=False’ by removing unnecessary white
     space included by default after separators.

   - JSON responses are now terminated with a newline character, because
     it is a convention that UNIX text files end with a newline and some
     clients don’t deal well when this newline is missing.  This came up
     originally as a part of
     ‘https://github.com/postmanlabs/httpbin/issues/168’.  #1262(1)

   - The automatically provided ‘OPTIONS’ method is now correctly
     disabled if the user registered an overriding rule with the
     lowercase-version ‘options’.  #1288(2)

   - ‘flask.json.jsonify’ now supports the ‘datetime.date’ type.
     #1326(3)

   - Don’t leak exception info of already caught exceptions to context
     teardown handlers.  #1393(4)

   - Allow custom Jinja environment subclasses.  #1422(5)

   - Updated extension dev guidelines.

   - ‘flask.g’ now has ‘pop()’ and ‘setdefault’ methods.

   - Turn on autoescape for ‘flask.templating.render_template_string’ by
     default.  #1515(6)

   - ‘flask.ext’ is now deprecated.  #1484(7)

   - ‘send_from_directory’ now raises BadRequest if the filename is
     invalid on the server OS. #1763(8)

   - Added the ‘JSONIFY_MIMETYPE’ configuration variable.  #1728(9)

   - Exceptions during teardown handling will no longer leave bad
     application contexts lingering around.

   - Fixed broken ‘test_appcontext_signals()’ test case.

   - Raise an AttributeError(10) in ‘flask.helpers.find_package()’ with
     a useful message explaining why it is raised when a PEP 302 import
     hook is used without an ‘is_package()’ method.

   - Fixed an issue causing exceptions raised before entering a request
     or app context to be passed to teardown handlers.

   - Fixed an issue with query parameters getting removed from requests
     in the test client when absolute URLs were requested.

   - Made ‘@before_first_request’ into a decorator as intended.

   - Fixed an etags bug when sending a file streams with a name.

   - Fixed ‘send_from_directory’ not expanding to the application root
     path correctly.

   - Changed logic of before first request handlers to flip the flag
     after invoking.  This will allow some uses that are potentially
     dangerous but should probably be permitted.

   - Fixed Python 3 bug when a handler from
     ‘app.url_build_error_handlers’ reraises the ‘BuildError’.

   ---------- Footnotes ----------

   (1) https://github.com/pallets/flask/pull/1262

   (2) https://github.com/pallets/flask/issues/1288

   (3) https://github.com/pallets/flask/pull/1326

   (4) https://github.com/pallets/flask/pull/1393

   (5) https://github.com/pallets/flask/pull/1422

   (6) https://github.com/pallets/flask/pull/1515

   (7) https://github.com/pallets/flask/pull/1484

   (8) https://github.com/pallets/flask/pull/1763

   (9) https://github.com/pallets/flask/pull/1728

   (10) https://docs.python.org/3/library/exceptions.html#AttributeError

3.7.26 Version 0.10.1
---------------------

Released 2013-06-14

   - Fixed an issue where ‘|tojson’ was not quoting single quotes which
     made the filter not work properly in HTML attributes.  Now it’s
     possible to use that filter in single quoted attributes.  This
     should make using that filter with angular.js easier.

   - Added support for byte strings back to the session system.  This
     broke compatibility with the common case of people putting binary
     data for token verification into the session.

   - Fixed an issue where registering the same method twice for the same
     endpoint would trigger an exception incorrectly.

3.7.27 Version 0.10
-------------------

Released 2013-06-13, codename Limoncello

   - Changed default cookie serialization format from pickle to JSON to
     limit the impact an attacker can do if the secret key leaks.

   - Added ‘template_test’ methods in addition to the already existing
     ‘template_filter’ method family.

   - Added ‘template_global’ methods in addition to the already existing
     ‘template_filter’ method family.

   - Set the content-length header for x-sendfile.

   - ‘tojson’ filter now does not escape script blocks in HTML5 parsers.

   - ‘tojson’ used in templates is now safe by default due.  This was
     allowed due to the different escaping behavior.

   - Flask will now raise an error if you attempt to register a new
     function on an already used endpoint.

   - Added wrapper module around simplejson and added default
     serialization of datetime objects.  This allows much easier
     customization of how JSON is handled by Flask or any Flask
     extension.

   - Removed deprecated internal ‘flask.session’ module alias.  Use
     ‘flask.sessions’ instead to get the session module.  This is not to
     be confused with ‘flask.session’ the session proxy.

   - Templates can now be rendered without request context.  The
     behavior is slightly different as the ‘request’, ‘session’ and ‘g’
     objects will not be available and blueprint’s context processors
     are not called.

   - The config object is now available to the template as a real global
     and not through a context processor which makes it available even
     in imported templates by default.

   - Added an option to generate non-ascii encoded JSON which should
     result in less bytes being transmitted over the network.  It’s
     disabled by default to not cause confusion with existing libraries
     that might expect ‘flask.json.dumps’ to return bytes by default.

   - ‘flask.g’ is now stored on the app context instead of the request
     context.

   - ‘flask.g’ now gained a ‘get()’ method for not erroring out on non
     existing items.

   - ‘flask.g’ now can be used with the ‘in’ operator to see what’s
     defined and it now is iterable and will yield all attributes
     stored.

   - ‘flask.Flask.request_globals_class’ got renamed to
     ‘flask.Flask.app_ctx_globals_class’ which is a better name to what
     it does since 0.10.

   - ‘request’, ‘session’ and ‘g’ are now also added as proxies to the
     template context which makes them available in imported templates.
     One has to be very careful with those though because usage outside
     of macros might cause caching.

   - Flask will no longer invoke the wrong error handlers if a proxy
     exception is passed through.

   - Added a workaround for chrome’s cookies in localhost not working as
     intended with domain names.

   - Changed logic for picking defaults for cookie values from sessions
     to work better with Google Chrome.

   - Added ‘message_flashed’ signal that simplifies flashing testing.

   - Added support for copying of request contexts for better working
     with greenlets.

   - Removed custom JSON HTTP exception subclasses.  If you were relying
     on them you can reintroduce them again yourself trivially.  Using
     them however is strongly discouraged as the interface was flawed.

   - Python requirements changed: requiring Python 2.6 or 2.7 now to
     prepare for Python 3.3 port.

   - Changed how the teardown system is informed about exceptions.  This
     is now more reliable in case something handles an exception halfway
     through the error handling process.

   - Request context preservation in debug mode now keeps the exception
     information around which means that teardown handlers are able to
     distinguish error from success cases.

   - Added the ‘JSONIFY_PRETTYPRINT_REGULAR’ configuration variable.

   - Flask now orders JSON keys by default to not trash HTTP caches due
     to different hash seeds between different workers.

   - Added ‘appcontext_pushed’ and ‘appcontext_popped’ signals.

   - The builtin run method now takes the ‘SERVER_NAME’ into account
     when picking the default port to run on.

   - Added ‘flask.request.get_json()’ as a replacement for the old
     ‘flask.request.json’ property.

3.7.28 Version 0.9
------------------

Released 2012-07-01, codename Campari

   - The flask.Request.on_json_loading_failed() now returns
     a JSON formatted response by default.

   - The flask.url_for() function now can generate anchors to
     the generated links.

   - The flask.url_for() function now can also explicitly
     generate URL rules specific to a given HTTP method.

   - Logger now only returns the debug log setting if it was not set
     explicitly.

   - Unregister a circular dependency between the WSGI environment and
     the request object when shutting down the request.  This means that
     environ ‘werkzeug.request’ will be ‘None’ after the response was
     returned to the WSGI server but has the advantage that the garbage
     collector is not needed on CPython to tear down the request unless
     the user created circular dependencies themselves.

   - Session is now stored after callbacks so that if the session
     payload is stored in the session you can still modify it in an
     after request callback.

   - The flask.Flask class will avoid importing the provided
     import name if it can (the required first parameter), to benefit
     tools which build Flask instances programmatically.  The Flask
     class will fall back to using import on systems with custom module
     hooks, e.g.  Google App Engine, or when the import name is inside a
     zip archive (usually a .egg) prior to Python 2.7.

   - Blueprints now have a decorator to add custom template filters
     application wide, see flask.Blueprint.app_template_filter().

   - The Flask and Blueprint classes now have a non-decorator method for
     adding custom template filters application wide, see
     flask.Flask.add_template_filter() and
     flask.Blueprint.add_app_template_filter().

   - The flask.get_flashed_messages() function now allows
     rendering flashed message categories in separate blocks, through a
     ‘category_filter’ argument.

   - The flask.Flask.run() method now accepts ‘None’ for
     ‘host’ and ‘port’ arguments, using default values when ‘None’.
     This allows for calling run using configuration values, e.g.
     ‘app.run(app.config.get('MYHOST'), app.config.get('MYPORT'))’, with
     proper behavior whether or not a config file is provided.

   - The flask.render_template() method now accepts a either
     an iterable of template names or a single template name.
     Previously, it only accepted a single template name.  On an
     iterable, the first template found is rendered.

   - Added flask.Flask.app_context() which works very
     similar to the request context but only provides access to the
     current application.  This also adds support for URL generation
     without an active request context.

   - View functions can now return a tuple with the first instance being
     an instance of flask.Response.  This allows for returning
     ‘jsonify(error="error msg"), 400’ from a view function.

   - Flask and Blueprint now provide a
     get_send_file_max_age() hook for subclasses to override
     behavior of serving static files from Flask when using
     flask.Flask.send_static_file(). (used for the default static
     file handler) and ‘send_file()’.  This hook is provided a filename,
     which for example allows changing cache controls by file extension.
     The default max-age for ‘send_file’ and static files can be
     configured through a new ‘SEND_FILE_MAX_AGE_DEFAULT’ configuration
     variable, which is used in the default ‘get_send_file_max_age’
     implementation.

   - Fixed an assumption in sessions implementation which could break
     message flashing on sessions implementations which use external
     storage.

   - Changed the behavior of tuple return values from functions.  They
     are no longer arguments to the response object, they now have a
     defined meaning.

   - Added ‘flask.Flask.request_globals_class’ to allow a specific class
     to be used on creation of the g instance of each
     request.

   - Added ‘required_methods’ attribute to view functions to force-add
     methods on registration.

   - Added flask.after_this_request().

   - Added flask.stream_with_context() and the ability to
     push contexts multiple times without producing unexpected behavior.

3.7.29 Version 0.8.1
--------------------

Released 2012-07-01

   - Fixed an issue with the undocumented ‘flask.session’ module to not
     work properly on Python 2.5.  It should not be used but did cause
     some problems for package managers.

3.7.30 Version 0.8
------------------

Released 2011-09-29, codename Rakija

   - Refactored session support into a session interface so that the
     implementation of the sessions can be changed without having to
     override the Flask class.

   - Empty session cookies are now deleted properly automatically.

   - View functions can now opt out of getting the automatic OPTIONS
     implementation.

   - HTTP exceptions and Bad Request errors can now be trapped so that
     they show up normally in the traceback.

   - Flask in debug mode is now detecting some common problems and tries
     to warn you about them.

   - Flask in debug mode will now complain with an assertion error if a
     view was attached after the first request was handled.  This gives
     earlier feedback when users forget to import view code ahead of
     time.

   - Added the ability to register callbacks that are only triggered
     once at the beginning of the first request.  (see
     Flask.before_first_request())

   - Malformed JSON data will now trigger a bad request HTTP exception
     instead of a value error which usually would result in a 500
     internal server error if not handled.  This is a backwards
     incompatible change.

   - Applications now not only have a root path where the resources and
     modules are located but also an instance path which is the
     designated place to drop files that are modified at runtime
     (uploads etc.).  Also this is conceptually only instance depending
     and outside version control so it’s the perfect place to put
     configuration files etc.

   - Added the ‘APPLICATION_ROOT’ configuration variable.

   - Implemented ‘session_transaction()’ to easily modify sessions from
     the test environment.

   - Refactored test client internally.  The ‘APPLICATION_ROOT’
     configuration variable as well as ‘SERVER_NAME’ are now properly
     used by the test client as defaults.

   - Added flask.views.View.decorators to support simpler
     decorating of pluggable (class-based) views.

   - Fixed an issue where the test client if used with the “with”
     statement did not trigger the execution of the teardown handlers.

   - Added finer control over the session cookie parameters.

   - HEAD requests to a method view now automatically dispatch to the
     ‘get’ method if no handler was implemented.

   - Implemented the virtual ‘flask.ext’ package to import extensions
     from.

   - The context preservation on exceptions is now an integral component
     of Flask itself and no longer of the test client.  This cleaned up
     some internal logic and lowers the odds of runaway request contexts
     in unittests.

   - Fixed the Jinja2 environment’s ‘list_templates’ method not
     returning the correct names when blueprints or modules were
     involved.

3.7.31 Version 0.7.2
--------------------

Released 2011-07-06

   - Fixed an issue with URL processors not properly working on
     blueprints.

3.7.32 Version 0.7.1
--------------------

Released 2011-06-29

   - Added missing future import that broke 2.5 compatibility.

   - Fixed an infinite redirect issue with blueprints.

3.7.33 Version 0.7
------------------

Released 2011-06-28, codename Grappa

   - Added make_default_options_response() which can be used
     by subclasses to alter the default behavior for ‘OPTIONS’
     responses.

   - Unbound locals now raise a proper RuntimeError(1) instead of an
     AttributeError(2).

   - Mimetype guessing and etag support based on file objects is now
     deprecated for flask.send_file() because it was
     unreliable.  Pass filenames instead or attach your own etags and
     provide a proper mimetype by hand.

   - Static file handling for modules now requires the name of the
     static folder to be supplied explicitly.  The previous
     autodetection was not reliable and caused issues on Google’s App
     Engine.  Until 1.0 the old behavior will continue to work but issue
     dependency warnings.

   - Fixed a problem for Flask to run on jython.

   - Added a ‘PROPAGATE_EXCEPTIONS’ configuration variable that can be
     used to flip the setting of exception propagation which previously
     was linked to ‘DEBUG’ alone and is now linked to either ‘DEBUG’ or
     ‘TESTING’.

   - Flask no longer internally depends on rules being added through the
     ‘add_url_rule’ function and can now also accept regular werkzeug
     rules added to the url map.

   - Added an ‘endpoint’ method to the flask application object which
     allows one to register a callback to an arbitrary endpoint with a
     decorator.

   - Use Last-Modified for static file sending instead of Date which was
     incorrectly introduced in 0.6.

   - Added ‘create_jinja_loader’ to override the loader creation
     process.

   - Implemented a silent flag for ‘config.from_pyfile’.

   - Added ‘teardown_request’ decorator, for functions that should run
     at the end of a request regardless of whether an exception
     occurred.  Also the behavior for ‘after_request’ was changed.  It’s
     now no longer executed when an exception is raised.

   - Implemented flask.has_request_context().

   - Deprecated ‘init_jinja_globals’.  Override the
     create_jinja_environment() method instead to achieve the same
     functionality.

   - Added ‘flask.safe_join()’

   - The automatic JSON request data unpacking now looks at the charset
     mimetype parameter.

   - Don’t modify the session on flask.get_flashed_messages().
     if there are no messages in the session.

   - ‘before_request’ handlers are now able to abort requests with
     errors.

   - It is not possible to define user exception handlers.  That way you
     can provide custom error messages from a central hub for certain
     errors that might occur during request processing (for instance
     database connection errors, timeouts from remote resources etc.).

   - Blueprints can provide blueprint specific error handlers.

   - Implemented generic class-based views.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/exceptions.html#RuntimeError

   (2) https://docs.python.org/3/library/exceptions.html#AttributeError

3.7.34 Version 0.6.1
--------------------

Released 2010-12-31

   - Fixed an issue where the default ‘OPTIONS’ response was not
     exposing all valid methods in the ‘Allow’ header.

   - Jinja2 template loading syntax now allows “./” in front of a
     template load path.  Previously this caused issues with module
     setups.

   - Fixed an issue where the subdomain setting for modules was ignored
     for the static folder.

   - Fixed a security problem that allowed clients to download arbitrary
     files if the host server was a windows based operating system and
     the client uses backslashes to escape the directory the files where
     exposed from.

3.7.35 Version 0.6
------------------

Released 2010-07-27, codename Whisky

   - After request functions are now called in reverse order of
     registration.

   - OPTIONS is now automatically implemented by Flask unless the
     application explicitly adds ‘OPTIONS’ as method to the URL rule.
     In this case no automatic OPTIONS handling kicks in.

   - Static rules are now even in place if there is no static folder for
     the module.  This was implemented to aid GAE which will remove the
     static folder if it’s part of a mapping in the .yml file.

   - The config is now available in the templates as
     ‘config’.

   - Context processors will no longer override values passed directly
     to the render function.

   - Added the ability to limit the incoming request data with the new
     ‘MAX_CONTENT_LENGTH’ configuration value.

   - The endpoint for the ‘flask.Module.add_url_rule()’ method is now
     optional to be consistent with the function of the same name on the
     application object.

   - Added a flask.make_response() function that simplifies
     creating response object instances in views.

   - Added signalling support based on blinker.  This feature is
     currently optional and supposed to be used by extensions and
     applications.  If you want to use it, make sure to have blinker(1)
     installed.

   - Refactored the way URL adapters are created.  This process is now
     fully customizable with the create_url_adapter().
     method.

   - Modules can now register for a subdomain instead of just an URL
     prefix.  This makes it possible to bind a whole module to a
     configurable subdomain.

   ---------- Footnotes ----------

   (1) https://pypi.org/project/blinker/

3.7.36 Version 0.5.2
--------------------

Released 2010-07-15

   - Fixed another issue with loading templates from directories when
     modules were used.

3.7.37 Version 0.5.1
--------------------

Released 2010-07-06

   - Fixes an issue with template loading from directories when modules
     where used.

3.7.38 Version 0.5
------------------

Released 2010-07-06, codename Calvados

   - Fixed a bug with subdomains that was caused by the inability to
     specify the server name.  The server name can now be set with the
     ‘SERVER_NAME’ config key.  This key is now also used to set the
     session cookie cross-subdomain wide.

   - Autoescaping is no longer active for all templates.  Instead it is
     only active for ‘.html’, ‘.htm’, ‘.xml’ and ‘.xhtml’.  Inside
     templates this behavior can be changed with the ‘autoescape’ tag.

   - Refactored Flask internally.  It now consists of more than a single
     file.

   - flask.send_file() now emits etags and has the ability
     to do conditional responses builtin.

   - (temporarily) dropped support for zipped applications.  This was a
     rarely used feature and led to some confusing behavior.

   - Added support for per-package template and static-file directories.

   - Removed support for ‘create_jinja_loader’ which is no longer used
     in 0.5 due to the improved module support.

   - Added a helper function to expose files from any directory.

3.7.39 Version 0.4
------------------

Released 2010-06-18, codename Rakia

   - Added the ability to register application wide error handlers from
     modules.

   - after_request() handlers are now also invoked if the
     request dies with an exception and an error handling page kicks in.

   - Test client has not the ability to preserve the request context for
     a little longer.  This can also be used to trigger custom requests
     that do not pop the request stack for testing.

   - Because the Python standard library caches loggers, the name of the
     logger is configurable now to better support unittests.

   - Added ‘TESTING’ switch that can activate unittesting helpers.

   - The logger switches to ‘DEBUG’ mode now if debug is enabled.

3.7.40 Version 0.3.1
--------------------

Released 2010-05-28

   - Fixed a error reporting bug with flask.Config.from_envvar().

   - Removed some unused code from flask

   - Release does no longer include development leftover files (.git
     folder for themes, built documentation in zip and pdf file and some
     .pyc files)

3.7.41 Version 0.3
------------------

Released 2010-05-28, codename Schnaps

   - Added support for categories for flashed messages.

   - The application now configures a logging.Handler(1) and will log
     request handling exceptions to that logger when not in debug mode.
     This makes it possible to receive mails on server errors for
     example.

   - Added support for context binding that does not require the use of
     the with statement for playing in the console.

   - The request context is now available within the with statement
     making it possible to further push the request context or pop it.

   - Added support for configurations.

   ---------- Footnotes ----------

   (1) https://docs.python.org/3/library/logging.html#logging.Handler

3.7.42 Version 0.2
------------------

Released 2010-05-12, codename J?germeister

   - Various bugfixes

   - Integrated JSON support

   - Added get_template_attribute() helper function.

   - add_url_rule() can now also register a view function.

   - Refactored internal request dispatching.

   - Server listens on 127.0.0.1 by default now to fix issues with
     chrome.

   - Added external URL support.

   - Added support for send_file().

   - Module support and internal request handling refactoring to better
     support pluggable applications.

   - Sessions can be set to be permanent now on a per-session basis.

   - Better error reporting on missing secret keys.

   - Added support for Google Appengine.

3.7.43 Version 0.1
------------------

Released 2010-04-16

   - First public preview release.