Merge pull request #4335 from pallets/drop-python-3.6

drop Python 3.6
This commit is contained in:
David Lord 2021-11-11 18:37:43 -08:00 committed by GitHub
commit ea93a52d7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 178 additions and 82 deletions

View File

@ -31,7 +31,6 @@ jobs:
- {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39}
- {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
- {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37}
- {name: '3.6', python: '3.6', os: ubuntu-latest, tox: py36}
- {name: 'PyPy', python: 'pypy-3.7', os: ubuntu-latest, tox: pypy37}
- {name: Typing, python: '3.10', os: ubuntu-latest, tox: typing}
steps:

View File

@ -7,8 +7,7 @@ Using ``async`` and ``await``
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]``). It requires Python 3.7+
where ``contextvars.ContextVar`` is available. This allows views to be
``async`` extra (``pip install flask[async]``). This allows views to be
defined with ``async def`` and use ``await``.
.. code-block:: python
@ -30,6 +29,12 @@ well as all the HTTP method handlers in views that inherit from the
something like ``ValueError: set_wakeup_fd only works in main thread``,
please upgrade to Python 3.9.
.. admonition:: 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.
Performance
-----------

View File

@ -112,10 +112,9 @@ shell with the :func:`shell <cli.shell_command>` command. An application
context will be active, and the app instance will be imported. ::
$ flask shell
Python 3.6.2 (default, Jul 20 2017, 03:52:27)
[GCC 7.1.1 20170630] on linux
App: example
Instance: /home/user/Projects/hello/instance
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 :meth:`~Flask.shell_context_processor` to add other automatic imports.

View File

