Commit c4e9ee3f authored by Ludovic Courtès's avatar Ludovic Courtès
Browse files

Get rid of ";;guix kernel" and ";;guix run".

We now automatically spawn the kernel found in the environment instead
of running the "inner proxy".  That simplifies code (the inner proxy is
no longer used) and simplifies usage (no need to remember to do ";;guix
kernel" once you have set up the environment.)

* guix-jupyter-kernel.scm (reply-for-environment-kernel): New
procedure.
(create-environment): Build the profile from MANIFEST, and call
'available-kernel-specs-files' in there.  Return an error message if we
didn't find exactly one kernel spec.
(reply-execute-request): Support ";;guix environment NAME" magic.
Remove support for 'magic-run?' and 'magic-kernel?'.
(start-container): Take a profile rather than a manifest.
[spawn]: Use 'available-kernel-specs-files' and 'exec-kernel' rather
than 'run-inner-proxy'.
* guix/jupyter/magic.scm (magic-env?): Adjust accordingly.
* guix-kernel-demo.ipynb: Update.
parent e519e8d6
......@@ -32,6 +32,7 @@
(guix build utils)
(guix gexp)
(guix store)
(guix derivations)
(guix monads)
(guix modules)
(guix profiles)
......@@ -139,6 +140,23 @@ NAME with MANIFEST."
port)))
count))
(define* (reply-for-environment-kernel kernel message
#:key name specs (count 0))
"Send KERNEL a reply to MESSAGE saying that we found the kernel SPECS."
(send-message kernel
(reply message "execute_result"
(scm->json-string
`(("data" .
(("text/html"
. ,(sxml->html-string
`(div
"Running "
,(kernel-specs-display-name specs)
" kernel.")))))
("metadata" . ())
("execution_count" . ,count))))
#:kernel-socket kernel-iopub))
(define* (create-environment name specs state
#:key kernel message)
"Spawn a new execution environment called NAME and containing SPECS, a list
......@@ -147,23 +165,67 @@ to KERNEL as a reply to MESSAGE, and return STATE suitably adjusted."
(define manifest
(specifications->manifest (force %inferior) specs))
(define store
(proxy-state-store state))
(define counter
(proxy-state-execution-count state))
(format/log "creating new environment ~s~%" name)
;; Reply right away without waiting for the profile to be built.
(reply-for-environment kernel message
#:name name
#:manifest manifest
#:count (proxy-state-execution-count state))
(let* ((store (proxy-state-store state))
(container (run-with-store store
(start-container container-context
name manifest)))
(state (register-proxied name container state)))
(monitor-client container)
(send-message container message)
(set-proxy-state-default-environment (increment-execution-count state)
name)))
#:count counter)
(let ((profile (run-with-store store
(mlet %store-monad ((drv (profile-derivation manifest)))
(mbegin %store-monad
(built-derivations (list drv)) ;XXX: somewhat ugly
(return drv))))))
(match (available-kernel-specs-files
(list (string-append (derivation->output-path profile)
"/share/jupyter")))
((specs-file)
(let* ((container (run-with-store store
(start-container container-context
name profile)))
(specs (call-with-input-file specs-file
json->kernel-specs))
(state (register-proxied name container state)))
(monitor-client container)
(reply-for-environment-kernel kernel message
#:name name
#:specs specs
#:count counter)
(set-proxy-state-default-environment (increment-execution-count state)
name)))
(()
(reply-html kernel message
(sxml->html-string
`(bold "No kernel found in environment "
(code ,name) "!"))
counter)
;; TODO: Send "error".
state)
((lst ...)
(reply-html kernel message
(sxml->html-string
`(div
(bold "Found " ,(length lst)
" kernels in environment "
(code ,name) ":")
(ul ,@(map (lambda (file)
(let ((specs (call-with-input-file file
json->kernel-specs)))
`(li ,(kernel-specs-display-name specs))))
lst))
"Which one should we use? Please create an "
"environment containing exactly one kernel."))
counter)
;; TODO: Send "error".
state))))
(define (reply-execute-request kernel kind message state)
(let* ((content (message-content message))
......@@ -191,6 +253,21 @@ to KERNEL as a reply to MESSAGE, and return STATE suitably adjusted."
(unregister-proxied name state)
#:kernel kernel
#:message message))))
((_ "environment" name)
(match (lookup-proxied name state)
(#f
(reply-html kernel message
(sxml->html-string
`(bold "Unknown environment "
(code ,name) "."))
count)
(increment-execution-count state))
((? kernel? proxy)
(format/log "evaluating code in container ~s (PID ~s)~%"
name (kernel-pid proxy))
(send-message proxy message)
(let ((state (increment-execution-count state)))
(set-proxy-state-default-environment state name)))))
((lst ...)
(reply-html kernel message
(sxml->html-string
......@@ -198,15 +275,6 @@ to KERNEL as a reply to MESSAGE, and return STATE suitably adjusted."
" magic."))
count)
(increment-execution-count state))))
((or (magic-run? code)
(magic-kernel? code))
(let* ((list (string-split magic #\ ))
(env-name (list-ref list 2))
(proxy (lookup-proxied env-name state)))
(format/log "evaluating code in container ~s (PID ~s)~%"
env-name (kernel-pid proxy))
(send-message proxy message)
(increment-execution-count state)))
((magic-html? code)
(reply-html kernel message (delete-magic code) count)
(increment-execution-count state))
......@@ -228,9 +296,10 @@ yet. You can create one by entering a “magic command” in a cell as follows:
python-ipython python-ipykernel"))
"You can then select a kernel and hand it code
to execute:"
(p (code
(pre ";;guix kernel my-environment ipython\n"
"2 + 2")))))
(p (pre
(code
";;guix kernel my-environment ipython\n"
"2 + 2")))))
count)
state)))
(λ error
......@@ -274,9 +343,9 @@ imported."
(('jupyter _ ...) #t)
(_ #f)))
(define (start-container context name manifest)
"Start a container with the given NAME and with a profile built from
MANIFEST. Return, as a monadic value, a <kernel> connected to the process in
(define (start-container context name profile)
"Start a container with the given NAME, and run the kernel found in
PROFILE. Return, as a monadic value, a <kernel> connected to the process in
that container."
(define guile-gcrypt
(specification->package "guile-gcrypt"))
......@@ -290,20 +359,14 @@ that container."
(with-imported-modules (source-module-closure
'((guix profiles)
(guix search-paths)
(guix jupyter inner-proxy)
(jupyter kernels))
#:select? module-to-import?)
#~(begin
(use-modules (guix profiles)
(guix search-paths)
(guix jupyter inner-proxy)
(jupyter kernels) ;json->connection
(ice-9 match))
(format (current-error-port)
"starting inner proxy with profile '~a'...~%"
#$profile)
;; (set-network-interface-up "lo") ;up lo interface
;; Do like 'guix environment'.
......@@ -321,8 +384,14 @@ that container."
(profile-search-paths #$profile))
(let ((str #$(scm->json-string (connection->json connection))))
(run-inner-proxy #$(number->string session-id)
(json->connection str)))))))
(match (available-kernel-specs-files)
((specs-file)
(format (current-error-port)
"starting kernel from '~a'...~%"
specs-file)
(let ((specs (call-with-input-file specs-file
json->kernel-specs)))
(exec-kernel specs (json->connection str))))))))))
(define-values (connection kernel)
(allocate-connection context "tcp" "127.0.0.1"
......@@ -346,13 +415,12 @@ that container."
;; network namespace. TODO: Arrange to use Unix-domain sockets instead.
(delq 'net %namespaces))
(mlet* %store-monad ((_ ((lift1 mkdir-p %store-monad) root))
(profile (profile-derivation manifest))
(pid (eval/container* (spawn profile) root
#:mounts fs
#:namespaces namespaces
#:guest-uid 1000
#:guest-gid 1000)))
(mlet* %store-monad ((_ ((lift1 mkdir-p %store-monad) root))
(pid (eval/container* (spawn profile) root
#:mounts fs
#:namespaces namespaces
#:guest-uid 1000
#:guest-gid 1000)))
(return (set-kernel-pid kernel pid))))
......
%% Cell type:markdown id: tags:
# Demo Guix Jupyter kernel
Project source code: [Guix-kernel](https://gitlab.inria.fr/guix-hpc/guix-kernel).
![Guix-Jupyter](logo.png)
---
## Features
## Getting Started
### Magic command
This kernel use magic cells for kernel command.
```scheme
;;guix <command> <args>
<code>
```
Different commands are available:
- **environment** to create new environment with container
- **run** eval code in guix kernel container
- **kernel** eval code in specific kernel
The `;;guix environment` _magic command_ allows you to create an execution environment containing a Jupyter kernel, or to run code in a previously-created environment.
---
%% Cell type:markdown id: tags:
## Usage
### Evaluating Code in a Jupyter Kernel
### Environment: Multi environment
#### Default environment
Create an environment called `guile-kernel` containing the Guile kernel for Jupyter:
%% Cell type:code id: tags:
``` scheme
;;guix environment env-guile <- guile@2.2 guile-commonmark
(version)
```
%% Cell type:markdown id: tags:
### Run: Run code in existing environment
Check current guile-version:
%% Cell type:code id: tags:
``` scheme
(version)
```
%% Cell type:code id: tags:
``` scheme
;;guix run env-guile
(system* "guile" "--version")
(search-path (string-split (getenv "PATH") #\;) "guile")
;;guix environment guile-kernel-env <- jupyter-guile-kernel
```
%% Cell type:markdown id: tags:
Define function:
Run code in guile kernel:
%% Cell type:code id: tags:
``` scheme
;;guix environment guile-kernel-env
(define (list-environ env)
(for-each (lambda (variable)
(display variable)
(newline))
(environ)))
```
%% Cell type:markdown id: tags:
Use function:
%% Cell type:code id: tags:
``` scheme
;;guix run env-guile
;; Here we don't repeat "guix environment", so we use the
;; same environment as the previous cell.
(list-environ (environ))
```
%% Cell type:markdown id: tags:
### Kernel: Eval code in other jupyter kernel
Create environment 'guile-kernel' with guile kernel for jupyter:
%% Cell type:code id: tags:
``` scheme