Flask

Table of Contents

1 Notes

1.1 Flask

  • Flask's design allows more than one application in the same Python process.

1.2 Python

  • Python interpreter is a C program, thus we have the notion of the interpreter stack, which is a virtual stack within the C program, and the "actual" stack, which is the stack of the C program that the Python interpreter is running in.

2 Werkzeug

2.1 Context Locals

2.1.1 Purpose

Python standard library has a concept called "thread-locals", but the WSGI does not guarantee that every request gets its own thread.

Werkzeug provides its own implementation of local data storage called werkzeug.local.

3 Design

There are two different "states" in which code is exectud:

  1. Application setup
    • Application is implicitly available on the module level
    • Starts when the Flask object is instatiated
    • Implicitly ends when the first request comes in
  2. After application setup - request handling

There is also a third state which is sitting between a little bit, e.g. when you're in a shell and messing with the application without any actual request handling going on.

3.1 Application setup

  • The programmer can modify the application object safely
  • No request handling happened so far
  • You have a reference to the application object in order to modify it, there is no magic proxy that can give you a reference to the application object you're currently creating or modifying.

3.2 Request handling

  • While a request is active, the context local objects (flask.request and others) point to the current request
  • Any code can get hold of these objects at any time

4 Application Context

See here for more info.

4.1 Purpose

In the past a bunch of functionality was attached to the request context for lack of a better solution.

5 Deployment

5.1 Gevent

/Gevent is a coroutine-based Python networking library that uses greenlet to porvide a high-level synchronous API on top of the libev event loop.

5.1.1 Greenlets

A greenlet is a notion of micro-thread with no implicit scheduling: that is, coroutines.

.switch()

  • Pause current + yield control flow
  • resume next.switch()

5.1.2 Pros / Cons

5.1.2.1 Pros

Excellent for workloads that are:

  • I/O bound
  • Highly concurrent
5.1.2.2 Cons
  • No parallelism
  • Non-cooperative code will block the entire process:
    • C-extensions –> use pure Python libraries
      • MySQLdb, the C extension driver, is not compatible
    • compute-bound greenlets -> use =gevent.sleep(0)
  • Monkey patching may have confusing implications.

5.1.3 How it works

This is where I learned most of it. GREAT VIDEO!

Greenlet is written in C, and when we talk about the stack here, we mean the C stack, not the Python interpreter stack.

Creating a greenlet –> allocates a struct for the the greenlet. Can think of this holding the state of the greenlet.

  1. When we call gr1.switch(), we store the stack pointer pointing to the start of the stack in which print_red runs in a struct.
  2. Then when we call gr2.switch() from print_red we store the stack pointer pointing to the current place in the stack memory.
  3. We then enter print_blue, and allocate a new struct, storing the pointer pointing to the beginning of this functions stack.
  4. Finally, we call gr1.switch() from print_blue. What happens then is that we copy the current stack of print_blue over to the heap, and reset the stack pointer to where we left of in print_red, resuming execution. This is called stack slicing (and is implemented in Assembly in this case).

Thus, if we would like to head back to print_blue again, we could grab its stack memory from the heap, copy it over to the stack and resume execution! Pretty neat, huh?

from greenlet import greenlet

gr1 = greenlet(print_red)
gr2 = greenlet(print_blue)


def print_red():
    print("red")
    gr2.switch()
    print("red done!")


def print_blue():
    print("blue")
    gr1.switch()
    print("blue done!")
5.1.3.1 Libev
5.1.3.2 Hub greenlet
  • Runs the eventloop
  • One hub greenlet per thread
5.1.3.3 Monkey patching
  • Patches the Python standard library
    • socket –> greenlet.socket
      • This is a non-blocking socket

5.2 uWSGI

5.2.1 IMPORTANT

5.2.1.1 Database connection

If you're using a database connection or something of the sort, it's essential that you run the uWSGI with lazy-apps set to true.

When running uWSGI with multiple processes with a master process, uwsgi initializes the application in the master process and then copies the application over to each worker process. The problem is if you open a database connection when initializing your application, you then have multiple processes sharing the same connection, which will cause errors.

Setting lazy-apps to true will make it so that uWSGI will fork() after having created the app 🡆 each process will have it's own database connection.

6 Appendix A - Glossary

Monkey patching
way for a program to extend or modify supporting system software locally (affecting only the running instance of the program.
Proxy
object functioning as an interface to something else.
Stack
specialized buffer which stores data from the top down.
Stack pointer
small register that stores the address of the last program request in the stack.
Thread locals / thread-local data
global object which is thread-safe and thread-specific.
WSGI
Web Server Interface Gateway, is a specification for a simple and universal interface between web servers and web applications or frameworks for Python.