What is WSGI?

WSGI, or Web Server Gateway Interface, is based on the existing CGI standard. WSGI is a Python wrapper around CGI and a specification that defines how Web servers interact with Python applications. WSGI enables Web applications written in Python to interconnect with Web servers. Django, Flask and other major Python Web frameworks implement WSGI

WSGI Web architecture

A server or Web application that complies with the WSGI protocol does not care who sends the data, only that the data is in a certain format and that both sides can process the data from the other side. The interaction process is as follows:

  • The browser sends it for us as the user agentHTTPrequest
  • Request network forwarding to find the corresponding processing server
  • HTTPThe server program will hand over the request toWebThe application processes the request
  • WebAfter the application is processed, the data is handed overHTTPServer software
  • HTTPThe server returns the final result to the browser
  • The browser receives the response and displays it to the user

Introduction of Flask

Flask is a lightweight WSGI Web application framework. It is designed to make getting started quick and easy, and to be able to scale to complex applications. It began as a simple wrapper around Werkzeug and Jinja and has become one of the most popular Python Web application frameworks.

We can easily create a Web application based on Flask, as follows:

from flask import Flask, escape, request
app = Flask(__name__)

@app.route('/')
def hello() :
    name = request.args.get("name"."World")
    return f'Hello, {escape(name)}! '
Copy the code
$ flask run
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Copy the code

Flask source code

Source download: git clone https://github.com/pallets/flask.git in order to analyze the author’s original architectural design, we switch directly to the original branches: git checkout 0.1 below we see the specific Demo of a boot, and analyze the source code

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello() :
    return "Hello, World!"    
app.run()

# * Running on http://localhost:5000/ (Press CTRL+C to quit)
Copy the code

Initialize the

def __init__(self, package_name) :
    self.debug = False      # Whether to enable Debug mode
	self.package_name = package_name    Package name or module name
	self.root_path = _get_package_path(self.package_name)   # app directory
	self.view_functions = {}    Store a dictionary of view functions registered with the @route decorator
    self.error_handlers = {}	A dictionary that stores error exception handling, registered with the @errorHandler decorator
    self.before_request_funcs = []  # preprocessing the list of functions to execute, registered with the @before_request decorator
    self.after_request_funcs = []   List of afterprocessing execution functions, registered using the @after_REQUEST decorator
    # template context
    self.template_context_processors = [_default_template_ctx_processor]
    self.url_map = Map()    # URL mapping
    if self.static_path is not None:    # static file
        self.url_map.add(Rule(self.static_path + '/<filename>', build_only=True, endpoint='static'))
        if pkg_resources is not None:
            target = (self.package_name, 'static')
        else:
            target = os.path.join(self.root_path, 'static')
        self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
            self.static_path: target
        })
    Initialize the JinJa2 template environment
    self.jinja_env = Environment(loader=self.create_jinja_loader(), **self.jinja_options)
    self.jinja_env.globals.update(url_for=url_for, get_flashed_messages=get_flashed_messages)
Copy the code

Start the process

Flask projects are started from the app.run() portal with the following code:

def run(self, host='localhost', port=5000, **options) :
        """ start local development server, debug=True, code hot deployment flag :param host: listening IP address :param port: listening port, default 5000 :param options: running other important parameters related to """
        from werkzeug import run_simple
        if 'debug' in options:
            self.debug = options.pop('debug')
        options.setdefault('use_reloader', self.debug)
        options.setdefault('use_debugger', self.debug)
        return run_simple(host, port, self, **options)
Copy the code

The app.run() method mainly calls werkzeug.run_simple to start the service.

def run_simple(hostname, port, application, use_reloader=False,
               use_debugger=False, use_evalex=True,
               extra_files=None, reloader_interval=1, threaded=False,
               processes=1, request_handler=None, static_files=None,
               passthrough_errors=False, ssl_context=None) :
    if use_debugger:	Whether to start in WerkZeug debug mode
        from werkzeug.debug import DebuggedApplication
        application = DebuggedApplication(application, use_evalex)
    if static_files:	Static files are encapsulated as services
        from werkzeug.wsgi import SharedDataMiddleware
        application = SharedDataMiddleware(application, static_files)

    def inner() :	Start the core method
        make_server(hostname, port, application, threaded, processes, request_handler, passthrough_errors, ssl_context).serve_forever()

    if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': display_hostname = hostname ! =The '*' and hostname or 'localhost'
        if ':' in display_hostname:
            display_hostname = '[%s]' % display_hostname
        _log('info'.' * Running on %s://%s:%d/', ssl_context is None and 'http' or 'https', display_hostname, port)
    if use_reloader:	Whether the service is hot updated
        test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        test_socket.bind((hostname, port))
        test_socket.close()
        run_with_reloader(inner, extra_files, reloader_interval)
    else:
        inner()
Copy the code

