Home > Backend Development > Python Tutorial > Analyze the running process of Python Django source code

Analyze the running process of Python Django source code

WBOY
Release: 2023-05-07 12:22:07
forward
1346 people have browsed it

Django running sequence

  • WSGI will continue to monitor requests sent from the client

  • passes through## first #MiddlewarePerform analysis and verification processing

  • Then after

    url distribution and verification

  • view Layer is processed

  • and then goes through

    middleware for analysis and verification processing

  • returns

    responseContent

1. Start

1.1 Command line startup (test server)

Command line conclusion: It is The second step utility.execute() function will be distributed to different classes for processing according to the command line parameters

In manage.py, execute_from_command_line(sys.argv) enters the key code

def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testDjango.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
		---
    execute_from_command_line(sys.argv)
Copy after login

2.In the execute_from_command_line function, its

instantiation ManagementUtility class then executes the utility.execute() function [2.1. This function is
specially used to analyze parameters, such as python manage.py runserver, python manage.py help2.2 which will be additionally added by analyzing
The parameters select the class or function to be used, the class or function corresponds to the class

def execute_from_command_line(argv=None):
    utility = ManagementUtility(argv)
    utility.execute()
Copy after login
in django\core\management\commands

3.From

self.fetch_command(subcommand).run_from_argv(self.argv)[about line 413]3.1
self.fetch_command(subcommand), this function The runserver.Command object is returned (you can view it in depth by yourself), and then the run_from_argv function in the Command parent class is executed

 def execute(self):
			---
        if subcommand == 'help':
			---
        elif subcommand == 'version' or self.argv[1:] == ['--version']:
            sys.stdout.write(django.get_version() + '\n')
        elif self.argv[1:] in (['--help'], ['-h']):
            sys.stdout.write(self.main_help_text() + '\n')
        else:
            self.fetch_command(subcommand).run_from_argv(self.argv)
Copy after login

4. Enter

run_from_argv function self.execute(*args, **cmd_options) to enter 4.1 The current class also has this
execute function, but due to the inheritance relationship (this self also points to the Command class). If the subclass already exists, the function will overwrite the execution. execute is in the subclass Command class (later due to super Will also go to the parent class) [about line 354]

    def run_from_argv(self, argv):
        self._called_from_command_line = True
        parser = self.create_parser(argv[0], argv[1])
        options = parser.parse_args(argv[2:])
        cmd_options = vars(options)

        args = cmd_options.pop('args', ())
        handle_default_options(options)
        try:
            self.execute(*args, **cmd_options)
        except CommandError as e:
			---
Copy after login

5.executeFunction executionoutput = self.handle(*args, * *options)[About line 398] Jump into the handle function of the subclass runserver.Command class5.1 is now located in the
Command class execute in the parent class, because super().execute(*args, **options) #inherited from the parent class

    def handle(self, *args, **options):
        if not settings.DEBUG and not settings.ALLOWED_HOSTS:
            raise CommandError('You must set settings.ALLOWED_HOSTS if DEBUG is False.')
        self.use_ipv6 = options['use_ipv6']
        if self.use_ipv6 and not socket.has_ipv6:
            raise CommandError('Your Python does not support IPv6.')
        self._raw_ipv6 = False
        if not options['addrport']:
        	---
        else:
           	---
        if not self.addr:
            self.addr = self.default_addr_ipv6 if self.use_ipv6 else self.default_addr
            self._raw_ipv6 = self.use_ipv6
        self.run(**options)
Copy after login

6. handle The last line of the function, from self.run(**options) Enter

    def run(self, **options):
        use_reloader = options['use_reloader']

        if use_reloader:
            autoreload.run_with_reloader(self.inner_run, **options)
        else:
            self.inner_run(None, **options)
Copy after login

7. From

def inner_run(self, *args, \*\*options )Execute the run function again

    def inner_run(self, *args, **options):
    		---
        try:
            handler = self.get_handler(*args, **options)
            run(self.addr, int(self.port), handler,
                ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
        except OSError as e:
          	 ---
Copy after login

8. Finally start the service and jump to the run function# of

django.core.servers.basehttp.py ##8.1 httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
This step is particularly important, as it involves a long inheritance relationship, 2. Listening-4.1 This link will introduce

def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
    server_address = (addr, port)
    if threading:
        httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
    else:
        httpd_cls = server_cls
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        httpd.daemon_threads = True
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()
Copy after login
Summary process:

    Parse the parameters provided by running python manage.py, for example: help
  • Load all apps

  • Find the corresponding
  • Command management tool according to the parameters

  • Check port

    ,ipv4 detection,ipv6 detection,whether the port is occupied,thread check

  • orm

    Object check table is created

  • Finally start
  • WSGIServer## in the python Lib library

    #2. Monitoring
Explanation: After WSGI is turned on, it will continuously monitor external requests

Quick reading: The following is more troublesome, The quickest way to understand

the process before listening and reaching the middleware

is to read

1, 12.1 and 132.1 runserver (test server)

1 After .runserver is successfully opened, the key step is

httpd.serve_forever()

, which causes

monitoring, which is an infinite loop

def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
	---
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()
Copy after login
2 .Executed in the serve_forever()

function. When

ready has a value, it means that a request has been sent, and then enters self._handle_request_noblock()

    def serve_forever(self, poll_interval=0.5):
        self.__is_shut_down.clear()
        try:
            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)
                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    if self.__shutdown_request:
                        break
                    if ready:
                        self._handle_request_noblock()

                    self.service_actions()
		---
Copy after login
3. From self._handle_request_noblock()

Normal request will enter

self.process_request(request, client_address)

    def _handle_request_noblock(self):
        try:
            request, client_address = self.get_request()
        except OSError:
            return
        if self.verify_request(request, client_address):
            try:
                self.process_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
            except:
                self.shutdown_request(request)
                raise
        else:
            self.shutdown_request(request)
Copy after login

4.从self.process_request(request, client_address)进入来到了ThreadingMixIn.process_request
4.1 此时,如果没有搞清楚此时的self是谁,就搞不明白为什么进入到ThreadingMixIn.process_request,而不是其它的process_request,这时候就关联到上面提到的httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
4.2 type的用法是动态的创建类,此时httpd_cls 是一个新类,里面分别继承了ThreadingMixIn和server_cls对应得WSGIServer,这时就不难理解为什么找的是ThreadingMixIn.process_request

    def process_request(self, request, client_address):
        """Start a new thread to process the request."""
        t = threading.Thread(target = self.process_request_thread,
                             args = (request, client_address))
        t.daemon = self.daemon_threads
        if not t.daemon and self.block_on_close:
            if self._threads is None:
                self._threads = []
            self._threads.append(t)
        t.start()
Copy after login

5.在def process_request(self, request, client_address)里面的t = threading.Thread(target = self.process_request_thread,args = (request, client_address))实际调用了self.process_request_thread,但是等t.start()才会真正执行

    def process_request_thread(self, request, client_address):
        """Same as in BaseServer but as a thread.

        In addition, exception handling is done here.

        """
        try:
            self.finish_request(request, client_address)
        except Exception:
            self.handle_error(request, client_address)
        finally:
            self.shutdown_request(request)
Copy after login

6.从def process_request_thread(self, request, client_address)进入,self.finish_request(request, client_address),继续完成请求
6.1 这时候又需要回顾之前的代码,因为self.RequestHandlerClass不是已经有的类,而是初始化的时候赋值,其值变为了某个类
6.2 这个过程就在1.启动-8里面的httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6),此时的httpd_cls是type动态创建的,继承了ThreadingMixIn和server_cls对应得WSGIServer,实例化时会执行def __init__方法,其关键执行了self.RequestHandlerClass = RequestHandlerClass

