emacs-ipython-notebook/lisp/ein-jupyterhub.el
dickmao 0490031ec8 Sane File Navigation
As emacs users we prefer and have the luxury of fuzzy file navigation
via ido and projectile.  From a notebook or notebooklist buffer, the commands

`C-c C-f` ein:file-open
`C-c C-o` ein:notebook-open

offer an ido alternative to point and click navigation.

To populate the ido lists, retrieving the content hierarchy is on by
default.  Two custom variables determine how wide and deep the content query
probes (currently at 2 levels deep and 6 directories wide).  Set both
to zero to turn off.

tkf half finished code to quickly go from local file buffers to notebook
mode via `C-c C-z` or `C-c C-o`.  This is now possible.  EIN will
start the server from a suitable parent directory of the visited file.

Enable ido completion for `notebooklist-login`.

Remove the albatross `ein-loaddefs.el` in favor of more standard
`ein-autoloads.el` that is not git tracked.

Convenience `make install` from git source (local alternative to
melpa).
2018-10-26 18:40:19 -04:00

162 lines
6.3 KiB
EmacsLisp

;;; ein-jupyterhub.el --- Interface to Jupyterhub -*- lexical-binding: t -*-
;; Copyright (C) 2016 - John Miller
;; Authors: Takafumi Arakaki <aka.tkf at gmail.com>
;; John M. Miller <millejoh at mac.com>
;; This file is NOT part of GNU Emacs.
;; ein-jupyterhub.el 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.
;; ein-jupyterhub.el 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 ein-jupyter.el. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;;
;;; An interface to the Jupyterhub login and management API as described in
;;; http://jupyterhub.readthedocs.io/en/latest/api/index.html
;;;
;;
;;; Code:
(require 'ein-query)
(require 'ein-contents-api)
(defun ein:jupyterhub-api-url (url-or-port command &rest args)
(if args
(apply #'ein:url url-or-port "hub/api" command args)
(ein:url url-or-port "hub/api" command)))
(defun ein:jh-ask-url-or-port ()
(let* ((url-or-port-list (mapcar (lambda (x) (format "%s" x))
ein:url-or-port))
(default (format "%s" (ein:default-url-or-port)))
(url-or-port
(completing-read (format "URL or port number (default %s): " default)
url-or-port-list
nil nil nil nil
default)))
(if (string-match "^[0-9]+$" url-or-port)
(string-to-number url-or-port)
url-or-port)))
(defun ein:jupyterhub--do-connect (url-or-port user password)
(deferred:$
(ein:query-deferred
(ein:jupyterhub-api-url url-or-port "/")
:type "GET"
:parser #'ein:json-read)
(deferred:nextc it
(lambda (response)
(when (and response (request-response-data response))
(let ((conn (make-ein:$jh-conn :url (or (ein:get-response-redirect response)
url-or-port)
:version (plist-get (request-response-data response) :version))))
conn))))
(deferred:nextc it
(lambda (conn)
(ein:jupyterhub-login conn user password)))
(deferred:nextc it
(lambda (conn)
(unless conn
(error "Connection to Jupyterhub server at %s failed! Maybe you used the wrong URL?" url-or-port))
(ein:jupyterhub-token-request conn)))
(deferred:nextc it
(lambda (conn)
(setf (gethash (ein:$jh-conn-url conn) *ein:jupyterhub-servers*) conn)
(ein:jupyterhub-start-server conn user)))))
(defun ein:jupyterhub-login (conn username password)
(deferred:$
(ein:query-deferred
(ein:url (ein:$jh-conn-url conn) "hub/login")
:type "POST"
:parser #'ein:json-read
:data (format "username=%s&password=%s" username password) ;; (json-encode`((username . ,username)
;; (password . , password)))
)
(deferred:nextc it
(lambda (response)
(ein:log 'info "Login for user %s with response %s." username (request-response-status-code response))
conn))))
(defun ein:jupyterhub-token-request (conn)
(deferred:$
(ein:query-deferred
(ein:jupyterhub-api-url (ein:$jh-conn-url conn)
"authorizations/token")
:type "POST"
:timeout ein:content-query-timeout
:parser #'ein:json-read)
(deferred:nextc it
(lambda (response)
(ein:log 'info "response-data: %s, %s"
(request-response-data response)
(cadr (request-response-data response))) ;; FIXME: Why doesn't plist-get work?
(unless (eql (request-response-status-code response) 403)
(setf (ein:$jh-conn-token conn) (cadr (request-response-data response))))
conn))))
(defun ein:jupyterhub-get-user (conn username)
(deferred:$
(ein:query-deferred
(ein:jupyterhub-api-url (ein:$jh-conn-url conn)
"users"
username)
:type "GET"
:parser #'ein:json-read)
(deferred:nextc it
(lambda (response)
(let* ((data (request-response-data response))
(user (make-ein:$jh-user :name (plist-get data :name)
:admin (plist-get data :admin)
:groups (plist-get data :groups)
:server (plist-get data :server)
:pending (plist-get data :pending)
:last-activity (plist-get data :last_activity))))
(ein:log 'info "Jupyterhub: Found user: %s" user)
user)))))
(defun ein:jupyterhub-start-server (conn username)
(deferred:$
(ein:query-deferred
(ein:jupyterhub-api-url (ein:$jh-conn-url conn)
"users"
username
"server")
:type "POST"
:parser #'ein:json-read)
(deferred:nextc it
(lambda (response)
(ein:log 'info "Jupyterhub: Response status: %s" (request-response-status-code response))
(case (request-response-status-code response)
((201 400)
(ein:log 'info "Jupyterhub: Finding user: %s" username)
(ein:jupyterhub-get-user conn username)))))
(deferred:nextc it
(lambda (user)
(ein:log 'info "Jupyterhub: Found user? (%s)" user)
(when (ein:$jh-user-p user)
(setf (ein:$jh-conn-user conn) user)
(ein:log 'info "Jupyterhub: Opening notebook at %s: " (ein:$jh-conn-url conn))
(ein:notebooklist-open* (ein:$jh-conn-url conn) nil nil #'pop-to-buffer))))))
;;;###autoload
(defun ein:jupyterhub-connect (url user password)
"Log on to a jupyterhub server using PAM authentication. Requires jupyterhub version 0.8 or greater."
(interactive (list (ein:jh-ask-url-or-port)
(read-string "User: ")
(read-passwd "Password: ")))
(ein:jupyterhub--do-connect url user password))
(provide 'ein-jupyterhub)