The inner() method in werkzeug.run_simple encapsulates the core logic of make_server().

def make_server(host, port, app=None, threaded=False, processes=1,
                request_handler=None, passthrough_errors=False,
                ssl_context=None) :
    if threaded and processes > 1:
        raise ValueError("cannot have a multithreaded and multi process server.")
    elif threaded:
        return ThreadedWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context)
    elif processes > 1:
        return ForkingWSGIServer(host, port, app, processes, request_handler, passthrough_errors, ssl_context)
    else:
        return BaseWSGIServer(host, port, app, request_handler,  passthrough_errors, ssl_context)
Copy the code

The make_server() function creates WSGI servers based on the number of threads or processes. By default, the BaseWSGIServer service is created. Next, let’s look at the implementation logic of BaseWSGIServer, ThreadedWSGIServer and ForkingWSGIServer. The specific code is as follows:

class BaseWSGIServer(HTTPServer, object) :
    """Simple single-threaded, single-process WSGI server."""
    multithread = False
    multiprocess = False.class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer) :
    """A WSGI server that does threading."""
    multithread = True

class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer) :
    """A WSGI server that does forking."""
    multiprocess = True.Copy the code

The inheritance relationship of the three service classes is as follows:

Open the start_server() method of BaseWSGIServer

def serve_forever(self) :
    try:
        HTTPServer.serve_forever(self)
   	except KeyboardInterrupt:
    	pass
Copy the code

As you can see, the entire startup process ends up using the HTTPServer class interface from Python’s standard library. HTTPServer is a subclass of SocketServer. TCPServer. Code similar to the following can be implemented:

# -*- coding: utf-8 -
try:
    # Python2 logic
    import BaseHTTPServer
    import CGIHTTPServer
    server = BaseHTTPServer.HTTPServer(('127.0.0.1'.8000), CGIHTTPServer.CGIHTTPRequestHandler)
except:
    # Python3 logic
    from http import server
    server = server.HTTPServer(('127.0.0.1'.8000), server.CGIHTTPRequestHandler)
server.serve_forever()

Copy the code

At this point, the service is started and the process is as follows:

Routing registered

@app.route('/')
def index() :
	pass
Copy the code

The above is a Flask registered Demo. Routes are registered by @app.route decorator for URL and view functions. The specific code is as follows:

def route(self, rule, **options) :
    def decorator(f) :
        self.add_url_rule(rule, f.__name__, **options)
        self.view_functions[f.__name__] = f
        return f
    return decorator
Copy the code
def add_url_rule(self, rule, endpoint, **options) :
    options['endpoint'] = endpoint
    options.setdefault('methods', ('GET',))
    self.url_map.add(Rule(rule, **options))
Copy the code

Template rendering

from flask import render_template

@app.route('/hello/')
def hello(name=None) :
    return render_template('hello.html', name=name)
Copy the code
def render_template(template_name, **context) :
    current_app.update_template_context(context)
    return current_app.jinja_env.get_template(template_name).render(context)
Copy the code

The logic is simple, find the file named template_name in the templates folder and render it. Where current_app and jinja_env are both initialized in flask.init ()

What is Werkzeug?

Etymology: werk (” work “), zeug (” stuff “)

Werkzeug is a comprehensive WSGI Web application library. Originally a simple collection of utilities for WSGI applications, it has become one of the most advanced WSGI utility libraries available.

We can easily create WSGI Applications based on Werkzeug as follows:

from werkzeug.wrappers import Request, Response

@Request.application
def application(request) :
    return Response("Hello, World!")

if __name__ == "__main__":
    from werkzeug.serving import run_simple
    run_simple("localhost".5000, application)
Copy the code

What is Jinja?

Jinja is a fast, expressive, extensible templating engine. Special placeholders in the template allow you to write code similar to Python syntax and then pass data to the template to render the final document.

Let’s look at an example of using template rendering

from flask import render_template

@app.route('/hello/')
def hello(name=None) :
    return render_template('hello.html', name=name)
Copy the code

Create a templates directory at the root of your project and create a hello.html file

/templates
    /hello.html
Copy the code

Hello.html is as follows:

<! doctypehtml>
<title>Hello Web Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello, Hello!</h1>
{% endif %}
Copy the code

Render_template is used to render the hello. HTML template file.

conclusion

Flask early versions encapsulated the Werkzeug and Jinja libraries, providing WEB framework services as a decorator, which was relatively simple and concise. The entire service flow revolves around the flask.run () method to start the service.

reference

  • palletsprojects.com/p/flask/
  • flask.palletsprojects.com/en/2.0.x/
  • werkzeug.palletsprojects.com/en/2.0.x/
  • jinja.palletsprojects.com/en/3.0.x/

❤️❤️❤️ Every love from readers is my motivation! I’m Li 31. Thank you for your likes, favorites and comments. See you next time!