Python 代码块 :session 模式下能否执行受空行影响

比如执行这段代码失败:

#+begin_src python :session
if True:
    1
2
#+end_src
*Python* Buffer 中的内容
if True:
Python 3.6.4 (default, Mar  9 2018, 23:15:03) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
    1
>>> 2
... 
... open('/var/folders/7f/s191h4q97p90374yw15ssrs00000gn/T/babel-YA00Ot/python-DOI8ZG', 'w').write(str(_))
  File "<stdin>", line 3
    2
    ^
SyntaxError: invalid syntax

>>> 
>>> 'org_babel_python_eoe'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_' is not defined
>>> 'org_babel_python_eoe'
>>> 

下面这段代码可以:

#+begin_src python :session
if True:
    1

2
#+end_src

#+RESULTS:
: 2

但 Python 没有这个要求吧,因为第一段代码保存到 foo.py,然后 python foo.py 没有报错。

首先这个是錯的。Python 用的是 if ...: elif ... else: ... 句式。

if True:
    1
else:
2

因為

if True:
    1

是一個合法语句,第二段代码结果的 2 实际是 2 的返回值。

Python 3.6.4 (default, Jan 13 2018, 17:39:32) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> if True:
...     1 # if statement ends here
... 
1
>>> 2
2
>>> ^D

另外 interactive test 还是直接上 REPL 吧,Python 又不是 C 这種要编绎才能运行的。你看你这樣用就出了理解误差。

那是你的理解,1 跟 2 没有关系,我举的例子不好,用这个吧

if True:
    print("True is found")
print("Program ends here")

if 语句没有错:

$ echo '
if True:
    print(1)
print(2)' | python
1
2

应该是 orgmode 在处理空行的时候出了问题。没空行的时候,临时文件的内容是 org_babel_python_eoe,正常应该是最后一个表达式的结果 2

1 个赞

@twlz0ne 这不算是是 org 的问題,因為 org 的 :session 执行 python 代碼块用的是 REPL,而 python 出于某種原因做成了 REPL 中的 if 要換行,batch 不用。

我上面的 REPL 例子中就是要多一个空行來结束 if statement。

3 个赞

所以还是 org 的问题嘛。

@xuchunyang 我简单修复了一下:

(defun my/org-babel-python-evaluate-session
    (session body &optional result-type result-params)
  "Pass BODY to the Python process in SESSION.
If RESULT-TYPE equals `output' then return standard output as a
string.  If RESULT-TYPE equals `value' then return the value of the
last statement in BODY, as elisp."
  (let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5)))
	 (dump-last-value
	  (lambda
	    (tmp-file pp)
	    (mapc
	     (lambda (statement) (insert statement) (funcall send-wait))
	     (if pp
		 (list
		  "import pprint"
		  (format "open('%s', 'w').write(pprint.pformat(_))"
			  (org-babel-process-file-name tmp-file 'noquote)))
	       (list (format "open('%s', 'w').write(str(_))"
			     (org-babel-process-file-name tmp-file
                                                          'noquote)))))))
         ;; ----------- 8< -----------
         (last-indent 0)
         ;; ----------- >8 -----------
	 (input-body (lambda (body)
		       (mapc (lambda (line)
                               ;; ----------- 8< -----------
                               (let ((curr-indent (string-match "[^\s]" line)))
                                 (if curr-indent
                                  (progn
                                    (when (< curr-indent last-indent)
                                      (message "[ob-python] >>> <insert_blank>")
                                      (insert "")
                                      (funcall send-wait))
                                    (setq last-indent curr-indent))
                                  (setq last-indent 0)))
                               (message "[ob-python] >>> %S" line)
                               ;; ----------- >8 -----------
                               (insert line)
                               (funcall send-wait))
			     (split-string body "[\r\n]"))
		       (funcall send-wait)))
         (results
          (pcase result-type
            (`output
	     (let ((body (if (string-match-p ".\n+." body) ; Multiline
			     (let ((tmp-src-file (org-babel-temp-file
						  "python-")))
			       (with-temp-file tmp-src-file (insert body))
			       (format org-babel-python--exec-tmpfile
				       tmp-src-file))
			   body)))
	       (mapconcat
		#'org-trim
		(butlast
		 (org-babel-comint-with-output
		     (session org-babel-python-eoe-indicator t body)
		   (funcall input-body body)
		   (funcall send-wait) (funcall send-wait)
		   (insert org-babel-python-eoe-indicator)
		   (funcall send-wait))
		 2) "\n")))
            (`value
             (let ((tmp-file (org-babel-temp-file "python-")))
               (org-babel-comint-with-output
                   (session org-babel-python-eoe-indicator nil body)
                 (let ((comint-process-echoes nil))
                   (funcall input-body body)
                   (funcall dump-last-value tmp-file
                            (member "pp" result-params))
                   (funcall send-wait) (funcall send-wait)
                   (insert org-babel-python-eoe-indicator)
                   (funcall send-wait)))
               (org-babel-eval-read-file tmp-file))))))
    (unless (string= (substring org-babel-python-eoe-indicator 1 -1) results)
      (org-babel-result-cond result-params
	results
        (org-babel-python-table-or-string results)))))

(advice-add 'org-babel-python-evaluate-session :override 'my/org-babel-python-evaluate-session)
2 个赞

我试了下,得到了正确结果。

开始玩 ob-python 了,下一步考虑 ob-ipython,scimax 不?不如直接入 lpy 的坑吧 :joy:

@twlz0ne 能提交PATCH到Org去么?Org接受少量行数的PATCH。行数多的话,只要签一个电子版PDF的FSF文档就行了。我签好,等了3天就好了。

2 个赞

我已经按要求制作好 PATCH 了,正准备提交。

3 个赞

PATCH 终于被采纳,等了 5 天。

3 个赞

great. 我提交patch经常这样,尤其是大的patch。中间修改好多次。毕竟不是高手。有几次心累,缓几天,再重新拿起来修改。