From ff33ae6bed51016ebba6e933070601fa70de598c Mon Sep 17 00:00:00 2001 From: Sebastien Tricaud Date: Sun, 26 Feb 2023 23:01:58 +0100 Subject: [PATCH 1/3] Make sure datetimes objects are used in their timestamp format to avoid the exception "TypeError: can't compare offset-naive and offset-aware datetimes" with Python 3.11 --- sanic/response/convenience.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanic/response/convenience.py b/sanic/response/convenience.py index 429b3214a4..0a74264e64 100644 --- a/sanic/response/convenience.py +++ b/sanic/response/convenience.py @@ -148,7 +148,7 @@ async def validate_file( last_modified = datetime.fromtimestamp( float(last_modified), tz=timezone.utc ).replace(microsecond=0) - if last_modified <= if_modified_since: + if last_modified.timestamp() <= if_modified_since.timestamp(): return HTTPResponse(status=304) From 737087de43afed01fe93db28870d9906f90a15ec Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Sun, 19 Mar 2023 23:12:49 +0200 Subject: [PATCH 2/3] Add unit tests --- sanic/response/convenience.py | 21 +++++++++++++ tests/test_response_file.py | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/test_response_file.py diff --git a/sanic/response/convenience.py b/sanic/response/convenience.py index 0a74264e64..0cb18f7b0d 100644 --- a/sanic/response/convenience.py +++ b/sanic/response/convenience.py @@ -148,6 +148,27 @@ async def validate_file( last_modified = datetime.fromtimestamp( float(last_modified), tz=timezone.utc ).replace(microsecond=0) + + print("lm", last_modified.utcoffset()) + print("ims", if_modified_since.utcoffset()) + if ( + last_modified.utcoffset() is None + and if_modified_since.utcoffset() is not None + ): + logger.warning( + "Cannot compare tz-aware and tz-naive datetimes. To avoid " + "this conflict Sanic is converting last_modified to UTC." + ) + last_modified.replace(tzinfo=timezone.utc) + elif ( + last_modified.utcoffset() is not None + and if_modified_since.utcoffset() is None + ): + logger.warning( + "Cannot compare tz-aware and tz-naive datetimes. To avoid " + "this conflict Sanic is converting if_modified_since to UTC." + ) + if_modified_since.replace(tzinfo=timezone.utc) if last_modified.timestamp() <= if_modified_since.timestamp(): return HTTPResponse(status=304) diff --git a/tests/test_response_file.py b/tests/test_response_file.py new file mode 100644 index 0000000000..366e613e8d --- /dev/null +++ b/tests/test_response_file.py @@ -0,0 +1,55 @@ +from datetime import datetime, timezone +from logging import INFO + +import pytest + +from sanic.compat import Header +from sanic.response.convenience import validate_file + + +@pytest.mark.parametrize( + "ifmod,lastmod,expected", + ( + ("Sat, 01 Apr 2023 00:00:00 GMT", 1672524000, None), + ( + "Sat, 01 Apr 2023 00:00:00", + 1672524000, + "converting if_modified_since", + ), + ( + "Sat, 01 Apr 2023 00:00:00 GMT", + datetime(2023, 1, 1, 0, 0, 0), + "converting last_modified", + ), + ( + "Sat, 01 Apr 2023 00:00:00", + datetime(2023, 1, 1, 0, 0, 0), + None, + ), + ( + "Sat, 01 Apr 2023 00:00:00 GMT", + datetime(2023, 1, 1, 0, 0, 0).replace(tzinfo=timezone.utc), + None, + ), + ( + "Sat, 01 Apr 2023 00:00:00", + datetime(2023, 1, 1, 0, 0, 0).replace(tzinfo=timezone.utc), + "converting if_modified_since", + ), + ), +) +@pytest.mark.asyncio +async def test_file_timestamp_validation( + lastmod, ifmod, expected, caplog: pytest.LogCaptureFixture +): + headers = Header([["If-Modified-Since", ifmod]]) + + with caplog.at_level(INFO): + response = await validate_file(headers, lastmod) + assert response.status == 304 + records = caplog.records + if not expected: + assert len(records) == 0 + else: + record = records[0] + assert expected in record.message From 064f63b7db39847dd02d37355a18e14077763e61 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Sun, 19 Mar 2023 23:13:14 +0200 Subject: [PATCH 3/3] squash --- sanic/response/convenience.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sanic/response/convenience.py b/sanic/response/convenience.py index 0cb18f7b0d..d8a3059773 100644 --- a/sanic/response/convenience.py +++ b/sanic/response/convenience.py @@ -149,8 +149,6 @@ async def validate_file( float(last_modified), tz=timezone.utc ).replace(microsecond=0) - print("lm", last_modified.utcoffset()) - print("ims", if_modified_since.utcoffset()) if ( last_modified.utcoffset() is None and if_modified_since.utcoffset() is not None