前言

我想做 python 开发的人尤其是 django 开发都会有一种经历:进入 python 交互模式 (直接 执行 python 回车) 或者进入 django-shell 调试某功能,然后修改源码,退出交互模式或者 djangoshell,重新进入在吧那些模块一一 import... 问题是什么呢?浪费时间,为啥不像 web 框架那样修改源码自动 reload?

本来我花了 2 个多礼拜一直在做这件事情,其实原理就是封装 ipython 到我的 shell,然后在我的 shell 加这个 autoreload 功能,但是昨晚看 ipython 源码发现:ipython 早已经实现了...

我的系统的实现的源码文件是 /usr/lib64/python2.7/site-packages/IPython/Extensions/ipy_autoreload.py

在 ipython 交互模式实现

和 ipython 版本有关,大于 0.11 这样加载

%load_ext autoreload
%autoreload 2

小于 0.11 的就要这样加载

import ipy_autoreload
%autoreload 2

ipython 交互模式自动加载

你总不像每次进入 ipython 都执行这么 2 句吧,那么可以加到 ipython 的自定义配置里面。因操作系统和 ipython 版本不同,ipython 的用户自定义目录有所不同,增加的配置也 有所不同

首先创建 ipython 个人配置

ipython profile create

gentoo ~/.ipython ipython :0.10.2

配置文件是.ipython/ipy_user_conf.py 添加

import ipy_autoreload
o.autoexec.append('%autoreload 2')

opensuse ~/.config/ipython :0.13

配置文件是~/.config/ipython/profile_default/ipython_config.py

c.InteractiveShellApp.exec_lines.append('%load_ext autoreload')
c.InteractiveShellApp.exec_lines.append('%autoreload 2')

Django shell 的实现

遗憾的是改 django 源码中的 core/management/commands/shell.py, 没有提供自动 reload, 但是当你修改了 ipython 配置 他也是会起作用 (这个操作系统有关,下面我会说不起作用的 geek 方法)

PS: 你还可以使用 django-extensions 中的 shell_plus.py

当你系统有 ipython,bpython 和默认的 python,django shell 选择的顺序

  • 当你系统没有 ipython 和 bpython,那么就会选择默认的 python

  • 上面说的 shell_plus 的遍历列表顺序是 bpython->ipython

  • django 自带的 shell 的遍历顺序是 ipython->bpython

上面说 djangoshell 不起作用怎么办?首先看了 django/ipython 源码 (0.13.2),其实 djangoshell 甚至 django-extensions 里面的 shell_plus 都没有问题,关键是 ipython 的问题

我只说在用户配置里面的外部模块为啥没有正确执行

ipython 进入交互模式的流程

  1. 当调用 ipython,都是通过 IPython.frontend.terminal.ipapp 的 launch_new_instance 函数开始
  2. 通过 IPython.core.shellapp 的 InteractiveShellApp 类里面的 init_code 方法去初始化启动后的加载
  3. 在 init_code 方法会执行 self._run_exec_lines (), 这个就是上面的模块导入执行

djangoshell 进入交互模式的流程

  1. 通过 IPython.frontend.terminal.embed 的 TerminalInteractiveShell 类开始
  2. 在 TerminalInteractiveShell 初始化中,没有执行上面的第三条

解决办法就是暴力修改 IPython/frontend/terminal/interactiveshell.py 源码 给他加上模块初始化的操作

在 326 行开始的地方,这样添加一段 (包含存在的代码帮你你理解在什么位置添加代码,也就是下面的 3-8 行)

self.init_usage(usage)                                                 
self.init_banner(banner1, banner2, display_banner) 
exex_lines = self.config['InteractiveShellApp']['exec_lines']          
for line in exex_lines:                                                
    try:                                                                        
        self.run_cell(line, store_history=False)                       
    except:                                                                     
        pass
#-------------------------------------------------------------------------          
# Overrides of init stages                                                          
#-------------------------------------------------------------------------