LoginSignup
5
5

More than 5 years have passed since last update.

Emacs + IPythonでmoduleをautoreloadする設定

Posted at

概要

Emacsで(setq python-shell-interpreter "ipython")してる人は,IPythonのconfig.pyをいじってない限り,次のコードをinit.elに書いておくと良い.

init.el
(let* (
       (profile-name "profile_for_emacs")
       (folder-name-getter `(substring (shell-command-to-string (concat "ipython locate profile " ,profile-name)) 0 -1))
       )
    (unless (file-directory-p (eval folder-name-getter))
      (shell-command (concat "ipython profile create " profile-name))
      (let ((ipython-config-folder (eval folder-name-getter)))
        (shell-command (concat "echo \"\n\nc.InteractiveShellApp.extensions.append(\\\"autoreload\\\")\nc.InteractiveShellApp.exec_lines.append(\\\"%autoreload 2\\\")\" >> " ipython-config-folder "/ipython_config.py"))
        )
      )
    (set-variable 'python-shell-interpreter-args (concat "--profile=" profile-name " " python-shell-interpreter-args))
    )

問題定義

みなさん,Emacs使ってますか?

Emacs内でPythonを書いているとき,C-c C-c (python-shell-send-buffer)でコードを実行する機会は多いと思います.
しかし,何も設定していない場合,一度importされたmoduleはインタプリタを閉じるまで再ロードされません.

すなわち,

  1. 複数ファイルに渡るPythonコードを書く.
  2. C-c C-cでバッファの内容をインタプリタに送り,実行する.
  3. 自前のmodule内でエラーが出たので,エラー箇所を修正する
  4. 再び実行する.
  5. 修正したはずなのに,同じエラーが出る.

ということが起こります(起こりました).

これは,2. の段階でimportされたmoduleが,3. で修正されたあとも再importされないことによって起こっています.
この問題を解決しましょう.

解決案

解決案1 Pythonファイルに直接書く

引用元

pythonファイルで,以下のようにimportします:

foo.py
import mymodule
import importlib
importlib.reload(mymodule)

ソースコードの方に,毎回リロードを強制するように書く,という案です.
moduleが多いと大変だし,Emacsの設定不足をソースコードに押し付けているという点でちょっとダサいです.

解決案2 Pythonプロセスを毎回killする

引用元

init.elに以下を追記します:

init.el
;; Run python and pop-up its shell.
;; Kill process to solve the reload modules problem.
(defun my-python-shell-run ()
  (interactive)
  (when (get-buffer-process "*Python*")
     (set-process-query-on-exit-flag (get-buffer-process "*Python*") nil)
     (kill-process (get-buffer-process "*Python*"))
     ;; Uncomment If you want to clean the buffer too.
     ;;(kill-buffer "*Python*")
     ;; Not so fast!
     (sleep-for 0.5))
  (run-python (python-shell-parse-command) nil nil)
  (python-shell-send-buffer)
  ;; Pop new window only if shell isnt visible
  ;; in any frame.
  (unless (get-buffer-window "*Python*" t) 
    (python-shell-switch-to-shell)))

(defun my-python-shell-run-region ()
  (interactive)
  (python-shell-send-region (region-beginning) (region-end))
  (python-shell-switch-to-shell))

(eval-after-load "python"
  '(progn
     (define-key python-mode-map (kbd "C-c C-c") 'my-python-shell-run)
     (define-key python-mode-map (kbd "C-c C-r") 'my-python-shell-run-region)
     (define-key python-mode-map (kbd "C-h f") 'python-eldoc-at-point)))

python-shell-send-bufferを自前で用意して,Pythonコードを実行するタイミングで*python*バッファにCtrl-Dを送り,インタプリタを再起動しています.
問題は解決していますが,複数のコードを順番に実行したいときや,インタプリタに少し書き込んでからバッファを実行したいときに困ってしまいます.

moduleの再インポートがしたいだけなので,もっと副作用の少ない方法が望ましいです.

解決案3 IPythonを使い,%autoreloadする

引用元

インタプリタとしてIPythonを使います.そのために,init.elに以下を追記します:

init.el
(setq python-shell-interpreter "ipython")

C-c C-cを最初にしたときに,インタプリタ上で以下を実行します:

IPython-interpreter
In [1]: %load_ext autoreload
In [2]: %autoreload 2

これで,このバッファが閉じるまではmoduleが自動的にリロードされるようになり,問題は解決します.

これでも良いですが,一度Emacsを閉じるとまたこのコマンドを打たなければならず,やっぱりダサいです.

解決案4 IPython config fileを使い,起動時自動的に%autoreloadする

引用元

解決案3の自動化です.
インタプリタとしてIPythonを使います.

まず,shellで以下のようにconfigファイルを作成します:

bash
$ ipython profile create foo
[ProfileCreate] Generating default config file: u'/home/username/.config/ipython/profile_foo/ipython_config.py'

作成したipython_config.pyの場所を教えてくれるので,そのconfigファイルに以下を追記します:

ipython_config.py
c.InteractiveShellApp.extensions = ['autoreload']
c.InteractiveShellApp.exec_lines = ['%autoreload 2']

最後に,init.elに以下を追記します:

init.el
(setq
   python-shell-interpreter "ipython"
   python-shell-interpreter-args "--profile=foo"
)

この方法では,一度Profileを作ってしまえば,起動時に自動的にそのProfileを読んでくれるので,かなり手間を省くことができます.

これでいいじゃん と思う方もおられるかと思いますが,Emacs教のいち信者としては,Emacsの設定なのにshellを触らなければならないのはダサいです.

理想はやはり,まっさらな環境に最小構成の.emacs.dを持ってきて,チョンとEmacsを起動するとドコドコ必要な設定がインストールされていつものエディタが立ち上がる,という感じではないでしょうか.

提案手法

以上を踏まえて,解決案4をemacs lispだけで書いたのがこちらです.

init.el
(setq python-shell-interpreter "ipython")
(let* (
       (profile-name "profile_for_emacs")
       (folder-name-getter `(substring (shell-command-to-string (concat "ipython locate profile " ,profile-name)) 0 -1))
       )
    (unless (file-directory-p (eval folder-name-getter))
      (shell-command (concat "ipython profile create " profile-name))
      (let ((ipython-config-folder (eval folder-name-getter)))
        (shell-command (concat "echo \"\n\nc.InteractiveShellApp.extensions.append(\\\"autoreload\\\")\nc.InteractiveShellApp.exec_lines.append(\\\"%autoreload 2\\\")\" >> " ipython-config-folder "/ipython_config.py"))
        )
      )
    (set-variable 'python-shell-interpreter-args (concat "--profile=" profile-name " " python-shell-interpreter-args))
    )

やっていることは解決案4とほぼ同じです.
起動時にipython locate profile (profile-name)を実行して,profileが無かったら作ります.
作ったconfigファイルにautoreload設定を直接echoでぶち込みます.

これでだいぶ気が楽になりました.

それでは,よいEmacsライフを.

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5