diff options
| author | Kyle Meyer | 2024-06-09 13:06:28 -0400 |
|---|---|---|
| committer | Kyle Meyer | 2024-06-09 16:54:38 -0400 |
| commit | 5a125fb5a9736bd3c67cf6ff9acc185d8e2260e2 (patch) | |
| tree | dcadcaa7be32c0edb4087e0d4139368f9409083e /lisp/org/ob-python.el | |
| parent | e1cc2d1f61836e1da08817524999878b639e6761 (diff) | |
| download | emacs-5a125fb5a9736bd3c67cf6ff9acc185d8e2260e2.tar.gz emacs-5a125fb5a9736bd3c67cf6ff9acc185d8e2260e2.zip | |
Update to Org 9.7.3
Diffstat (limited to 'lisp/org/ob-python.el')
| -rw-r--r-- | lisp/org/ob-python.el | 450 |
1 files changed, 281 insertions, 169 deletions
diff --git a/lisp/org/ob-python.el b/lisp/org/ob-python.el index 1a442a5a08f..89cdf4c4795 100644 --- a/lisp/org/ob-python.el +++ b/lisp/org/ob-python.el | |||
| @@ -36,53 +36,70 @@ | |||
| 36 | (require 'org-macs) | 36 | (require 'org-macs) |
| 37 | (require 'python) | 37 | (require 'python) |
| 38 | 38 | ||
| 39 | (declare-function py-shell "ext:python-mode" (&rest args)) | ||
| 40 | (declare-function py-choose-shell "ext:python-mode" (&optional shell)) | ||
| 41 | (declare-function py-shell-send-string "ext:python-mode" (strg &optional process)) | ||
| 42 | |||
| 43 | (defvar org-babel-tangle-lang-exts) | 39 | (defvar org-babel-tangle-lang-exts) |
| 44 | (add-to-list 'org-babel-tangle-lang-exts '("python" . "py")) | 40 | (add-to-list 'org-babel-tangle-lang-exts '("python" . "py")) |
| 45 | 41 | ||
| 46 | (defvar org-babel-default-header-args:python '()) | 42 | (defvar org-babel-default-header-args:python '()) |
| 47 | 43 | ||
| 48 | (defcustom org-babel-python-command "python" | 44 | (defconst org-babel-header-args:python |
| 49 | "Name of the command for executing Python code." | 45 | '((return . :any) |
| 50 | :version "24.4" | 46 | (python . :any) |
| 51 | :package-version '(Org . "8.0") | 47 | (async . ((yes no)))) |
| 48 | "Python-specific header arguments.") | ||
| 49 | |||
| 50 | (defcustom org-babel-python-command 'auto | ||
| 51 | "Command (including arguments) for interactive and non-interactive Python code. | ||
| 52 | When not `auto', it overrides `org-babel-python-command-session' | ||
| 53 | and `org-babel-python-command-nonsession'." | ||
| 54 | :package-version '(Org . "9.7") | ||
| 52 | :group 'org-babel | 55 | :group 'org-babel |
| 53 | :type 'string) | 56 | :type '(choice string (const auto))) |
| 57 | |||
| 58 | (defcustom org-babel-python-command-session 'auto | ||
| 59 | "Command (including arguments) for starting interactive Python sessions. | ||
| 60 | If `auto' (the default), uses the values from | ||
| 61 | `python-shell-interpreter' and `python-shell-interpreter-args'. | ||
| 62 | If `org-babel-python-command' is set, then it overrides this | ||
| 63 | option." | ||
| 64 | :package-version '(Org . "9.7") | ||
| 65 | :group 'org-babel | ||
| 66 | :type '(choice string (const auto))) | ||
| 54 | 67 | ||
| 55 | (defcustom org-babel-python-mode | 68 | (defcustom org-babel-python-command-nonsession "python" |
| 56 | (if (featurep 'python-mode) 'python-mode 'python) | 69 | "Command (including arguments) for executing non-interactive Python code. |
| 57 | "Preferred python mode for use in running python interactively. | 70 | If `org-babel-python-command' is set, then it overrides this option." |
| 58 | This will typically be either `python' or `python-mode'." | 71 | :package-version '(Org . "9.7") |
| 59 | :group 'org-babel | 72 | :group 'org-babel |
| 60 | :version "24.4" | 73 | :type 'string) |
| 61 | :package-version '(Org . "8.0") | ||
| 62 | :type 'symbol) | ||
| 63 | 74 | ||
| 64 | (defcustom org-babel-python-hline-to "None" | 75 | (defcustom org-babel-python-hline-to "None" |
| 65 | "Replace hlines in incoming tables with this when translating to python." | 76 | "Replace hlines in incoming tables with this when translating to python." |
| 66 | :group 'org-babel | 77 | :group 'org-babel |
| 67 | :version "24.4" | ||
| 68 | :package-version '(Org . "8.0") | 78 | :package-version '(Org . "8.0") |
| 69 | :type 'string) | 79 | :type 'string) |
| 70 | 80 | ||
| 71 | (defcustom org-babel-python-None-to 'hline | 81 | (defcustom org-babel-python-None-to 'hline |
| 72 | "Replace `None' in python tables with this before returning." | 82 | "Replace `None' in python tables with this before returning." |
| 73 | :group 'org-babel | 83 | :group 'org-babel |
| 74 | :version "24.4" | ||
| 75 | :package-version '(Org . "8.0") | 84 | :package-version '(Org . "8.0") |
| 76 | :type 'symbol) | 85 | :type 'symbol) |
| 77 | 86 | ||
| 87 | (defun org-babel-python-associate-session (session) | ||
| 88 | "Associate Python code buffer with an Python session. | ||
| 89 | Make SESSION without earmuffs be the Python buffer name." | ||
| 90 | (setq-local python-shell-buffer-name | ||
| 91 | (org-babel-python-without-earmuffs session))) | ||
| 92 | |||
| 78 | (defun org-babel-execute:python (body params) | 93 | (defun org-babel-execute:python (body params) |
| 79 | "Execute a block of Python code with Babel. | 94 | "Execute Python BODY according to PARAMS. |
| 80 | This function is called by `org-babel-execute-src-block'." | 95 | This function is called by `org-babel-execute-src-block'." |
| 81 | (let* ((org-babel-python-command | 96 | (let* ((org-babel-python-command |
| 82 | (or (cdr (assq :python params)) | 97 | (or (cdr (assq :python params)) |
| 83 | org-babel-python-command)) | 98 | org-babel-python-command)) |
| 84 | (session (org-babel-python-initiate-session | 99 | (session (org-babel-python-initiate-session |
| 85 | (cdr (assq :session params)))) | 100 | (cdr (assq :session params)))) |
| 101 | (graphics-file (and (member "graphics" (assq :result-params params)) | ||
| 102 | (org-babel-graphical-output-file params))) | ||
| 86 | (result-params (cdr (assq :result-params params))) | 103 | (result-params (cdr (assq :result-params params))) |
| 87 | (result-type (cdr (assq :result-type params))) | 104 | (result-type (cdr (assq :result-type params))) |
| 88 | (return-val (when (eq result-type 'value) | 105 | (return-val (when (eq result-type 'value) |
| @@ -98,7 +115,7 @@ This function is called by `org-babel-execute-src-block'." | |||
| 98 | (format (if session "\n%s" "\nreturn %s") return-val)))) | 115 | (format (if session "\n%s" "\nreturn %s") return-val)))) |
| 99 | (result (org-babel-python-evaluate | 116 | (result (org-babel-python-evaluate |
| 100 | session full-body result-type | 117 | session full-body result-type |
| 101 | result-params preamble async))) | 118 | result-params preamble async graphics-file))) |
| 102 | (org-babel-reassemble-table | 119 | (org-babel-reassemble-table |
| 103 | result | 120 | result |
| 104 | (org-babel-pick-name (cdr (assq :colname-names params)) | 121 | (org-babel-pick-name (cdr (assq :colname-names params)) |
| @@ -130,8 +147,63 @@ VARS contains resolved variable references." | |||
| 130 | 147 | ||
| 131 | ;; helper functions | 148 | ;; helper functions |
| 132 | 149 | ||
| 150 | (defconst org-babel-python--output-graphics-wrapper "\ | ||
| 151 | import matplotlib.pyplot | ||
| 152 | matplotlib.pyplot.gcf().clear() | ||
| 153 | %s | ||
| 154 | matplotlib.pyplot.savefig('%s')" | ||
| 155 | "Format string for saving Python graphical output. | ||
| 156 | Has two %s escapes, for the Python code to be evaluated, and the | ||
| 157 | file to save the graphics to.") | ||
| 158 | |||
| 159 | (defconst org-babel-python--def-format-value "\ | ||
| 160 | def __org_babel_python_format_value(result, result_file, result_params): | ||
| 161 | with open(result_file, 'w') as f: | ||
| 162 | if 'graphics' in result_params: | ||
| 163 | result.savefig(result_file) | ||
| 164 | elif 'pp' in result_params: | ||
| 165 | import pprint | ||
| 166 | f.write(pprint.pformat(result)) | ||
| 167 | elif 'list' in result_params and isinstance(result, dict): | ||
| 168 | f.write(str(['{} :: {}'.format(k, v) for k, v in result.items()])) | ||
| 169 | else: | ||
| 170 | if not set(result_params).intersection(\ | ||
| 171 | ['scalar', 'verbatim', 'raw']): | ||
| 172 | def dict2table(res): | ||
| 173 | if isinstance(res, dict): | ||
| 174 | return [(k, dict2table(v)) for k, v in res.items()] | ||
| 175 | elif isinstance(res, list) or isinstance(res, tuple): | ||
| 176 | return [dict2table(x) for x in res] | ||
| 177 | else: | ||
| 178 | return res | ||
| 179 | if 'table' in result_params: | ||
| 180 | result = dict2table(result) | ||
| 181 | try: | ||
| 182 | import pandas | ||
| 183 | except ImportError: | ||
| 184 | pass | ||
| 185 | else: | ||
| 186 | if isinstance(result, pandas.DataFrame) and 'table' in result_params: | ||
| 187 | result = [[result.index.name or ''] + list(result.columns)] + \ | ||
| 188 | [None] + [[i] + list(row) for i, row in result.iterrows()] | ||
| 189 | elif isinstance(result, pandas.Series) and 'table' in result_params: | ||
| 190 | result = list(result.items()) | ||
| 191 | try: | ||
| 192 | import numpy | ||
| 193 | except ImportError: | ||
| 194 | pass | ||
| 195 | else: | ||
| 196 | if isinstance(result, numpy.ndarray): | ||
| 197 | if 'table' in result_params: | ||
| 198 | result = result.tolist() | ||
| 199 | else: | ||
| 200 | result = repr(result) | ||
| 201 | f.write(str(result))" | ||
| 202 | "Python function to format value result and save it to file.") | ||
| 203 | |||
| 133 | (defun org-babel-variable-assignments:python (params) | 204 | (defun org-babel-variable-assignments:python (params) |
| 134 | "Return a list of Python statements assigning the block's variables." | 205 | "Return a list of Python statements assigning the block's variables. |
| 206 | The assignments are defined in PARAMS." | ||
| 135 | (mapcar | 207 | (mapcar |
| 136 | (lambda (pair) | 208 | (lambda (pair) |
| 137 | (format "%s=%s" | 209 | (format "%s=%s" |
| @@ -153,9 +225,13 @@ specifying a variable of the same value." | |||
| 153 | 225 | ||
| 154 | (defun org-babel-python-table-or-string (results) | 226 | (defun org-babel-python-table-or-string (results) |
| 155 | "Convert RESULTS into an appropriate elisp value. | 227 | "Convert RESULTS into an appropriate elisp value. |
| 156 | If the results look like a list or tuple, then convert them into an | 228 | If the results look like a list or tuple (but not a dict), then |
| 157 | Emacs-lisp table, otherwise return the results as a string." | 229 | convert them into an Emacs-lisp table. Otherwise return the |
| 158 | (let ((res (org-babel-script-escape results))) | 230 | results as a string." |
| 231 | (let ((res (if (and (> (length results) 0) | ||
| 232 | (string-equal "{" (substring results 0 1))) | ||
| 233 | results ;don't covert dicts to elisp | ||
| 234 | (org-babel-script-escape results)))) | ||
| 159 | (if (listp res) | 235 | (if (listp res) |
| 160 | (mapcar (lambda (el) (if (eq el 'None) | 236 | (mapcar (lambda (el) (if (eq el 'None) |
| 161 | org-babel-python-None-to el)) | 237 | org-babel-python-None-to el)) |
| @@ -169,6 +245,7 @@ Emacs-lisp table, otherwise return the results as a string." | |||
| 169 | (cdr (assoc session org-babel-python-buffers))) | 245 | (cdr (assoc session org-babel-python-buffers))) |
| 170 | 246 | ||
| 171 | (defun org-babel-python-with-earmuffs (session) | 247 | (defun org-babel-python-with-earmuffs (session) |
| 248 | "Return SESSION name as string, ensuring *...* around." | ||
| 172 | (let ((name (if (stringp session) session (format "%s" session)))) | 249 | (let ((name (if (stringp session) session (format "%s" session)))) |
| 173 | (if (and (string= "*" (substring name 0 1)) | 250 | (if (and (string= "*" (substring name 0 1)) |
| 174 | (string= "*" (substring name (- (length name) 1)))) | 251 | (string= "*" (substring name (- (length name) 1)))) |
| @@ -176,74 +253,113 @@ Emacs-lisp table, otherwise return the results as a string." | |||
| 176 | (format "*%s*" name)))) | 253 | (format "*%s*" name)))) |
| 177 | 254 | ||
| 178 | (defun org-babel-python-without-earmuffs (session) | 255 | (defun org-babel-python-without-earmuffs (session) |
| 256 | "Return SESSION name as string, without *...* around." | ||
| 179 | (let ((name (if (stringp session) session (format "%s" session)))) | 257 | (let ((name (if (stringp session) session (format "%s" session)))) |
| 180 | (if (and (string= "*" (substring name 0 1)) | 258 | (if (and (string= "*" (substring name 0 1)) |
| 181 | (string= "*" (substring name (- (length name) 1)))) | 259 | (string= "*" (substring name (- (length name) 1)))) |
| 182 | (substring name 1 (- (length name) 1)) | 260 | (substring name 1 (- (length name) 1)) |
| 183 | name))) | 261 | name))) |
| 184 | 262 | ||
| 185 | (defvar py-which-bufname) | 263 | (defun org-babel-session-buffer:python (session &optional _) |
| 186 | (defvar python-shell-buffer-name) | 264 | "Return session buffer name for SESSION." |
| 265 | (or (org-babel-python-session-buffer session) | ||
| 266 | (org-babel-python-with-earmuffs session))) | ||
| 267 | |||
| 268 | (defun org-babel-python--python-util-comint-end-of-output-p () | ||
| 269 | "Return non-nil if the last prompt matches input prompt. | ||
| 270 | Backport of `python-util-comint-end-of-output-p' to emacs28. To | ||
| 271 | be removed after minimum supported version reaches emacs29." | ||
| 272 | (when-let ((prompt (python-util-comint-last-prompt))) | ||
| 273 | (python-shell-comint-end-of-output-p | ||
| 274 | (buffer-substring-no-properties | ||
| 275 | (car prompt) (cdr prompt))))) | ||
| 276 | |||
| 277 | (defun org-babel-python--command (is-session) | ||
| 278 | "Helper function to return the Python command. | ||
| 279 | This checks `org-babel-python-command', and then | ||
| 280 | `org-babel-python-command-session' (if IS-SESSION) or | ||
| 281 | `org-babel-python-command-nonsession' (if not IS-SESSION). If | ||
| 282 | IS-SESSION, this might return `nil', which means to use | ||
| 283 | `python-shell-calculate-command'." | ||
| 284 | (or (unless (eq org-babel-python-command 'auto) | ||
| 285 | org-babel-python-command) | ||
| 286 | (if is-session | ||
| 287 | (unless (eq org-babel-python-command-session 'auto) | ||
| 288 | org-babel-python-command-session) | ||
| 289 | org-babel-python-command-nonsession))) | ||
| 290 | |||
| 187 | (defvar-local org-babel-python--initialized nil | 291 | (defvar-local org-babel-python--initialized nil |
| 188 | "Flag used to mark that python session has been initialized.") | 292 | "Flag used to mark that python session has been initialized.") |
| 293 | (defun org-babel-python--setup-session () | ||
| 294 | "Babel Python session setup code, to be run once per session. | ||
| 295 | Function should be run from within the Python session buffer. | ||
| 296 | This is often run as a part of `python-shell-first-prompt-hook', | ||
| 297 | unless the Python session was created outside Org." | ||
| 298 | (python-shell-send-string-no-output org-babel-python--def-format-value) | ||
| 299 | (setq-local org-babel-python--initialized t)) | ||
| 189 | (defun org-babel-python-initiate-session-by-key (&optional session) | 300 | (defun org-babel-python-initiate-session-by-key (&optional session) |
| 190 | "Initiate a python session. | 301 | "Initiate a python session. |
| 191 | If there is not a current inferior-process-buffer in SESSION | 302 | If there is not a current inferior-process-buffer matching |
| 192 | then create. Return the initialized session." | 303 | SESSION then create it. If inferior process already |
| 304 | exists (e.g. if it was manually started with `run-python'), make | ||
| 305 | sure it's configured to work with ob-python. If session has | ||
| 306 | already been configured as such, do nothing. Return the | ||
| 307 | initialized session." | ||
| 193 | (save-window-excursion | 308 | (save-window-excursion |
| 194 | (let* ((session (if session (intern session) :default)) | 309 | (let* ((session (if session (intern session) :default)) |
| 195 | (py-buffer (org-babel-python-session-buffer session)) | 310 | (py-buffer (org-babel-session-buffer:python session)) |
| 196 | (cmd (if (member system-type '(cygwin windows-nt ms-dos)) | 311 | (python-shell-buffer-name |
| 197 | (concat org-babel-python-command " -i") | 312 | (org-babel-python-without-earmuffs py-buffer)) |
| 198 | org-babel-python-command))) | 313 | (existing-session-p (comint-check-proc py-buffer)) |
| 199 | (cond | 314 | (cmd (org-babel-python--command t))) |
| 200 | ((eq 'python org-babel-python-mode) ; python.el | 315 | (if cmd |
| 201 | (unless py-buffer | 316 | (let* ((cmd-split (split-string-and-unquote cmd)) |
| 202 | (setq py-buffer (org-babel-python-with-earmuffs session))) | 317 | (python-shell-interpreter (car cmd-split)) |
| 203 | (let ((python-shell-buffer-name | 318 | (python-shell-interpreter-args |
| 204 | (org-babel-python-without-earmuffs py-buffer))) | 319 | (combine-and-quote-strings |
| 205 | (run-python cmd) | 320 | (append (cdr cmd-split) |
| 206 | (with-current-buffer py-buffer | 321 | (when (member system-type |
| 207 | (add-hook | 322 | '(cygwin windows-nt ms-dos)) |
| 208 | 'python-shell-first-prompt-hook | 323 | (list "-i")))))) |
| 209 | (lambda () | 324 | (run-python)) |
| 210 | (setq-local org-babel-python--initialized t) | 325 | (run-python)) |
| 211 | (message "I am running!!!")) | 326 | (with-current-buffer py-buffer |
| 212 | nil 'local)))) | 327 | (if existing-session-p |
| 213 | ((and (eq 'python-mode org-babel-python-mode) | 328 | ;; Session was created outside Org. Assume first prompt |
| 214 | (fboundp 'py-shell)) ; python-mode.el | 329 | ;; already happened; run session setup code directly |
| 215 | (require 'python-mode) | 330 | (unless org-babel-python--initialized |
| 216 | ;; Make sure that py-which-bufname is initialized, as otherwise | 331 | ;; Ensure first prompt. Based on python-tests.el |
| 217 | ;; it will be overwritten the first time a Python buffer is | 332 | ;; (`python-tests-shell-wait-for-prompt') |
| 218 | ;; created. | 333 | (while (not (org-babel-python--python-util-comint-end-of-output-p)) |
| 219 | (py-choose-shell) | 334 | (sit-for 0.1)) |
| 220 | ;; `py-shell' creates a buffer whose name is the value of | 335 | (org-babel-python--setup-session)) |
| 221 | ;; `py-which-bufname' with '*'s at the beginning and end | 336 | ;; Adding to `python-shell-first-prompt-hook' immediately |
| 222 | (let* ((bufname (if (and py-buffer (buffer-live-p py-buffer)) | 337 | ;; after `run-python' should be safe from race conditions, |
| 223 | (replace-regexp-in-string ;; zap surrounding * | 338 | ;; because subprocess output only arrives when Emacs is |
| 224 | "^\\*\\([^*]+\\)\\*$" "\\1" py-buffer) | 339 | ;; waiting (see elisp manual, "Output from Processes") |
| 225 | (concat "Python-" (symbol-name session)))) | 340 | (add-hook |
| 226 | (py-which-bufname bufname)) | 341 | 'python-shell-first-prompt-hook |
| 227 | (setq py-buffer (org-babel-python-with-earmuffs bufname)) | 342 | #'org-babel-python--setup-session |
| 228 | (py-shell nil nil t org-babel-python-command py-buffer nil nil t nil))) | 343 | nil 'local))) |
| 229 | (t | 344 | ;; Wait until Python initializes |
| 230 | (error "No function available for running an inferior Python"))) | 345 | ;; This is more reliable compared to |
| 231 | ;; Wait until Python initializes. | 346 | ;; `org-babel-comint-wait-for-output' as python may emit |
| 232 | (if (eq 'python org-babel-python-mode) ; python.el | 347 | ;; multiple prompts during initialization. |
| 233 | ;; This is more reliable compared to | 348 | (with-current-buffer py-buffer |
| 234 | ;; `org-babel-comint-wait-for-output' as python may emit | 349 | (while (not org-babel-python--initialized) |
| 235 | ;; multiple prompts during initialization. | 350 | (sleep-for 0.010))) |
| 236 | (with-current-buffer py-buffer | ||
| 237 | (while (not org-babel-python--initialized) | ||
| 238 | (sleep-for 0.01))) | ||
| 239 | (org-babel-comint-wait-for-output py-buffer)) | ||
| 240 | (setq org-babel-python-buffers | 351 | (setq org-babel-python-buffers |
| 241 | (cons (cons session py-buffer) | 352 | (cons (cons session py-buffer) |
| 242 | (assq-delete-all session org-babel-python-buffers))) | 353 | (assq-delete-all session org-babel-python-buffers))) |
| 243 | session))) | 354 | session))) |
| 244 | 355 | ||
| 245 | (defun org-babel-python-initiate-session (&optional session _params) | 356 | (defun org-babel-python-initiate-session (&optional session _params) |
| 246 | "Create a session named SESSION according to PARAMS." | 357 | "Initiate Python session named SESSION according to PARAMS. |
| 358 | If there is not a current inferior-process-buffer matching | ||
| 359 | SESSION then create it. If inferior process already | ||
| 360 | exists (e.g. if it was manually started with `run-python'), make | ||
| 361 | sure it's configured to work with ob-python. If session has | ||
| 362 | already been configured as such, do nothing." | ||
| 247 | (unless (string= session "none") | 363 | (unless (string= session "none") |
| 248 | (org-babel-python-session-buffer | 364 | (org-babel-python-session-buffer |
| 249 | (org-babel-python-initiate-session-by-key session)))) | 365 | (org-babel-python-initiate-session-by-key session)))) |
| @@ -251,31 +367,10 @@ then create. Return the initialized session." | |||
| 251 | (defvar org-babel-python-eoe-indicator "org_babel_python_eoe" | 367 | (defvar org-babel-python-eoe-indicator "org_babel_python_eoe" |
| 252 | "A string to indicate that evaluation has completed.") | 368 | "A string to indicate that evaluation has completed.") |
| 253 | 369 | ||
| 254 | (defconst org-babel-python-wrapper-method | ||
| 255 | " | ||
| 256 | def main(): | ||
| 257 | %s | ||
| 258 | |||
| 259 | open('%s', 'w').write( str(main()) )") | ||
| 260 | (defconst org-babel-python-pp-wrapper-method | ||
| 261 | " | ||
| 262 | import pprint | ||
| 263 | def main(): | ||
| 264 | %s | ||
| 265 | |||
| 266 | open('%s', 'w').write( pprint.pformat(main()) )") | ||
| 267 | |||
| 268 | (defconst org-babel-python--exec-tmpfile "\ | ||
| 269 | with open('%s') as __org_babel_python_tmpfile: | ||
| 270 | exec(compile(__org_babel_python_tmpfile.read(), __org_babel_python_tmpfile.name, 'exec'))" | ||
| 271 | "Template for Python session command with output results. | ||
| 272 | |||
| 273 | Has a single %s escape, the tempfile containing the source code | ||
| 274 | to evaluate.") | ||
| 275 | |||
| 276 | (defun org-babel-python-format-session-value | 370 | (defun org-babel-python-format-session-value |
| 277 | (src-file result-file result-params) | 371 | (src-file result-file result-params) |
| 278 | "Return Python code to evaluate SRC-FILE and write result to RESULT-FILE." | 372 | "Return Python code to evaluate SRC-FILE and write result to RESULT-FILE. |
| 373 | RESULT-PARAMS defines the result type." | ||
| 279 | (format "\ | 374 | (format "\ |
| 280 | import ast | 375 | import ast |
| 281 | with open('%s') as __org_babel_python_tmpfile: | 376 | with open('%s') as __org_babel_python_tmpfile: |
| @@ -286,30 +381,25 @@ if isinstance(__org_babel_python_final, ast.Expr): | |||
| 286 | exec(compile(__org_babel_python_ast, '<string>', 'exec')) | 381 | exec(compile(__org_babel_python_ast, '<string>', 'exec')) |
| 287 | __org_babel_python_final = eval(compile(ast.Expression( | 382 | __org_babel_python_final = eval(compile(ast.Expression( |
| 288 | __org_babel_python_final.value), '<string>', 'eval')) | 383 | __org_babel_python_final.value), '<string>', 'eval')) |
| 289 | with open('%s', 'w') as __org_babel_python_tmpfile: | ||
| 290 | if %s: | ||
| 291 | import pprint | ||
| 292 | __org_babel_python_tmpfile.write(pprint.pformat(__org_babel_python_final)) | ||
| 293 | else: | ||
| 294 | __org_babel_python_tmpfile.write(str(__org_babel_python_final)) | ||
| 295 | else: | 384 | else: |
| 296 | exec(compile(__org_babel_python_ast, '<string>', 'exec')) | 385 | exec(compile(__org_babel_python_ast, '<string>', 'exec')) |
| 297 | __org_babel_python_final = None" | 386 | __org_babel_python_final = None |
| 387 | __org_babel_python_format_value(__org_babel_python_final, '%s', %s)" | ||
| 298 | (org-babel-process-file-name src-file 'noquote) | 388 | (org-babel-process-file-name src-file 'noquote) |
| 299 | (org-babel-process-file-name result-file 'noquote) | 389 | (org-babel-process-file-name result-file 'noquote) |
| 300 | (if (member "pp" result-params) "True" "False"))) | 390 | (org-babel-python-var-to-python result-params))) |
| 301 | 391 | ||
| 302 | (defun org-babel-python-evaluate | 392 | (defun org-babel-python-evaluate |
| 303 | (session body &optional result-type result-params preamble async) | 393 | (session body &optional result-type result-params preamble async graphics-file) |
| 304 | "Evaluate BODY as Python code." | 394 | "Evaluate BODY as Python code." |
| 305 | (if session | 395 | (if session |
| 306 | (if async | 396 | (if async |
| 307 | (org-babel-python-async-evaluate-session | 397 | (org-babel-python-async-evaluate-session |
| 308 | session body result-type result-params) | 398 | session body result-type result-params graphics-file) |
| 309 | (org-babel-python-evaluate-session | 399 | (org-babel-python-evaluate-session |
| 310 | session body result-type result-params)) | 400 | session body result-type result-params graphics-file)) |
| 311 | (org-babel-python-evaluate-external-process | 401 | (org-babel-python-evaluate-external-process |
| 312 | body result-type result-params preamble))) | 402 | body result-type result-params preamble graphics-file))) |
| 313 | 403 | ||
| 314 | (defun org-babel-python--shift-right (body &optional count) | 404 | (defun org-babel-python--shift-right (body &optional count) |
| 315 | (with-temp-buffer | 405 | (with-temp-buffer |
| @@ -325,33 +415,40 @@ else: | |||
| 325 | (buffer-string))) | 415 | (buffer-string))) |
| 326 | 416 | ||
| 327 | (defun org-babel-python-evaluate-external-process | 417 | (defun org-babel-python-evaluate-external-process |
| 328 | (body &optional result-type result-params preamble) | 418 | (body &optional result-type result-params preamble graphics-file) |
| 329 | "Evaluate BODY in external python process. | 419 | "Evaluate BODY in external python process. |
| 330 | If RESULT-TYPE equals `output' then return standard output as a | 420 | If RESULT-TYPE equals `output' then return standard output as a |
| 331 | string. If RESULT-TYPE equals `value' then return the value of the | 421 | string. If RESULT-TYPE equals `value' then return the value of |
| 332 | last statement in BODY, as elisp." | 422 | the last statement in BODY, as elisp. If GRAPHICS-FILE is |
| 423 | non-nil, then save graphical results to that file instead." | ||
| 333 | (let ((raw | 424 | (let ((raw |
| 334 | (pcase result-type | 425 | (pcase result-type |
| 335 | (`output (org-babel-eval org-babel-python-command | 426 | (`output (org-babel-eval (org-babel-python--command nil) |
| 336 | (concat preamble (and preamble "\n") | 427 | (concat preamble (and preamble "\n") |
| 337 | body))) | 428 | (if graphics-file |
| 338 | (`value (let ((tmp-file (org-babel-temp-file "python-"))) | 429 | (format org-babel-python--output-graphics-wrapper |
| 339 | (org-babel-eval | 430 | body graphics-file) |
| 340 | org-babel-python-command | 431 | body)))) |
| 432 | (`value (let ((results-file (or graphics-file | ||
| 433 | (org-babel-temp-file "python-")))) | ||
| 434 | (org-babel-eval (org-babel-python--command nil) | ||
| 341 | (concat | 435 | (concat |
| 342 | preamble (and preamble "\n") | 436 | preamble (and preamble "\n") |
| 343 | (format | 437 | (format |
| 344 | (if (member "pp" result-params) | 438 | (concat org-babel-python--def-format-value " |
| 345 | org-babel-python-pp-wrapper-method | 439 | def main(): |
| 346 | org-babel-python-wrapper-method) | 440 | %s |
| 347 | (org-babel-python--shift-right body) | 441 | |
| 348 | (org-babel-process-file-name tmp-file 'noquote)))) | 442 | __org_babel_python_format_value(main(), '%s', %s)") |
| 349 | (org-babel-eval-read-file tmp-file)))))) | 443 | (org-babel-python--shift-right body) |
| 444 | (org-babel-process-file-name results-file 'noquote) | ||
| 445 | (org-babel-python-var-to-python result-params)))) | ||
| 446 | (org-babel-eval-read-file results-file)))))) | ||
| 350 | (org-babel-result-cond result-params | 447 | (org-babel-result-cond result-params |
| 351 | raw | 448 | raw |
| 352 | (org-babel-python-table-or-string (org-trim raw))))) | 449 | (org-babel-python-table-or-string raw)))) |
| 353 | 450 | ||
| 354 | (defun org-babel-python--send-string (session body) | 451 | (defun org-babel-python-send-string (session body) |
| 355 | "Pass BODY to the Python process in SESSION. | 452 | "Pass BODY to the Python process in SESSION. |
| 356 | Return output." | 453 | Return output." |
| 357 | (with-current-buffer session | 454 | (with-current-buffer session |
| @@ -369,48 +466,54 @@ finally: | |||
| 369 | print('%s')" | 466 | print('%s')" |
| 370 | (org-babel-python--shift-right body 4) | 467 | (org-babel-python--shift-right body 4) |
| 371 | org-babel-python-eoe-indicator))) | 468 | org-babel-python-eoe-indicator))) |
| 372 | (if (not (eq 'python-mode org-babel-python-mode)) | 469 | (let ((python-shell-buffer-name |
| 373 | (let ((python-shell-buffer-name | 470 | (org-babel-python-without-earmuffs session))) |
| 374 | (org-babel-python-without-earmuffs session))) | 471 | (python-shell-send-string body)) |
| 375 | (python-shell-send-string body)) | ||
| 376 | (require 'python-mode) | ||
| 377 | (py-shell-send-string body (get-buffer-process session))) | ||
| 378 | ;; same as `python-shell-comint-end-of-output-p' in emacs-25.1+ | 472 | ;; same as `python-shell-comint-end-of-output-p' in emacs-25.1+ |
| 379 | (while (not (string-match | 473 | (while (not (and (python-shell-comint-end-of-output-p string-buffer) |
| 380 | org-babel-python-eoe-indicator | 474 | (string-match |
| 381 | string-buffer)) | 475 | org-babel-python-eoe-indicator |
| 476 | string-buffer))) | ||
| 382 | (accept-process-output (get-buffer-process (current-buffer)))) | 477 | (accept-process-output (get-buffer-process (current-buffer)))) |
| 383 | (org-babel-chomp (substring string-buffer 0 (match-beginning 0)))))) | 478 | (org-babel-chomp (substring string-buffer 0 (match-beginning 0)))))) |
| 384 | 479 | ||
| 385 | (defun org-babel-python-evaluate-session | 480 | (defun org-babel-python-evaluate-session |
| 386 | (session body &optional result-type result-params) | 481 | (session body &optional result-type result-params graphics-file) |
| 387 | "Pass BODY to the Python process in SESSION. | 482 | "Pass BODY to the Python process in SESSION. |
| 388 | If RESULT-TYPE equals `output' then return standard output as a | 483 | If RESULT-TYPE equals `output' then return standard output as a |
| 389 | string. If RESULT-TYPE equals `value' then return the value of the | 484 | string. If RESULT-TYPE equals `value' then return the value of |
| 390 | last statement in BODY, as elisp." | 485 | the last statement in BODY, as elisp. If GRAPHICS-FILE is |
| 486 | non-nil, then save graphical results to that file instead." | ||
| 391 | (let* ((tmp-src-file (org-babel-temp-file "python-")) | 487 | (let* ((tmp-src-file (org-babel-temp-file "python-")) |
| 392 | (results | 488 | (results |
| 393 | (progn | 489 | (progn |
| 394 | (with-temp-file tmp-src-file (insert body)) | 490 | (with-temp-file tmp-src-file |
| 491 | (insert (if (and graphics-file (eq result-type 'output)) | ||
| 492 | (format org-babel-python--output-graphics-wrapper | ||
| 493 | body graphics-file) | ||
| 494 | body))) | ||
| 395 | (pcase result-type | 495 | (pcase result-type |
| 396 | (`output | 496 | (`output |
| 397 | (let ((body (format org-babel-python--exec-tmpfile | 497 | (let ((body (format "\ |
| 498 | with open('%s') as f: | ||
| 499 | exec(compile(f.read(), f.name, 'exec'))" | ||
| 398 | (org-babel-process-file-name | 500 | (org-babel-process-file-name |
| 399 | tmp-src-file 'noquote)))) | 501 | tmp-src-file 'noquote)))) |
| 400 | (org-babel-python--send-string session body))) | 502 | (org-babel-python-send-string session body))) |
| 401 | (`value | 503 | (`value |
| 402 | (let* ((tmp-results-file (org-babel-temp-file "python-")) | 504 | (let* ((results-file (or graphics-file |
| 505 | (org-babel-temp-file "python-"))) | ||
| 403 | (body (org-babel-python-format-session-value | 506 | (body (org-babel-python-format-session-value |
| 404 | tmp-src-file tmp-results-file result-params))) | 507 | tmp-src-file results-file result-params))) |
| 405 | (org-babel-python--send-string session body) | 508 | (org-babel-python-send-string session body) |
| 406 | (sleep-for 0.01) | 509 | (sleep-for 0.010) |
| 407 | (org-babel-eval-read-file tmp-results-file))))))) | 510 | (org-babel-eval-read-file results-file))))))) |
| 408 | (org-babel-result-cond result-params | 511 | (org-babel-result-cond result-params |
| 409 | results | 512 | results |
| 410 | (org-babel-python-table-or-string results)))) | 513 | (org-babel-python-table-or-string results)))) |
| 411 | 514 | ||
| 412 | (defun org-babel-python-read-string (string) | 515 | (defun org-babel-python-read-string (string) |
| 413 | "Strip \\='s from around Python string." | 516 | "Strip \\='s from around Python STRING." |
| 414 | (if (and (string-prefix-p "'" string) | 517 | (if (and (string-prefix-p "'" string) |
| 415 | (string-suffix-p "'" string)) | 518 | (string-suffix-p "'" string)) |
| 416 | (substring string 1 -1) | 519 | (substring string 1 -1) |
| @@ -428,7 +531,7 @@ last statement in BODY, as elisp." | |||
| 428 | (org-babel-python-table-or-string results)))) | 531 | (org-babel-python-table-or-string results)))) |
| 429 | 532 | ||
| 430 | (defun org-babel-python-async-evaluate-session | 533 | (defun org-babel-python-async-evaluate-session |
| 431 | (session body &optional result-type result-params) | 534 | (session body &optional result-type result-params graphics-file) |
| 432 | "Asynchronously evaluate BODY in SESSION. | 535 | "Asynchronously evaluate BODY in SESSION. |
| 433 | Returns a placeholder string for insertion, to later be replaced | 536 | Returns a placeholder string for insertion, to later be replaced |
| 434 | by `org-babel-comint-async-filter'." | 537 | by `org-babel-comint-async-filter'." |
| @@ -436,28 +539,37 @@ by `org-babel-comint-async-filter'." | |||
| 436 | session (current-buffer) | 539 | session (current-buffer) |
| 437 | "ob_comint_async_python_\\(.+\\)_\\(.+\\)" | 540 | "ob_comint_async_python_\\(.+\\)_\\(.+\\)" |
| 438 | 'org-babel-chomp 'org-babel-python-async-value-callback) | 541 | 'org-babel-chomp 'org-babel-python-async-value-callback) |
| 439 | (let ((python-shell-buffer-name (org-babel-python-without-earmuffs session))) | 542 | (pcase result-type |
| 440 | (pcase result-type | 543 | (`output |
| 441 | (`output | 544 | (let ((uuid (org-id-uuid))) |
| 442 | (let ((uuid (md5 (number-to-string (random 100000000))))) | 545 | (with-temp-buffer |
| 443 | (with-temp-buffer | 546 | (insert (format org-babel-python-async-indicator "start" uuid)) |
| 444 | (insert (format org-babel-python-async-indicator "start" uuid)) | 547 | (insert "\n") |
| 445 | (insert "\n") | 548 | (insert (if graphics-file |
| 446 | (insert body) | 549 | (format org-babel-python--output-graphics-wrapper |
| 447 | (insert "\n") | 550 | body graphics-file) |
| 448 | (insert (format org-babel-python-async-indicator "end" uuid)) | 551 | body)) |
| 449 | (python-shell-send-buffer)) | 552 | (insert "\n") |
| 450 | uuid)) | 553 | (insert (format org-babel-python-async-indicator "end" uuid)) |
| 451 | (`value | 554 | (let ((python-shell-buffer-name |
| 452 | (let ((tmp-results-file (org-babel-temp-file "python-")) | 555 | (org-babel-python-without-earmuffs session))) |
| 453 | (tmp-src-file (org-babel-temp-file "python-"))) | 556 | (python-shell-send-buffer))) |
| 454 | (with-temp-file tmp-src-file (insert body)) | 557 | uuid)) |
| 455 | (with-temp-buffer | 558 | (`value |
| 456 | (insert (org-babel-python-format-session-value tmp-src-file tmp-results-file result-params)) | 559 | (let ((results-file (or graphics-file |
| 457 | (insert "\n") | 560 | (org-babel-temp-file "python-"))) |
| 458 | (insert (format org-babel-python-async-indicator "file" tmp-results-file)) | 561 | (tmp-src-file (org-babel-temp-file "python-"))) |
| 459 | (python-shell-send-buffer)) | 562 | (with-temp-file tmp-src-file (insert body)) |
| 460 | tmp-results-file))))) | 563 | (with-temp-buffer |
| 564 | (insert (org-babel-python-format-session-value | ||
| 565 | tmp-src-file results-file result-params)) | ||
| 566 | (insert "\n") | ||
| 567 | (unless graphics-file | ||
| 568 | (insert (format org-babel-python-async-indicator "file" results-file))) | ||
| 569 | (let ((python-shell-buffer-name | ||
| 570 | (org-babel-python-without-earmuffs session))) | ||
| 571 | (python-shell-send-buffer))) | ||
| 572 | results-file)))) | ||
| 461 | 573 | ||
| 462 | (provide 'ob-python) | 574 | (provide 'ob-python) |
| 463 | 575 | ||