如何处理 Python 代码块中的 input()

正在学习 Python,在 Org mode 实验代码块,发现不能处理 input

#+begin_src python :session
input("> ")
#+end_src

这本身可以理解,但是有没有人试过另一个执行思路:

  1. 切换到 *Python* Buffer
  2. 执行代码,并且等待执行完成
  3. 返回 Org mode 的 Buffer,插入结果

感觉值得一试,不过我目前没时间尝试。不知道大家有考虑过没?


Emacs SE 上有同样的问题,但它们都拘泥于现有的 Babel 框架,没有我想要的方法。

1 个赞

interactive test 应當是用 REPL 做的工作。

嗯,我之所以有这个想法,就是因为 *Python* (Inferior Python mode)已经支持了 input,Org 执行代码的时候直接利用它就好了。

觉得你可以尝试给 ob-python.el 添加这个功能。

以前讨论过类似的问题:怎样让 output buffer 接受输入?

这样间接实现用户输入是有点繁琐。不过从另一个角度看,用户输入也是相当重要的信息,明确写在文档里,对于过一段时间再来回顾/重现当时的情形还是有用的。

感觉 Shell 代码更有这个需要。

对,ob-shell更需要交互。也许可以写一个

#+begin_src shell :var name=(read-minibuffer "Name: ")
echo $name
#+end_src

#+RESULTS[<2018-04-06 10:08:10> 27a280ff89c19cf5c6685796570366c04a13cbd5]:
: ni

我的意思大概是这样:

(defun foo ()
  (interactive)
  (let ((code (plist-get (cadr (org-element-at-point)) :value)))
    (switch-to-buffer-other-window "*shell*")
    (goto-char (point-max))
    (insert code)
    (recursive-edit)
    (message "Update Org Mode Buffer")))

要是有可能的话,还可以用外部终端(如 Terminal.app)运行代码。

这种方式通常获取结果很困难,除非终端eval函数支持返回结果。继承自comint-mode的REPL也不知道是否支持这种操作。

用 Org Mode 代码块练习书上的代码,Tangle 繁琐且不必要,暂且就这么解决,很简单的代码就已经实现了我的大部分需求。

(defun chunyang-org-babel-execute-python-in-iTerm ()
  (interactive)
  (seq-let (type plist) (org-element-at-point)
    (cond ((not (eq 'src-block type))
           (user-error "Not a src block"))
          ((not (string= "python" (plist-get plist :language)))
           (user-error "Not a Python src block"))
          (t
           (let ((filename (make-temp-file "" nil ".py")))
             (org-babel-tangle '(4) filename)
             (chunyang-mac-iTerm-send-string (concat "python " filename)))))))

看个人习惯。

我更偏向静默的方式使用代码块,把输入参数尽量落到文档上。

如果需要处理 stdin,我希望是这样的:

#+BEGIN_SRC python :results value :preamble "# -*- coding: utf-8 -*-"
foo = input("> ")  # <<< 1
bar = input("> ")  # <<< 1
assert foo == bar

foo = input("> ")  # <<< 1
bar = input("> ")  # <<< 2
assert foo != bar
#+END_SRC

代码发送到解释器之前替换成:

foo = 1
bar = 1
assert foo == bar

foo = 1
bar = 2
assert foo != bar

而原始代码放到 .py 文件中执行仍然可以跟用户交互。

请教这步具体该怎么做呢?这个应该和 Org-mode 没有很大的关系,假设进程一直处于运行状态,process-send-string 多个语句时(不一定要有输入,也可以是比较耗时的语句),怎么判断语句都执行完毕并获得结果?

我初步想到了两种方法:Process Filter Functions 和 REPL buffer 中的 after-change-functions,但是感觉都不太好?前者有点理解不透,有没有更具体一点的例子可以参考一下?后者有时会返回不全。还有无其它可用的方法?