diff options
| author | Bastien Guerry | 2020-12-13 13:44:15 +0100 |
|---|---|---|
| committer | Bastien Guerry | 2020-12-13 13:44:15 +0100 |
| commit | f22856a5c54d99867cd24c08a14bbda23d5c6229 (patch) | |
| tree | b6bd688963531eccb8b9d18195df0edfc34ba59d /lisp/org/ob-python.el | |
| parent | 6aa9fe3e1b4052b2acde86404a90e35893ebfa00 (diff) | |
| download | emacs-f22856a5c54d99867cd24c08a14bbda23d5c6229.tar.gz emacs-f22856a5c54d99867cd24c08a14bbda23d5c6229.zip | |
Update to Org 9.4.1
Diffstat (limited to 'lisp/org/ob-python.el')
| -rw-r--r-- | lisp/org/ob-python.el | 219 |
1 files changed, 119 insertions, 100 deletions
diff --git a/lisp/org/ob-python.el b/lisp/org/ob-python.el index 823f6e63d57..ffb8ee855ef 100644 --- a/lisp/org/ob-python.el +++ b/lisp/org/ob-python.el | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | ;; Authors: Eric Schulte | 5 | ;; Authors: Eric Schulte |
| 6 | ;; Dan Davison | 6 | ;; Dan Davison |
| 7 | ;; Maintainer: Jack Kamm <jackkamm@gmail.com> | ||
| 7 | ;; Keywords: literate programming, reproducible research | 8 | ;; Keywords: literate programming, reproducible research |
| 8 | ;; Homepage: https://orgmode.org | 9 | ;; Homepage: https://orgmode.org |
| 9 | 10 | ||
| @@ -29,10 +30,11 @@ | |||
| 29 | ;;; Code: | 30 | ;;; Code: |
| 30 | (require 'ob) | 31 | (require 'ob) |
| 31 | (require 'org-macs) | 32 | (require 'org-macs) |
| 33 | (require 'python) | ||
| 32 | 34 | ||
| 33 | (declare-function py-shell "ext:python-mode" (&optional argprompt)) | 35 | (declare-function py-shell "ext:python-mode" (&rest args)) |
| 34 | (declare-function py-toggle-shells "ext:python-mode" (arg)) | 36 | (declare-function py-toggle-shells "ext:python-mode" (arg)) |
| 35 | (declare-function run-python "ext:python" (&optional cmd dedicated show)) | 37 | (declare-function py-shell-send-string "ext:python-mode" (strg &optional process)) |
| 36 | 38 | ||
| 37 | (defvar org-babel-tangle-lang-exts) | 39 | (defvar org-babel-tangle-lang-exts) |
| 38 | (add-to-list 'org-babel-tangle-lang-exts '("python" . "py")) | 40 | (add-to-list 'org-babel-tangle-lang-exts '("python" . "py")) |
| @@ -104,7 +106,8 @@ VARS contains resolved variable references." | |||
| 104 | (org-babel-comint-in-buffer session | 106 | (org-babel-comint-in-buffer session |
| 105 | (mapc (lambda (var) | 107 | (mapc (lambda (var) |
| 106 | (end-of-line 1) (insert var) (comint-send-input) | 108 | (end-of-line 1) (insert var) (comint-send-input) |
| 107 | (org-babel-comint-wait-for-output session)) var-lines)) | 109 | (org-babel-comint-wait-for-output session)) |
| 110 | var-lines)) | ||
| 108 | session)) | 111 | session)) |
| 109 | 112 | ||
| 110 | (defun org-babel-load-session:python (session body params) | 113 | (defun org-babel-load-session:python (session body params) |
| @@ -177,42 +180,40 @@ Emacs-lisp table, otherwise return the results as a string." | |||
| 177 | "Initiate a python session. | 180 | "Initiate a python session. |
| 178 | If there is not a current inferior-process-buffer in SESSION | 181 | If there is not a current inferior-process-buffer in SESSION |
| 179 | then create. Return the initialized session." | 182 | then create. Return the initialized session." |
| 180 | (require org-babel-python-mode) | ||
| 181 | (save-window-excursion | 183 | (save-window-excursion |
| 182 | (let* ((session (if session (intern session) :default)) | 184 | (let* ((session (if session (intern session) :default)) |
| 183 | (python-buffer (org-babel-python-session-buffer session)) | 185 | (py-buffer (org-babel-python-session-buffer session)) |
| 184 | (cmd (if (member system-type '(cygwin windows-nt ms-dos)) | 186 | (cmd (if (member system-type '(cygwin windows-nt ms-dos)) |
| 185 | (concat org-babel-python-command " -i") | 187 | (concat org-babel-python-command " -i") |
| 186 | org-babel-python-command))) | 188 | org-babel-python-command))) |
| 187 | (cond | 189 | (cond |
| 188 | ((and (eq 'python org-babel-python-mode) | 190 | ((eq 'python org-babel-python-mode) ; python.el |
| 189 | (fboundp 'run-python)) ; python.el | 191 | (unless py-buffer |
| 190 | (if (not (version< "24.1" emacs-version)) | 192 | (setq py-buffer (org-babel-python-with-earmuffs session))) |
| 191 | (run-python cmd) | 193 | (let ((python-shell-buffer-name |
| 192 | (unless python-buffer | 194 | (org-babel-python-without-earmuffs py-buffer))) |
| 193 | (setq python-buffer (org-babel-python-with-earmuffs session))) | 195 | (run-python cmd) |
| 194 | (let ((python-shell-buffer-name | 196 | (sleep-for 0 10))) |
| 195 | (org-babel-python-without-earmuffs python-buffer))) | ||
| 196 | (run-python cmd)))) | ||
| 197 | ((and (eq 'python-mode org-babel-python-mode) | 197 | ((and (eq 'python-mode org-babel-python-mode) |
| 198 | (fboundp 'py-shell)) ; python-mode.el | 198 | (fboundp 'py-shell)) ; python-mode.el |
| 199 | (require 'python-mode) | ||
| 199 | ;; Make sure that py-which-bufname is initialized, as otherwise | 200 | ;; Make sure that py-which-bufname is initialized, as otherwise |
| 200 | ;; it will be overwritten the first time a Python buffer is | 201 | ;; it will be overwritten the first time a Python buffer is |
| 201 | ;; created. | 202 | ;; created. |
| 202 | (py-toggle-shells py-default-interpreter) | 203 | (py-toggle-shells py-default-interpreter) |
| 203 | ;; `py-shell' creates a buffer whose name is the value of | 204 | ;; `py-shell' creates a buffer whose name is the value of |
| 204 | ;; `py-which-bufname' with '*'s at the beginning and end | 205 | ;; `py-which-bufname' with '*'s at the beginning and end |
| 205 | (let* ((bufname (if (and python-buffer (buffer-live-p python-buffer)) | 206 | (let* ((bufname (if (and py-buffer (buffer-live-p py-buffer)) |
| 206 | (replace-regexp-in-string ;; zap surrounding * | 207 | (replace-regexp-in-string ;; zap surrounding * |
| 207 | "^\\*\\([^*]+\\)\\*$" "\\1" python-buffer) | 208 | "^\\*\\([^*]+\\)\\*$" "\\1" py-buffer) |
| 208 | (concat "Python-" (symbol-name session)))) | 209 | (concat "Python-" (symbol-name session)))) |
| 209 | (py-which-bufname bufname)) | 210 | (py-which-bufname bufname)) |
| 210 | (py-shell) | 211 | (setq py-buffer (org-babel-python-with-earmuffs bufname)) |
| 211 | (setq python-buffer (org-babel-python-with-earmuffs bufname)))) | 212 | (py-shell nil nil t org-babel-python-command py-buffer nil nil t nil))) |
| 212 | (t | 213 | (t |
| 213 | (error "No function available for running an inferior Python"))) | 214 | (error "No function available for running an inferior Python"))) |
| 214 | (setq org-babel-python-buffers | 215 | (setq org-babel-python-buffers |
| 215 | (cons (cons session python-buffer) | 216 | (cons (cons session py-buffer) |
| 216 | (assq-delete-all session org-babel-python-buffers))) | 217 | (assq-delete-all session org-babel-python-buffers))) |
| 217 | session))) | 218 | session))) |
| 218 | 219 | ||
| @@ -222,8 +223,9 @@ then create. Return the initialized session." | |||
| 222 | (org-babel-python-session-buffer | 223 | (org-babel-python-session-buffer |
| 223 | (org-babel-python-initiate-session-by-key session)))) | 224 | (org-babel-python-initiate-session-by-key session)))) |
| 224 | 225 | ||
| 225 | (defvar org-babel-python-eoe-indicator "'org_babel_python_eoe'" | 226 | (defvar org-babel-python-eoe-indicator "org_babel_python_eoe" |
| 226 | "A string to indicate that evaluation has completed.") | 227 | "A string to indicate that evaluation has completed.") |
| 228 | |||
| 227 | (defconst org-babel-python-wrapper-method | 229 | (defconst org-babel-python-wrapper-method |
| 228 | " | 230 | " |
| 229 | def main(): | 231 | def main(): |
| @@ -238,14 +240,39 @@ def main(): | |||
| 238 | 240 | ||
| 239 | open('%s', 'w').write( pprint.pformat(main()) )") | 241 | open('%s', 'w').write( pprint.pformat(main()) )") |
| 240 | 242 | ||
| 241 | (defconst org-babel-python--exec-tmpfile | 243 | (defconst org-babel-python--exec-tmpfile "\ |
| 242 | (concat | 244 | with open('%s') as __org_babel_python_tmpfile: |
| 243 | "__org_babel_python_fname = '%s'; " | 245 | exec(compile(__org_babel_python_tmpfile.read(), __org_babel_python_tmpfile.name, 'exec'))" |
| 244 | "__org_babel_python_fh = open(__org_babel_python_fname); " | 246 | "Template for Python session command with output results. |
| 245 | "exec(compile(" | 247 | |
| 246 | "__org_babel_python_fh.read(), __org_babel_python_fname, 'exec'" | 248 | Has a single %s escape, the tempfile containing the source code |
| 247 | ")); " | 249 | to evaluate.") |
| 248 | "__org_babel_python_fh.close()")) | 250 | |
| 251 | (defun org-babel-python-format-session-value | ||
| 252 | (src-file result-file result-params) | ||
| 253 | "Return Python code to evaluate SRC-FILE and write result to RESULT-FILE." | ||
| 254 | (format "\ | ||
| 255 | import ast | ||
| 256 | with open('%s') as __org_babel_python_tmpfile: | ||
| 257 | __org_babel_python_ast = ast.parse(__org_babel_python_tmpfile.read()) | ||
| 258 | __org_babel_python_final = __org_babel_python_ast.body[-1] | ||
| 259 | if isinstance(__org_babel_python_final, ast.Expr): | ||
| 260 | __org_babel_python_ast.body = __org_babel_python_ast.body[:-1] | ||
| 261 | exec(compile(__org_babel_python_ast, '<string>', 'exec')) | ||
| 262 | __org_babel_python_final = eval(compile(ast.Expression( | ||
| 263 | __org_babel_python_final.value), '<string>', 'eval')) | ||
| 264 | with open('%s', 'w') as __org_babel_python_tmpfile: | ||
| 265 | if %s: | ||
| 266 | import pprint | ||
| 267 | __org_babel_python_tmpfile.write(pprint.pformat(__org_babel_python_final)) | ||
| 268 | else: | ||
| 269 | __org_babel_python_tmpfile.write(str(__org_babel_python_final)) | ||
| 270 | else: | ||
| 271 | exec(compile(__org_babel_python_ast, '<string>', 'exec')) | ||
| 272 | __org_babel_python_final = None" | ||
| 273 | (org-babel-process-file-name src-file 'noquote) | ||
| 274 | (org-babel-process-file-name result-file 'noquote) | ||
| 275 | (if (member "pp" result-params) "True" "False"))) | ||
| 249 | 276 | ||
| 250 | (defun org-babel-python-evaluate | 277 | (defun org-babel-python-evaluate |
| 251 | (session body &optional result-type result-params preamble) | 278 | (session body &optional result-type result-params preamble) |
| @@ -256,6 +283,19 @@ open('%s', 'w').write( pprint.pformat(main()) )") | |||
| 256 | (org-babel-python-evaluate-external-process | 283 | (org-babel-python-evaluate-external-process |
| 257 | body result-type result-params preamble))) | 284 | body result-type result-params preamble))) |
| 258 | 285 | ||
| 286 | (defun org-babel-python--shift-right (body &optional count) | ||
| 287 | (with-temp-buffer | ||
| 288 | (python-mode) | ||
| 289 | (insert body) | ||
| 290 | (goto-char (point-min)) | ||
| 291 | (while (not (eobp)) | ||
| 292 | (unless (python-syntax-context 'string) | ||
| 293 | (python-indent-shift-right (line-beginning-position) | ||
| 294 | (line-end-position) | ||
| 295 | count)) | ||
| 296 | (forward-line 1)) | ||
| 297 | (buffer-string))) | ||
| 298 | |||
| 259 | (defun org-babel-python-evaluate-external-process | 299 | (defun org-babel-python-evaluate-external-process |
| 260 | (body &optional result-type result-params preamble) | 300 | (body &optional result-type result-params preamble) |
| 261 | "Evaluate BODY in external python process. | 301 | "Evaluate BODY in external python process. |
| @@ -276,89 +316,70 @@ last statement in BODY, as elisp." | |||
| 276 | (if (member "pp" result-params) | 316 | (if (member "pp" result-params) |
| 277 | org-babel-python-pp-wrapper-method | 317 | org-babel-python-pp-wrapper-method |
| 278 | org-babel-python-wrapper-method) | 318 | org-babel-python-wrapper-method) |
| 279 | (mapconcat | 319 | (org-babel-python--shift-right body) |
| 280 | (lambda (line) (format "\t%s" line)) | ||
| 281 | (split-string (org-remove-indentation (org-trim body)) | ||
| 282 | "[\r\n]") | ||
| 283 | "\n") | ||
| 284 | (org-babel-process-file-name tmp-file 'noquote)))) | 320 | (org-babel-process-file-name tmp-file 'noquote)))) |
| 285 | (org-babel-eval-read-file tmp-file)))))) | 321 | (org-babel-eval-read-file tmp-file)))))) |
| 286 | (org-babel-result-cond result-params | 322 | (org-babel-result-cond result-params |
| 287 | raw | 323 | raw |
| 288 | (org-babel-python-table-or-string (org-trim raw))))) | 324 | (org-babel-python-table-or-string (org-trim raw))))) |
| 289 | 325 | ||
| 326 | (defun org-babel-python--send-string (session body) | ||
| 327 | "Pass BODY to the Python process in SESSION. | ||
| 328 | Return output." | ||
| 329 | (with-current-buffer session | ||
| 330 | (let* ((string-buffer "") | ||
| 331 | (comint-output-filter-functions | ||
| 332 | (cons (lambda (text) (setq string-buffer | ||
| 333 | (concat string-buffer text))) | ||
| 334 | comint-output-filter-functions)) | ||
| 335 | (body (format "\ | ||
| 336 | try: | ||
| 337 | %s | ||
| 338 | except: | ||
| 339 | raise | ||
| 340 | finally: | ||
| 341 | print('%s')" | ||
| 342 | (org-babel-python--shift-right body 4) | ||
| 343 | org-babel-python-eoe-indicator))) | ||
| 344 | (if (not (eq 'python-mode org-babel-python-mode)) | ||
| 345 | (let ((python-shell-buffer-name | ||
| 346 | (org-babel-python-without-earmuffs session))) | ||
| 347 | (python-shell-send-string body)) | ||
| 348 | (require 'python-mode) | ||
| 349 | (py-shell-send-string body (get-buffer-process session))) | ||
| 350 | ;; same as `python-shell-comint-end-of-output-p' in emacs-25.1+ | ||
| 351 | (while (not (string-match | ||
| 352 | org-babel-python-eoe-indicator | ||
| 353 | string-buffer)) | ||
| 354 | (accept-process-output (get-buffer-process (current-buffer)))) | ||
| 355 | (org-babel-chomp (substring string-buffer 0 (match-beginning 0)))))) | ||
| 356 | |||
| 290 | (defun org-babel-python-evaluate-session | 357 | (defun org-babel-python-evaluate-session |
| 291 | (session body &optional result-type result-params) | 358 | (session body &optional result-type result-params) |
| 292 | "Pass BODY to the Python process in SESSION. | 359 | "Pass BODY to the Python process in SESSION. |
| 293 | If RESULT-TYPE equals `output' then return standard output as a | 360 | If RESULT-TYPE equals `output' then return standard output as a |
| 294 | string. If RESULT-TYPE equals `value' then return the value of the | 361 | string. If RESULT-TYPE equals `value' then return the value of the |
| 295 | last statement in BODY, as elisp." | 362 | last statement in BODY, as elisp." |
| 296 | (let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5))) | 363 | (let* ((tmp-src-file (org-babel-temp-file "python-")) |
| 297 | (dump-last-value | ||
| 298 | (lambda | ||
| 299 | (tmp-file pp) | ||
| 300 | (mapc | ||
| 301 | (lambda (statement) (insert statement) (funcall send-wait)) | ||
| 302 | (if pp | ||
| 303 | (list | ||
| 304 | "import pprint" | ||
| 305 | (format "open('%s', 'w').write(pprint.pformat(_))" | ||
| 306 | (org-babel-process-file-name tmp-file 'noquote))) | ||
| 307 | (list (format "open('%s', 'w').write(str(_))" | ||
| 308 | (org-babel-process-file-name tmp-file | ||
| 309 | 'noquote))))))) | ||
| 310 | (last-indent 0) | ||
| 311 | (input-body (lambda (body) | ||
| 312 | (dolist (line (split-string body "[\r\n]")) | ||
| 313 | ;; Insert a blank line to end an indent | ||
| 314 | ;; block. | ||
| 315 | (let ((curr-indent (string-match "\\S-" line))) | ||
| 316 | (if curr-indent | ||
| 317 | (progn | ||
| 318 | (when (< curr-indent last-indent) | ||
| 319 | (insert "") | ||
| 320 | (funcall send-wait)) | ||
| 321 | (setq last-indent curr-indent)) | ||
| 322 | (setq last-indent 0))) | ||
| 323 | (insert line) | ||
| 324 | (funcall send-wait)) | ||
| 325 | (funcall send-wait))) | ||
| 326 | (results | 364 | (results |
| 327 | (pcase result-type | 365 | (progn |
| 328 | (`output | 366 | (with-temp-file tmp-src-file (insert body)) |
| 329 | (let ((body (if (string-match-p ".\n+." body) ; Multiline | 367 | (pcase result-type |
| 330 | (let ((tmp-src-file (org-babel-temp-file | 368 | (`output |
| 331 | "python-"))) | 369 | (let ((body (format org-babel-python--exec-tmpfile |
| 332 | (with-temp-file tmp-src-file (insert body)) | 370 | (org-babel-process-file-name |
| 333 | (format org-babel-python--exec-tmpfile | 371 | tmp-src-file 'noquote)))) |
| 334 | tmp-src-file)) | 372 | (org-babel-python--send-string session body))) |
| 335 | body))) | 373 | (`value |
| 336 | (mapconcat | 374 | (let* ((tmp-results-file (org-babel-temp-file "python-")) |
| 337 | #'org-trim | 375 | (body (org-babel-python-format-session-value |
| 338 | (butlast | 376 | tmp-src-file tmp-results-file result-params))) |
| 339 | (org-babel-comint-with-output | 377 | (org-babel-python--send-string session body) |
| 340 | (session org-babel-python-eoe-indicator t body) | 378 | (sleep-for 0 10) |
| 341 | (funcall input-body body) | 379 | (org-babel-eval-read-file tmp-results-file))))))) |
| 342 | (funcall send-wait) (funcall send-wait) | 380 | (org-babel-result-cond result-params |
| 343 | (insert org-babel-python-eoe-indicator) | 381 | results |
| 344 | (funcall send-wait)) | 382 | (org-babel-python-table-or-string results)))) |
| 345 | 2) "\n"))) | ||
| 346 | (`value | ||
| 347 | (let ((tmp-file (org-babel-temp-file "python-"))) | ||
| 348 | (org-babel-comint-with-output | ||
| 349 | (session org-babel-python-eoe-indicator nil body) | ||
| 350 | (let ((comint-process-echoes nil)) | ||
| 351 | (funcall input-body body) | ||
| 352 | (funcall dump-last-value tmp-file | ||
| 353 | (member "pp" result-params)) | ||
| 354 | (funcall send-wait) (funcall send-wait) | ||
| 355 | (insert org-babel-python-eoe-indicator) | ||
| 356 | (funcall send-wait))) | ||
| 357 | (org-babel-eval-read-file tmp-file)))))) | ||
| 358 | (unless (string= (substring org-babel-python-eoe-indicator 1 -1) results) | ||
| 359 | (org-babel-result-cond result-params | ||
| 360 | results | ||
| 361 | (org-babel-python-table-or-string results))))) | ||
| 362 | 383 | ||
| 363 | (defun org-babel-python-read-string (string) | 384 | (defun org-babel-python-read-string (string) |
| 364 | "Strip \\='s from around Python string." | 385 | "Strip \\='s from around Python string." |
| @@ -369,6 +390,4 @@ last statement in BODY, as elisp." | |||
| 369 | 390 | ||
| 370 | (provide 'ob-python) | 391 | (provide 'ob-python) |
| 371 | 392 | ||
| 372 | |||
| 373 | |||
| 374 | ;;; ob-python.el ends here | 393 | ;;; ob-python.el ends here |