mirror of
https://github.com/vale981/emacs-ipython-notebook
synced 2025-03-07 01:51:39 -05:00
Merge branch 'tramp'
This commit is contained in:
commit
5f10dcccb4
5 changed files with 183 additions and 8 deletions
|
@ -332,6 +332,8 @@ MuMaMo
|
||||||
Misc
|
Misc
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
.. el:variable:: ein:filename-translations
|
||||||
|
.. el:function:: ein:tramp-create-filename-translator
|
||||||
.. el:variable:: ein:query-timeout
|
.. el:variable:: ein:query-timeout
|
||||||
|
|
||||||
|
|
||||||
|
@ -485,6 +487,7 @@ v0.1.1
|
||||||
:el:symbol:`ein:notebook-turn-on-autoexec`).
|
:el:symbol:`ein:notebook-turn-on-autoexec`).
|
||||||
* Start completion when "." is inserted.
|
* Start completion when "." is inserted.
|
||||||
Use :el:symbol:`ein:complete-on-dot` to disable this feature.
|
Use :el:symbol:`ein:complete-on-dot` to disable this feature.
|
||||||
|
* Support tramp. See :el:symbol:`ein:filename-translations`.
|
||||||
|
|
||||||
|
|
||||||
v0.1
|
v0.1
|
||||||
|
|
|
@ -495,6 +495,14 @@ http://ipython.org/ipython-doc/dev/development/messaging.html#complete
|
||||||
|
|
||||||
;;; Utility functions
|
;;; Utility functions
|
||||||
|
|
||||||
|
(defun ein:kernel-filename-to-python (kernel filename)
|
||||||
|
"See: `ein:filename-to-python'."
|
||||||
|
(ein:filename-to-python (ein:$kernel-url-or-port kernel) filename))
|
||||||
|
|
||||||
|
(defun ein:kernel-filename-from-python (kernel filename)
|
||||||
|
"See: `ein:filename-from-python'."
|
||||||
|
(ein:filename-from-python (ein:$kernel-url-or-port kernel) filename))
|
||||||
|
|
||||||
(defun ein:kernel-construct-defstring (content)
|
(defun ein:kernel-construct-defstring (content)
|
||||||
"Construct call signature from CONTENT of ``:object_info_reply``.
|
"Construct call signature from CONTENT of ``:object_info_reply``.
|
||||||
Used in `ein:cell-finish-tooltip', etc."
|
Used in `ein:cell-finish-tooltip', etc."
|
||||||
|
@ -524,6 +532,10 @@ Used in `ein:cell-finish-tooltip', etc."
|
||||||
help))
|
help))
|
||||||
|
|
||||||
(defun ein:kernel-request-stream (kernel code func &optional args)
|
(defun ein:kernel-request-stream (kernel code func &optional args)
|
||||||
|
"Run lisp callback FUNC with the output stream returned by Python CODE.
|
||||||
|
|
||||||
|
The first argument to the lisp function FUNC is the stream output
|
||||||
|
as a string and the rest of the argument is the optional ARGS."
|
||||||
(ein:kernel-execute
|
(ein:kernel-execute
|
||||||
kernel
|
kernel
|
||||||
code
|
code
|
||||||
|
@ -542,8 +554,9 @@ When no such directory exists, `default-directory' will not be changed."
|
||||||
(ein:kernel-request-stream
|
(ein:kernel-request-stream
|
||||||
kernel
|
kernel
|
||||||
"__import__('sys').stdout.write(__import__('os').getcwd())"
|
"__import__('sys').stdout.write(__import__('os').getcwd())"
|
||||||
(lambda (path buffer)
|
(lambda (path kernel buffer)
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
|
(setq path (ein:kernel-filename-from-python kernel path))
|
||||||
(if (file-accessible-directory-p path)
|
(if (file-accessible-directory-p path)
|
||||||
(progn
|
(progn
|
||||||
(setq default-directory path)
|
(setq default-directory path)
|
||||||
|
@ -553,7 +566,7 @@ When no such directory exists, `default-directory' will not be changed."
|
||||||
(ein:log 'info
|
(ein:log 'info
|
||||||
"Syncing directory of %s with kernel...FAILED (no dir: %s)"
|
"Syncing directory of %s with kernel...FAILED (no dir: %s)"
|
||||||
buffer path))))
|
buffer path))))
|
||||||
(list buffer)))
|
(list kernel buffer)))
|
||||||
|
|
||||||
(defun ein:kernelinfo-init (kernelinfo buffer)
|
(defun ein:kernelinfo-init (kernelinfo buffer)
|
||||||
(setf (ein:$kernelinfo-buffer kernelinfo) buffer))
|
(setf (ein:$kernelinfo-buffer kernelinfo) buffer))
|
||||||
|
@ -576,7 +589,8 @@ When no such directory exists, `default-directory' will not be changed."
|
||||||
(ein:kernel-request-stream
|
(ein:kernel-request-stream
|
||||||
kernel
|
kernel
|
||||||
"__import__('sys').stdout.write(__import__('os').getcwd())"
|
"__import__('sys').stdout.write(__import__('os').getcwd())"
|
||||||
(lambda (cwd kernelinfo buffer)
|
(lambda (cwd kernel kernelinfo buffer)
|
||||||
|
(setq cwd (ein:kernel-filename-from-python kernel cwd))
|
||||||
(setf (ein:$kernelinfo-ccwd kernelinfo) cwd)
|
(setf (ein:$kernelinfo-ccwd kernelinfo) cwd)
|
||||||
;; sync buffer's `default-directory' with CWD
|
;; sync buffer's `default-directory' with CWD
|
||||||
(when (buffer-live-p buffer)
|
(when (buffer-live-p buffer)
|
||||||
|
@ -584,7 +598,7 @@ When no such directory exists, `default-directory' will not be changed."
|
||||||
(when (file-accessible-directory-p cwd)
|
(when (file-accessible-directory-p cwd)
|
||||||
(setq default-directory (file-name-as-directory cwd))))))
|
(setq default-directory (file-name-as-directory cwd))))))
|
||||||
(let ((kernelinfo (ein:$kernel-kernelinfo kernel)))
|
(let ((kernelinfo (ein:$kernel-kernelinfo kernel)))
|
||||||
(list kernelinfo (ein:$kernelinfo-buffer kernelinfo)))))
|
(list kernel kernelinfo (ein:$kernelinfo-buffer kernelinfo)))))
|
||||||
|
|
||||||
(defun ein:kernelinfo-update-hostname (kernel)
|
(defun ein:kernelinfo-update-hostname (kernel)
|
||||||
(ein:kernel-request-stream
|
(ein:kernel-request-stream
|
||||||
|
|
|
@ -109,9 +109,8 @@ If OTHER-WINDOW is non-`nil', open the file in the other window."
|
||||||
:output
|
:output
|
||||||
(cons
|
(cons
|
||||||
(lambda (packed msg-type content)
|
(lambda (packed msg-type content)
|
||||||
(let ((object (nth 0 packed))
|
(destructuring-bind (kernel object other-window notebook-buffer)
|
||||||
(other-window (nth 1 packed))
|
packed
|
||||||
(notebook-buffer (nth 2 packed)))
|
|
||||||
(ein:case-equal msg-type
|
(ein:case-equal msg-type
|
||||||
(("stream")
|
(("stream")
|
||||||
(ein:aif (plist-get content :data)
|
(ein:aif (plist-get content :data)
|
||||||
|
@ -122,6 +121,8 @@ If OTHER-WINDOW is non-`nil', open the file in the other window."
|
||||||
(destructuring-bind (filename &optional lineno &rest ignore)
|
(destructuring-bind (filename &optional lineno &rest ignore)
|
||||||
(split-string it "\n")
|
(split-string it "\n")
|
||||||
(setq lineno (string-to-number lineno))
|
(setq lineno (string-to-number lineno))
|
||||||
|
(setq filename
|
||||||
|
(ein:kernel-filename-from-python kernel filename))
|
||||||
(ein:goto-file filename lineno other-window)
|
(ein:goto-file filename lineno other-window)
|
||||||
(when (and notebook-buffer (not ein:@connect))
|
(when (and notebook-buffer (not ein:@connect))
|
||||||
(ein:connect-to-notebook-buffer notebook-buffer))
|
(ein:connect-to-notebook-buffer notebook-buffer))
|
||||||
|
@ -131,7 +132,7 @@ If OTHER-WINDOW is non-`nil', open the file in the other window."
|
||||||
(("pyerr")
|
(("pyerr")
|
||||||
(ein:log 'info
|
(ein:log 'info
|
||||||
"Jumping to the source of %s...Not found" object)))))
|
"Jumping to the source of %s...Not found" object)))))
|
||||||
(list object other-window notebook-buffer)))))
|
(list kernel object other-window notebook-buffer)))))
|
||||||
|
|
||||||
(defun ein:pytools-jump-to-source-command (&optional other-window)
|
(defun ein:pytools-jump-to-source-command (&optional other-window)
|
||||||
"Jump to the source code of the object at point.
|
"Jump to the source code of the object at point.
|
||||||
|
|
|
@ -28,6 +28,11 @@
|
||||||
(eval-when-compile (require 'cl))
|
(eval-when-compile (require 'cl))
|
||||||
(require 'json)
|
(require 'json)
|
||||||
|
|
||||||
|
;; Optional dependency on tramp:
|
||||||
|
(declare-function tramp-make-tramp-file-name "tramp")
|
||||||
|
(declare-function tramp-file-name-localname "tramp")
|
||||||
|
(declare-function tramp-dissect-file-name "tramp")
|
||||||
|
|
||||||
(defgroup ein nil
|
(defgroup ein nil
|
||||||
"IPython notebook client in Emacs"
|
"IPython notebook client in Emacs"
|
||||||
:group 'applications
|
:group 'applications
|
||||||
|
@ -61,6 +66,44 @@ format string which can be passed to `format-time-string'."
|
||||||
:type '(string :tag "Format string")
|
:type '(string :tag "Format string")
|
||||||
:group 'ein)
|
:group 'ein)
|
||||||
|
|
||||||
|
(defcustom ein:filename-translations nil
|
||||||
|
"Convert file paths between Emacs and Python process.
|
||||||
|
|
||||||
|
This value can take these form:
|
||||||
|
|
||||||
|
alist
|
||||||
|
Its key specifies URL-OR-PORT and value must be a list of two
|
||||||
|
functions: (TO-PYTHON FROM-PYTHON). Key (URL-OR-PORT) can be
|
||||||
|
string (URL), integer (port), or `default' (symbol). The
|
||||||
|
value of `default' is used when other key does not much.
|
||||||
|
function
|
||||||
|
Called with an argument URL-OR-PORT (integer or string).
|
||||||
|
This function must return a list of two functions:
|
||||||
|
(TO-PYTHON FROM-PYTHON).
|
||||||
|
|
||||||
|
Here, the functions TO-PYTHON and FROM-PYTHON are defined as:
|
||||||
|
|
||||||
|
TO-PYTHON
|
||||||
|
A function which converts a file name (returned by
|
||||||
|
`buffer-file-name') to the one Python understands.
|
||||||
|
FROM-PYTHON
|
||||||
|
A function which converts a file path returned by
|
||||||
|
Python process to the one Emacs understands.
|
||||||
|
|
||||||
|
Use `ein:tramp-create-filename-translator' to easily generate the
|
||||||
|
pair of TO-PYTHON and FROM-PYTHON."
|
||||||
|
;; I've got the idea from `slime-filename-translations'.
|
||||||
|
:type '(choice
|
||||||
|
(alist :tag "Translations mapping"
|
||||||
|
:key-type (choice :tag "URL or PORT"
|
||||||
|
(string :tag "URL" "http://127.0.0.1:8888")
|
||||||
|
(integer :tag "PORT" 8888)
|
||||||
|
(const default))
|
||||||
|
:value-type (list (function :tag "TO-PYTHON")
|
||||||
|
(function :tag "FROM-PYTHON")))
|
||||||
|
(function :tag "Translations getter"))
|
||||||
|
:group 'ein)
|
||||||
|
|
||||||
|
|
||||||
;;; Macros and core functions/variables
|
;;; Macros and core functions/variables
|
||||||
|
|
||||||
|
@ -388,6 +431,68 @@ NOTE: This function creates new list."
|
||||||
(ein:join-str " " (mapcar #'file-name-nondirectory it))))
|
(ein:join-str " " (mapcar #'file-name-nondirectory it))))
|
||||||
(message "Compiled %s files" (length files))))
|
(message "Compiled %s files" (length files))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; File name translation
|
||||||
|
|
||||||
|
;; Probably it's better to define `ein:filename-translations-get' as
|
||||||
|
;; an EIEIO method so that I don't have to re-define functions such as
|
||||||
|
;; `ein:kernel-filename-to-python' and `ein:kernel-filename-from-python'.
|
||||||
|
|
||||||
|
(defun ein:filename-translations-get (url-or-port)
|
||||||
|
(ein:choose-setting 'ein:filename-translations url-or-port))
|
||||||
|
|
||||||
|
(defun ein:filename-to-python (url-or-port filename)
|
||||||
|
(ein:aif (car (ein:filename-translations-get url-or-port))
|
||||||
|
(funcall it filename)
|
||||||
|
filename))
|
||||||
|
|
||||||
|
(defun ein:filename-from-python (url-or-port filename)
|
||||||
|
(ein:aif (cadr (ein:filename-translations-get url-or-port))
|
||||||
|
(funcall it filename)
|
||||||
|
filename))
|
||||||
|
|
||||||
|
(defun ein:make-tramp-file-name (username remote-host python-filename)
|
||||||
|
"Old (with multi-hops) tramp compatability function.
|
||||||
|
Adapted from `slime-make-tramp-file-name'."
|
||||||
|
(if (boundp 'tramp-multi-methods)
|
||||||
|
(tramp-make-tramp-file-name nil nil
|
||||||
|
username
|
||||||
|
remote-host
|
||||||
|
python-filename)
|
||||||
|
(tramp-make-tramp-file-name nil
|
||||||
|
username
|
||||||
|
remote-host
|
||||||
|
python-filename)))
|
||||||
|
|
||||||
|
(defun ein:tramp-create-filename-translator (remote-host &optional username)
|
||||||
|
"Generate a pair of TO-PYTHON and FROM-PYTHON for
|
||||||
|
`ein:filename-translations'.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
(setq ein:filename-translations
|
||||||
|
`((8888
|
||||||
|
. ,(ein:tramp-create-filename-translator \"MY-HOSTNAME\"))))
|
||||||
|
;; Equivalently:
|
||||||
|
(setq ein:filename-translations
|
||||||
|
(lambda (url-or-port)
|
||||||
|
(when (equal url-or-port 8888)
|
||||||
|
(ein:tramp-create-filename-translator \"MY-HOSTNAME\"))))
|
||||||
|
|
||||||
|
This setting assumes that the IPython server which can be
|
||||||
|
connected using the port 8888 in localhost is actually running in
|
||||||
|
the host named MY-HOSTNAME.
|
||||||
|
|
||||||
|
Adapted from `slime-create-filename-translator'."
|
||||||
|
(require 'tramp)
|
||||||
|
(lexical-let ((remote-host remote-host)
|
||||||
|
(username (or username (user-login-name))))
|
||||||
|
(list (lambda (emacs-filename)
|
||||||
|
(tramp-file-name-localname
|
||||||
|
(tramp-dissect-file-name emacs-filename)))
|
||||||
|
(lambda (python-filename)
|
||||||
|
(ein:make-tramp-file-name username remote-host python-filename)))))
|
||||||
|
|
||||||
|
|
||||||
;;; utils.js compatible
|
;;; utils.js compatible
|
||||||
|
|
||||||
|
|
|
@ -85,3 +85,55 @@ def func():
|
||||||
(ert-deftest ein:version ()
|
(ert-deftest ein:version ()
|
||||||
"Check if `ein:version' can be parsed by `version-to-list'."
|
"Check if `ein:version' can be parsed by `version-to-list'."
|
||||||
(version-to-list ein:version))
|
(version-to-list ein:version))
|
||||||
|
|
||||||
|
|
||||||
|
;;; File name translation
|
||||||
|
|
||||||
|
;; Requiring `tramp' during (inside of) tests yields error from
|
||||||
|
;; MuMaMo. Although I don't understand the reason, requiring it
|
||||||
|
;; before running tests workarounds this problem.
|
||||||
|
(require 'tramp)
|
||||||
|
|
||||||
|
(ert-deftest ein:filename-translations-from-to-tramp ()
|
||||||
|
(loop with ein:filename-translations =
|
||||||
|
`((8888 . ,(ein:tramp-create-filename-translator "HOST" "USER")))
|
||||||
|
with filename = "/file/name"
|
||||||
|
for port in '(7777 8888) ; check for the one w/o translation
|
||||||
|
for emacs-filename = (ein:filename-from-python port filename)
|
||||||
|
do (message "emacs-filename = %s" emacs-filename)
|
||||||
|
do (should
|
||||||
|
(equal (ein:filename-to-python port emacs-filename)
|
||||||
|
filename))))
|
||||||
|
|
||||||
|
(ert-deftest ein:filename-translations-to-from-tramp ()
|
||||||
|
(loop with ein:filename-translations =
|
||||||
|
`((8888 . ,(ein:tramp-create-filename-translator "HOST" "USER")))
|
||||||
|
with filename = "/USER@HOST:/filename"
|
||||||
|
for port in '(8888)
|
||||||
|
do (should
|
||||||
|
(equal (ein:filename-from-python
|
||||||
|
port (ein:filename-to-python port filename))
|
||||||
|
filename))))
|
||||||
|
|
||||||
|
(ert-deftest ein:filename-to-python-tramp ()
|
||||||
|
(let* ((port 8888)
|
||||||
|
(ein:filename-translations
|
||||||
|
`((,port . ,(ein:tramp-create-filename-translator "DUMMY")))))
|
||||||
|
(loop with python-filename = "/file/name"
|
||||||
|
for emacs-filename in '("/scpc:HOST:/file/name"
|
||||||
|
"/USER@HOST:/file/name")
|
||||||
|
do (should
|
||||||
|
(equal (ein:filename-to-python port emacs-filename)
|
||||||
|
python-filename)))
|
||||||
|
;; Error: Not a Tramp file name: /file/name
|
||||||
|
(should-error (ein:filename-to-python port "/file/name"))))
|
||||||
|
|
||||||
|
(ert-deftest ein:filename-from-python-tramp ()
|
||||||
|
(loop with ein:filename-translations =
|
||||||
|
`((8888 . ,(ein:tramp-create-filename-translator "HOST" "USER")))
|
||||||
|
with python-filename = "/file/name"
|
||||||
|
for emacs-filename in '("/USER@HOST:/file/name" "/file/name")
|
||||||
|
for port in '(8888 7777)
|
||||||
|
do (should
|
||||||
|
(equal (ein:filename-from-python port python-filename)
|
||||||
|
emacs-filename))))
|
||||||
|
|
Loading…
Add table
Reference in a new issue