What object EXACTLY is returned by session._get_cu

2020-05-09 17:21发布

问题:

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:

  1. What exactly is returned by session._get_current_object()?
  2. 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)?

回答1:

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.



回答2:

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.



标签: python flask