What is a Flask

Flask is a Micro framework. The so-called Micro framework is very lightweight. The author defines what Flask should be responsible for (request routing, request processing, response return) and what Flask should not be responsible for (database abstraction, form validation). It’s about not reinventing wheels, and combined with the community’s great libraries, it makes Flask more flexible and customizable.

How does Flask process requests

Flask run up

Write a simple Flask application (main.py)

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index() :
    return 'Hello World'

app.run("127.0.0.1".80, debug=True)
Copy the code

Perform the test

> python main.py
>The curl http://127.0.0.1:80
Hello World
Copy the code

Analysis of the source code

Take a look at the source of the app.run() function

    def run(self, host=None, port=None, debug=None, load_dotenv=True, **options) :.from werkzeug.serving import run_simple
    
    try:
        run_simple(host, port, self, **options)
    finally:
        self._got_first_request = False
Copy the code

The core logic is to execute the run_simple function, and the third argument, self, is the Flask object

Take a look at the Flask class implementation

class Flask: def wsgi_app(self, environ, start_response): CTX = self.request_context(environ) error = None try: try: Response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): Auto_pop (error) def call__(self, environ, start_response): return self.wsgi_app(environ, start_response)Copy the code

When the request comes, the program calls the app, and because __call__ is implemented, the wsgi_app() function is called through that function

Wsgi_app function:

  • Generate request object and request context (wrapped in request_context)
  • Push the generated request context (the environment for this request) onto the stack and store it.
  • Request entry preprocessing (for example before_request), error handling, and request forwarding to response (full_dispatch_request function)

A Local class

The Local class is a class implemented with the Werkzeug library for storing data by class. It supports multi-threaded safe storage.

Using the principle of

The __storage__ structure uses __storage__(dict type) to store data, but is isolated by obtaining the thread ID as the identifier. Each thread reads and writes its own thread’s data

{" thread id1 ": {" stack" : [CTX,]}, "thread id2" : {" stack ": []}.}Copy the code

The actual source

from thread import get_ident
class Local(object) :
    __slots__ = ("__storage__"."__ident_func__")

    def __init__(self) :
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)

    def __getattr__(self, name) :
        }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value) :
        }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

    def __delattr__(self, name) :
        }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)
Copy the code

LocalStack class

LocalStack is a stack class based on the Local class, so it supports thread safety. The actual source

class LocalStack(object) :
    def __init__(self) :
        self._local = Local()

    def push(self, obj) :
        """Pushes a new item to the stack"""
        rv = getattr(self._local, "stack".None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

    def pop(self) :
        """Removes the topmost item from the stack, will return the old value or `None` if the stack was already empty. """
        stack = getattr(self._local, "stack".None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()

    @property
    def top(self) :
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None

Copy the code
  1. The LocalStack creates a Local object in init, where the storage is an empty dictionary
  2. When a thread or process object is passed into a push call, it is determined whether it already exists. Otherwise, a new space (list, as the stack) is created and pushed
  3. When top is called, the top element of the stack is returned
  4. When pop is called, if there is only one element left in the stack, then the stack space is removed. Otherwise, pop is the top element of the stack