flask
flask copied to clipboard
Test failures in tutorial with python3.12
The tests runned with pytest
works with python3.11 and not in 3.12:
How to reproduce:
git clone [email protected]:pallets/flask.git
cd flask/examples/tutorial
python3.11 -m venv venv3.11
python3.12 -m venv venv3.12
# install dependencies
# setup db
In both cases, the webservice runs properly with ./venv3.1x/bin/flask --app flaskr run --debug
.
However, running the tests show different results. In venv3.11
, the 24 tests are green. In venv3.12
virtualenv, there is 6 failures (and 18 green). The failures are due to a DeprecationWarning
which becomes an error:
DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
The full output is provided at the end of the bug report.
The DeprecationWarning
is documented in the 3.12 release: 'default adapters and converters are now deprecated. Instead, use the Adapter and converter recipes and tailor them to your needs.' Copy-pasting blindly the recipes in tests/conftest.py
, flaskr/__init__.py
and flaskr/db.py
does not fix the errors.
The errors can be fixed by adding thoses lines in conftest.py:
def convert_timestamp(val):
"""Convert Unix epoch timestamp to datetime.datetime object."""
return datetime.datetime.strptime(val.decode("utf-8"), "%Y-%m-%d %H:%M:%S").replace(tzinfo=datetime.timezone.utc)
sqlite3.register_converter("timestamp", convert_timestamp)
It's probably not the best fix but it's a start.
Pytest output:
(venv3.12) $ ./venv3.12/bin/pytest
========================================= test session starts ==========================================
platform linux -- Python 3.12.4, pytest-8.3.2, pluggy-1.5.0
rootdir: /home/stephane/src/flasktuto/flask/examples/tutorial
configfile: pyproject.toml
testpaths: tests
collected 24 items
tests/test_auth.py ....F... [ 33%]
tests/test_blog.py F...F...F.FF [ 83%]
tests/test_db.py .. [ 91%]
tests/test_factory.py .. [100%]
=============================================== FAILURES ===============================================
______________________________________________ test_login ______________________________________________
client = <FlaskClient <Flask 'flaskr'>>, auth = <conftest.AuthActions object at 0x7f297abc6900>
def test_login(client, auth):
# test that viewing the page renders without template errors
assert client.get("/auth/login").status_code == 200
# test that successful login redirects to the index page
response = auth.login()
assert response.headers["Location"] == "/"
# login request set the user_id in the session
# check that the user is loaded from the session
with client:
> client.get("/")
tests/test_auth.py:50:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1162: in get
return self.open(*args, **kw)
venv3.12/lib/python3.12/site-packages/flask/testing.py:235: in open
response = super().open(
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1116: in open
response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:988: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1264: in run_wsgi_app
app_rv = app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1498: in __call__
return self.wsgi_app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1476: in wsgi_app
response = self.handle_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:1473: in wsgi_app
response = self.full_dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:882: in full_dispatch_request
rv = self.handle_user_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:880: in full_dispatch_request
rv = self.dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:865: in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
flaskr/blog.py:24: in index
).fetchall()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
val = b'2018-01-01 00:00:00'
def convert_timestamp(val):
> warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
E DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
/usr/lib/python3.12/sqlite3/dbapi2.py:76: DeprecationWarning
______________________________________________ test_index ______________________________________________
client = <FlaskClient <Flask 'flaskr'>>, auth = <conftest.AuthActions object at 0x7f297ad26390>
def test_index(client, auth):
> response = client.get("/")
tests/test_blog.py:7:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1162: in get
return self.open(*args, **kw)
venv3.12/lib/python3.12/site-packages/flask/testing.py:235: in open
response = super().open(
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1116: in open
response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:988: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1264: in run_wsgi_app
app_rv = app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1498: in __call__
return self.wsgi_app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1476: in wsgi_app
response = self.handle_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:1473: in wsgi_app
response = self.full_dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:882: in full_dispatch_request
rv = self.handle_user_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:880: in full_dispatch_request
rv = self.dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:865: in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
flaskr/blog.py:24: in index
).fetchall()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
val = b'2018-01-01 00:00:00'
def convert_timestamp(val):
> warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
E DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
/usr/lib/python3.12/sqlite3/dbapi2.py:76: DeprecationWarning
_________________________________________ test_author_required _________________________________________
app = <Flask 'flaskr'>, client = <FlaskClient <Flask 'flaskr'>>
auth = <conftest.AuthActions object at 0x7f297ad3e210>
def test_author_required(app, client, auth):
# change the post author to another user
with app.app_context():
db = get_db()
db.execute("UPDATE post SET author_id = 2 WHERE id = 1")
db.commit()
auth.login()
# current user can't modify other user's post
> assert client.post("/1/update").status_code == 403
tests/test_blog.py:34:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1167: in post
return self.open(*args, **kw)
venv3.12/lib/python3.12/site-packages/flask/testing.py:235: in open
response = super().open(
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1116: in open
response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:988: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1264: in run_wsgi_app
app_rv = app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1498: in __call__
return self.wsgi_app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1476: in wsgi_app
response = self.handle_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:1473: in wsgi_app
response = self.full_dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:882: in full_dispatch_request
rv = self.handle_user_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:880: in full_dispatch_request
rv = self.dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:865: in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
flaskr/auth.py:27: in wrapped_view
return view(**kwargs)
flaskr/blog.py:90: in update
post = get_post(id)
flaskr/blog.py:48: in get_post
.fetchone()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
val = b'2018-01-01 00:00:00'
def convert_timestamp(val):
> warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
E DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
/usr/lib/python3.12/sqlite3/dbapi2.py:76: DeprecationWarning
_____________________________________________ test_update ______________________________________________
client = <FlaskClient <Flask 'flaskr'>>, auth = <conftest.AuthActions object at 0x7f297abc7920>
app = <Flask 'flaskr'>
def test_update(client, auth, app):
auth.login()
> assert client.get("/1/update").status_code == 200
tests/test_blog.py:59:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1162: in get
return self.open(*args, **kw)
venv3.12/lib/python3.12/site-packages/flask/testing.py:235: in open
response = super().open(
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1116: in open
response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:988: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1264: in run_wsgi_app
app_rv = app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1498: in __call__
return self.wsgi_app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1476: in wsgi_app
response = self.handle_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:1473: in wsgi_app
response = self.full_dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:882: in full_dispatch_request
rv = self.handle_user_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:880: in full_dispatch_request
rv = self.dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:865: in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
flaskr/auth.py:27: in wrapped_view
return view(**kwargs)
flaskr/blog.py:90: in update
post = get_post(id)
flaskr/blog.py:48: in get_post
.fetchone()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
val = b'2018-01-01 00:00:00'
def convert_timestamp(val):
> warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
E DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
/usr/lib/python3.12/sqlite3/dbapi2.py:76: DeprecationWarning
________________________________ test_create_update_validate[/1/update] ________________________________
client = <FlaskClient <Flask 'flaskr'>>, auth = <conftest.AuthActions object at 0x7f297ace5160>
path = '/1/update'
@pytest.mark.parametrize("path", ("/create", "/1/update"))
def test_create_update_validate(client, auth, path):
auth.login()
> response = client.post(path, data={"title": "", "body": ""})
tests/test_blog.py:71:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1167: in post
return self.open(*args, **kw)
venv3.12/lib/python3.12/site-packages/flask/testing.py:235: in open
response = super().open(
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1116: in open
response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:988: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1264: in run_wsgi_app
app_rv = app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1498: in __call__
return self.wsgi_app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1476: in wsgi_app
response = self.handle_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:1473: in wsgi_app
response = self.full_dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:882: in full_dispatch_request
rv = self.handle_user_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:880: in full_dispatch_request
rv = self.dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:865: in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
flaskr/auth.py:27: in wrapped_view
return view(**kwargs)
flaskr/blog.py:90: in update
post = get_post(id)
flaskr/blog.py:48: in get_post
.fetchone()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
val = b'2018-01-01 00:00:00'
def convert_timestamp(val):
> warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
E DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
/usr/lib/python3.12/sqlite3/dbapi2.py:76: DeprecationWarning
_____________________________________________ test_delete ______________________________________________
client = <FlaskClient <Flask 'flaskr'>>, auth = <conftest.AuthActions object at 0x7f297ada5610>
app = <Flask 'flaskr'>
def test_delete(client, auth, app):
auth.login()
> response = client.post("/1/delete")
tests/test_blog.py:77:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1167: in post
return self.open(*args, **kw)
venv3.12/lib/python3.12/site-packages/flask/testing.py:235: in open
response = super().open(
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1116: in open
response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:988: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
venv3.12/lib/python3.12/site-packages/werkzeug/test.py:1264: in run_wsgi_app
app_rv = app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1498: in __call__
return self.wsgi_app(environ, start_response)
venv3.12/lib/python3.12/site-packages/flask/app.py:1476: in wsgi_app
response = self.handle_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:1473: in wsgi_app
response = self.full_dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:882: in full_dispatch_request
rv = self.handle_user_exception(e)
venv3.12/lib/python3.12/site-packages/flask/app.py:880: in full_dispatch_request
rv = self.dispatch_request()
venv3.12/lib/python3.12/site-packages/flask/app.py:865: in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
flaskr/auth.py:27: in wrapped_view
return view(**kwargs)
flaskr/blog.py:121: in delete
get_post(id)
flaskr/blog.py:48: in get_post
.fetchone()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
val = b'2018-01-01 00:00:00'
def convert_timestamp(val):
> warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
E DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
/usr/lib/python3.12/sqlite3/dbapi2.py:76: DeprecationWarning
======================================= short test summary info ========================================
FAILED tests/test_auth.py::test_login - DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite...
FAILED tests/test_blog.py::test_index - DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite...
FAILED tests/test_blog.py::test_author_required - DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite...
FAILED tests/test_blog.py::test_update - DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite...
FAILED tests/test_blog.py::test_create_update_validate[/1/update] - DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite...
FAILED tests/test_blog.py::test_delete - DeprecationWarning: The default timestamp converter is deprecated as of Python 3.12; see the sqlite...
===================================== 6 failed, 18 passed in 8.05s =====================================
``