`__ 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 :exc:`~werkzeug.exceptions.HTTPException`.
+
+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.
+
+
+Registering
+```````````
+
+Register handlers by decorating a function with
+:meth:`~flask.Flask.errorhandler`. Or use
+:meth:`~flask.Flask.register_error_handler` to register the function later.
+Remember to set the error code when returning the response.
+
+.. code-block:: python
+
+ @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)
+
+:exc:`werkzeug.exceptions.HTTPException` subclasses like
+:exc:`~werkzeug.exceptions.BadRequest` 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
+:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and
+register and raise that exception class.
+
+.. code-block:: python
+
+ 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
+:exc:`~werkzeug.exceptions.HTTPException` subclasses or HTTP status
+codes. Handlers can be registered for a specific class, or for all subclasses
+of a parent class.
+
+
+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) an "500 Internal Server Error"
+registered), a "500 Internal Server Error"
(:exc:`~werkzeug.exceptions.InternalServerError`) will be returned by default.
-Similarly, if a request is sent to an unregistered route a "404 Not Found"
-(:exc:`~werkzeug.exceptions.NotFound`) error will occur. If a route receives an
-unallowed request method a "405 Method Not Allowed"
+Similarly, "404 Not Found"
+(:exc:`~werkzeug.exceptions.NotFound`) 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"
(:exc:`~werkzeug.exceptions.MethodNotAllowed`) will be raised. These are all
subclasses of :class:`~werkzeug.exceptions.HTTPException` and are provided by
default in Flask.
Flask gives you to the ability to raise any HTTP exception registered by
-werkzeug. However, as the default HTTP exceptions return simple exception
-pages, Flask also offers the opportunity to customise these HTTP exceptions via
-custom error handlers as well as to add exception handlers for builtin and
-custom exceptions.
+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 an exception is caught by Flask while handling a request, it is first
-looked up by code. If no handler is registered for the code, it is looked up
-by its class hierarchy; the most specific handler is chosen. If no handler is
-registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a
+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, :class:`~werkzeug.exceptions.HTTPException` subclasses show a
generic message about their code, while other exceptions are converted to a
generic "500 Internal Server Error".
@@ -107,21 +174,118 @@ because the 404 occurs at the routing level before the blueprint can be
determined.
+Generic Exception Handlers
+``````````````````````````
-.. _handling-errors:
+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.
-Handling Errors
-```````````````
+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.
+
+.. code-block:: python
+
+ 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.
+
+.. code-block:: python
+
+ 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.
+
+
+Unhandled Exceptions
+````````````````````
+
+When there is no error handler registered for an exception, a 500
+Internal Server Error will be returned instead. See
+:meth:`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``.
+Until Werkzeug 1.0.0, this attribute will only exist during unhandled
+errors, use ``getattr`` to get access it for compatibility.
+
+.. code-block:: python
+
+ @app.errorhandler(InternalServerError)
+ def handle_500(e):
+ original = getattr(e, "original_exception", None)
+
+ if original is None:
+ # direct 500 error, such as abort(500)
+ return render_template("500.html"), 500
+
+ # wrapped unhandled error
+ return render_template("500_unhandled.html", e=original), 500
+
+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.
+
+
+Custom Error Pages
+------------------
Sometimes when building a Flask application, you might want to raise a
:exc:`~werkzeug.exceptions.HTTPException` to signal to the user that
something is wrong with the request. Fortunately, Flask comes with a handy
:func:`~flask.abort` function that aborts a request with a HTTP error from
-werkzeug as desired.
+werkzeug as desired. It will also provide a plain black and white error page
+for you with a basic description, but nothing fancy.
-Consider the code below, we might have a user profile route, but if the user
-fails to pass a username we raise a "400 Bad Request" and if the user passes a
-username but we can't find it, we raise a "404 Not Found".
+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".
.. code-block:: python
@@ -143,133 +307,51 @@ username but we can't find it, we raise a "404 Not Found".
return render_template("profile.html", user=user)
-
-
-.. _custom-error-handlers:
-
-Custom error handlers
-`````````````````````
-
-The default :exc:`~werkzeug.exceptions.HTTPException` returns a black and white
-error page with a basic description, but nothing fancy. Considering
-these errors *will* be thrown during the lifetime of your application, it is
-highly advisable to customise these exceptions to improve the user experience
-of your site. This can be done by registering error handlers.
-
-An error handler is a normal view function that returns a response, but instead
-of being registered for a route, it is registered for an exception or HTTP
-status code that would be raised while trying to handle a request.
-
-It is passed the instance of the error being handled, which is most
-likely an integer that represents a :exc:`~werkzeug.exceptions.HTTPException`
-status code. For example 500 (an "Internal Server Error") which maps to
-:exc:`~werkzeug.exceptions.InternalServerError`.
-
-It is registered with the :meth:`~flask.Flask.errorhandler`
-decorator or the :meth:`~flask.Flask.register_error_handler` to register
-the function later. A handler can be registered for a status code,
-like 404 or 500, or for an built-in exception class, like KeyError,
-or a custom exception class that inherits from Exception or its subclasses.
-
-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 or a 200 OK HTTP code will be sent instead.
+Here is another example implementation for a "404 Page Not Found" exception:
.. code-block:: python
- from werkzeug.exceptions import InternalServerError
+ from flask import render_template
- # as a decorator with an int as the exception code
- @app.errorhandler(500)
- def handle_internal_server_error(e):
- # returning 500 with the text sets the error handler's code
- # make sure to provide the appropriate HTTP status code
- # otherwise 200 will be returned as default
- return 'Internal Server Error!', 500
+ @app.errorhandler(404)
+ def page_not_found(e):
+ # note that we set the 404 status explicitly
+ return render_template('404.html'), 404
- # or, as a decorator with the werkzeug exception for internal server error
- @app.errorhandler(InternalServerError)
- def handle_internal_server_error(e):
- # werkzeug exceptions have a code attribute
- return 'Internal Server Error!', e.code
-
- # or, without the decorator
- app.register_error_handler(500, handle_internal_server_error)
-
- # similarly with a werkzeug exception
- app.register_error_handler(InternalServerError, handle_internal_server_error)
-
-
-
-A handler for "500 Internal Server Error" will not be used when running in
-debug mode. Instead, the interactive debugger will be shown.
-
-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``.
-Until Werkzeug 1.0.0, this attribute will only exist during unhandled
-errors, use ``getattr`` to get access it for compatibility.
+When using :doc:`/patterns/appfactories`:
.. code-block:: python
- @app.errorhandler(InternalServerError)
- def handle_500(e):
- original = getattr(e, "original_exception", None)
+ from flask import Flask, render_template
- if original is None:
- # direct 500 error, such as abort(500)
- return render_template("500.html"), 500
+ def page_not_found(e):
+ return render_template('404.html'), 404
- # wrapped unhandled error
- return render_template("500_unhandled.html", e=original), 500
+ def create_app(config_filename):
+ app = Flask(__name__)
+ app.register_error_handler(404, page_not_found)
+ return app
+
+An example template might be this:
+
+.. code-block:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block title %}Page Not Found{% endblock %}
+ {% block body %}
+ Page Not Found
+ What you were looking for is just not there.
+
go somewhere nice
+ {% endblock %}
-
-Registering Custom Exceptions
------------------------------
-
-You can create your own custom exceptions by subclassing
-:exc:`werkzeug.exceptions.HTTPException`. As shown above, integer HTTP codes
-are interchangable 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
-:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and
-register and raise that exception class:
-
-.. code-block:: python
-
- class InsufficientStorage(werkzeug.exceptions.HTTPException):
- code = 507
- description = 'Not enough storage space.'
-
- def handle_507(e):
- return 'Not enough storage space!', 507
-
- app.register_error_handler(InsufficientStorage, handle_507)
-
- # during an request
- raise InsufficientStorage()
-
-Handlers can be registered for any exception class that inherits from Exception.
-
-
-Unhandled Exceptions
---------------------
-
-If an exception is raised in the code while Flask is handling a request and
-there is no error handler registered for that exception, a "500 Internal Server
-Error" will be returned instead. See :meth:`flask.Flask.handle_exception` for
-information about this behavior.
-
-Custom error pages
-------------------
+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:
-.. sourcecode:: html+jinja
+.. code-block:: html+jinja
{% extends "layout.html" %}
{% block title %}Internal Server Error{% endblock %}
@@ -290,12 +372,10 @@ It can be implemented by rendering the template on "500 Internal Server Error":
# note that we set the 500 status explicitly
return render_template('500.html'), 500
-
-When using the :doc:`/patterns/appfactories`:
+When using :doc:`/patterns/appfactories`:
.. code-block:: python
-
from flask import Flask, render_template
def internal_server_error(e):
@@ -306,7 +386,6 @@ When using the :doc:`/patterns/appfactories`:
app.register_error_handler(500, internal_server_error)
return app
-
When using :doc:`/blueprints`:
.. code-block:: python
@@ -324,16 +403,21 @@ When using :doc:`/blueprints`:
blog.register_error_handler(500, internal_server_error)
+Blueprint Error Handlers
+------------------------
-In blueprints 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:
+In :doc:`/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.
.. code-block:: python
@@ -351,7 +435,6 @@ at the application level using the ``request`` proxy object:
# 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
@@ -363,21 +446,16 @@ at the application level using the ``request`` proxy object:
return render_template("405.html"), 405
+Returning API Errors as JSON
+----------------------------
-More information on error handling with blueprint can be found in
-:doc:`/blueprints`.
-
-
-Returning API errors as JSON
-````````````````````````````
-
-When building APIs in Flask, some developers realise that the builtin
+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
:mimetype:`text/html` they are emitting is not very useful for API consumers.
Using the same techniques as above and :func:`~flask.json.jsonify` we can return JSON
responses to API errors. :func:`~flask.abort` is called
-with a ``description`` parameter. The errorhandler will
+with a ``description`` parameter. The error handler will
use that as the JSON error message, and set the status code to 404.
.. code-block:: python
@@ -397,10 +475,8 @@ use that as the JSON error message, and set the status code to 404.
return jsonify(resource)
-
-
-We can also create custom exception classes; for instance, for an API we can
-introduce a new custom exception that can take a proper human readable message,
+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.
@@ -443,212 +519,20 @@ This is a simple example:
return jsonify(user.to_dict())
-
-A view can now raise that exception with an error message. Additionally
+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.
-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.
-
-An error handler for ``HTTPException`` might be useful for turning
-the default HTML errors pages into JSON, for example. 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.
-
-.. code-block:: python
-
- 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
-
- # or using jsonify
- @app.errorhandler(HTTPException)
- def handle_exception(e):
- return jsonify("code": e.code, "name": e.name, "description": e.description), e.code
-
-
-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.
-
-.. code-block:: python
-
- 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.
-
-
-Generic Error Pages
--------------------
-
-If we pass in the exception into a template as below:
-
-.. code-block:: python
-
- from werkzeug.exceptions import HTTPException
-
- @app.errorhandler(HTTPException)
- def handle_exception(e):
- return render_template("exception.html", e=e), e.code
-
-
-
-.. sourcecode:: html+jinja
-
- {% extends "layout.html" %}
- {% block title %}{{ e.name }}{% endblock %}
- {% block body %}
-
{{ e.code }} {{ e.name }}
- {{ e.description }}
- Go home
- {% endblock %}
-
-
-
-Debugging Application Errors
-````````````````````````````
-
-For production applications, configure your application with logging and
-notifications as described in :doc:`/logging`. This section provides
-pointers when debugging deployment configuration and digging deeper with a
-full-featured Python debugger.
-
Logging
-------
-See :doc:`/logging` for information on how to log exceptions, such as by
-emailing them to admins.
+See :doc:`/logging` for information about how to log exceptions, such as
+by emailing them to admins.
+Debugging
+---------
-When in Doubt, Run Manually
----------------------------
-
-Having problems getting your application configured for production? If you
-have shell access to your host, verify that you can run your application
-manually from the shell in the deployment environment. Be sure to run under
-the same user account as the configured deployment to troubleshoot permission
-issues. You can use Flask's builtin development server with `debug=True` on
-your production host, which is helpful in catching configuration issues, but
-**be sure to do this temporarily in a controlled environment.** Do not run in
-production with `debug=True`.
-
-
-.. _working-with-debuggers:
-
-Working with Debuggers
-----------------------
-
-To dig deeper, possibly to trace code execution, Flask provides a debugger out
-of the box (see :ref:`debug-mode`). If you would like to use another Python
-debugger, note that debuggers interfere with each other. You have to set some
-options in order to use your favorite debugger:
-
-* ``debug`` - whether to enable debug mode and catch exceptions
-* ``use_debugger`` - whether to use the internal Flask debugger
-* ``use_reloader`` - whether to reload and fork the process if modules
- were changed
-
-``debug`` must be True (i.e., exceptions must be caught) in order for the other
-two options to have any value.
-
-If you're using Aptana/Eclipse for debugging you'll need to set both
-``use_debugger`` and ``use_reloader`` to False.
-
-A possible useful pattern for configuration is to set the following in your
-config.yaml (change the block as appropriate for your application, of course)::
-
- FLASK:
- DEBUG: True
- DEBUG_WITH_APTANA: True
-
-Then in your application's entry-point (main.py),
-you could have something like::
-
- if __name__ == "__main__":
- # To allow aptana to receive errors, set use_debugger=False
- app = create_app(config="config.yaml")
-
- use_debugger = app.debug and not(app.config.get('DEBUG_WITH_APTANA'))
- app.run(use_debugger=use_debugger, debug=app.debug,
- use_reloader=use_debugger, host='0.0.0.0')
-
-
-.. _error-logging-tools:
-
-
-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
-`_ for dealing with application errors. It's
-available as an Open Source project `on GitHub
-`_ and is also available as a `hosted version
-`_ 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.
-
-Follow-up reads:
-
-* Sentry also supports catching errors from your worker queue (RQ, Celery) in a
- similar fashion. See the `Python SDK docs
- `_ for more information.
-* `Getting started with Sentry `_
-* `Flask-specific documentation `_.
+See :doc:`/debugging` for information about how to debug errors in
+development and production.
diff --git a/docs/index.rst b/docs/index.rst
index eef180d6..ec47b232 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -44,6 +44,7 @@ instructions for web development with Flask.
templating
testing
errorhandling
+ debugging
logging
config
signals
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index b3028998..7a0e8cea 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -111,59 +111,43 @@ 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.
-.. _debug-mode:
Debug Mode
----------
-(Want to just log errors and stack traces? See :doc:`errorhandling`)
+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.
-The :command:`flask` script is nice to start a local development server, but
-you would have to restart it manually after each change to your code.
-That is not very nice and Flask can do better. If you enable debug
-support the server will reload itself on code changes, and it will also
-provide you with a helpful debugger if things go wrong.
+.. image:: _static/debugger.png
+ :align: center
+ :class: screenshot
+ :alt: The interactive debugger in action.
-To enable all development features (including debug mode) you can export
-the ``FLASK_ENV`` environment variable and set it to ``development``
-before running the server::
+.. 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``.
+
+.. code-block:: text
$ export FLASK_ENV=development
$ flask run
-(On Windows you need to use ``set`` instead of ``export``.)
+See also:
-This does the following things:
-
-1. it activates the debugger
-2. it activates the automatic reloader
-3. it enables the debug mode on the Flask application.
-
-You can also control debug mode separately from the environment by
-exporting ``FLASK_DEBUG=1``.
-
-There are more parameters that are explained in :doc:`/server`.
-
-.. admonition:: Attention
-
- Even though the interactive debugger does not work in forking environments
- (which makes it nearly impossible to use on production servers), it still
- allows the execution of arbitrary code. This makes it a major security risk
- and therefore it **must never be used on production machines**.
-
-Screenshot of the debugger in action:
-
-.. image:: _static/debugger.png
- :align: center
- :class: screenshot
- :alt: screenshot of debugger in action
-
-More information on using the debugger can be found in the `Werkzeug
-documentation`_.
-
-.. _Werkzeug documentation: https://werkzeug.palletsprojects.com/debug/#using-the-debugger
-
-Have another debugger in mind? See :ref:`working-with-debuggers`.
+- :doc:`/server` and :doc:`/cli` for information about running in
+ development mode.
+- :doc:`/debugging` for information about using the built-in debugger
+ and other debuggers.
+- :doc:`/logging` and :doc:`/errorhandling` to log errors and display
+ nice error pages.
HTML Escaping