-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathipython-shell-send.el
178 lines (161 loc) · 7.45 KB
/
ipython-shell-send.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
;;; ipython-shell-send.el --- Send code (including magics) to ipython shell -*- lexical-binding: t; -*-
;; Copyright (C) 2017 Jack Kamm
;; Author: Jack Kamm <[email protected]>
;; Version: 1.1.1
;; Package-Requires: ((emacs "24"))
;; Keywords: tools, processes
;; URL: https://github.com/jackkamm/ipython-shell-send-el
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This is a package for sending code to the IPython interpreter.
;; It provides functionality similar to the `python-shell-send-*'
;; functions in python.el, but is able to send code regions
;; containing IPython magic (such as `!ls' or `%timeit'),
;; whereas python.el only has limited support for this.
;; In addition, the final result of executed ipython regions
;; are printed, as in Jupyter cell evaluation.
;;
;; The functions provided by ipython-shell-send are
;; `ipython-shell-send-region', `ipython-shell-send-buffer',
;; and `ipython-shell-send-defun'. They are essentially equivalent
;; to their `python-shell-send-*' equivalents in `python.el',
;; except better able to handle IPython magic.
;;
;; Note to use the ipython-shell-send, you must make sure
;; to start an IPython shell when calling `run-python'.
;;; Code:
(require 'python)
(defun ipython-shell-send--save-temp-file (string)
"Send STRING to temp file with .ipy suffix.
Returns the tempfile name."
(let* ((temporary-file-directory
(if (file-remote-p default-directory)
(concat (file-remote-p default-directory) "/tmp")
temporary-file-directory))
(temp-file-name (make-temp-file "ipy" nil ".ipy"))
(coding-system-for-write (python-info-encoding)))
(with-temp-file temp-file-name
(insert string)
(delete-trailing-whitespace))
temp-file-name))
(defun ipython-shell-send-string (string &optional process msg)
"Send STRING to inferior Python PROCESS.
When optional argument MSG is non-nil, forces display of a
user-friendly message if there's no process running; defaults to
t when called interactively."
(interactive
(list (read-string "Python command: ") nil t))
(let ((process (or process (python-shell-get-process-or-error msg))))
(if (string-match ".\n+." string) ;Multiline.
(let* ((temp-file-name (ipython-shell-send--save-temp-file string))
(file-name (or (buffer-file-name) temp-file-name)))
(ipython-shell-send-file file-name process temp-file-name t))
(comint-send-string process string)
(when (or (not (string-match "\n\\'" string))
(string-match "\n[ \t].*\n?\\'" string))
(comint-send-string process "\n")))))
;;;###autoload
(defun ipython-shell-send-region (start end &optional send-main msg)
"Send the region delimited by START and END to inferior IPython process.
When optional argument SEND-MAIN is non-nil, allow execution of
code inside blocks delimited by \"if __name__== \\='__main__\\=':\".
When called interactively SEND-MAIN defaults to nil, unless it's
called with prefix argument. When optional argument MSG is
non-nil, forces display of a user-friendly message if there's no
process running; defaults to t when called interactively."
(interactive
(list (region-beginning) (region-end) current-prefix-arg t))
(let* ((string (python-shell-buffer-substring start end (not send-main)))
(process (python-shell-get-process-or-error msg))
(original-string (buffer-substring-no-properties start end))
(_ (string-match "\\`\n*\\(.*\\)" original-string)))
(message "Sent: %s..." (match-string 1 original-string))
(ipython-shell-send-string string process)))
;;;###autoload
(defun ipython-shell-send-buffer (&optional send-main msg)
"Send the entire buffer to inferior IPython process.
When optional argument SEND-MAIN is non-nil, allow execution of
code inside blocks delimited by \"if __name__== \\='__main__\\=':\".
When called interactively SEND-MAIN defaults to nil, unless it's
called with prefix argument. When optional argument MSG is
non-nil, forces displa qqy of a user-friendly message if there's no
process running; defaults to t when called interactively."
(interactive (list current-prefix-arg t))
(save-restriction
(widen)
(ipython-shell-send-region (point-min) (point-max) send-main msg)))
;;;###autoload
(defun ipython-shell-send-defun (&optional arg msg)
"Send the current defun to inferior IPython process.
When argument ARG is non-nil do not include decorators. When
optional argument MSG is non-nil, forces display of a
user-friendly message if there's no process running; defaults to
t when called interactively."
(interactive (list current-prefix-arg t))
(save-excursion
(ipython-shell-send-region
(progn
(end-of-line 1)
(while (and (or (python-nav-beginning-of-defun)
(beginning-of-line 1))
(> (current-indentation) 0)))
(when (not arg)
(while (and (forward-line -1)
(looking-at (python-rx decorator))))
(forward-line 1))
(point-marker))
(progn
(or (python-nav-end-of-defun)
(end-of-line 1))
(point-marker))
nil ;; noop
msg)))
(defun ipython-shell-send-file (file-name &optional process temp-file-name
delete msg)
"Send FILE-NAME to inferior Python PROCESS.
If TEMP-FILE-NAME is passed then that file is used for processing
instead, while internally the shell will continue to use
FILE-NAME. If TEMP-FILE-NAME and DELETE are non-nil, then
TEMP-FILE-NAME is deleted after evaluation is performed. When
optional argument MSG is non-nil, forces display of a
user-friendly message if there's no process running; defaults to
t when called interactively."
(interactive
(list
(read-file-name "File to send: ") ; file-name
nil ; process
nil ; temp-file-name
nil ; delete
t)) ; msg
(let* ((process (or process (python-shell-get-process-or-error msg)))
(file-name (expand-file-name
(or (file-remote-p file-name 'localname)
file-name)))
(temp-file-name (when temp-file-name
(expand-file-name
(or (file-remote-p temp-file-name 'localname)
temp-file-name)))))
(python-shell-send-string
(format
(concat
"import IPython, os;"
"__pyfile = open('''%s''');"
"__code = __pyfile.read();"
"__pyfile.close();"
(when (and delete temp-file-name)
(format "os.remove('''%s''');" temp-file-name))
;; assign & omit final semicolon, to print the result exactly once
"__code=IPython.get_ipython().run_cell(__code)")
(or temp-file-name file-name))
process)))
(provide 'ipython-shell-send)
;;; ipython-shell-send.el ends here