Merge branch 'tramp'

This commit is contained in:
Takafumi Arakaki 2012-08-04 04:20:39 +02:00
commit 5f10dcccb4
5 changed files with 183 additions and 8 deletions

View file

@ -332,6 +332,8 @@ MuMaMo
Misc
^^^^
.. el:variable:: ein:filename-translations
.. el:function:: ein:tramp-create-filename-translator
.. el:variable:: ein:query-timeout
@ -485,6 +487,7 @@ v0.1.1
:el:symbol:`ein:notebook-turn-on-autoexec`).
* Start completion when "." is inserted.
Use :el:symbol:`ein:complete-on-dot` to disable this feature.
* Support tramp. See :el:symbol:`ein:filename-translations`.
v0.1

View file

@ -495,6 +495,14 @@ http://ipython.org/ipython-doc/dev/development/messaging.html#complete
;;; 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)
"Construct call signature from CONTENT of ``:object_info_reply``.
Used in `ein:cell-finish-tooltip', etc."
@ -524,6 +532,10 @@ Used in `ein:cell-finish-tooltip', etc."
help))
(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
kernel
code
@ -542,8 +554,9 @@ When no such directory exists, `default-directory' will not be changed."
(ein:kernel-request-stream
kernel
"__import__('sys').stdout.write(__import__('os').getcwd())"
(lambda (path buffer)
(lambda (path kernel buffer)
(with-current-buffer buffer
(setq path (ein:kernel-filename-from-python kernel path))
(if (file-accessible-directory-p path)
(progn
(setq default-directory path)
@ -553,7 +566,7 @@ When no such directory exists, `default-directory' will not be changed."
(ein:log 'info
"Syncing directory of %s with kernel...FAILED (no dir: %s)"
buffer path))))
(list buffer)))
(list kernel buffer)))
(defun ein:kernelinfo-init (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
kernel
"__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)
;; sync buffer's `default-directory' with CWD
(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)
(setq default-directory (file-name-as-directory cwd))))))
(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)
(ein:kernel-request-stream

View file

@ -109,9 +109,8 @@ If OTHER-WINDOW is non-`nil', open the file in the other window."
:output
(cons
(lambda (packed msg-type content)
(let ((object (nth 0 packed))
(other-window (nth 1 packed))
(notebook-buffer (nth 2 packed)))
(destructuring-bind (kernel object other-window notebook-buffer)
packed
(ein:case-equal msg-type
(("stream")
(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)
(split-string it "\n")
(setq lineno (string-to-number lineno))
(setq filename
(ein:kernel-filename-from-python kernel filename))
(ein:goto-file filename lineno other-window)
(when (and notebook-buffer (not ein:@connect))
(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")
(ein:log 'info
"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)
"Jump to the source code of the object at point.

View file

@ -28,6 +28,11 @@
(eval-when-compile (require 'cl))
(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
"IPython notebook client in Emacs"
:group 'applications
@ -61,6 +66,44 @@ format string which can be passed to `format-time-string'."
:type '(string :tag "Format string")
: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
@ -388,6 +431,68 @@ NOTE: This function creates new list."
(ein:join-str " " (mapcar #'file-name-nondirectory it))))
(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

View file

@ -85,3 +85,55 @@ def func():
(ert-deftest ein:version ()
"Check if `ein:version' can be parsed by `version-to-list'."
(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))))