class BaseServer:
    timeout = None

    def __init__(self, server_address, RequestHandlerClass):
        """Constructor.  May be extended, do not override."""
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False
Copy after login
    def finish_request(self, request, client_address):
        self.RequestHandlerClass(request, client_address, self)
        # self.RequestHandlerClass等同于self.WSGIRequestHandler
Copy after login

7.从self.RequestHandlerClass(request, client_address, self),即去WSGIRequestHandler类里面初始化,根据一层层继承关系,只要最老类BaseRequestHandler有初始化方法

class BaseRequestHandler:
    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()
Copy after login

def __init__(self, request, client_address, server):进入self.handle()
8.1 此时的self.handle(),根据继承关系,其就在最小子类WSGIRequestHandler里面

    def handle(self):
        self.close_connection = True
        self.handle_one_request()
        while not self.close_connection:
            self.handle_one_request()
        try:
            self.connection.shutdown(socket.SHUT_WR)
        except (AttributeError, OSError):
            pass
Copy after login

9.从def handle(self)进入self.handle_one_request()

    def handle_one_request(self):
        """Copy of WSGIRequestHandler.handle() but with different ServerHandler"""
        self.raw_requestline = self.rfile.readline(65537)
        if len(self.raw_requestline) > 65536:
            self.requestline = ''
            self.request_version = ''
            self.command = ''
            self.send_error(414)
            return

        if not self.parse_request():  # An error code has been sent, just exit
            return

        handler = ServerHandler(
            self.rfile, self.wfile, self.get_stderr(), self.get_environ()
        )
        handler.request_handler = self      # backpointer for logging & connection closing
        handler.run(self.server.get_app())
Copy after login

10.从def handle_one_request(self)进入handler.run(self.server.get_app())
10.1 注意此时handler为ServerHandler实例化对象,run方法存在它的最大父类BaseHandler里面
10.2 此时handler.run(self.server.get_app())执行了self.server.get_app(),其返回django.contrib.staticfiles.handlers.StaticFilesHandlerhandler.run把其当参数传递了过去

    def run(self, application):
        try:
            self.setup_environ()
            self.result = application(self.environ, self.start_response)
            self.finish_response()
        except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError):

            return
        except:
			---
