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:
- 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
- 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)
- C-extensions –> use pure Python libraries
- 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
.
- 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.
- Then when we call
gr2.switch()
fromprint_red
we store the stack pointer pointing to the current place in the stack memory. - We then enter
print_blue
, and allocate a new struct, storing the pointer pointing to the beginning of this functions stack. - Finally, we call
gr1.switch()
fromprint_blue
. What happens then is that we copy the current stack ofprint_blue
over to the heap, and reset the stack pointer to where we left of inprint_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.