I am playing around with Flask, striving to understand details of how sessions are working, I am using:
- Python 3.6.1
- Flask 0.12.2
Flask documentation clearly states (bold is mine):
The session object works pretty much like an ordinary dict, with the
difference that it keeps track on modifications.
This is a proxy.
...
Section on proxies mentions that (again, bold is mine):
If you need to get access
to the underlying object that is proxied, you can use the
_get_current_object()
method
Thus, underlying object (session._get_current_object()
) must remain the same for the request or, as was suggested by this answer and comment, a thread. Though, it does not persist nor inside request, nor thread.
Here is a demonstration code:
import threading
from flask import (
Flask,
session,
)
app = Flask(__name__)
app.secret_key = 'some random secret key'
@app.route('/')
def index():
print("session ID is: {}".format(id(session)))
print("session._get_current_object() ID is: {}".format(id(session._get_current_object())))
print("threading.current_thread().ident is: {}".format(threading.current_thread().ident))
print('________________________________')
return 'Check the console! ;-)'
If you will run Flask application above, and repeatedly go to the /
— id returned by session._get_current_object()
will, occasionally, change, while threading.current_thread().ident
never changes.
This leads me to ask the following questions:
- What exactly is returned by
session._get_current_object()
?
- I get that it is an object underlying
session
proxy, but what this underlying object is bound to (if it is not a request and not a thread, if anything I would expect it never to change, for the simple application above)?
What exactly is returned by session._get_current_object()
?
Technically speaking, it is the object referenced in the session
attribute of the top-most element in the LocalStack
instance named _request_ctx_stack
.
This top-most element of that stack is a RequestContext
that is instantiated in Flask.wsgi_app
, which is called for every HTTP request.
The RequestContext
implements methods to push
and pop
itself to and from the local stack _request_ctx_stack
. The push
method also takes care of requesting a new session for the context.
This session is what is made available in the session
proxy; the request, that the RequestContext
has been initialized with, is made available via the request
proxy. These two proxies are only usable inside a request context, i.e. with an active HTTP request being processed.
I get that it is an object underlying session proxy, but what this
underlying object is bound to (if it is not a request and not a
thread, if anything I would expect it never to change, for the simple
application above)?
As outlined above, the request context's session, proxied by the session
local proxy, belongs to the RequestContext
. And it is changing with every request. As documented in Lifetime of the Context, a new context is created for each request, and it creates a new session every time push
is executed.
The id of session._get_current_object()
staying the same between consecutive requests is, probably, due to the new session object being created in the same memory address that the old one from the previous request occupied.
See also: How the Context Works section of the Flask documentation.
Here is a modified code snippet, to illustrate answer by shmee
import threading
from flask import (
Flask,
session,
request
)
app = Flask(__name__)
app.secret_key = 'some random secret key'
@app.route('/')
def index():
print(">>> session <<<")
session_id = id(session)
session_object_id = id(session._get_current_object())
print("ID: {}".format(session_id),
"Same as previous: {}".format(session.get('prev_sess_id', '') == session_id))
print("_get_current_object() ID: {}".format(session_object_id),
"Same as previous: {}".format(session.get('prev_sess_obj_id', '') == session_object_id))
session['prev_sess_id'] = session_id
session['prev_sess_obj_id'] = session_object_id
print("\n>>> request <<<")
request_id = id(request)
request_object_id = id(request._get_current_object())
print("request ID is: {}".format(request_id),
"Same as previous: {}".format(session.get('prev_request_id', '') == request_id))
print("request._get_current_object() ID is: {}".format(id(request._get_current_object())),
"Same as previous: {}".format(session.get('prev_request_obj_id', '') == request_object_id))
session['prev_request_id'] = request_id
session['prev_request_obj_id'] = request_object_id
print("\n>>> thread <<<")
thread_id = threading.current_thread().ident
print("threading.current_thread().ident is: {}".format(threading.current_thread().ident),
"Same as previous: {}".format(session.get('prev_thread', '') == thread_id))
session['prev_thread'] = thread_id
print('-' * 100)
return 'Check the console! ;-)'
The only obscurity left is, indeed, why sometimes session._get_current_object()
remains unchanged between between consecutive requests. And as suggested by shmee (bold is mine), it is:
probably, due to the new session object being created in the same memory address that the old one from the previous request occupied.