@ -1,67 +1,171 @@
Standalone WSGI Containers
==========================
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 :ref:`deploying-proxy-setups` if you
run into issues with that.
There are popular servers written in Python that contain WSGI applications and
serve HTTP. These servers stand alone when they run; you can proxy to them
from your web server. Note the section on :ref:`deploying-proxy-setups` if you
run into issues.
Gunicorn
--------
`Gunicorn`_ 'Green Unicorn' is a WSGI HTTP Server for UNIX. It's a pre-fork
worker model ported from Ruby's Unicorn project. It supports both `eventlet`_
and `greenlet`_. Running a Flask application on this server is quite simple::
`Gunicorn`_ is a WSGI and HTTP server for UNIX. To run a Flask
application, tell Gunicorn how to import your Flask app object.
$ gunicorn myproject:app
.. code-block:: text
`Gunicorn`_ provides many command-line options -- see ``gunicorn -h``.
For example, to run a Flask application with 4 worker processes (``-w
4``) binding to localhost port 4000 (``-b 127.0.0.1:4000``)::
$ gunicorn -w 4 -b 0.0.0.0:5000 your_project:app
$ gunicorn -w 4 -b 127.0.0.1:4000 myproject: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.
The ``gunicorn`` command expects the names of your application module or
package and the application instance within the module. If you use the
application factory pattern, you can pass a call to that::
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.
.. code-block:: text
$ gunicorn -w 4 -b 0.0.0.0:5000 "myproject:create_app()"
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`_
or `eventlet`_. 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:
.. code-block:: text
$ gunicorn -k gevent -b 0.0.0.0:5000 your_project:app
To use eventlet:
.. code-block:: text
$ gunicorn -k eventlet -b 0.0.0.0:5000 your_project:app
$ gunicorn "myproject:create_app()"
.. _Gunicorn: https://gunicorn.org/
.. _gevent: http://www.gevent.org/
.. _eventlet: https://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
uWSGI
--------
-----
`uWSGI`_ is a fast application server written in C. It is very configurable
which makes it more complicated to setup than gunicorn.
`uWSGI`_ 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 Gunicorn how to import
your Flask app object.
Running `uWSGI HTTP Router`_::
.. code-block:: text
$ uwsgi --http 127.0.0.1:5000 --module myproject:app
$ uwsgi --master -p 4 --http 0.0.0.0:5000 -w your_project:app
For a more optimized setup, see :doc:`configuring uWSGI and NGINX <uwsgi>`.
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 :doc:`configuring uWSGI and Nginx <uwsgi>`.
Async with Gevent
~~~~~~~~~~~~~~~~~
The default sync worker is appropriate for many use cases. If you need
asynchronous support, uWSGI provides workers using `gevent`_. 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.
.. code-block:: text
$ uwsgi --master --gevent 100 --http 0.0.0.0:5000 -w your_project:app
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
.. _uWSGI HTTP Router: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html#the-uwsgi-http-https-router
Gevent
-------
------
`Gevent`_ is a coroutine-based Python networking library that uses
`greenlet`_ to provide a high-level synchronous API on top of `libev`_
event loop::
Prefer using `Gunicorn`_ 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`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ 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`_, 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``.
.. code-block:: python
from gevent.pywsgi import WSGIServer
from yourapplication import app
from your_project import app
http_server = WSGIServer(('', 5000), app)
http_server = WSGIServer(("", 5000), app)
http_server.serve_forever()
.. _Gevent: http://www.gevent.org/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
.. _libev: http://software.schmorp.de/pkg/libev.html
Eventlet
--------
Prefer using `Gunicorn`_ 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`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ 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`_, 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``.
.. code-block:: python
import eventlet
from eventlet import wsgi
from your_project import app
wsgi.server(eventlet.listen(("", 5000), app)
Twisted Web
-----------
@ -69,7 +173,9 @@ Twisted Web
`Twisted Web`_ is the web server shipped with `Twisted`_, 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::
the ``twistd`` utility:
.. code-block:: text
$ twistd web --wsgi myproject.app
@ -79,13 +185,16 @@ This example will run a Flask application called ``app`` from a module named
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``::
application from ``myproject``:
.. code-block:: text
$ twistd -n web --port tcp:8080 --wsgi myproject.app
.. _Twisted: https://twistedmatrix.com/trac/
.. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb
.. _deploying-proxy-setups:
Proxy Setups

View File

@ -322,9 +322,9 @@ ecosystem remain consistent and compatible.
`Official Pallets Themes`_. 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.6+ is recommended as of
2020. Use ``python_requires=">= 3.6"`` in ``setup.py`` to indicate
supported versions.
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.
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
.. _mailinglist: https://mail.python.org/mailman/listinfo/flask

View File

@ -6,9 +6,7 @@ Python Version
--------------
We recommend using the latest version of Python. Flask supports Python
3.6 and newer.
``async`` support in Flask requires Python 3.7+ for ``contextvars.ContextVar``.
3.7 and newer.
Dependencies
@ -51,6 +49,18 @@ use them if you install them.
.. _watchdog: https://pythonhosted.org/watchdog/
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.
Virtual environments
--------------------

View File

@ -38,7 +38,7 @@ filelock==3.3.2
# via
# tox
# virtualenv
greenlet==1.1.2
greenlet==1.1.2 ; python_version < "3.11"
# via -r requirements/tests.in
identify==2.3.3
# via pre-commit

View File

@ -1,5 +1,5 @@
pytest
asgiref
blinker
greenlet
greenlet ; python_version < "3.11"
python-dotenv

View File

@ -10,7 +10,7 @@ attrs==21.2.0
# via pytest
blinker==1.4
# via -r requirements/tests.in
greenlet==1.1.2
greenlet==1.1.2 ; python_version < "3.11"
# via -r requirements/tests.in
iniconfig==1.1.1
# via pytest

View File

@ -35,7 +35,7 @@ classifiers =
packages = find:
package_dir = = src
include_package_data = true
python_requires = >= 3.6
python_requires = >= 3.7
# Dependencies are in setup.py for GitHub's dependency graph.
[options.packages.find]
@ -88,7 +88,7 @@ per-file-ignores =
[mypy]
files = src/flask
python_version = 3.6
python_version = 3.7
allow_redefinition = True
disallow_subclassing_any = True
# disallow_untyped_calls = True

View File

@ -16,7 +16,6 @@ from werkzeug.exceptions import BadRequest
from werkzeug.exceptions import BadRequestKeyError
from werkzeug.exceptions import HTTPException
from werkzeug.exceptions import InternalServerError
from werkzeug.local import ContextVar
from werkzeug.routing import BuildError
from werkzeug.routing import Map
from werkzeug.routing import MapAdapter
@ -1621,13 +1620,6 @@ class Flask(Scaffold):
"Install Flask with the 'async' extra in order to use async views."
) from None
# Check that Werkzeug isn't using its fallback ContextVar class.
if ContextVar.__module__ == "werkzeug.local":
raise RuntimeError(
"Async cannot be used with this combination of Python "
"and Greenlet versions."
)
return asgiref_async_to_sync(func)
def make_response(self, rv: ResponseReturnValue) -> Response:

View File

@ -1,3 +1,4 @@
import dataclasses
import decimal
import io
import json as _json
@ -16,12 +17,6 @@ if t.TYPE_CHECKING:
from ..app import Flask
from ..wrappers import Response
try:
import dataclasses
except ImportError:
# Python < 3.7
dataclasses = None # type: ignore
class JSONEncoder(_json.JSONEncoder):
"""The default JSON encoder. Handles extra types compared to the

View File

@ -1,5 +1,4 @@
import asyncio
import sys
import pytest
@ -79,7 +78,6 @@ def _async_app():
return app
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
@pytest.mark.parametrize("path", ["/", "/home", "/bp/", "/view", "/methodview"])
def test_async_route(path, async_app):
test_client = async_app.test_client()
@ -89,7 +87,6 @@ def test_async_route(path, async_app):
assert b"POST" in response.get_data()
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
@pytest.mark.parametrize("path", ["/error", "/bp/error"])
def test_async_error_handler(path, async_app):
test_client = async_app.test_client()
@ -97,7 +94,6 @@ def test_async_error_handler(path, async_app):
assert response.status_code == 412
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
def test_async_before_after_request():
app_first_called = False
app_before_called = False
@ -154,10 +150,3 @@ def test_async_before_after_request():
test_client.get("/bp/")
assert bp_before_called
assert bp_after_called
@pytest.mark.skipif(sys.version_info >= (3, 7), reason="should only raise Python < 3.7")
def test_async_runtime_error():
app = Flask(__name__)
with pytest.raises(RuntimeError):
app.async_to_sync(None)

View File

@ -1,6 +1,5 @@
import gc
import re
import sys
import time
import uuid
import weakref
@ -1323,7 +1322,6 @@ def test_jsonify_mimetype(app, req_ctx):
assert rv.mimetype == "application/vnd.api+json"
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
def test_json_dump_dataclass(app, req_ctx):
from dataclasses import make_dataclass

View File

@ -1,6 +1,6 @@
[tox]
envlist =
py3{11,10,9,8,7,6},pypy37
py3{11,10,9,8,7},pypy3{8,7}
py39-click7
style
typing