2012-05-07 14:41:15 +02:00
;;; ein-kernel.el --- Communicate with IPython notebook server
;; Copyright (C) 2012- Takafumi Arakaki
2012-07-01 20:18:05 +02:00
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
2012-05-07 14:41:15 +02:00
;; This file is NOT part of GNU Emacs.
;; ein-kernel.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-kernel.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-kernel.el. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
2018-11-03 16:02:04 -04:00
;; `ein:kernel' is the proxy class of notebook server state.
;; It agglomerates both the "kernel" and "session" objects of server described here
;; https://github.com/jupyter/jupyter/wiki/Jupyter-Notebook-Server-API
;; It may have been better to keep them separate to allow parallel reasoning with
;; the notebook server, but that time is past.
2012-05-07 14:41:15 +02:00
;;; Code:
( eval-when-compile ( require 'cl ) )
2012-07-08 13:45:30 +02:00
( require 'ansi-color )
2012-05-07 14:41:15 +02:00
2012-08-28 15:26:32 +02:00
( require 'ein-core )
2017-07-12 14:38:04 -05:00
( require 'ein-classes )
2012-05-07 14:41:15 +02:00
( require 'ein-log )
2012-09-11 01:47:50 +02:00
;; FIXME: use websocket.el directly once v1.0 is released.
2012-05-07 14:41:15 +02:00
( require 'ein-websocket )
2012-05-22 13:34:38 +02:00
( require 'ein-events )
2012-05-26 21:08:48 +02:00
( require 'ein-query )
2015-05-15 23:23:56 -05:00
( require 'ein-ipdb )
2012-08-28 14:53:41 +02:00
;; "Public" getters. Use them outside of this package.
2012-09-01 20:51:55 +02:00
2014-10-22 21:35:20 -05:00
( defun ein:$kernel-session-url ( kernel )
( concat " /api/sessions/ " ( ein:$kernel-session-id kernel ) ) )
2012-09-01 20:51:55 +02:00
;;;###autoload
2012-08-28 14:53:41 +02:00
( defalias 'ein:kernel-url-or-port 'ein:$kernel-url-or-port )
2012-09-01 20:51:55 +02:00
;;;###autoload
2012-08-15 20:54:22 +02:00
( defalias 'ein:kernel-id 'ein:$kernel-kernel-id )
2012-05-31 15:43:49 +02:00
2018-05-12 17:32:32 -05:00
( defcustom ein:pre-kernel-execute-functions nil
" List of functions to call before sending a message to the kernel for execution. Each function is called with the message (see `ein:kernel--get-msg' ) about to be sent. "
:type 'list
:group 'ein )
( defcustom ein:on-shell-reply-functions nil
" List of functions to call when the kernel responds on the shell channel.
Each function should have the call signature: msg-id header content metadata "
:type 'list
:group 'ein )
2012-05-22 13:34:38 +02:00
;;; Initialization and connection.
2016-01-13 07:52:02 -06:00
( defun ein:kernel-new ( url-or-port base-url events &optional api-version )
2012-05-07 14:41:15 +02:00
( make-ein:$kernel
2012-05-13 15:59:40 +02:00
:url-or-port url-or-port
2012-05-22 13:34:38 +02:00
:events events
2018-10-30 14:17:29 -04:00
:api-version ( or api-version 5 )
2014-11-16 08:12:04 -06:00
:session-id ( ein:utils-uuid )
2012-05-07 14:41:15 +02:00
:kernel-id nil
2018-10-30 14:17:29 -04:00
:websocket nil
2012-05-22 13:34:38 +02:00
:base-url base-url
2015-03-31 16:55:14 -05:00
:stdin-activep nil
2012-05-07 14:41:15 +02:00
:username " username "
2012-08-26 15:14:59 +02:00
:msg-callbacks ( make-hash-table :test 'equal ) ) )
2012-05-07 14:41:15 +02:00
2012-05-19 17:58:02 +02:00
( defun ein:kernel-del ( kernel )
" Destructor for `ein:$kernel' . "
2015-07-08 13:10:45 -05:00
( ein:kernel-disconnect kernel ) )
2012-05-19 17:58:02 +02:00
2012-05-22 13:34:38 +02:00
( defun ein:kernel--get-msg ( kernel msg-type content )
2012-05-07 14:41:15 +02:00
( list
:header ( list
:msg_id ( ein:utils-uuid )
:username ( ein:$kernel-username kernel )
:session ( ein:$kernel-session-id kernel )
2018-08-07 08:36:26 -05:00
:version " 5.0 "
2016-08-26 20:52:54 -05:00
:date ( format-time-string " %Y-%m-%dT%T " ( current-time ) ) ; ISO 8601 timestamp
2012-05-07 14:41:15 +02:00
:msg_type msg-type )
2012-08-07 00:54:36 +02:00
:metadata ( make-hash-table )
2017-04-19 10:12:45 -05:00
2012-05-07 14:41:15 +02:00
:content content
:parent_header ( make-hash-table ) ) )
2018-11-03 16:02:04 -04:00
( defun* ein:kernel-session-p ( notebook callback &optional iteration &aux ( kernel ( ein:$notebook-kernel notebook ) ) )
" Don't make any changes on the server side. CALLBACK with arity 1, a boolean whether session exists on server. "
( unless iteration
( setq iteration 0 ) )
( let ( ( session-id ( ein:$kernel-session-id kernel ) ) )
( ein:query-singleton-ajax
( list 'kernel-session-p session-id )
( ein:url ( ein:$kernel-url-or-port kernel ) " api/sessions " session-id )
:type " GET "
:sync ein:force-sync
:parser #' ein:json-read
:complete ( apply-partially #' ein:kernel-session-p--complete session-id )
:success ( apply-partially #' ein:kernel-session-p--success session-id callback )
:error ( apply-partially #' ein:kernel-session-p--error notebook callback iteration ) ) ) )
( defun* ein:kernel-session-p--complete ( session-id &key data response
&allow-other-keys
&aux ( resp-string ( format " STATUS: %s DATA: %s " ( request-response-status-code response ) data ) ) )
( ein:log 'debug " ein:kernel-session-p--complete %s " resp-string ) )
( defun* ein:kernel-session-p--error ( notebook callback iteration &key error-thrown symbol-status data &allow-other-keys )
( if ( ein:aand ( plist-get data :message ) ( search " not found " it ) )
( when callback ( funcall callback nil ) )
( let* ( ( max-tries 3 )
( tries-left ( 1- ( - max-tries iteration ) ) ) )
( ein:log 'verbose " ein:kernel-session-p--error [%s], %s tries left "
( car error-thrown ) tries-left )
( if ( > tries-left 0 )
( ein:kernel-session-p notebook callback ( 1+ iteration ) ) ) ) ) )
( defun* ein:kernel-session-p--success ( session-id callback &key data &allow-other-keys )
( let ( ( session-p ( equal ( plist-get data :id ) session-id ) ) )
( ein:log 'verbose " ein:kernel-session-p--success: session-id=%s session-p=%s "
session-id session-p )
( when callback ( funcall callback session-p ) ) ) )
( defun* ein:kernel-restart-session ( notebook
&aux ( kernel ( ein:$notebook-kernel notebook ) ) )
" Server side delete of NOTEBOOK session and subsequent restart with all new state "
( ein:kernel-delete-session kernel
( apply-partially
( lambda ( notebook* )
( ein:events-trigger ( ein:$kernel-events kernel )
'status_restarting.Kernel )
( ein:kernel-retrieve-session notebook* 0
( apply-partially
( lambda ( nb )
( with-current-buffer ( ein:notebook-buffer nb )
( ein:notification-status-set
( slot-value ein:%notification% 'kernel )
'status_restarted.Kernel ) ) )
notebook* ) ) )
notebook ) ) )
( defun* ein:kernel-retrieve-session ( notebook &optional iteration callback
&aux ( kernel ( ein:$notebook-kernel notebook ) ) )
" Formerly ein:kernel-start, but that was misnomer because 1. the server really starts a session (and an accompanying kernel), and 2. it may not even start a session if one exists for the same path.
The server logic is here ( could not find other documentation )
https://github.com/jupyter/notebook/blob/04a686dbaf9dfe553324a03cb9e6f778cf1e3da1/notebook/services/sessions/handlers.py#L56-L81
CALLBACK of arity 0 ( e.g., print a message kernel started )
This no longer works in iPython-2.0. Protocol is to create a session for a
notebook, which will automatically create and associate a kernel with the notebook.
"
2018-10-30 14:17:29 -04:00
( unless iteration
( setq iteration 0 ) )
2018-11-01 20:08:10 -04:00
( if ( <= ( ein:$kernel-api-version kernel ) 2 )
2018-11-03 16:02:04 -04:00
( error " Api %s unsupported " ( ein:$kernel-api-version kernel ) )
( let ( ( kernelspec ( ein:$notebook-kernelspec notebook ) )
( kernel-id ( ein:$kernel-kernel-id kernel ) ) )
2018-11-01 20:08:10 -04:00
( ein:query-singleton-ajax
2018-11-03 16:02:04 -04:00
( list 'kernel-retrieve-session kernel-id )
2018-11-01 20:08:10 -04:00
( ein:url ( ein:$kernel-url-or-port kernel ) " api/sessions " )
:type " POST "
:data ( json-encode
( cond ( ( <= ( ein:$kernel-api-version kernel ) 4 )
` ( ( " notebook " .
2018-11-01 20:26:42 -05:00
( ( " path " . , ( ein:$notebook-notebook-path notebook ) ) ) )
2018-11-01 20:08:10 -04:00
,@ ( if kernelspec
` ( ( " kernel " .
( ( " name " . , ( ein:$kernelspec-name kernelspec ) ) ) ) ) ) ) )
( t ` ( ( " path " . , ( ein:$notebook-notebook-path notebook ) )
( " type " . " notebook " )
2018-11-04 19:45:22 -05:00
,@ ( if kernelspec
` ( ( " kernel " .
( ( " name " . , ( ein:$kernelspec-name kernelspec ) )
,@ ( if kernel-id
` ( ( " id " . , kernel-id ) ) ) ) ) ) ) ) ) ) )
2018-11-01 20:08:10 -04:00
:sync ein:force-sync
:parser #' ein:json-read
2018-11-03 16:02:04 -04:00
:complete ( apply-partially #' ein:kernel-retrieve-session--complete kernel callback )
:success ( apply-partially #' ein:kernel-retrieve-session--success kernel callback )
:error ( apply-partially #' ein:kernel-retrieve-session--error notebook iteration callback ) ) ) ) )
2012-05-07 14:41:15 +02:00
2018-11-03 16:02:04 -04:00
( defun* ein:kernel-retrieve-session--complete ( kernel callback &key data response
&allow-other-keys
2018-11-01 20:08:10 -04:00
&aux ( resp-string ( format " STATUS: %s DATA: %s " ( request-response-status-code response ) data ) ) )
2018-11-03 16:02:04 -04:00
( ein:log 'debug " ein:kernel-retrieve-session--complete %s " resp-string ) )
2018-11-01 20:08:10 -04:00
2018-11-03 16:02:04 -04:00
( defun* ein:kernel-retrieve-session--error ( notebook iteration callback &key error-thrown symbol-status &allow-other-keys )
2018-11-04 19:45:22 -05:00
( let* ( ( max-tries 3 )
2018-10-30 14:17:29 -04:00
( tries-left ( 1- ( - max-tries iteration ) ) ) )
2018-11-03 16:02:04 -04:00
( ein:log 'verbose " ein:kernel-retrieve-session--error [%s], %s tries left "
2018-10-30 14:17:29 -04:00
( car error-thrown ) tries-left )
2018-11-04 18:59:30 -05:00
( sleep-for 0 ( * ( 1+ iteration ) 500 ) )
2018-10-30 14:17:29 -04:00
( if ( > tries-left 0 )
2018-11-03 16:02:04 -04:00
( ein:kernel-retrieve-session notebook ( 1+ iteration ) callback ) ) ) )
2018-10-30 14:17:29 -04:00
2018-11-03 16:02:04 -04:00
( defun* ein:kernel-retrieve-session--success ( kernel callback &key data &allow-other-keys )
2014-10-17 16:44:04 -05:00
( let ( ( session-id ( plist-get data :id ) ) )
( if ( plist-get data :kernel )
( setq data ( plist-get data :kernel ) ) )
( destructuring-bind ( &key id &allow-other-keys ) data
2018-11-03 16:02:04 -04:00
( ein:log 'verbose " ein:kernel-retrieve-session--success: kernel-id=%s session-id=%s "
2018-11-01 20:08:10 -04:00
id session-id )
2014-10-17 16:44:04 -05:00
( setf ( ein:$kernel-kernel-id kernel ) id )
( setf ( ein:$kernel-session-id kernel ) session-id )
2015-07-08 13:37:09 -05:00
( setf ( ein:$kernel-ws-url kernel ) ( ein:kernel--ws-url ( ein:$kernel-url-or-port kernel ) ) )
2014-10-17 16:44:04 -05:00
( setf ( ein:$kernel-kernel-url kernel )
2018-10-30 14:17:29 -04:00
( concat ( file-name-as-directory ( ein:$kernel-base-url kernel ) ) id ) ) )
( ein:kernel-start-websocket kernel )
( when callback ( funcall callback ) ) ) )
2012-05-07 14:41:15 +02:00
2015-07-08 13:37:09 -05:00
( defun ein:kernel--ws-url ( url-or-port &optional securep )
" Use `ein:$kernel-url-or-port' if BASE_URL is an empty string.
2013-06-10 19:56:46 +02:00
See: https://github.com/ipython/ipython/pull/3307 "
2017-07-25 15:36:07 -05:00
( let* ( ( base-url url-or-port )
( url-or-port ( ein:jupyterhub-correct-query-url-maybe url-or-port ) )
( protocol ( if ( or securep
2016-03-30 21:38:33 -05:00
( and ( stringp url-or-port )
( string-match " ^https:// " url-or-port ) ) )
2016-03-30 21:32:14 -05:00
" wss "
" ws " ) ) )
2015-07-08 13:37:09 -05:00
( if ( integerp url-or-port )
( format " %s://127.0.0.1:%s " protocol url-or-port )
2015-07-08 16:17:54 -05:00
( let* ( ( url ( if ( string-match " ^https?:// " url-or-port )
url-or-port
( format " http://%s " url-or-port ) ) )
( parsed-url ( url-generic-parse-url url ) ) )
2017-07-25 15:36:07 -05:00
( if ( ein:jupyterhub-url-p base-url )
2017-07-25 16:57:32 -05:00
( ein:trim-right ( format " %s://%s:%s%s "
protocol
( url-host parsed-url )
( url-port parsed-url )
( url-filename parsed-url ) )
" / " )
2017-07-25 15:36:07 -05:00
( format " %s://%s:%s " protocol ( url-host parsed-url ) ( url-port parsed-url ) ) ) ) ) ) )
2013-06-10 19:56:46 +02:00
2012-12-17 17:14:26 +01:00
( defun ein:kernel-send-cookie ( channel host )
;; cookie can be an empty string for IPython server with no password,
;; but something must be sent to start channel.
( let ( ( cookie ( ein:query-get-cookie host " / " ) ) )
( ein:websocket-send channel cookie ) ) )
2012-05-07 14:41:15 +02:00
2018-10-30 14:17:29 -04:00
( defun ein:kernel--handle-websocket-reply ( kernel ws frame )
( ein:and-let* ( ( packet ( websocket-frame-payload frame ) )
( channel ( plist-get ( ein:json-read-from-string packet ) :channel ) ) )
( cond ( ( string-equal channel " iopub " )
( ein:kernel--handle-iopub-reply kernel packet ) )
( ( string-equal channel " shell " )
( ein:kernel--handle-shell-reply kernel packet ) )
( ( string-equal channel " stdin " )
( ein:kernel--handle-stdin-reply kernel packet ) )
( t ( ein:log 'warn " Received reply from unforeseen channel %s " channel ) ) ) ) )
( defun ein:start-single-websocket ( kernel )
( let ( ( ws-url ( concat ( ein:$kernel-ws-url kernel )
( ein:$kernel-kernel-url kernel )
" /channels?session_id= "
( ein:$kernel-session-id kernel ) ) ) )
( ein:log 'verbose " WS start: %s " ws-url )
2018-11-01 20:26:42 -05:00
( setf ( ein:$kernel-websocket kernel )
2018-10-30 14:17:29 -04:00
( ein:websocket ws-url kernel
( apply-partially #' ein:kernel--handle-websocket-reply kernel )
( lambda ( ws )
( let* ( ( websocket ( websocket-client-data ws ) )
( kernel ( ein:$websocket-kernel websocket ) ) )
( unless ( ein:$websocket-closed-by-client websocket )
( ein:log 'warn " WS closed unexpectedly: %s " ( websocket-url ws ) )
( ein:kernel-disconnect kernel ) ) ) )
( lambda ( ws )
( let* ( ( websocket ( websocket-client-data ws ) )
( kernel ( ein:$websocket-kernel websocket ) ) )
( when ( ein:kernel-live-p kernel )
( ein:kernel-run-after-start-hook kernel ) )
( ein:log 'verbose " WS opened: %s " ( websocket-url ws ) ) ) ) ) ) ) )
( defun ein:kernel-start-websocket ( kernel )
( cond ( ( <= ( ein:$kernel-api-version kernel ) 2 )
( error " Api version %s unsupported " ( ein:$kernel-api-version kernel ) ) )
( t ( ein:start-single-websocket kernel ) ) ) )
2012-05-07 14:41:15 +02:00
2014-11-16 11:49:02 -06:00
( defun ein:kernel-on-connect ( kernel content -metadata-not-used- )
( ein:log 'info " Kernel connect_request_reply received. " ) )
2012-05-07 14:41:15 +02:00
2012-05-31 19:33:00 +02:00
( defun ein:kernel-run-after-start-hook ( kernel )
( ein:log 'debug " EIN:KERNEL-RUN-AFTER-START-HOOK " )
( mapc #' ein:funcall-packed
( ein:$kernel-after-start-hook kernel ) ) )
2018-10-30 14:17:29 -04:00
( defun ein:kernel-disconnect ( kernel )
2018-11-03 16:02:04 -04:00
" Close websocket connection to running kernel, but do not
delete the kernel on the server side "
2018-11-02 11:29:51 -04:00
( ein:events-trigger ( ein:$kernel-events kernel ) 'status_disconnected.Kernel )
2018-10-30 14:17:29 -04:00
( ein:aif ( ein:$kernel-websocket kernel )
( progn ( ein:websocket-close it )
( setf ( ein:$kernel-websocket kernel ) nil ) ) ) )
2012-05-07 14:41:15 +02:00
2012-07-19 00:16:00 +02:00
( defun ein:kernel-live-p ( kernel )
2018-10-30 14:17:29 -04:00
( and ( ein:$kernel-p kernel )
( ein:aand ( ein:$kernel-websocket kernel ) ( ein:websocket-open-p it ) ) ) )
2012-05-13 16:54:22 +02:00
( defmacro ein:kernel-if-ready ( kernel &rest body )
2012-05-13 16:51:20 +02:00
" Execute BODY if KERNEL is ready. Warn user otherwise. "
2012-05-13 16:54:22 +02:00
( declare ( indent 1 ) )
2012-07-19 00:16:00 +02:00
` ( if ( ein:kernel-live-p , kernel )
2012-05-13 16:54:22 +02:00
( progn ,@ body )
2018-10-30 14:17:29 -04:00
( message " Kernel unready or closed " )
( ein:log 'verbose " Kernel %s unavailable " ( ein:$kernel-kernel-id , kernel ) ) ) )
2012-05-13 16:54:22 +02:00
2012-05-22 13:34:38 +02:00
;;; Main public methods
;; NOTE: The argument CALLBACKS for the following functions is almost
;; same as the JS implementation in IPython. However, as Emacs
;; lisp does not support closure, value is "packed" using
;; `cons': `car' is the actual callback function and `cdr' is
;; its first argument. It's like using `cons' instead of
;; `$.proxy'.
2017-04-21 17:57:16 -05:00
( defun ein:kernel-object-info-request ( kernel objname callbacks &optional cursor-pos detail-level )
2012-05-22 13:34:38 +02:00
" Send object info request of OBJNAME to KERNEL.
When calling this method pass a CALLBACKS structure of the form:
( :object_info_reply ( FUNCTION . ARGUMENT ) )
2012-08-08 23:27:07 +02:00
Call signature::
( ` funcall ' FUNCTION ARGUMENT CONTENT METADATA )
2015-03-17 20:41:31 -05:00
CONTENT and METADATA are given by ` object_info_reply ' message.
2012-05-13 16:54:22 +02:00
2015-03-17 20:41:31 -05:00
` object_info_reply ' message is documented here:
2012-05-22 13:34:38 +02:00
http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
"
2014-11-16 11:49:02 -06:00
( assert ( ein:kernel-live-p kernel ) nil " object_info_reply: Kernel is not active. " )
2012-05-07 14:41:15 +02:00
( when objname
2018-10-30 14:17:29 -04:00
( if ( <= ( ein:$kernel-api-version kernel ) 2 )
( error " Api version %s unsupported " ( ein:$kernel-api-version kernel ) ) )
2018-09-09 09:44:59 -05:00
( let* ( ( content ( if ( < ( ein:$kernel-api-version kernel ) 5 )
( list
;; :text ""
:oname ( format " %s " objname )
:cursor_pos ( or cursor-pos 0 )
:detail_level ( or detail-level 0 ) )
( list
:code ( format " %s " objname )
:cursor_pos ( or cursor-pos 0 )
:detail_level ( or detail-level 0 ) ) ) )
2017-04-21 17:57:16 -05:00
( msg ( ein:kernel--get-msg kernel " inspect_request "
( append content ( list :detail_level 1 ) ) ) )
( msg-id ( plist-get ( plist-get msg :header ) :msg_id ) ) )
2015-01-14 17:07:28 -06:00
( ein:websocket-send-shell-channel kernel msg )
2015-03-17 20:41:31 -05:00
( ein:kernel-set-callbacks-for-msg kernel msg-id callbacks ) ) ) )
2012-05-22 13:34:38 +02:00
2012-05-22 22:28:32 +02:00
( defun* ein:kernel-execute ( kernel code &optional callbacks
2012-05-22 13:34:38 +02:00
&key
( silent t )
2018-08-07 08:36:26 -05:00
( store-history t )
2012-05-22 13:34:38 +02:00
( user-expressions ( make-hash-table ) )
2018-08-07 08:36:26 -05:00
( allow-stdin t )
( stop-on-error nil ) )
2012-05-22 13:34:38 +02:00
" Execute CODE on KERNEL.
When calling this method pass a CALLBACKS structure of the form:
2012-06-04 16:23:53 +02:00
( :execute_reply EXECUTE-REPLY-CALLBACK
:output OUTPUT-CALLBACK
:clear_output CLEAR-OUTPUT-CALLBACK
2012-06-04 16:18:17 +02:00
:set_next_input SET-NEXT-INPUT )
2012-05-22 13:34:38 +02:00
2018-10-27 17:52:17 -04:00
Right hand sides ending -CALLBACK above must pack a FUNCTION and its
2012-08-08 20:00:32 +02:00
first ARGUMENT in a ` cons ' : :
2012-05-07 14:41:15 +02:00
2012-05-22 13:34:38 +02:00
( FUNCTION . ARGUMENT )
2012-05-07 14:41:15 +02:00
2012-08-08 20:00:32 +02:00
Call signature
--------------
: :
2012-08-08 21:18:00 +02:00
( ` funcall ' EXECUTE-REPLY-CALLBACK ARGUMENT CONTENT METADATA )
( ` funcall ' OUTPUT-CALLBACK ARGUMENT MSG-TYPE CONTENT METADATA )
( ` funcall ' CLEAR-OUTPUT-CALLBACK ARGUMENT CONTENT METADATA )
2012-08-08 20:00:32 +02:00
( ` funcall ' SET-NEXT-INPUT ARGUMENT TEXT )
2012-08-08 21:18:00 +02:00
* Both CONTENT and METADATA objects are plist.
2012-08-08 20:00:32 +02:00
* The MSG-TYPE argument for OUTPUT-CALLBACK is a string
( one of ` stream ', ` display_data ', ` pyout ' and ` pyerr ' ) .
* The CONTENT object for CLEAR-OUTPUT-CALLBACK has
` stdout ', ` stderr ' and ` other ' fields that are booleans.
* The SET-NEXT-INPUT callback will be passed the ` set_next_input ' payload,
which is a string.
See ` ein:kernel--handle-shell-reply ' for how the callbacks are called.
Links
-----
2012-08-08 22:15:22 +02:00
* For general description of CONTENT and METADATA:
http://ipython.org/ipython-doc/dev/development/messaging.html#general-message-format
2012-08-08 20:00:32 +02:00
* ` execute_reply ' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#execute
* Output type messages is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
Sample implementations
----------------------
* ` ein:cell--handle-execute-reply '
* ` ein:cell--handle-output '
* ` ein:cell--handle-clear-output '
* ` ein:cell--handle-set-next-input '
"
2012-08-08 22:15:22 +02:00
;; FIXME: Consider changing callback to use `&key'.
;; Otherwise, adding new arguments to callback requires
;; backward incompatible changes (hence a big diff), unlike
;; Javascript. Downside of this is that there is no short way
;; to write anonymous callback because there is no `lambda*'.
;; You can use `function*', but that's bit long...
;; FIXME: Consider allowing a list of fixed argument so that the
;; call signature becomes something like:
;; (funcall FUNCTION [ARG ...] CONTENT METADATA)
2014-11-16 11:49:02 -06:00
( assert ( ein:kernel-live-p kernel ) nil " execute_reply: Kernel is not active. " )
2018-10-27 17:52:17 -04:00
( let* ( ( content ( list
:code code
:silent ( or silent json-false )
:store_history ( or store-history json-false )
:user_expressions user-expressions
:allow_stdin allow-stdin
:stop_on_error ( or stop-on-error json-false ) ) )
( msg ( ein:kernel--get-msg kernel " execute_request " content ) )
( msg-id ( plist-get ( plist-get msg :header ) :msg_id ) ) )
( ein:log 'debug " KERNEL-EXECUTE: code=%s msg_id=%s " code msg-id )
( run-hook-with-args 'ein:pre-kernel-execute-functions msg )
( ein:websocket-send-shell-channel kernel msg )
( ein:kernel-set-callbacks-for-msg kernel msg-id callbacks )
( unless silent
( mapc #' ein:funcall-packed
( ein:$kernel-after-execute-hook kernel ) ) )
msg-id ) )
2012-05-22 13:34:38 +02:00
( defun ein:kernel-complete ( kernel line cursor-pos callbacks )
" Complete code at CURSOR-POS in a string LINE on KERNEL.
CURSOR-POS is the position in the string LINE, not in the buffer.
When calling this method pass a CALLBACKS structure of the form:
( :complete_reply ( FUNCTION . ARGUMENT ) )
2012-05-07 14:41:15 +02:00
2012-08-08 23:27:07 +02:00
Call signature::
( ` funcall ' FUNCTION ARGUMENT CONTENT METADATA )
CONTENT and METADATA are given by ` complete_reply ' message.
2012-05-07 14:41:15 +02:00
2012-05-22 13:34:38 +02:00
` complete_reply ' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#complete
"
2014-11-16 11:49:02 -06:00
( assert ( ein:kernel-live-p kernel ) nil " complete_reply: Kernel is not active. " )
2018-08-16 08:43:25 -05:00
( let* ( ( content ( if ( < ( ein:$kernel-api-version kernel ) 5 )
( list
;; :text ""
:line line
:cursor_pos cursor-pos )
( list
:code line
:cursor_pos cursor-pos ) ) )
2012-05-22 13:34:38 +02:00
( msg ( ein:kernel--get-msg kernel " complete_request " content ) )
( msg-id ( plist-get ( plist-get msg :header ) :msg_id ) ) )
2015-01-14 17:07:28 -06:00
( ein:websocket-send-shell-channel kernel msg )
2012-05-22 13:34:38 +02:00
( ein:kernel-set-callbacks-for-msg kernel msg-id callbacks )
msg-id ) )
2012-05-07 14:41:15 +02:00
2012-08-28 12:18:12 +02:00
( defun* ein:kernel-history-request ( kernel callbacks
&key
( output nil )
( raw t )
( hist-access-type " tail " )
session
start
stop
2012-09-08 00:04:08 +02:00
( n 10 )
2013-01-18 14:28:21 +01:00
pattern
unique )
2012-08-28 12:18:12 +02:00
" Request execution history to KERNEL.
When calling this method pass a CALLBACKS structure of the form:
( :history_reply ( FUNCTION . ARGUMENT ) )
Call signature::
( ` funcall ' FUNCTION ARGUMENT CONTENT METADATA )
CONTENT and METADATA are given by ` history_reply ' message.
` history_reply ' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#history
2012-09-08 00:04:08 +02:00
Relevant Python code:
* :py:method: ` IPython.zmq.ipkernel.Kernel.history_request `
* :py:class: ` IPython.core.history.HistoryAccessor `
2012-08-28 12:18:12 +02:00
"
2014-11-16 11:49:02 -06:00
( assert ( ein:kernel-live-p kernel ) nil " history_reply: Kernel is not active. " )
2012-08-28 12:18:12 +02:00
( let* ( ( content ( list
:output ( ein:json-any-to-bool output )
:raw ( ein:json-any-to-bool raw )
:hist_access_type hist-access-type
:session session
:start start
:stop stop
:n n
2013-01-18 14:28:21 +01:00
:pattern pattern
:unique unique ) )
2012-08-28 12:18:12 +02:00
( msg ( ein:kernel--get-msg kernel " history_request " content ) )
( msg-id ( plist-get ( plist-get msg :header ) :msg_id ) ) )
2015-01-14 17:07:28 -06:00
( ein:websocket-send-shell-channel kernel msg )
2012-08-28 12:18:12 +02:00
( ein:kernel-set-callbacks-for-msg kernel msg-id callbacks )
msg-id ) )
2014-10-22 21:35:20 -05:00
( defun ein:kernel-connect-request ( kernel callbacks )
" Request basic information for a KERNEL.
When calling this method pass a CALLBACKS structure of the form::
( :connect_reply ( FUNCTION . ARGUMENT ) )
Call signature::
( ` funcall ' FUNCTION ARGUMENT CONTENT METADATA )
CONTENT and METADATA are given by ` kernel_info_reply ' message.
` connect_request ' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#connect
Example::
( ein:kernel-connect-request
( ein:get-kernel )
' ( :kernel_connect_reply ( message . \"CONTENT: %S\\nMETADATA: %S\" ) ) )
"
2015-01-14 17:07:28 -06:00
;(assert (ein:kernel-live-p kernel) nil "connect_reply: Kernel is not active.")
2014-10-22 21:35:20 -05:00
( let* ( ( msg ( ein:kernel--get-msg kernel " connect_request " ( make-hash-table ) ) )
( msg-id ( plist-get ( plist-get msg :header ) :msg_id ) ) )
2015-01-14 17:07:28 -06:00
( ein:websocket-send-shell-channel kernel msg )
2014-10-22 21:35:20 -05:00
( ein:kernel-set-callbacks-for-msg kernel msg-id callbacks )
msg-id ) )
2012-08-28 12:18:12 +02:00
2012-12-15 22:39:02 +01:00
( defun ein:kernel-kernel-info-request ( kernel callbacks )
" Request core information of KERNEL.
When calling this method pass a CALLBACKS structure of the form::
( :kernel_info_reply ( FUNCTION . ARGUMENT ) )
Call signature::
( ` funcall ' FUNCTION ARGUMENT CONTENT METADATA )
CONTENT and METADATA are given by ` kernel_info_reply ' message.
` kernel_info_reply ' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info
Example::
( ein:kernel-kernel-info-request
( ein:get-kernel )
' ( :kernel_info_reply ( message . \"CONTENT: %S\\nMETADATA: %S\" ) ) )
"
2014-11-16 11:49:02 -06:00
( assert ( ein:kernel-live-p kernel ) nil " kernel_info_reply: Kernel is not active. " )
2018-02-14 18:24:20 -06:00
( ein:log 'debug " EIN:KERNEL-KERNEL-INFO-REQUEST: Sending request. " )
2012-12-15 22:39:02 +01:00
( let* ( ( msg ( ein:kernel--get-msg kernel " kernel_info_request " nil ) )
( msg-id ( plist-get ( plist-get msg :header ) :msg_id ) ) )
2015-01-14 17:07:28 -06:00
( ein:websocket-send-shell-channel kernel msg )
2012-12-15 22:39:02 +01:00
( ein:kernel-set-callbacks-for-msg kernel msg-id callbacks )
msg-id ) )
2012-05-07 14:41:15 +02:00
( defun ein:kernel-interrupt ( kernel )
2018-10-30 14:17:29 -04:00
( when ( ein:kernel-live-p kernel )
2012-05-07 14:41:15 +02:00
( ein:log 'info " Interrupting kernel " )
2012-06-20 21:01:58 +02:00
( ein:query-singleton-ajax
( list 'kernel-interrupt ( ein:$kernel-kernel-id kernel ) )
2012-05-26 21:08:48 +02:00
( ein:url ( ein:$kernel-url-or-port kernel )
( ein:$kernel-kernel-url kernel )
" interrupt " )
:type " POST "
2012-12-29 17:17:39 +01:00
:success ( lambda ( &rest ignore )
( ein:log 'info " Sent interruption command. " ) ) ) ) )
2012-05-07 14:41:15 +02:00
2018-11-03 16:02:04 -04:00
( defun ein:kernel-delete-session ( kernel &optional callback )
2018-11-01 20:08:10 -04:00
" Regardless of success or error, we clear all state variables of kernel and funcall CALLBACK of arity 0 (e.g., kernel restart) "
2018-11-03 16:02:04 -04:00
( ein:and-let* ( ( session-id ( ein:$kernel-session-id kernel ) ) )
2012-06-20 21:01:58 +02:00
( ein:query-singleton-ajax
2018-11-03 16:02:04 -04:00
( list 'kernel-delete-session session-id )
2018-10-30 14:17:29 -04:00
( ein:url ( ein:$kernel-url-or-port kernel ) " api/sessions " session-id )
2012-05-26 21:08:48 +02:00
:type " DELETE "
2018-11-03 16:02:04 -04:00
:complete ( apply-partially #' ein:kernel-delete-session--complete kernel session-id callback )
:error ( apply-partially #' ein:kernel-delete-session--error session-id callback )
:success ( apply-partially #' ein:kernel-delete-session--success session-id callback ) ) ) )
2018-10-30 14:17:29 -04:00
2018-11-03 16:02:04 -04:00
( defun* ein:kernel-delete-session--error ( session-id callback
2018-10-30 14:17:29 -04:00
&key response error-thrown
&allow-other-keys )
2018-11-03 16:02:04 -04:00
( ein:log 'error " ein:kernel-delete-session--error %s: ERROR %s DATA %s "
2018-10-30 14:17:29 -04:00
session-id ( car error-thrown ) ( cdr error-thrown ) ) )
2018-11-03 16:02:04 -04:00
( defun* ein:kernel-delete-session--success ( session-id callback &key data symbol-status response
2018-10-30 14:17:29 -04:00
&allow-other-keys )
2018-11-03 16:02:04 -04:00
( ein:log 'verbose " ein:kernel-delete-session--success: %s deleted " session-id ) )
2018-10-30 14:17:29 -04:00
2018-11-03 16:02:04 -04:00
( defun* ein:kernel-delete-session--complete ( kernel session-id callback &key data response
&allow-other-keys
2018-11-01 20:08:10 -04:00
&aux ( resp-string ( format " STATUS: %s DATA: %s " ( request-response-status-code response ) data ) ) )
2018-11-03 16:02:04 -04:00
( ein:log 'debug " ein:kernel-delete-session--complete %s " resp-string )
2018-11-01 20:08:10 -04:00
( ein:kernel-disconnect kernel )
( when callback ( funcall callback ) ) )
2012-05-22 13:34:38 +02:00
2018-10-30 14:17:29 -04:00
;; Reply handlers.
2012-05-22 13:34:38 +02:00
( defun ein:kernel-get-callbacks-for-msg ( kernel msg-id )
2012-05-23 01:13:10 +02:00
( gethash msg-id ( ein:$kernel-msg-callbacks kernel ) ) )
2012-05-22 13:34:38 +02:00
( defun ein:kernel-set-callbacks-for-msg ( kernel msg-id callbacks )
( puthash msg-id callbacks ( ein:$kernel-msg-callbacks kernel ) ) )
2015-03-31 16:55:14 -05:00
( defun ein:kernel--handle-stdin-reply ( kernel packet )
( ein:log 'debug " KERNEL--HANDLE-STDIN-REPLY " )
( setf ( ein:$kernel-stdin-activep kernel ) t )
( destructuring-bind
( &key header parent_header metadata content &allow-other-keys )
( ein:json-read-from-string packet )
( let ( ( msg-type ( plist-get header :msg_type ) )
( msg-id ( plist-get header :msg_id ) )
( password ( plist-get content :password ) ) )
2018-10-27 17:52:17 -04:00
( ein:log 'debug " KERNEL--HANDLE-STDIN-REPLY: msg_type=%s msg_id=%s "
msg-type msg-id )
2015-03-31 16:55:14 -05:00
( cond ( ( string-equal msg-type " input_request " )
2015-03-31 17:26:00 -05:00
( if ( not ( eql password :json-false ) )
2015-03-31 16:55:14 -05:00
( let* ( ( passwd ( read-passwd ( plist-get content :prompt ) ) )
( content ( list :value passwd ) )
( msg ( ein:kernel--get-msg kernel " input_reply " content ) ) )
( ein:websocket-send-stdin-channel kernel msg )
2015-03-31 17:26:00 -05:00
( setf ( ein:$kernel-stdin-activep kernel ) nil ) )
2016-11-29 22:06:03 -06:00
( cond ( ( string-match " ipdb> " ( plist-get content :prompt ) ) ( ein:run-ipdb-session kernel " ipdb> " ) )
2018-03-05 15:56:16 -06:00
( ( string-match " (Pdb) " ( plist-get content :prompt ) ) ( ein:run-ipdb-session kernel " (Pdb) " ) )
( t ( let* ( ( in ( read-string ( plist-get content :prompt ) ) )
( content ( list :value in ) )
( msg ( ein:kernel--get-msg kernel " input_reply " content ) ) )
( ein:websocket-send-stdin-channel kernel msg )
( setf ( ein:$kernel-stdin-activep kernel ) nil ) ) ) ) ) ) ) ) ) )
2015-03-31 16:55:14 -05:00
2012-05-22 13:34:38 +02:00
( defun ein:kernel--handle-shell-reply ( kernel packet )
2012-07-22 13:04:09 +02:00
( ein:log 'debug " KERNEL--HANDLE-SHELL-REPLY " )
2012-05-22 13:34:38 +02:00
( destructuring-bind
2012-08-08 21:18:00 +02:00
( &key header content metadata parent_header &allow-other-keys )
2012-05-22 13:34:38 +02:00
( ein:json-read-from-string packet )
2012-05-23 01:31:56 +02:00
( let* ( ( msg-type ( plist-get header :msg_type ) )
2012-05-22 22:28:32 +02:00
( msg-id ( plist-get parent_header :msg_id ) )
( callbacks ( ein:kernel-get-callbacks-for-msg kernel msg-id ) )
2012-05-23 01:31:56 +02:00
( cb ( plist-get callbacks ( intern ( format " :%s " msg-type ) ) ) ) )
2018-10-27 17:52:17 -04:00
( ein:log 'debug " KERNEL--HANDLE-SHELL-REPLY: msg_type=%s msg_id=%s "
msg-type msg-id )
2018-05-12 17:32:32 -05:00
( run-hook-with-args 'ein:on-shell-reply-functions msg-type header content metadata )
2018-10-27 17:52:17 -04:00
( ein:aif cb ( ein:funcall-packed it content metadata ) )
2012-05-22 13:34:38 +02:00
( ein:aif ( plist-get content :payload )
2012-10-09 20:09:23 +02:00
( ein:kernel--handle-payload kernel callbacks it ) )
( let ( ( events ( ein:$kernel-events kernel ) ) )
( ein:case-equal msg-type
( ( " execute_reply " )
2012-10-09 20:26:45 +02:00
( ein:aif ( plist-get content :execution_count )
;; It can be `nil' for silent execution
( ein:events-trigger events 'execution_count.Kernel it ) ) ) ) ) ) )
2012-07-22 13:04:09 +02:00
( ein:log 'debug " KERNEL--HANDLE-SHELL-REPLY: finished " ) )
2012-05-22 13:34:38 +02:00
2012-06-04 16:18:17 +02:00
( defun ein:kernel--handle-payload ( kernel callbacks payload )
2012-05-22 13:34:38 +02:00
( loop with events = ( ein:$kernel-events kernel )
for p in payload
2016-12-18 23:09:07 -06:00
for text = ( or ( plist-get p :text )
( plist-get ( plist-get p :data )
:text/plain ) )
2012-05-22 13:34:38 +02:00
for source = ( plist-get p :source )
2013-02-10 05:00:40 +01:00
if ( member source ' ( " IPython.kernel.zmq.page.page "
2014-05-23 15:21:16 -04:00
" IPython.zmq.page.page "
" page " ) )
2012-05-23 23:32:51 +02:00
do ( when ( not ( equal ( ein:trim text ) " " ) )
( ein:events-trigger
2012-06-06 21:03:54 +02:00
events 'open_with_text.Pager ( list :text text ) ) )
2012-05-22 13:34:38 +02:00
else if
2013-02-10 05:00:40 +01:00
( member
source
' ( " IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.set_next_input "
2016-12-18 23:09:07 -06:00
" IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input "
" set_next_input " ) )
2012-06-04 21:12:06 +02:00
do ( let ( ( cb ( plist-get callbacks :set_next_input ) ) )
( when cb ( ein:funcall-packed cb text ) ) ) ) )
2012-05-22 13:34:38 +02:00
( defun ein:kernel--handle-iopub-reply ( kernel packet )
2012-07-22 13:04:09 +02:00
( ein:log 'debug " KERNEL--HANDLE-IOPUB-REPLY " )
2015-04-10 13:59:36 -05:00
( if ( ein:$kernel-stdin-activep kernel )
( ein:ipdb--handle-iopub-reply kernel packet )
( destructuring-bind
( &key content metadata parent_header header &allow-other-keys )
( ein:json-read-from-string packet )
( let* ( ( msg-type ( plist-get header :msg_type ) )
2018-10-27 17:52:17 -04:00
( msg-id ( plist-get parent_header :msg_id ) )
( callbacks ( ein:kernel-get-callbacks-for-msg kernel msg-id ) )
2015-04-10 13:59:36 -05:00
( events ( ein:$kernel-events kernel ) ) )
2018-10-27 17:52:17 -04:00
( ein:log 'debug " KERNEL--HANDLE-IOPUB-REPLY: msg_type=%s msg_id=%s "
2018-11-01 20:26:42 -05:00
msg-type msg-id )
2015-04-10 13:59:36 -05:00
( if ( and ( not ( equal msg-type " status " ) ) ( null callbacks ) )
2018-11-01 20:08:10 -04:00
( ein:log 'verbose " Not processing msg_type=%s msg_id=%s " msg-type msg-id )
2015-04-10 13:59:36 -05:00
( ein:case-equal msg-type
( ( " stream " " display_data " " pyout " " pyerr " " error " " execute_result " )
( ein:aif ( plist-get callbacks :output )
( ein:funcall-packed it msg-type content metadata ) ) )
( ( " status " )
( ein:case-equal ( plist-get content :execution_state )
( ( " busy " )
( ein:events-trigger events 'status_busy.Kernel ) )
( ( " idle " )
( ein:events-trigger events 'status_idle.Kernel ) )
( ( " dead " )
2018-10-30 14:17:29 -04:00
( ein:kernel-disconnect kernel ) ) ) )
2015-04-10 13:59:36 -05:00
( ( " data_pub " )
( ein:log 'verbose ( format " Received data_pub message w/content %s " packet ) ) )
( ( " clear_output " )
( ein:aif ( plist-get callbacks :clear_output )
2018-11-01 20:08:10 -04:00
( ein:funcall-packed it content metadata ) ) ) ) ) ) ) )
( ein:log 'debug " KERNEL--HANDLE-IOPUB-REPLY: finished " ) )
2012-05-07 14:41:15 +02:00
2012-05-27 05:19:17 +02:00
;;; Utility functions
2012-08-04 00:26:30 +02:00
( 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 ) )
2012-07-08 13:45:30 +02:00
( defun ein:kernel-construct-defstring ( content )
" Construct call signature from CONTENT of ` ` :object_info_reply ` ` .
2012-08-19 21:36:35 +02:00
Used in ` ein:pytools-finish-tooltip ', etc. "
2012-07-08 13:45:30 +02:00
( or ( plist-get content :call_def )
( plist-get content :init_definition )
( plist-get content :definition ) ) )
( defun ein:kernel-construct-help-string ( content )
" Construct help string from CONTENT of ` ` :object_info_reply ` ` .
2012-08-19 21:36:35 +02:00
Used in ` ein:pytools-finish-tooltip ', etc. "
2012-07-25 18:59:41 +02:00
( ein:log 'debug " KERNEL-CONSTRUCT-HELP-STRING " )
2012-07-25 18:51:02 +02:00
( let* ( ( defstring ( ein:aand
( ein:kernel-construct-defstring content )
( ansi-color-apply it )
( ein:string-fill-paragraph it ) ) )
2012-07-25 18:55:55 +02:00
( docstring ( ein:aand
( or ( plist-get content :call_docstring )
( plist-get content :init_docstring )
( plist-get content :docstring )
;; "<empty docstring>"
)
( ansi-color-apply it ) ) )
2012-07-25 21:54:56 +02:00
( help ( ein:aand
( ein:filter 'identity ( list defstring docstring ) )
( ein:join-str " \n " it ) ) ) )
2012-07-25 18:59:41 +02:00
( ein:log 'debug " KERNEL-CONSTRUCT-HELP-STRING: help=%s " help )
2012-07-08 13:45:30 +02:00
help ) )
2012-05-27 05:49:41 +02:00
( defun ein:kernel-request-stream ( kernel code func &optional args )
2012-08-04 00:04:16 +02:00
" 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. "
2012-05-27 05:49:41 +02:00
( ein:kernel-execute
kernel
code
2012-08-08 21:30:14 +02:00
( list :output ( cons ( lambda ( packed msg-type content -metadata-not-used- )
2012-05-27 05:49:41 +02:00
( let ( ( func ( car packed ) )
( args ( cdr packed ) ) )
( when ( equal msg-type " stream " )
2018-10-13 19:59:41 +02:00
( ein:aif ( plist-get content :text )
2012-05-27 05:49:41 +02:00
( apply func it args ) ) ) ) )
( cons func args ) ) ) ) )
2012-09-05 22:46:34 +02:00
( defun* ein:kernel-history-request-synchronously
( kernel &rest args &key ( timeout 0.5 ) ( tick-time 0.05 ) &allow-other-keys )
" Send the history request and wait TIMEOUT seconds.
Return a list ( CONTENT METADATA ) .
This function checks the request reply every TICK-TIME seconds.
See ` ein:kernel-history-request ' for other usable options. "
;; As `result' and `finished' are set in callback, make sure they
;; won't be trapped in other let-bindings.
( lexical-let ( result finished )
( apply
#' ein:kernel-history-request
kernel
( list :history_reply
( cons ( lambda ( -ignore- content metadata )
( setq result ( list content metadata ) )
( setq finished t ) )
nil ) )
args )
( loop repeat ( floor ( / timeout tick-time ) )
do ( sit-for tick-time )
when finished
return t
finally ( error " Timeout " ) )
result ) )
( defun ein:kernel-history-search-synchronously ( kernel pattern &rest args )
" Search execution history in KERNEL using PATTERN.
Return matched history as a list of strings.
See ` ein:kernel-history-request-synchronously ' and
` ein:kernel-history-request ' for usable options. "
( let ( ( reply
( apply #' ein:kernel-history-request-synchronously
kernel
:hist-access-type " search "
:pattern pattern
args ) ) )
( mapcar #' caddr ( plist-get ( car reply ) :history ) ) ) )
2012-05-07 14:41:15 +02:00
( provide 'ein-kernel )
;;; ein-kernel.el ends here