Django 4.0.6源码分析:自动重启机制

之前分析了一波Flask的源码,其实DEBUG模式下,也有自动重启的功能,不过没有深究。最近在研究Django框架,同样也有自动重启的功能,这次我们就来研究一下吧。

Ps:Python看源码有个小技巧,可以随时修改源码文件用print来辅助我们边运行边看代码。

当我们用 python manage.py runserver 启动Django项目之后,每次改动保存都会自动进行服务器的重启。
先看下manage.py的代码

def main():

    execute_from_command_line(sys.argv)

if __name__ == '__main__':
    main()
def execute_from_command_line(argv=None):
    utility = ManagementUtility(argv)
    utility.execute()
class ManagementUtility:
    def execute(self):
        try:

            subcommand = self.argv[1]
        except IndexError:
            subcommand = "help"

        if subcommand == "help":
            pass
        else:

            self.fetch_command(subcommand).run_from_argv(self.argv)

    def fetch_command(self, subcommand):
        commands = get_commands()
        try:

            app_name = commands[subcommand]
        except KeyError:
            pass
        if isinstance(app_name, BaseCommand):

            klass = app_name
        else:

            klass = load_command_class(app_name, subcommand)
        return klass

下面先研究下 get_commands 方法

@functools.lru_cache(maxsize=None)
def get_commands():

    commands = {name: "django.core" for name in find_commands(__path__[0])}
    if not settings.configured:
        return commands

def find_commands(management_dir):

    command_dir = os.path.join(management_dir, "commands")

    return [
        name
        for _, name, is_pkg in pkgutil.iter_modules([command_dir])
        if not is_pkg and not name.startswith("_")
    ]

def load_command_class(app_name, name):
    module = import_module("%s.management.commands.%s" % (app_name, name))
    return module.Command()

由上可见,python manage.py runserver 根据 runserver命令,去寻找并构造了 特定的实例来执行具体逻辑。
runserver 对应的实例就是 django.core.management.commands.runserver.Command 实例,下面分析

class Command(BaseCommand):

    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)

发现监测文件改动并自动重启的逻辑在 django/utils/autoreload.py 中,下面重点研究

def run_with_reloader(main_func, *args, **kwargs):

    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
    try:

        if os.environ.get(DJANGO_AUTORELOAD_ENV) == "true":

            reloader = get_reloader()

            logger.info(
                "Watching for file changes with %s", reloader.__class__.__name__
            )
            start_django(reloader, main_func, *args, **kwargs)
        else:

            exit_code = restart_with_reloader()
            sys.exit(exit_code)
    except KeyboardInterrupt:
        pass

def restart_with_reloader():

    new_environ = {**os.environ, DJANGO_AUTORELOAD_ENV: "true"}

    args = get_child_arguments()

    while True:

        p = subprocess.run(args, env=new_environ, close_fds=False)

        if p.returncode != 3:
            return p.returncode

def get_reloader():
    try:
        WatchmanReloader.check_availability()
    except WatchmanUnavailable:

        return StatReloader()
    return WatchmanReloader()

def start_django(reloader, main_func, *args, **kwargs):
    ensure_echo_on()

    main_func = check_errors(main_func)

    django_main_thread = threading.Thread(
        target=main_func, args=args, kwargs=kwargs, name="django-main-thread"
    )

    django_main_thread.daemon = True
    django_main_thread.start()

    while not reloader.should_stop:
        try:

            reloader.run(django_main_thread)
        except WatchmanUnavailable as ex:

            reloader = StatReloader()
            logger.error("Error connecting to Watchman: %s", ex)
            logger.info(
                "Watching for file changes with %s", reloader.__class__.__name__
            )

可以看到 子进程检测文件改动以及自动退出的逻辑在 reloader 实例中,下面我们来看 StatReloader类

class StatReloader(BaseReloader):
    SLEEP_TIME = 1

    def run(self, django_main_thread):
        self.run_loop()

    def run_loop(self):
        ticker = self.tick()
        while not self.should_stop
            try:

                next(ticker)
            except StopIteration:
                break
        self.stop()

    def tick(self):
        mtimes = {}
        while True:

            for filepath, mtime in self.snapshot_files():
                old_time = mtimes.get(filepath)
                mtimes[filepath] = mtime
                if old_time is None:
                    logger.debug("File %s first seen with mtime %s", filepath, mtime)
                    continue
                elif mtime > old_time:
                    logger.debug(
                        "File %s previous mtime: %s, current mtime: %s",
                        filepath,
                        old_time,
                        mtime,
                    )

                    self.notify_file_changed(filepath)

            time.sleep(self.SLEEP_TIME)
            yield

    def notify_file_changed(self, path):
        results = file_changed.send(sender=self, file_path=path)
        logger.debug("%s notified as changed. Signal results: %s.", path, results)
        if not any(res[1] for res in results):

            trigger_reload(path)

def trigger_reload(filename):

    logger.info("%s changed, reloading.", filename)

    sys.exit(3)

总结一下:

PS: 如果我们也想在其他地方实现检测到代码改动自动重启的功能,可以直接把django.utils.autoreload.py 复制出去,然后用autoreload.run_with_reloader() 执行目标函数即可。

Original: https://blog.csdn.net/weixin_37882382/article/details/125874302
Author: 某工程师$
Title: Django 4.0.6源码分析:自动重启机制

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/736821/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • pytest笔记(三)-fixture

    Fixtures 根据官方文档所述, 可以把一个test 分成4个步骤: Arrange Act Assert Cleanup Arrange 就是为我们test准备的,这就意味着…

    Python 2023年9月15日
    053
  • DRF框架 序列化

    DRF(Django REST framework)是构建RESTful风格的Web api的强大而灵活的工具包。它是在Django框架基础之上,进行了二次开发。简称为DRF框架或…

    Python 2023年8月6日
    053
  • 21世纪,推荐你了解一下这12 个Python库

    1、Dash Dash是相对较新的。它是使用纯Python构建数据可视化应用程序的理想选择,因此特别适合处理数据的小伙伴。Dash是Flask,Plotly.js和React.js…

    Python 2023年9月24日
    047
  • pandas模块的基本使用

    numpy能够帮助我们处理数值,但是pandas除了能处理数值之外(基于numpy),还能够帮助我们处理其他类型的数据pandas技术文档:https://pandas.pydat…

    Python 2023年8月22日
    046
  • pandas.DataFrame.iloc函数总结

    pandas.DataFrame.iloc函数 property DataFrame.iloc:基于整数的用于选定所需数据的函数。 函数输入: 单个整数; 整数型list或者arr…

    Python 2023年8月7日
    059
  • pytest单元测试框架(三)

    用例的相关名称1.史诗(项目名称): @allure.epic(“项目名称:码尚教育接口自动化测试项目”)2.特性(模块名称):@allure.featur…

    Python 2023年9月14日
    051
  • 腾讯云部署novel ai (stable-diffusion-webui)

    其实前几天就已经部署好了,图已经产出一斤了x凭借记忆写点记录。 因为纯粹是在凭着记忆写,所以肯定会有遗漏的步骤。 感谢腾讯云的打折,让只拎了个轻薄本到学校的自己还可以继续搞点有意思…

    Python 2023年10月27日
    028
  • DBPack 读写分离功能发布公告

    在 v0.1.0 版本我们发布了分布式事务功能,并提供了读写分离功能预览。在 v0.2.0 这个版本,我们加入了通过 UseDB hint 自定义查询请求路由的功能,并修复了一些 …

    Python 2023年6月12日
    054
  • JUC包(java.util.concurrent)下的常用子类

    文章目录 前言 一、对象锁juc.locks包 二、原子类 三、四个常用工具类 * 3.1 信号量 Semaphore 3.2 CountDownLatch 总结 前言 博主个人社…

    Python 2023年8月2日
    070
  • 【Python 实战基础】Pandas 如何统计某个数据列的空值个数

    一、实战场景 二、主要知识点 文件读写 基础语法 Pandas numpy 三、菜鸟实战 1、创建 python 文件 2、运行结果 实战场景:Pandas 如何统计某个数据列的空…

    Python 2023年8月24日
    067
  • pythonscrapy爬虫安装_Python3爬虫利器:Scrapy的安装

    Scrapy是一个十分强大的爬虫框架,依赖的库比较多,至少需要依赖的库有Twisted 14.0、lxml 3.4和pyOpenSSL 0.14。在不同的平台环境下,它所依赖的库也…

    Python 2023年10月6日
    051
  • Pytest实战

    Pytest测试框架是动态语言Python专用的测试框架,使用起来非常的简单,这主要得易于它的设计,Pytest测试框架具备强大的功能,丰富的第三方插件,以及可扩展性好,可以很好的…

    Python 2023年8月13日
    066
  • Socket 编程

    Socket 编程 一、前行必备 1.1 网络中进程之间如何通信 网络进程间的通信,首要解决的问题是如何唯一标识一个进程,否则通信无从谈起! 在本地可以通过进程 PID 来唯一标识…

    Python 2023年10月18日
    057
  • ROS机械臂 Movelt 学习笔记4 | Move Group 接口 Python

    Python 的使用总是比 C++ 简单许多,Move Group 的 Python 接口更为便捷,也为使用者提供了很多用于操纵机器人和机械臂的函数,能够和 C++ 接口实现相同的…

    Python 2023年10月28日
    031
  • Flask-SQLAlchemy—-自定义查询条件的使用方法—ORM(7)

    文章目录 * – + 0.故事背景 + 1.filter_by()的特殊用法 + 2.如果通过继承,在基类里面定义查询方法 + 3.初始化的模型类的时候先定义一些字段,…

    Python 2023年8月15日
    043
  • pytest自动化测试框架02

    Pytest框架实现一些前后置(固件,夹具)的处理,常见三种 一、setup/teardown,setup_class/teardown_class 为什么需要这些功能? 比如:w…

    Python 2023年9月10日
    038
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球