From e316f0c5f0aa6d176a6d08d1f568f83658b51339 Mon Sep 17 00:00:00 2001 From: John Whitlock Date: Tue, 12 Jul 2022 18:00:59 -0700 Subject: [PATCH 1/5] Update changelog, 2022.1.0 and 2022.7.0 --- docs/changelog.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 028e616..2959c14 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,36 @@ Changelog --------- +2022.7.0 (2022-07-12) +~~~~~~~~~~~~~~~~~~~~~ + +- Django: Remove `default_app_config`, deprecated in Django 3.2 and removed in 4.1 + +- Add support for Python 3.10 + +- Add support for Flask 2.0 and 2.1 + +- Explicitly support Flask 1.0 and 1.1 (Flask 1.1 was previously tested as 1.0) + +- Drop support for Python 3.6 + +- Drop support for Django 2.2, 3.0, and 3.1 + +- Drop support for Flask 0.11 + +- Drop support for Sanic 19 + +- Target Black formatting to Python 3.7 to 3.10, drop 2.7 + +2022.1.0 (2022-01-31) +~~~~~~~~~~~~~~~~~~~~~ + +- Sanic: Limit `sanic_redis` to 0.3.0 or earlier, since later versions require + `aioredis` 2.x + +- Django: Support for 4.0 + + 2021.7.0 (2021-07-07) ^^^^^^^^^^^^^^^^^^^^^ From 3d47d6397790b459ffaace24f129b7e9eb69d27e Mon Sep 17 00:00:00 2001 From: Daniel Thorn Date: Thu, 18 Aug 2022 10:17:12 -0700 Subject: [PATCH 2/5] Add support for Sanic 21+ (#71) --- src/dockerflow/sanic/checks.py | 7 +++- tests/constraints/sanic-20.txt | 4 +- tests/constraints/sanic-21.txt | 1 + tests/constraints/sanic-22.txt | 1 + tests/requirements/sanic-20.txt | 7 ++++ tests/requirements/sanic.txt | 5 ++- tests/sanic/test_sanic.py | 71 +++++++++++++++++++++------------ tox.ini | 10 +++-- 8 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 tests/constraints/sanic-21.txt create mode 100644 tests/constraints/sanic-22.txt create mode 100644 tests/requirements/sanic-20.txt diff --git a/src/dockerflow/sanic/checks.py b/src/dockerflow/sanic/checks.py index 332c865..50c8925 100644 --- a/src/dockerflow/sanic/checks.py +++ b/src/dockerflow/sanic/checks.py @@ -56,12 +56,17 @@ async def check_redis_connected(redis): """ import aioredis + if aioredis.__version__.startswith("1."): + RedisConnectionError = aioredis.ConnectionClosedError + else: + RedisConnectionError = aioredis.ConnectionError + errors = [] try: with await redis.conn as r: result = await r.ping() - except aioredis.ConnectionClosedError as e: + except RedisConnectionError as e: msg = "Could not connect to redis: {!s}".format(e) errors.append(Error(msg, id=health.ERROR_CANNOT_CONNECT_REDIS)) except aioredis.RedisError as e: diff --git a/tests/constraints/sanic-20.txt b/tests/constraints/sanic-20.txt index d450454..7c6c3ea 100644 --- a/tests/constraints/sanic-20.txt +++ b/tests/constraints/sanic-20.txt @@ -1 +1,3 @@ -Sanic<21 +Sanic>=20,<21 +aioredis<2 +sanic_redis<0.3.0 diff --git a/tests/constraints/sanic-21.txt b/tests/constraints/sanic-21.txt new file mode 100644 index 0000000..0d223de --- /dev/null +++ b/tests/constraints/sanic-21.txt @@ -0,0 +1 @@ +Sanic>=21,<22 diff --git a/tests/constraints/sanic-22.txt b/tests/constraints/sanic-22.txt new file mode 100644 index 0000000..97479fa --- /dev/null +++ b/tests/constraints/sanic-22.txt @@ -0,0 +1 @@ +Sanic>=22,<23 diff --git a/tests/requirements/sanic-20.txt b/tests/requirements/sanic-20.txt new file mode 100644 index 0000000..e11c6f2 --- /dev/null +++ b/tests/requirements/sanic-20.txt @@ -0,0 +1,7 @@ +# these are constrained by the files in tests/constraints/*.txt +# to support a triple stack Django/Flask/Sanic +aiohttp +aioredis +Sanic +sanic_redis +uvloop>=0.14.0rc1 diff --git a/tests/requirements/sanic.txt b/tests/requirements/sanic.txt index 41ec1d2..20ffec4 100644 --- a/tests/requirements/sanic.txt +++ b/tests/requirements/sanic.txt @@ -1,7 +1,8 @@ # these are constrained by the files in tests/constraints/*.txt # to support a triple stack Django/Flask/Sanic aiohttp -aioredis<2.0.0 +aioredis Sanic -sanic_redis<0.3.0 +sanic_redis +sanic-testing uvloop>=0.14.0rc1 diff --git a/tests/sanic/test_sanic.py b/tests/sanic/test_sanic.py index f3682a2..b3c4be5 100644 --- a/tests/sanic/test_sanic.py +++ b/tests/sanic/test_sanic.py @@ -3,11 +3,11 @@ # file, you can obtain one at http://mozilla.org/MPL/2.0/. import functools import logging -import socket +import uuid import aioredis import pytest -import sanic.testing +import sanic import sanic_redis.core from sanic import Sanic, response from sanic_redis import SanicRedis @@ -15,9 +15,14 @@ from dockerflow import health from dockerflow.sanic import Dockerflow, checks +if sanic.__version__.startswith("20."): + from sanic.testing import SanicTestClient +else: + from sanic_testing.testing import SanicTestClient + class FakeRedis: - def __init__(self, error=None, **kw): + def __init__(self, *args, error=None, **kw): self.error = error def __await__(self): @@ -30,7 +35,7 @@ def __enter__(self): def __exit__(self, *exc_info): pass - def close(self): + async def close(self): pass async def wait_closed(self): @@ -38,7 +43,11 @@ async def wait_closed(self): async def ping(self): if self.error == "connection": - raise aioredis.ConnectionClosedError("fake") + if aioredis.__version__.startswith("1."): + RedisConnectionError = aioredis.ConnectionClosedError + else: + RedisConnectionError = aioredis.ConnectionError + raise RedisConnectionError("fake") elif self.error == "redis": raise aioredis.RedisError("fake") elif self.error == "malformed": @@ -47,13 +56,20 @@ async def ping(self): return b"PONG" -async def fake_redis(**kw): - return FakeRedis(**kw) +class FakeRedis1(FakeRedis): + def close(self): + pass + + +async def fake_redis(*args, **kw): + if aioredis.__version__.startswith("1."): + return FakeRedis1(*args, **kw) + return FakeRedis(*args, **kw) @pytest.fixture(scope="function") def app(): - app = Sanic("dockerflow") + app = Sanic(f"dockerflow-{uuid.uuid4().hex}") @app.route("/") async def root(request): @@ -77,30 +93,27 @@ def dockerflow_redis(app): @pytest.fixture def test_client(app): - # Create SanicTestClient manually and provide a socket object instead of host - # and port when calling Sanic.run in order to avoid parallel test failures - # caused by Sanic.test_client bindngs to a static port - s = socket.socket() - s.bind((sanic.testing.HOST, 0)) - try: - # initialize test_client with socket's port - test_client = sanic.testing.SanicTestClient(app, s.getsockname()[1]) - # override app.run to drop host and port in favor of socket - run = app.run - app.run = lambda host, port, **kw: run(sock=s, **kw) - # yield test_client - yield test_client - finally: - s.close() + return SanicTestClient(app) -def test_instantiating(app): +@pytest.mark.skipif(not sanic.__version__.startswith("20."), reason="requires sanic 20") +def test_instantiating_sanic_20(app): dockerflow = Dockerflow() assert "dockerflow.heartbeat" not in app.router.routes_names dockerflow.init_app(app) assert "dockerflow.heartbeat" in app.router.routes_names +@pytest.mark.skipif( + sanic.__version__.startswith("20."), reason="requires sanic 21 or later" +) +def test_instantiating(app): + Dockerflow() + assert ("__heartbeat__",) not in app.router.routes_all + Dockerflow(app) + assert ("__heartbeat__",) in app.router.routes_all + + def test_version_exists(dockerflow, mocker, test_client, version_content): mocker.patch.object(dockerflow, "_version_callback", return_value=version_content) _, response = test_client.get("/__version__") @@ -178,7 +191,10 @@ async def warning_check2(): def test_redis_check(dockerflow_redis, mocker, test_client): assert "check_redis_connected" in dockerflow_redis.checks - mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis) + if aioredis.__version__.startswith("1."): + mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis) + else: + mocker.patch.object(sanic_redis.core, "from_url", fake_redis) _, response = test_client.get("/__heartbeat__") assert response.status == 200 assert response.json["status"] == "ok" @@ -198,7 +214,10 @@ def test_redis_check(dockerflow_redis, mocker, test_client): def test_redis_check_error(dockerflow_redis, mocker, test_client, error, messages): assert "check_redis_connected" in dockerflow_redis.checks fake_redis_error = functools.partial(fake_redis, error=error) - mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis_error) + if aioredis.__version__.startswith("1."): + mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis_error) + else: + mocker.patch.object(sanic_redis.core, "from_url", fake_redis_error) _, response = test_client.get("/__heartbeat__") assert response.status == 500 assert response.json["status"] == "error" diff --git a/tox.ini b/tox.ini index 7f04c32..378e2a4 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,8 @@ envlist = py{37,38,39,310}-dj32 py{38,39,310}-dj{40} py{37,38,39,310}-fl{012,10,11,20,21} - py{37,38,39}-s{20} + py{37,38,39}-s20 + py{37,38,39,310}-s{21,22} [gh-actions] python = @@ -26,7 +27,8 @@ deps = -rtests/requirements/default.txt dj{32,40}: -rtests/requirements/django.txt fl{012,10,11,20,21}: -rtests/requirements/flask.txt - s{20}: -rtests/requirements/sanic.txt + s20: -rtests/requirements/sanic-20.txt + s{21,22}: -rtests/requirements/sanic.txt dj32: -ctests/constraints/django-3.2.txt dj40: -ctests/constraints/django-4.0.txt fl012: -ctests/constraints/flask-0.12.txt @@ -35,11 +37,13 @@ deps = fl20: -ctests/constraints/flask-2.0.txt fl21: -ctests/constraints/flask-2.0.txt s20: -ctests/constraints/sanic-20.txt + s21: -ctests/constraints/sanic-21.txt + s22: -ctests/constraints/sanic-22.txt commands = python --version dj{32,40}: pytest tests/core/ tests/django --nomigrations {posargs:} fl{012,10,11,20,21}: pytest tests/core/ tests/flask/ {posargs:} - s{20}: pytest tests/core/ tests/sanic/ {posargs:} + s{20,21,22}: pytest tests/core/ tests/sanic/ {posargs:} [testenv:py38-docs] basepython = python3.8 From 9d819b6abf36ccb7a1057767b7fd3ba410c873fd Mon Sep 17 00:00:00 2001 From: John Whitlock Date: Thu, 18 Aug 2022 14:20:49 -0500 Subject: [PATCH 3/5] Fix missing links in docs (#72) --- docs/flask.rst | 2 +- docs/sanic.rst | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/flask.rst b/docs/flask.rst index 9941551..db11ea2 100644 --- a/docs/flask.rst +++ b/docs/flask.rst @@ -127,7 +127,7 @@ specific casters for typical Flask configuration values. E.g.: Flask-Env ~~~~~~~~~ -If you need to solve :ref:`more complex configuration scenarios ` +If you need to solve more complex configuration scenarios there are tools like Flask-Env_ which allows loading settings for different environments (e.g. dev, stage, prod) via environment variables. It provides a small Python meta class to allow setting up the configuration values: diff --git a/docs/sanic.rst b/docs/sanic.rst index ce60bb9..646bae6 100644 --- a/docs/sanic.rst +++ b/docs/sanic.rst @@ -80,11 +80,13 @@ for settings and other variables. loaded by the application automatically and fed into the ``REQUEST_TIMEOUT`` config variable. - -- :doc:`Sanic docs on configuration `. + -- `Sanic docs on configuration`_. The downside of that is that it nicely works only for string based variables, since that's what ``os.environ`` returns. +.. _Sanic docs on configuration: https://sanic.dev/en/guide/deployment/configuration.html#environment-variables + python-decouple ~~~~~~~~~~~~~~~ @@ -190,8 +192,9 @@ Uvicorn, and Hypercorn. .. seealso:: - The :doc:`Sanic deployment documentation ` has more - for more details. + The `Sanic deployment documentation`_ has more details. + +.. _Sanic deployment documentation: https://sanic.dev/en/guide/deployment/running.html#asgi .. _sanic-versions: @@ -454,5 +457,7 @@ Alternatively you can also pass the same logging config dictionary to the Static content -------------- -Please refer to the Sanic documentation about -:doc:`serving static files ` for more information. +Please refer to the Sanic documentation about `serving static files`_ for more +information. + +.. _serving static files: https://sanic.dev/en/guide/basics/routing.html#static-files From 37e0365412132eff7613bd814d60f9dd38b325be Mon Sep 17 00:00:00 2001 From: John Whitlock Date: Thu, 18 Aug 2022 15:59:48 -0500 Subject: [PATCH 4/5] Add Django 4.1, Flask 2.2 (#73) * Add Django 4.1 to test matrix * Add Flask 2.2 to test matrix, fix Flask 2.1 The Flask 2.1 test was actually testing Flask 2.0 --- setup.py | 1 + tests/constraints/django-4.1.txt | 1 + tests/constraints/flask-2.2.txt | 1 + tests/django/settings.py | 2 ++ tox.ini | 16 +++++++++------- 5 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 tests/constraints/django-4.1.txt create mode 100644 tests/constraints/flask-2.2.txt diff --git a/setup.py b/setup.py index 0fbece6..e05b33f 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ def read(*parts): "Framework :: Django", "Framework :: Django :: 3.2", "Framework :: Django :: 4.0", + "Framework :: Django :: 4.1", "Framework :: Flask", "Intended Audience :: Developers", "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", diff --git a/tests/constraints/django-4.1.txt b/tests/constraints/django-4.1.txt new file mode 100644 index 0000000..a0aaa8b --- /dev/null +++ b/tests/constraints/django-4.1.txt @@ -0,0 +1 @@ +Django>=4.1,<4.2 diff --git a/tests/constraints/flask-2.2.txt b/tests/constraints/flask-2.2.txt new file mode 100644 index 0000000..1e7ae79 --- /dev/null +++ b/tests/constraints/flask-2.2.txt @@ -0,0 +1 @@ +Flask>=2.2,<2.3 diff --git a/tests/django/settings.py b/tests/django/settings.py index 875e136..341b3f3 100644 --- a/tests/django/settings.py +++ b/tests/django/settings.py @@ -66,3 +66,5 @@ }, "loggers": {"request.summary": {"handlers": ["console"], "level": "DEBUG"}}, } + +USE_TZ = True # Default False until Django 5.0, when it switches to True diff --git a/tox.ini b/tox.ini index 378e2a4..b28cbcb 100644 --- a/tox.ini +++ b/tox.ini @@ -5,8 +5,8 @@ envlist = py38-lint py38-docs py{37,38,39,310}-dj32 - py{38,39,310}-dj{40} - py{37,38,39,310}-fl{012,10,11,20,21} + py{38,39,310}-dj{40,41} + py{37,38,39,310}-fl{012,10,11,20,21,22} py{37,38,39}-s20 py{37,38,39,310}-s{21,22} @@ -25,24 +25,26 @@ setenv = PYTHONPATH = {toxinidir} deps = -rtests/requirements/default.txt - dj{32,40}: -rtests/requirements/django.txt - fl{012,10,11,20,21}: -rtests/requirements/flask.txt + dj{32,40,41}: -rtests/requirements/django.txt + fl{012,10,11,20,21,22}: -rtests/requirements/flask.txt s20: -rtests/requirements/sanic-20.txt s{21,22}: -rtests/requirements/sanic.txt dj32: -ctests/constraints/django-3.2.txt dj40: -ctests/constraints/django-4.0.txt + dj41: -ctests/constraints/django-4.1.txt fl012: -ctests/constraints/flask-0.12.txt fl10: -ctests/constraints/flask-1.0.txt fl11: -ctests/constraints/flask-1.1.txt fl20: -ctests/constraints/flask-2.0.txt - fl21: -ctests/constraints/flask-2.0.txt + fl21: -ctests/constraints/flask-2.1.txt + fl22: -ctests/constraints/flask-2.2.txt s20: -ctests/constraints/sanic-20.txt s21: -ctests/constraints/sanic-21.txt s22: -ctests/constraints/sanic-22.txt commands = python --version - dj{32,40}: pytest tests/core/ tests/django --nomigrations {posargs:} - fl{012,10,11,20,21}: pytest tests/core/ tests/flask/ {posargs:} + dj{32,40,41}: pytest tests/core/ tests/django --nomigrations {posargs:} + fl{012,10,11,20,21,22}: pytest tests/core/ tests/flask/ {posargs:} s{20,21,22}: pytest tests/core/ tests/sanic/ {posargs:} [testenv:py38-docs] From aae37b4d2b6ae34bef89bc636c9e7a70de900fad Mon Sep 17 00:00:00 2001 From: John Whitlock Date: Thu, 18 Aug 2022 16:04:00 -0500 Subject: [PATCH 5/5] Prepare for 2022.8.0 release --- docs/changelog.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 2959c14..8984677 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,15 @@ Changelog --------- +2022.8.0 (2022-08-18) +~~~~~~~~~~~~~~~~~~~~~ + +- Add support for Sanic 21 and 22, with `aioredis` 2.x + +- Add support for Django 4.1 + +- Add support for Flask 2.2 + 2022.7.0 (2022-07-12) ~~~~~~~~~~~~~~~~~~~~~