Copy after login

11.从def run(self, application)进入self.result = application(self.environ, self.start_response),其中applicationdjango.contrib.staticfiles.handlers.StaticFilesHandler
11.1 其中self.application已经初始化了是WSGIHandler

class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler):
    def __init__(self, application):
        self.application = application
        self.base_url = urlparse(self.get_base_url())
        super().__init__()

    def __call__(self, environ, start_response):
        if not self._should_handle(get_path_info(environ)):
            return self.application(environ, start_response)
        return super().__call__(environ, start_response)
Copy after login

12.进入后执行def __call__(self, environ, start_response)方法,进入return self.application(environ, start_response),此时self.application已经初始化了是WSGIHandler
12.1 request = self.request_class(environ)获取到用户请求的url后面就开始配置runserver启动时候加载的url; response = self.get_response(request)获取用户url对应的响应准备开始往视图转

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)
		---
Copy after login

13.进入response = self.get_response(request),结束,再下一步就要开始中间件的进行

    def get_response(self, request):
        set_urlconf(settings.ROOT_URLCONF)
        response = self._middleware_chain(request)
        response._resource_closers.append(request.close)
        if response.status_code >= 400:
            log_response(
                '%s: %s', response.reason_phrase, request.path,
                response=response,
                request=request,
            )
        return response
Copy after login

3.中间件的执行

解释:中间件的执行需要联系着上面运行过程,这个过程是一个递归的过程,下面介绍的五个函数中间件命名规则对应得内容

  • process_request():完成请求对象的创建,但用户访问的网址尚未与网站的路由地址匹配。

  • process_view():完成用户访问的网址与路由地址的匹配,但尚未执行视图函数。

  • process_exception():在执行视图函数的期间发生异常,比如代码异常,主动抛出404异常等。

  • process_response():完成视图函数的执行,但尚未将响应内容返回浏览器

  • process_template_response():默认不执行,在视图函数完成操作后调用,除非视图函数返回的response中有render方法(几乎不会用,可以忽略)

1.递归的进入阶段:循环进行下面的代码(此代码位置django\core\handlers\exception.py

1.1 此处出现process_request()process_response()

        @wraps(get_response)
        def inner(request):
            try:
                response = get_response(request) # 此进入循环
            except Exception as exc:
                response = response_for_exception(request, exc)
            return response
        return inner
Copy after login
Copy after login
    def __call__(self, request):
        # Exit out to async mode, if needed
        if asyncio.iscoroutinefunction(self.get_response):
            return self.__acall__(request)
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request) # 进行中间件的process_request步骤
        response = response or self.get_response(request) # 此进入循环
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response) # 此是递归后执行的
        return response
Copy after login

2.递归的结束准备回传:进行下面的代码(此代码位置django\core\handlers\base.py
2.1 此处出现process_view()process_template_response()process_exception()

进入视图的关键函数:

  • callback, callback_args, callback_kwargs = self.resolve_request(request) # callback即对于视图函数url匹配到对应的view函数

  • for middleware_method in self._view_middleware_view_middleware里面放着所有的process_view()函数(初始化时加载的), process_view()正是在该代码下面的环节循环执行

  • response = wrapped_callback(request, *callback_args, **callback_kwargs) 回调函数传参,并返回试图函数响应。

  • 沿着这个路径连续进入两次,就到了后面讲到的as_view里面(此内容是专门视图处理的前的关键步骤)

  • response = self.process_exception_by_middleware(e, request)对应process_exception()

  • self._template_response_middleware 循环加载模板中间件

 def _get_response(self, request):
        response = None
        callback, callback_args, callback_kwargs = self.resolve_request(request)
        for middleware_method in self._view_middleware:
            response = middleware_method(request, callback, callback_args, callback_kwargs)
            if response:
                break
        if response is None:
            wrapped_callback = self.make_view_atomic(callback) # 找到视图函数
            # If it is an asynchronous view, run it in a subthread.
            if asyncio.iscoroutinefunction(wrapped_callback):
                wrapped_callback = async_to_sync(wrapped_callback)
            try:
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise
        self.check_response(response, callback)
        if hasattr(response, 'render') and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                self.check_response(
                    response,
                    middleware_method,
                    name='%s.process_template_response' % (
                        middleware_method.__self__.__class__.__name__,
                    )
                )
            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise
        return response
Copy after login

3.递归的结束回传:循环进行下面的代码

        @wraps(get_response)
        def inner(request):
            try:
                response = get_response(request) # 此进入循环
            except Exception as exc:
                response = response_for_exception(request, exc)
            return response
        return inner
Copy after login
Copy after login
    def __call__(self, request):
        # Exit out to async mode, if needed
        if asyncio.iscoroutinefunction(self.get_response):
            return self.__acall__(request)
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request) 
        response = response or self.get_response(request) # 此进入循环
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response) # 进行中间件的process_response步骤
        return response
Copy after login

The above is the detailed content of Analyze the running process of Python Django source code. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:yisu.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template