_on_identity_loaded breaks when multiple test clients are created using with clause and flask==2.2.x
See original GitHub issueI think changes in flask==2.2.0
breaks flask-security-too
when FlaskTestClient
objects are used in a with
context.
All examples below use:
Flask-Login==0.6.2
Flask-Principal==0.4.0
Flask-Security-Too==5.0.1
SQLAlchemy==1.4.41
and flask version changes from 2.1.3
to 2.2.2
as described in details below.
I have a piece of test code that uses two flask test clients simultaneously in a with statement, along the lines of:
with application.test_client() as first_client, application.test_client() as second_client:
post_new_user(
client=first_client,
data={
"email": "first_user@gmail.com",
"password": "testtest"
})
post_new_user(
client=second_client,
data={
"email": "second_user@gmail.com",
"password": "testtest"
})
post_new_user
registers user via flask-security-too
with mysql datastore behind it.
In flask==2.1.3 this
works correctly.
In flask==2.2.2
this code breaks on call to GET /register
inside post_new_user(client=second_client, ...)
with message
{'request': {'body': {'form': {}}, 'method': 'GET', 'path': '/register', 'query_parameters': [], 'user': 'first_user@gmail.com', 'user_agent': 'werkzeug/2.2.2'}, 'exception': {'type': "<class 'sqlalchemy.orm.exc.DetachedInstanceError'>", 'string': "Parent instance <User at 0x7f393fe98730> is not bound to a Session; lazy load operation of attribute 'roles' cannot proceed (Background on this error at: https://sqlalche.me/e/14/bhk3)", 'traceback': [' File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1756, in full_dispatch_request\n rv = self.preprocess_request()\n', ' File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 2247, in preprocess_request\n rv = self.ensure_sync(before_func)()\n', ' File "/usr/local/lib/python3.8/site-packages/flask_principal.py", line 479, in _on_before_request\n self.set_identity(identity)\n', ' File "/usr/local/lib/python3.8/site-packages/flask_principal.py", line 418, in set_identity\n self._set_thread_identity(identity)\n', ' File "/usr/local/lib/python3.8/site-packages/flask_principal.py", line 462, in _set_thread_identity\n identity_loaded.send(current_app._get_current_object(),\n', ' File "/usr/local/lib/python3.8/site-packages/blinker/base.py", line 266, in send\n return [(receiver, receiver(sender, **kwargs))\n', ' File "/usr/local/lib/python3.8/site-packages/blinker/base.py", line 266, in <listcomp>\n return [(receiver, receiver(sender, **kwargs))\n', ' File "/usr/local/lib/python3.8/site-packages/flask_security/core.py", line 655, in _on_identity_loaded\n for role in getattr(current_user, "roles", []):\n', ' File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/attributes.py", line 482, in __get__\n return self.impl.get(state, dict_)\n', ' File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/attributes.py", line 942, in get\n value = self._fire_loader_callables(state, key, passive)\n', ' File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/attributes.py", line 978, in _fire_loader_callables\n return self.callable_(state, passive)\n', ' File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/strategies.py", line 863, in _load_for_state\n raise orm_exc.DetachedInstanceError(\n']}}
One might be tempted to say that this is an error with sqlalchemy
, because the last exception is DetachedInstanceError
, but I don’t think so - call to GET /register
performed by second_client
shouldn’t have a User instance associated with it (flask_login.current_user instance should be AnonymousUser), so no sql lookup should be triggered.
Please notice log says 'user': 'first_user@gmail.com'
even though endpoint was called by second_client
who at that point hasn’t even registered a user instance yet.
My guess is the problem lies with flask_principal
doing some caching or context retrieval wrong, and ending up with first_client data when second_client data should be used.
flask-principal hasn’t been updated since 2013, and flask==2.2.0
made some changes to app context handling that most likely affect flask_principal's
logic.
If I don’t define flask test clients using with
clause, and instead just use plain
first_client = application.test_client()
second_client = application.test_client()
in the same context as rest of code, everything works with flask==2.2.2
Above is just the simplest case I was able to reproduce to illustrate that something in flask==2.2.0 breaks flask-security related code.
I have errors showing up in cases when multiple test clients are used even without with
context, they were just harder to extract to a simple case I can share.
Issue Analytics
- State:
- Created a year ago
- Comments:7 (5 by maintainers)
Top GitHub Comments
Since this is a library, it should be compatible with the latest versions of the dependencies. You can specify the versions in your testing requirements (in this project folder you will only find requirements for Testing, Docs and Dev), but that would have no advantage except that you will not see problems with newer versions of your dependencies. The requirements for production use can be found in
setup.py
. You will find some broad version restrictions there. But all in all: if you want to test your application with different dependency versions, you will do that with tox, which is a great tool for that. And Flask-Security-Too does that to detect problems with deps very early, but of course you can’t test a lib with every single package version.@kuba-lilz Thank you very much. 😃