新手python入门之debug=True自动编译原理

来源:互联网 发布:mac和win7传输文件工具 编辑:程序博客网 时间:2024/06/05 20:20

本人菜鸟一只,大大们若看过后请多多指正(因为自己也是新手,难免有很多错误,请大家指正下哈),当然如果还有像我这样的小菜,咱可以一起交流交流羡慕

大家在编写python程序的时候有没有重复这样一个步骤:   ya ,***(执行py文件后渲染模板出现500,或py执行程序里直接报错),然后找呀找,发现错误啦,怒改之,关闭py再运行,又出现错误,looping~~~,所以这样的过程无疑是繁琐的,tornado提供了autoreload这个模块,从此再也不用重启再运行了。

这里我们利用tornado的那个hello.py和图形化界面的IDE--pyCharm

Hello.py代码:

import tornado.ioloopimport tornado.web class MainHandler(tornado.web.RequestHandler):    def get(self):        self.write("Hello, world")application = tornado.web.Application([    (r"/", MainHandler),], debug = True)if __name__ == "__main__":    application.listen(8888)tornado.ioloop.IOLoop.instance().start()

当然作为菜鸟的我有很多疑问,__name__ == “__main__”是什么?百度翻贴发现,其实这个是为了判断操作者是import这个py还是直接运行这个py,如果是在交互环境import这个py,那么我们永远不会走入if语句的内容,因为__name__的名字已经变成当前的模块名hello了。在这里,我们是先对application先实例化再判断,当然如果我们把一步步在交互换将输入这些代码 #if __name__ == "__main__":前的代码,再加上:

application.listen(8888)

#选择监听端口

tornado.ioloop.IOLoop.instance().start()

#笔者猜这里是类似C++的单列模式singleton,开始初始化instance()

if not hasattr(cls, "_instance"):    cls._instance = cls()return cls._instance

这样我们依旧能再localhost8888下访问到我们的网页。

 

好吧,现在来谈谈application实例化吧,在这里下个断点,debug运行,断了下来,F7进入application构造函数:

def __init__(self, handlers=None, default_host="", transforms=None,                 wsgi=False, **settings):        if transforms is None:            ''' 省略部分代码'''        else:            ''' 省略部分代码'''        if self.settings.get("static_path"):#找到一些熟悉的了,  static_path,handler参数的传入赋值            path = self.settings["static_path"]            handlers = list(handlers or [])            static_url_prefix = settings.get("static_url_prefix",                                             "/static/")            handlers = [                (re.escape(static_url_prefix) + r"(.*)", StaticFileHandler,                 dict(path=path)),                (r"/(favicon\.ico)", StaticFileHandler, dict(path=path)),                (r"/(robots\.txt)", StaticFileHandler, dict(path=path)),            ] + handlers        if handlers: self.add_handlers(".*$", handlers)#关键部分,import了autoreload模块,顾名思义,在start处跟进去        # Automatically reload modified modules        if self.settings.get("debug") and not wsgi:            import autoreload            autoreload.start()

autoreload.py

def start(io_loop=None, check_time=500):    io_loop = io_loop or ioloop.IOLoop.instance()             #start后io_loop也就赋值了    modify_times = {}callback = functools.partial(_reload_on_update, io_loop, modify_times)#partial函数,绑定一部分参数,简化参数的输入#scheduler = ioloop.PeriodicCallback(callback, check_time, io_loop=io_loop)#PeriodicCallback,我猜是定时调用函数callback,参数中check_time=500意味500s调用一次,io_loop = io_loop说明前俩参数是必写的,这个呢函数会帮你赋值scheduler.start()      #跟进去

Ioloop.pyclassPeriodicCallback::

    def start(self):        self._running = True        timeout = time.time() + self.callback_time / 1000.0        #callback_time即传入的check_time=500,/1000变成500ms了        self.io_loop.add_timeout(timeout, self._run)            #进入后发现timeout传进了deadline,我猜是半秒执行一次这个实例对象scheduler(猜错别骂~),


现在我们返回来看看autoreload.py里的_reload_on_update

def _reload_on_update(io_loop, modify_times):    global _reload_attempted    if _reload_attempted:        # We already tried to reload and it didn't work, so don't try again.        return    for module in sys.modules.values():        # 检测内存中已有的模块(import过的)        if not isinstance(module, types.ModuleType): continue        #这里搜索ModuleType类的对象        path = getattr(module, "__file__", None)        #path赋值为当前的模块文件名        if not path: continue        if path.endswith(".pyc") or path.endswith(".pyo"):            path = path[:-1]        #统一变成*.py格式的文件名        try:            modified = os.stat(path).st_mtime·  #st_mtime: time of most recent content modification.把path即当前模块最后修改时间入modified.        except:            continue        if path not in modify_times:            modify_times[path] = modified            Continue        #把这些模块st.mtime信息存入modify_times以便对比发现是否模块做出新的修改        if modify_times[path] != modified:            logging.info("%s modified; restarting server", path)            _reload_attempted = True        #检测到模块文件做出了新的修改后重启服务器:            for fd in io_loop._handlers.keys():                try:                    os.close(fd)                except:                    Pass        #重启前先关闭所有IO事件,关进程了            if hasattr(signal, "setitimer"):        #检查信号中是否设置了计时器                # Clear the alarm signal set by                # ioloop.set_blocking_log_threshold so it doesn't fire                # after the exec.                signal.setitimer(signal.ITIMER_REAL, 0, 0)        #原型signal.setitimer(which, seconds[, interval]),在经过seconds时间后触发which(signal.ITIMER_REAL, signal.ITIMER_VIRTUAL or signal.ITIMER_PROF),并且        #以后每隔interval时间进行fire。经过interval时间后,一个信号会被发送给进程.interval设置为0,表示清除该alarm signal。            try:                os.execv(sys.executable, [sys.executable] + sys.argv)#os.execv:启动新进程,os.spawnv:后台执行程序#用sys.executable即解释器(我这里是pythn.exe,可以自己在交互环境import sys#后查看) sys.argv命令行第一个参数不就是cmd下我们输入python *.py的”*.py”吗#意思就是用python.exe打开这个.py吧            except OSError:                os.spawnv(os.P_NOWAIT, sys.executable,                                   [sys.executable] + sys.argv)                sys.exit(0)#Mac OS X versions prior to 10.6 do not support execv in a process that contains #multiple threads. So using spawnv instead.


对于autoreload.py,大家可以参考下这个https://github.com/tornadoweb/tornado/blob/master/tornado/autoreload.py代码解释很清晰到这里,我已经把debug=True时代码自动编译的原理水了一边了.


过程:

<1>在application实例化时检测有没debug字眼:if self.settings.get("debug") and not wsgi:

<2>有则autoreload.start()

<3>设置回调函数callback = functools.partial(_reload_on_update, io_loop, modify_times) 与 scheduler = ioloop.PeriodicCallback(callback, check_time, io_loop=io_loop)

<4>然后就可以在指定check_time下刷新_reloader_on_update函数。

0 0
原创粉丝点击