Skip to content

Commit

Permalink
nREPL support (#3)
Browse files Browse the repository at this point in the history
* Added nrepl-server support
  • Loading branch information
ikappaki authored Dec 2, 2024
1 parent 425fad5 commit 6a6a97c
Show file tree
Hide file tree
Showing 15 changed files with 752 additions and 26 deletions.
27 changes: 16 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
name: Basilisp Jupyter Kernel package

on:
push:
pull_request:
types: [ opened, synchronize, reopened ]
push:
branches: [ main ]

jobs:
build:
Expand All @@ -11,22 +13,25 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.10"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy-3.10"]
include:
- os: windows-latest
python-version: "3.12"
python-version: "3.13"
- os: macos-latest
python-version: "3.12"
python-version: "3.13"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -e ".[test]"
- name: Test with kernel tester
pipx install hatch
hatch env create test
hatch env find
- name: Test
run: |
python -m unittest -v
hatch run test:basilisp test -- -v
- name: Check Kernel
run: |
cd $HOME
jupyter kernelspec list | grep basilisp
hatch run test:jupyter kernelspec list | grep -E "jupyter/kernels/basilisp|jupyter\\kernels\\basilisp"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ data_kernelspec
/notebooks/new
/notebooks/no2_concentrations.png

.nrepl-port
.calva/
58 changes: 58 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Table of contents
- [`basilisp-kernel.nrepl-server`](#basilisp-kernel.nrepl-server)
- [`server-shut`](#basilisp-kernel.nrepl-server/server-shut) - Convenience function to shutdown the <code>SERVER</code>.
- [`server-start`](#basilisp-kernel.nrepl-server/server-start) - Starts the nrepl-server in async mode according to <code>OPTS</code>, using a asyncio task to schedule any pending client work every 100ms.

-----
# <a name="basilisp-kernel.nrepl-server">basilisp-kernel.nrepl-server</a>






## <a name="basilisp-kernel.nrepl-server/server-shut">`server-shut`</a><a name="basilisp-kernel.nrepl-server/server-shut"></a>
``` clojure

(server-shut server)
```
Function.

Convenience function to shutdown the `SERVER`.
<p><sub><a href="https://github.com/ikappaki/basilisp-kernel/blob/master/basilisp_kernel/nrepl_server.lpy#L87-L90">Source</a></sub></p>

## <a name="basilisp-kernel.nrepl-server/server-start">`server-start`</a><a name="basilisp-kernel.nrepl-server/server-start"></a>
``` clojure

(server-start)
(server-start {:keys [host port dir interval-sec], :as opts, :or {port 0, interval-sec 0.1}})
```
Function.

Starts the nrepl-server in async mode according to `OPTS`, using a
asyncio task to schedule any pending client work every 100ms.

`OPTS` is a map that can have the following keys. It defaults to {}.

`:dir` The directory where the `.nrepl-port` file should be created
at. It defaults to the current working directory if not given or
empty.

`:host` The interface address the server should be bound to. It
defaults to 127.0.0.1 if not given or empty.

`:port` The port number the server should listen to. It defaults to
0, which indicates a random available port number.

It return the `server`, which is a map with the following keys

`:host` The host interface to which the server is bound.

`:nrepl-port-file` The path to the `.nrepl-port` file created by the
server.

`:port` The port on the host interface the server listens for client
connections.

`:shutdown!` A function to shutdown the server.
<p><sub><a href="https://github.com/ikappaki/basilisp-kernel/blob/master/basilisp_kernel/nrepl_server.lpy#L25-L85">Source</a></sub></p>
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Added nREPL Server support.
- Dropped Python 3.8 support and added 3.13 in CI testing.
- Set minimum Basilisp version to >=0.3.2.

## 1.1.0

- Optimized output to stdout/stderr by removing proxy string intermediary.
Expand Down
190 changes: 178 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

# Basilisp Kernel for Jupyter

Welcome to the Basilisp Kernel for Jupyter! This kernel allows you to run Basilisp code directly in your Jupyter notebooks.
[Basilisp](https://github.com/basilisp-lang/basilisp) is a Python-based Lisp implementation that offers broad compatibility with Clojure. Refer to [documentation](https://basilisp.readthedocs.io/en/latest/index.html) to get started.

[Basilisp](https://github.com/basilisp-lang/basilisp) is a Python-based Lisp implementation that offers broad compatibility with Clojure.

# Overview

The Basilisp Kernel enables running Basilisp Clojure code directly within Jupyter notebooks.

## Features

- Full integration with Jupyter Notebook and JupyterLab
- Enhanced autocompletion features
- Seamless interoperability with Python libraries
- Full integration with Jupyter Notebook and JupyterLab.
- Enhanced autocompletion features using the `Tab` key.
- Seamless interoperability with Python libraries.
- Interactive development with your preferred editor, powered by the nREPL server running inside the notebook.

## Installation

Expand All @@ -35,20 +39,182 @@ Start your Jupyter notebook server:
jupyter notebook
```

In the Jupyter interface, select the Basilisp kernel when creating a new notebook.


## Documentation

For full documentation on Basilisp, visit [Basilisp Documentation](https://basilisp.readthedocs.io/en/latest/).
In the Jupyter interface, select the `Basilisp` Kernel when creating a new notebook.

## Examples

This project includes a series of Jupyter notebooks that demonstrate various features and capabilities. You can find these notebooks in the [notebooks](notebooks) directory of this repository.

![notebook plotting example](notebooks/nb-plot.png)

## nREPL support

See [API.md](API.md).

The Basilisp Kernel includes an nREPL server, allowing remote interaction with notebooks using Clojure-Enabled Editors like Emacs (via [CIDER](https://docs.cider.mx/cider/platforms/basilisp.html)) and VS code (via [Calva](https://calva.io/basilisp/)). For detailed use cases, visit the [Connecting Your Editor to the nREPL Server](https://github.com/ikappaki/basilisp-kernel/wiki/Connecting-Your-Editor-to-the-nREPL-Server) page.

### Starting the nREPL Server

Start the nREPL in your notebook by running:
```clojure
(require '[basilisp-kernel.nrepl-server :refer [server-start server-shut]])
(def server (server-start))
=> nREPL server started on port 58966 on host 127.0.0.1 - nrepl://127.0.0.1:58966
=> #'user/server
```

For additional configuration options, such as specifying a port with `:port` or directory for the `.nrepl-port` file with `:dir`, consult the [server-start](API.md#basilisp-kernel.nrepl-server/server-start) documentation.

### Stopping the nREPL Server

```clojure
(server-shut server)
=> :shut
```

## Getting Started with Basilisp Notebooks Development

Below are various methods to help you start writing Basilisp code that can be loaded in a Basilisp notebook.

### 🔋 Batteries Included Project: `basilex-notebook`

The [Basilisp Example Notebook](https://github.com/ikappaki/basilex-notebook) repository, is an excellent resource for exploring Basilisp notebooks. It includes Jupyter, the Basilisp kernel, a skeleton Basilisp library, and a development notebook to help you get started.

1. Install [Poetry](https://python-poetry.org/docs/) for dependency management.

2. Clone the repository, install dependencies, activate the environment and start Jupyter:

```shell
$ git clone https://github.com/ikappaki/basilex-notebook.git
$ cd basilex-notebook
$ poetry install
$ poetry shell
(<env>) $ jupyter notebook
```

3. Open the `dev.ipynb` notebook from the Jupyter interface. Refer to the [basilex-notebook documentation](https://github.com/ikappaki/basilex-notebook) for more details.

***
### 💾 Local Basilisp Files

You can seamlessly write and use Basilisp `.lpy` files in the same directory as your Notebook. These files can be required in the Notebook just like standard Basilisp namespaces.

For example, if you create a file named `dev.lpy` with the following content

`./dev.lpy`
```clojure
(ns dev)

(defn hello []
:hi)
```

You can load and use it in your Notebook as follows

```Clojure
[n]: (require 'dev)
[n+1]: (dev/hello)
:hi
```

#### nREPL development

You can start an nREPL server directly within a Notebook and connect to it using a Clojure-enabled editor.

In a notebook cell:

1. Load the nREPL server namespace:
```clojure
[n]: (require '[basilisp-kernel.nrepl-server :as nrepl-server])
```
2. Start the nREPL server at a random port
```Clojure
[n]: (def server (nrepl-server/server-start))
nREPL server started on port 59498 on host 127.0.0.1 - nrepl://127.0.0.1:59498
#'user/server
```

To connect your editor to the nREPL server create a `basilisp.edn` file in the same directory as your Notebook.

Open your Clojure-enabled editor and use its nREPL connection commands. The server generated a `.nrepl-port file` in the directory, which helps the editor locate the port.

Both [Emacs/CIDER](https://docs.cider.mx/cider/platforms/basilisp.html) and [VSCode/Calva](https://calva.io/basilisp/) offer explicit support for Basilisp.

#### CIDER (Emacs)

1. Run `M-x cider-connect-clj`.
2. Select `localhost`.
3. Select the `<project-dir>:<port number>` option.

#### Calva (VSCode)
1. Press `Ctrl-Alt-P` to open the Command Palette.
2. Select `Calva: Connect to a Running REPL Server, in your project`>`basilisp`.
3. The editor will automatically find the port using `.nrepl-port`.

The Editor should now connect seamlessly to the nREPL server.

***
### 📚 Basilisp Example Library Project: `basilex-basilib`

The [Basilisp Example Library](https://github.com/ikappaki/basilex-basilib) repository provides a starting point for setting up an external Basilisp library that can be integrated with your notebooks.

The instructions below assume you are working in a virtual environment where the Basilisp Notebooks will be launched (e.g. the `basiliex-notebook` setup above). This is indicated by the environment name prefix in your command prompt, such as `<venv>` in the examples below.

#### Setup

Clone the repository and install the `basilib` library as an editable package in in the same virtual environment as your Basilisp notebooks:

```shell
(<env>) $ git clone https://github.com/ikappaki/basilex-basilib.git
(<env>) $ cd basilex-basilib
# install example library in virtual environment as editable package
(<env>) $ pip install -e .
```

#### Usage

Load and interact with the library in your notebooks:

```clojure
[n]: (require '[basilib.core :as bc])
[n+1]: (bc/time-now)
=> '2024-11-30 14:53:00'
```

#### nREPL development

For interactive coding, you can connect your preferred Clojure-Enabled Editor to a running nREPL server in your Basilisp Notebook.

In a notebook cell:

1. Load the nREPL server namespace:
```clojure
[n]: (require '[basilisp-kernel.nrepl-server :as nrepl-server])
```
2. Start the nREPL server on a fixed port:
```Clojure
[n]: (def server (server-start {:port 9998}))
nREPL server started on port 9998 on host 127.0.0.1 - nrepl://127.0.0.1:9998
#'user/server
```

In your code Editor, open the `basilisp.edn` file located in your project root directory (e.g. in `basilex-notebook`, `basilex-basilib` or your own Basilisp Project) to enable Clojure-specific features in your editor. Then, use your editor's commands to connect to the nREPL server.

Both [Emacs/CIDER](https://docs.cider.mx/cider/platforms/basilisp.html) and [VSCode/Calva](https://calva.io/basilisp/) offer explicit support for Basilisp.

###### Connecting via CIDER (Emacs)

1. Run `M-x cider-connect-clj`
2. Select `localhost`.
3. Enter the port number from the nREPL server output.

###### Connecting Calva (VSCode)

1. Press `Ctrl-Shift-P` to open the Command Palette.
2. Select `Calva: Connect to a Running REPL Server, not in your project`>`basilisp`.
3. Enter the port number from the nREPL server output.

## Acknowledgments

This kernel was developed based on the [echo_kernel](https://github.com/jupyter/echo_kernel) as a starting point.
This kernel was developed using the [echo_kernel](https://github.com/jupyter/echo_kernel) as a starting point.

44 changes: 44 additions & 0 deletions basilisp.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
(import [IPython.display :as id]
[ipywidgets :as iw])



(def v )

(def )

(comment
(import
[matplotlib.pyplot :as plt])

(type (iw/Output))
;;
)

(defn abc []
(let [v (iw.widgets/IntSlider ** :value 0 :min 0 :max 100 :description "hey there")
output (iw/Output)]
(id/display v output)
))

(def xyz (abc))

(def code-js "
const notebook = document.querySelector('#notebook-container') || document.querySelector('.jp-NotebookPanel-notebook');
if (notebook) {
// For both Classic Notebook and JupyterLab
IPython.notebook.insert_cell_below();
IPython.notebook.select_next();
IPython.notebook.get_selected_cell().set_text("print('This content was injected!')");
IPython.notebook.get_selected_cell().execute();
}
")

(def code-js2
"var cell = IPython.notebook.insert_cell_below('code');
cell.set_text(\"128\");
cell.execute()"
)

(id/display (id/Javascript
code-js2))
2 changes: 1 addition & 1 deletion basilisp_kernel/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""The Basilisp Jupyter kernel"""

__version__ = '1.1.0'
__version__ = '1.2.0b1'

from .kernel import BasilispKernel
Loading

0 comments on commit 6a6a97c

Please sign in to comment.