Interpretation of Flask source code (1) and flask source code
FlaskIs a lightweight Web application framework written in Python. Flask is only a bridge between Werkezug and Jinja2. The former implements a suitable WSGI application and the latter processes the template. Of course, Flask is also bound to some common standard library packages, such as logging. Everything else is done by extension. I will track the running of a simple FlaskApp and see how request and response are implemented. The following is a simple flask app code that can be accessed by a browser.
#!/usr/bin/env python# encoding: utf-8from flask import Flaskapp = Flask(__name__)@app.route('/')def index(): import pdb;pdb.set_trace() return '
Capture stack calls through pdb.
/Home/steinliber/flask-source-code/route/a. py (18) <module> ()
-> App. run ()
/Home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/flask/app. py (772) run ()
-> Run_simple (host, port, self, ** options)
/Home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/serving. py (692) run_simple ()
-> Inner ()
/Home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/serving. py (657) inner ()
-> Srv. serve_forever ()
/Home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/serving. py (497) serve_forever ()
-> HTTPServer. serve_forever (self)
This is the function called to start the flask server. You can see that because the app is a Flask instance, the app is called. run () will call run () in the Flask class, while the run () method calls run_simple () of werkzeug simply by setting the default value for the host and port ()
Run_simple () function in werkzeug
def run_simple(hostname, port, application, use_reloader=False, use_debugger=False, use_evalex=True, extra_files=None, reloader_interval=1, reloader_type='auto', threaded=False, processes=1, request_handler=None, static_files=None, passthrough_errors=False, ssl_context=None): if use_debugger: from werkzeug.debug import DebuggedApplication application = DebuggedApplication(application, use_evalex) if static_files: from werkzeug.wsgi import SharedDataMiddleware application = SharedDataMiddleware(application, static_files) def log_startup(sock): display_hostname = hostname not in ('', '*') and hostname or 'localhost' if ':' in display_hostname: display_hostname = '[%s]' % display_hostname quit_msg = '(Press CTRL+C to quit)' port = sock.getsockname()[1] _log('info', ' * Running on %s://%s:%d/ %s', ssl_context is None and 'http' or 'https', display_hostname, port, quit_msg) def inner(): try: fd = int(os.environ['WERKZEUG_SERVER_FD']) except (LookupError, ValueError): fd = None srv = make_server(hostname, port, application, threaded, processes, request_handler, passthrough_errors, ssl_context, fd=fd) if fd is None: log_startup(srv.socket) srv.serve_forever()
if use_reloader:
# If we're not running already in the subprocess that is the
# reloader we want to open up a socket early to make sure the
# port is actually available.
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
if port == 0 and not can_open_by_fd:
raise ValueError('Cannot bind to a random port with enabled '
'reloader if the Python interpreter does '
'not support socket opening by fd.')
# Create and destroy a socket so that any exceptions are
# raised before we spawn a separate Python interpreter and
# lose this ability.
address_family = select_ip_version(hostname, port)
s = socket.socket(address_family, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((hostname, port))
if hasattr(s, 'set_inheritable'):
s.set_inheritable(True)
# If we can open the socket by file descriptor, then we can just
# reuse this one and our socket will survive the restarts.
if can_open_by_fd:
os.environ['WERKZEUG_SERVER_FD'] = str(s.fileno())
s.listen(LISTEN_QUEUE)
log_startup(s)
else:
s.close()
from ._reloader import run_with_reloader
run_with_reloader(inner, extra_files, reloader_interval,
reloader_type)
else:
inner()
This is the main function of run_simple (). The first two judgment statements are packaging the debug mode and static files. ShareDataMiddleware is a middleware. Here, it serves to convert the file into a Response form acceptable to the server.
Use_reloader is used to determine whether to restart the server when the app code changes. If it is True, a socket is established. can_open_by_fd is determined by the fromfd feature in the socket, if you can store fd in environment variables for reuse after restart, the socket starts to listen, and then calls run_with_reloader, which also accepts the inner function. we can see that when use_reloader is True or not, the inner function inside the function is called. In the inner function, the key WERKZEUG_SERVER_FD in the Environment stores reusable sockets, if not, set it to None and then call the make_server function. Select the appropriate server based on the process and threads parameters. After obtaining the Server Object, call the run_forever method, the server is started ., Werkzeug provides multiple optional servers. Here is a basic Single-threaded, single-process server.
class BaseWSGIServer(HTTPServer, object): """Simple single-threaded, single-process WSGI server.""" multithread = False multiprocess = False request_queue_size = LISTEN_QUEUE def __init__(self, host, port, app, handler=None, passthrough_errors=False, ssl_context=None, fd=None): if handler is None: handler = WSGIRequestHandler self.address_family = select_ip_version(host, port) if fd is not None: real_sock = socket.fromfd(fd, self.address_family, socket.SOCK_STREAM) port = 0 HTTPServer.__init__(self, (host, int(port)), handler) self.app = app self.passthrough_errors = passthrough_errors self.shutdown_signal = False self.host = host self.port = port # Patch in the original socket. if fd is not None: self.socket.close() self.socket = real_sock self.server_address = self.socket.getsockname() if ssl_context is not None: if isinstance(ssl_context, tuple): ssl_context = load_ssl_context(*ssl_context) if ssl_context == 'adhoc': ssl_context = generate_adhoc_ssl_context() # If we are on Python 2 the return value from socket.fromfd # is an internal socket object but what we need for ssl wrap # is the wrapper around it :( sock = self.socket if PY2 and not isinstance(sock, socket.socket): sock = socket.socket(sock.family, sock.type, sock.proto, sock) self.socket = ssl_context.wrap_socket(sock, server_side=True) self.ssl_context = ssl_context else: self.ssl_context = None def log(self, type, message, *args): _log(type, message, *args) def serve_forever(self): self.shutdown_signal = False try: HTTPServer.serve_forever(self) except KeyboardInterrupt: pass finally: self.server_close() def handle_error(self, request, client_address): if self.passthrough_errors: raise return HTTPServer.handle_error(self, request, client_address) def get_request(self): con, info = self.socket.accept() return con, info
This server inherits the basic HTTPServer, HTTPServer can receive data at the specified port and process the results passed to RequestHandlerClass, specific can see the official documentation https://docs.python.org/2/library/basehttpserver.html
In the code, request_queue_size specifies the maximum number of connections in the Request queue. In the _ init _ function, handler is the request processor. If the parameter is provided, it is set to the default value, the available socket is obtained. ssl_context is mainly used to help implement SSL. In addition, the HTTPServer method is simply rewritten. In serve_forever, The HTTPServer method is called to implement server functions.
In summary, flask is the implementation of a basic server. In this example, you can see how to add multiple functions to a simple server, such as SSL, socket multiplexing, and server overloading, the next step is the implementation of reloader and SSL.