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.9', python: '3.9', os: ubuntu-latest, tox: py39}
- {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38} - {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
- {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37} - {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: 'PyPy', python: 'pypy-3.7', os: ubuntu-latest, tox: pypy37}
- {name: Typing, python: '3.10', os: ubuntu-latest, tox: typing} - {name: Typing, python: '3.10', os: ubuntu-latest, tox: typing}
steps: steps:

View File

@ -7,8 +7,7 @@ Using ``async`` and ``await``
Routes, error handlers, before request, after request, and teardown Routes, error handlers, before request, after request, and teardown
functions can all be coroutine functions if Flask is installed with the functions can all be coroutine functions if Flask is installed with the
``async`` extra (``pip install flask[async]``). It requires Python 3.7+ ``async`` extra (``pip install flask[async]``). This allows views to be
where ``contextvars.ContextVar`` is available. This allows views to be
defined with ``async def`` and use ``await``. defined with ``async def`` and use ``await``.
.. code-block:: python .. 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``, something like ``ValueError: set_wakeup_fd only works in main thread``,
please upgrade to Python 3.9. 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 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. :: context will be active, and the app instance will be imported. ::
$ flask shell $ flask shell
Python 3.6.2 (default, Jul 20 2017, 03:52:27) Python 3.10.0 (default, Oct 27 2021, 06:59:51) [GCC 11.1.0] on linux
[GCC 7.1.1 20170630] on linux App: example [production]
App: example Instance: /home/david/Projects/pallets/flask/instance
Instance: /home/user/Projects/hello/instance
>>> >>>
Use :meth:`~Flask.shell_context_processor` to add other automatic imports. 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
-------- --------
`Gunicorn`_ 'Green Unicorn' is a WSGI HTTP Server for UNIX. It's a pre-fork `Gunicorn`_ is a WSGI and HTTP server for UNIX. To run a Flask
worker model ported from Ruby's Unicorn project. It supports both `eventlet`_ application, tell Gunicorn how to import your Flask app object.
and `greenlet`_. Running a Flask application on this server is quite simple::
$ gunicorn myproject:app .. code-block:: text
`Gunicorn`_ provides many command-line options -- see ``gunicorn -h``. $ gunicorn -w 4 -b 0.0.0.0:5000 your_project:app
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 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 Gunicorn provides many options for configuring the server, either
package and the application instance within the module. If you use the through a configuration file or with command line options. Use
application factory pattern, you can pass a call to that:: ``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/ .. _Gunicorn: https://gunicorn.org/
.. _gevent: http://www.gevent.org/
.. _eventlet: https://eventlet.net/ .. _eventlet: https://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
uWSGI uWSGI
-------- -----
`uWSGI`_ is a fast application server written in C. It is very configurable `uWSGI`_ is a fast application server written in C. It is very
which makes it more complicated to setup than gunicorn. 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: 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
------- ------
`Gevent`_ is a coroutine-based Python networking library that uses Prefer using `Gunicorn`_ with Gevent workers rather than using Gevent
`greenlet`_ to provide a high-level synchronous API on top of `libev`_ directly. Gunicorn provides a much more configurable and
event loop:: 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 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() http_server.serve_forever()
.. _Gevent: http://www.gevent.org/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/ Eventlet
.. _libev: http://software.schmorp.de/pkg/libev.html --------
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 Twisted Web
----------- -----------
@ -69,7 +173,9 @@ Twisted Web
`Twisted Web`_ is the web server shipped with `Twisted`_, a mature, `Twisted Web`_ is the web server shipped with `Twisted`_, a mature,
non-blocking event-driven networking library. Twisted Web comes with a non-blocking event-driven networking library. Twisted Web comes with a
standard WSGI container which can be controlled from the command line using 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 $ 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 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 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 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 $ twistd -n web --port tcp:8080 --wsgi myproject.app
.. _Twisted: https://twistedmatrix.com/trac/ .. _Twisted: https://twistedmatrix.com/trac/
.. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb .. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb
.. _deploying-proxy-setups: .. _deploying-proxy-setups:
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 `Official Pallets Themes`_. A link to the documentation or project
website must be in the PyPI metadata or the readme. website must be in the PyPI metadata or the readme.
7. For maximum compatibility, the extension should support the same 7. For maximum compatibility, the extension should support the same
versions of Python that Flask supports. 3.6+ is recommended as of versions of Python that Flask supports. 3.7+ is recommended as of
2020. Use ``python_requires=">= 3.6"`` in ``setup.py`` to indicate December 2021. Use ``python_requires=">= 3.7"`` in ``setup.py`` to
supported versions. indicate supported versions.
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask .. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
.. _mailinglist: https://mail.python.org/mailman/listinfo/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 We recommend using the latest version of Python. Flask supports Python
3.6 and newer. 3.7 and newer.
``async`` support in Flask requires Python 3.7+ for ``contextvars.ContextVar``.
Dependencies Dependencies
@ -51,6 +49,18 @@ use them if you install them.
.. _watchdog: https://pythonhosted.org/watchdog/ .. _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 Virtual environments
-------------------- --------------------

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ classifiers =
packages = find: packages = find:
package_dir = = src package_dir = = src
include_package_data = true include_package_data = true
python_requires = >= 3.6 python_requires = >= 3.7
# Dependencies are in setup.py for GitHub's dependency graph. # Dependencies are in setup.py for GitHub's dependency graph.
[options.packages.find] [options.packages.find]
@ -88,7 +88,7 @@ per-file-ignores =
[mypy] [mypy]
files = src/flask files = src/flask
python_version = 3.6 python_version = 3.7
allow_redefinition = True allow_redefinition = True
disallow_subclassing_any = True disallow_subclassing_any = True
# disallow_untyped_calls = 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 BadRequestKeyError
from werkzeug.exceptions import HTTPException from werkzeug.exceptions import HTTPException
from werkzeug.exceptions import InternalServerError from werkzeug.exceptions import InternalServerError
from werkzeug.local import ContextVar
from werkzeug.routing import BuildError from werkzeug.routing import BuildError
from werkzeug.routing import Map from werkzeug.routing import Map
from werkzeug.routing import MapAdapter from werkzeug.routing import MapAdapter
@ -1621,13 +1620,6 @@ class Flask(Scaffold):
"Install Flask with the 'async' extra in order to use async views." "Install Flask with the 'async' extra in order to use async views."
) from None ) 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) return asgiref_async_to_sync(func)
def make_response(self, rv: ResponseReturnValue) -> Response: def make_response(self, rv: ResponseReturnValue) -> Response:

View File

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

View File

@ -1,5 +1,4 @@
import asyncio import asyncio
import sys
import pytest import pytest
@ -79,7 +78,6 @@ def _async_app():
return app return app
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
@pytest.mark.parametrize("path", ["/", "/home", "/bp/", "/view", "/methodview"]) @pytest.mark.parametrize("path", ["/", "/home", "/bp/", "/view", "/methodview"])
def test_async_route(path, async_app): def test_async_route(path, async_app):
test_client = async_app.test_client() test_client = async_app.test_client()
@ -89,7 +87,6 @@ def test_async_route(path, async_app):
assert b"POST" in response.get_data() 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"]) @pytest.mark.parametrize("path", ["/error", "/bp/error"])
def test_async_error_handler(path, async_app): def test_async_error_handler(path, async_app):
test_client = async_app.test_client() test_client = async_app.test_client()
@ -97,7 +94,6 @@ def test_async_error_handler(path, async_app):
assert response.status_code == 412 assert response.status_code == 412
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
def test_async_before_after_request(): def test_async_before_after_request():
app_first_called = False app_first_called = False
app_before_called = False app_before_called = False
@ -154,10 +150,3 @@ def test_async_before_after_request():
test_client.get("/bp/") test_client.get("/bp/")
assert bp_before_called assert bp_before_called
assert bp_after_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 gc
import re import re
import sys
import time import time
import uuid import uuid
import weakref import weakref
@ -1323,7 +1322,6 @@ def test_jsonify_mimetype(app, req_ctx):
assert rv.mimetype == "application/vnd.api+json" 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): def test_json_dump_dataclass(app, req_ctx):
from dataclasses import make_dataclass from dataclasses import make_dataclass

View File

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