├── .github
└── workflows
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── API.md
├── CHANGELOG.md
├── LICENSE
├── README.md
├── basilisp.edn
├── basilisp_kernel
├── __init__.py
├── __main__.py
├── kernel.py
└── nrepl_server.lpy
├── bb.edn
├── hatch_build.py
├── notebooks
├── clojure-flow-control.ipynb
├── nb-plot.png
├── pandas-03-select.ipynb
├── pandas-04-plot.ipynb
└── python-interop.ipynb
├── pyproject.toml
├── test_basilisp.py
└── tests
├── __init__.py
└── basilisp_kernel
├── __init__.py
├── integration
├── __init__.py
└── notebook_test.lpy
└── nrepl_server_test.lpy
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | # https://github.com/anze3db/words-tui/blob/main/.github/workflows/publish.yml
2 | name: Publish to PyPI
3 |
4 | on:
5 | release:
6 | types: [published]
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | deploy:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | environment: release
17 | permissions:
18 | id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
19 | contents: write # github release upload
20 |
21 | steps:
22 | - uses: actions/checkout@v4
23 | - name: Set up Python
24 | uses: actions/setup-python@v4
25 | with:
26 | python-version: '3.12'
27 | cache: 'pip'
28 | - name: Install dependencies
29 | run: |
30 | python -m pip install --upgrade pip
31 | pip install hatch
32 | - name: Build package
33 | run: hatch build
34 | - name: Upload release artifacts
35 | run: gh release upload ${{ github.event.release.tag_name }} dist/*.{tar.gz,whl}
36 | env:
37 | GH_TOKEN: ${{ github.token }}
38 | - name: Publish package distributions to PyPI
39 | uses: pypa/gh-action-pypi-publish@release/v1
40 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Basilisp Jupyter Kernel package
2 |
3 | on:
4 | pull_request:
5 | types: [ opened, synchronize, reopened ]
6 | push:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 | runs-on: ${{ matrix.os }}
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-latest]
16 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy-3.10"]
17 | include:
18 | - os: windows-latest
19 | python-version: "3.13"
20 | - os: macos-latest
21 | python-version: "3.13"
22 | steps:
23 | - uses: actions/checkout@v4
24 | - uses: actions/setup-python@v5
25 | with:
26 | python-version: ${{ matrix.python-version }}
27 | - name: Install dependencies
28 | run: |
29 | pipx install hatch
30 | hatch env create test
31 | hatch env find
32 | - name: Test
33 | run: |
34 | hatch run test:basilisp test -- -v
35 | - name: Check Kernel
36 | run: |
37 | hatch run test:jupyter kernelspec list | grep -E "jupyter/kernels/basilisp|jupyter\\kernels\\basilisp"
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | MANIFEST
2 | build
3 | dist
4 | _build
5 | *.py[co]
6 | __pycache__
7 | *.egg-info
8 | *~
9 | *.bak
10 | .ipynb_checkpoints
11 | .tox
12 | .DS_Store
13 | \#*#
14 | .#*
15 | .coverage
16 | htmlcov
17 | .cache
18 | .idea
19 | data_kernelspec
20 |
21 | .ipynb_checkpoints
22 | /notebooks/new
23 | /notebooks/no2_concentrations.png
24 |
25 | .nrepl-port
26 | .calva/
27 |
--------------------------------------------------------------------------------
/API.md:
--------------------------------------------------------------------------------
1 | # Table of contents
2 | - [`basilisp-kernel.nrepl-server`](#basilisp-kernel.nrepl-server)
3 | - [`server-shut`](#basilisp-kernel.nrepl-server/server-shut) - Convenience function to shutdown the SERVER
.
4 | - [`server-start`](#basilisp-kernel.nrepl-server/server-start) - Starts the nrepl-server in async mode according to OPTS
, using a asyncio task to schedule any pending client work every 100ms.
5 |
6 | -----
7 | # basilisp-kernel.nrepl-server
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## `server-shut`
15 | ``` clojure
16 |
17 | (server-shut server)
18 | ```
19 | Function.
20 |
21 | Convenience function to shutdown the `SERVER`.
22 |
Source
23 |
24 | ## `server-start`
25 | ``` clojure
26 |
27 | (server-start)
28 | (server-start {:keys [host port dir interval-sec], :as opts, :or {port 0, interval-sec 0.1}})
29 | ```
30 | Function.
31 |
32 | Starts the nrepl-server in async mode according to `OPTS`, using a
33 | asyncio task to schedule any pending client work every 100ms.
34 |
35 | `OPTS` is a map that can have the following keys. It defaults to {}.
36 |
37 | `:dir` The directory where the `.nrepl-port` file should be created
38 | at. It defaults to the current working directory if not given or
39 | empty.
40 |
41 | `:host` The interface address the server should be bound to. It
42 | defaults to 127.0.0.1 if not given or empty.
43 |
44 | `:port` The port number the server should listen to. It defaults to
45 | 0, which indicates a random available port number.
46 |
47 | It return the `server`, which is a map with the following keys
48 |
49 | `:host` The host interface to which the server is bound.
50 |
51 | `:nrepl-port-file` The path to the `.nrepl-port` file created by the
52 | server.
53 |
54 | `:port` The port on the host interface the server listens for client
55 | connections.
56 |
57 | `:shutdown!` A function to shutdown the server.
58 | Source
59 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## Unreleased
4 |
5 | ## 1.2.0
6 |
7 | - Added nREPL Server support.
8 | - Dropped Python 3.8 support and added 3.13 in CI testing.
9 | - Set minimum Basilisp version to >=0.3.2.
10 |
11 | ## 1.1.0
12 |
13 | - Optimized output to stdout/stderr by removing proxy string intermediary.
14 | - Improved Exception formatting (requires basilisp>=0.1.0b2)
15 |
16 | ## 1.0.0
17 |
18 | - Support Basilisp code execution and code completion.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2017, Project Jupyter Contributors
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://pypi.org/project/basilisp-kernel/) [](https://github.com/ikappaki/basilisp-kernel/actions/workflows/test.yml)
2 |
3 | # Basilisp Kernel for Jupyter
4 |
5 | [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.
6 |
7 |
8 | # Overview
9 |
10 | The Basilisp Kernel enables running Basilisp Clojure code directly within Jupyter notebooks.
11 |
12 | ## Features
13 |
14 | - Full integration with Jupyter Notebook and JupyterLab.
15 | - Enhanced autocompletion features using the `Tab` key.
16 | - Seamless interoperability with Python libraries.
17 | - Interactive development with your preferred editor, powered by the nREPL server running inside the notebook.
18 |
19 | ## Installation
20 |
21 | Ensure you have Jupyter installed. If not, install it using pip:
22 |
23 | ```shell
24 | pip install jupyter
25 |
26 | ```
27 |
28 | To install the Basilisp Kernel, run:
29 |
30 | ```shell
31 | pip install basilisp-kernel
32 | ```
33 |
34 | ## Usage
35 |
36 | Start your Jupyter notebook server:
37 |
38 | ```shell
39 | jupyter notebook
40 | ```
41 |
42 | In the Jupyter interface, select the `Basilisp` Kernel when creating a new notebook.
43 |
44 | ## Examples
45 |
46 | 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.
47 |
48 | 
49 |
50 | ## nREPL support
51 |
52 | See [API.md](API.md).
53 |
54 | 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.
55 |
56 | ### Starting the nREPL Server
57 |
58 | Start the nREPL in your notebook by running:
59 | ```clojure
60 | (require '[basilisp-kernel.nrepl-server :refer [server-start server-shut]])
61 | (def server (server-start))
62 | => nREPL server started on port 58966 on host 127.0.0.1 - nrepl://127.0.0.1:58966
63 | => #'user/server
64 | ```
65 |
66 | 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.
67 |
68 | ### Stopping the nREPL Server
69 |
70 | ```clojure
71 | (server-shut server)
72 | => :shut
73 | ```
74 |
75 | ## Getting Started with Basilisp Notebooks Development
76 |
77 | Below are various methods to help you start writing Basilisp code that can be loaded in a Basilisp notebook.
78 |
79 | ### 🔋 Batteries Included Project: `basilex-notebook`
80 |
81 | 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.
82 |
83 | 1. Install [Poetry](https://python-poetry.org/docs/) for dependency management.
84 |
85 | 2. Clone the repository, install dependencies, activate the environment and start Jupyter:
86 |
87 | ```shell
88 | $ git clone https://github.com/ikappaki/basilex-notebook.git
89 | $ cd basilex-notebook
90 | $ poetry install
91 | $ poetry shell
92 | () $ jupyter notebook
93 | ```
94 |
95 | 3. Open the `tutorial.ipynb` notebook from the Jupyter interface. Refer to the [basilex-notebook documentation](https://github.com/ikappaki/basilex-notebook) for more details.
96 |
97 | ***
98 | ### 💾 Local Basilisp Files
99 |
100 | 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.
101 |
102 | For example, if you create a file named `dev.lpy` with the following content
103 |
104 | `./dev.lpy`
105 | ```clojure
106 | (ns dev)
107 |
108 | (defn hello []
109 | :hi)
110 | ```
111 |
112 | You can load and use it in your Notebook as follows
113 |
114 | ```Clojure
115 | [n]: (require 'dev)
116 | [n+1]: (dev/hello)
117 | :hi
118 | ```
119 |
120 | #### nREPL development
121 |
122 | You can start an nREPL server directly within a Notebook and connect to it using a Clojure-enabled editor.
123 |
124 | In a notebook cell:
125 |
126 | 1. Load the nREPL server namespace:
127 | ```clojure
128 | [n]: (require '[basilisp-kernel.nrepl-server :as nrepl-server])
129 | ```
130 | 2. Start the nREPL server at a random port
131 | ```Clojure
132 | [n]: (def server (nrepl-server/server-start))
133 | nREPL server started on port 59498 on host 127.0.0.1 - nrepl://127.0.0.1:59498
134 | #'user/server
135 | ```
136 |
137 | To connect your editor to the nREPL server create a `basilisp.edn` file in the same directory as your Notebook.
138 |
139 | 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.
140 |
141 | Both [Emacs/CIDER](https://docs.cider.mx/cider/platforms/basilisp.html) and [VSCode/Calva](https://calva.io/basilisp/) offer explicit support for Basilisp.
142 |
143 | #### CIDER (Emacs)
144 |
145 | 1. Run `M-x cider-connect-clj`.
146 | 2. Select `localhost`.
147 | 3. Select the `:` option.
148 |
149 | #### Calva (VSCode)
150 | 1. Press `Ctrl-Alt-P` to open the Command Palette.
151 | 2. Select `Calva: Connect to a Running REPL Server, in your project`>`basilisp`.
152 | 3. The editor will automatically find the port using `.nrepl-port`.
153 |
154 | The Editor should now connect seamlessly to the nREPL server.
155 |
156 | ***
157 | ### 📚 Basilisp Example Library Project: `basilex-basilib`
158 |
159 | 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.
160 |
161 | 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 `` in the examples below.
162 |
163 | #### Setup
164 |
165 | Clone the repository and install the `basilib` library as an editable package in in the same virtual environment as your Basilisp notebooks:
166 |
167 | ```shell
168 | () $ git clone https://github.com/ikappaki/basilex-basilib.git
169 | () $ cd basilex-basilib
170 | # install example library in virtual environment as editable package
171 | () $ pip install -e .
172 | ```
173 |
174 | #### Usage
175 |
176 | Load and interact with the library in your notebooks:
177 |
178 | ```clojure
179 | [n]: (require '[basilib.core :as bc])
180 | [n+1]: (bc/time-now)
181 | => '2024-11-30 14:53:00'
182 | ```
183 |
184 | #### nREPL development
185 |
186 | For interactive coding, you can connect your preferred Clojure-Enabled Editor to a running nREPL server in your Basilisp Notebook.
187 |
188 | In a notebook cell:
189 |
190 | 1. Load the nREPL server namespace:
191 | ```clojure
192 | [n]: (require '[basilisp-kernel.nrepl-server :as nrepl-server])
193 | ```
194 | 2. Start the nREPL server on a fixed port:
195 | ```Clojure
196 | [n]: (def server (server-start {:port 9998}))
197 | nREPL server started on port 9998 on host 127.0.0.1 - nrepl://127.0.0.1:9998
198 | #'user/server
199 | ```
200 |
201 | 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.
202 |
203 | Both [Emacs/CIDER](https://docs.cider.mx/cider/platforms/basilisp.html) and [VSCode/Calva](https://calva.io/basilisp/) offer explicit support for Basilisp.
204 |
205 | ###### Connecting via CIDER (Emacs)
206 |
207 | 1. Run `M-x cider-connect-clj`
208 | 2. Select `localhost`.
209 | 3. Enter the port number from the nREPL server output.
210 |
211 | ###### Connecting Calva (VSCode)
212 |
213 | 1. Press `Ctrl-Shift-P` to open the Command Palette.
214 | 2. Select `Calva: Connect to a Running REPL Server, not in your project`>`basilisp`.
215 | 3. Enter the port number from the nREPL server output.
216 |
217 | ## Acknowledgments
218 |
219 | This kernel was developed using the [echo_kernel](https://github.com/jupyter/echo_kernel) as a starting point.
220 |
221 |
--------------------------------------------------------------------------------
/basilisp.edn:
--------------------------------------------------------------------------------
1 | (import [IPython.display :as id]
2 | [ipywidgets :as iw])
3 |
4 |
5 |
6 | (def v )
7 |
8 | (def )
9 |
10 | (comment
11 | (import
12 | [matplotlib.pyplot :as plt])
13 |
14 | (type (iw/Output))
15 | ;;
16 | )
17 |
18 | (defn abc []
19 | (let [v (iw.widgets/IntSlider ** :value 0 :min 0 :max 100 :description "hey there")
20 | output (iw/Output)]
21 | (id/display v output)
22 | ))
23 |
24 | (def xyz (abc))
25 |
26 | (def code-js "
27 | const notebook = document.querySelector('#notebook-container') || document.querySelector('.jp-NotebookPanel-notebook');
28 | if (notebook) {
29 | // For both Classic Notebook and JupyterLab
30 | IPython.notebook.insert_cell_below();
31 | IPython.notebook.select_next();
32 | IPython.notebook.get_selected_cell().set_text("print('This content was injected!')");
33 | IPython.notebook.get_selected_cell().execute();
34 | }
35 | ")
36 |
37 | (def code-js2
38 | "var cell = IPython.notebook.insert_cell_below('code');
39 | cell.set_text(\"128\");
40 | cell.execute()"
41 | )
42 |
43 | (id/display (id/Javascript
44 | code-js2))
--------------------------------------------------------------------------------
/basilisp_kernel/__init__.py:
--------------------------------------------------------------------------------
1 | """The Basilisp Jupyter kernel"""
2 |
3 | __version__ = '1.2.0'
4 |
5 | from .kernel import BasilispKernel
6 |
--------------------------------------------------------------------------------
/basilisp_kernel/__main__.py:
--------------------------------------------------------------------------------
1 | from ipykernel.kernelapp import IPKernelApp
2 | from . import BasilispKernel
3 |
4 | IPKernelApp.launch_instance(kernel_class=BasilispKernel)
5 |
--------------------------------------------------------------------------------
/basilisp_kernel/kernel.py:
--------------------------------------------------------------------------------
1 | from ipykernel.ipkernel import IPythonKernel
2 |
3 | from basilisp import cli
4 | from basilisp import main as basilisp
5 | from basilisp.lang import compiler as compiler
6 | from basilisp.lang import reader as reader
7 | from basilisp.lang import runtime as runtime
8 | from basilisp.lang import symbol as sym
9 | from basilisp.lang.exception import format_exception
10 |
11 | import re
12 | from typing import Any, Callable, Optional, Sequence, Type
13 | import sys
14 |
15 | opts = {}
16 | basilisp.init(opts)
17 | ctx = compiler.CompilerContext(filename="basilisp-kernel", opts=opts)
18 | eof = object()
19 |
20 | user_ns = runtime.Namespace.get_or_create(sym.symbol("user"))
21 | core_ns = runtime.Namespace.get(runtime.CORE_NS_SYM)
22 | cli.eval_str("(ns user (:require clojure.core))", ctx, core_ns, eof)
23 |
24 | _DELIMITED_WORD_PATTERN = re.compile(r"[\[\](){\}\s]+")
25 |
26 | def do_execute(code):
27 | with runtime.bindings({
28 | runtime.Var.find_safe(sym.symbol("*out*", ns=runtime.CORE_NS)) : sys.stdout,
29 | runtime.Var.find_safe(sym.symbol("*err*", ns=runtime.CORE_NS)) : sys.stderr
30 | }):
31 | try:
32 | return cli.eval_str(code, ctx, user_ns, eof)
33 | except reader.SyntaxError as e:
34 | msg = "".join(format_exception(e, reader.SyntaxError, e.__traceback__))
35 | raise reader.SyntaxError(msg) from None
36 | except compiler.CompilerException as e:
37 | msg = "".join(format_exception(e, compiler.CompilerException, e.__traceback__))
38 | raise compiler.CompilerException(msg, phase=e.phase, filename=e.filename) from None
39 | except Exception as e:
40 | msg = "".join(format_exception(e, Exception, e.__traceback__))
41 | raise Exception(msg) from None
42 |
43 |
44 | class BasilispKernel(IPythonKernel):
45 | implementation = 'basilisp-kernel'
46 | implementation_version = '1.0'
47 | language_info = {
48 | 'name': 'clojure',
49 | 'mimetype': 'text/x-clojure',
50 | 'file_extension': '.lpy',
51 | }
52 | banner = "Basilisp: a Clojure-compatible(-ish) Lisp dialect in Python"
53 |
54 | def __init__(self, **kwargs):
55 | super().__init__(**kwargs)
56 | self.imported = False
57 |
58 | def do_complete(self, acode, cursor_pos):
59 | code = acode[:cursor_pos]
60 | words = re.split(_DELIMITED_WORD_PATTERN, code)
61 |
62 | last_word = words[-1]
63 | completions = list(runtime.repl_completions(last_word)) or ()
64 |
65 | return {"status" : "ok",
66 | "matches" : completions,
67 | "cursor_start" : cursor_pos-len(last_word),
68 | "cursor_end" : cursor_pos,
69 | "metadata" : dict()
70 | }
71 |
72 | def do_execute(self, code, silent, store_history=True, user_expressions=None,
73 | allow_stdin=False):
74 | bas_code = f'basilisp_kernel.kernel.do_execute({repr(code)})'
75 | if not self.imported:
76 | bas_code = f'import basilisp_kernel\n{bas_code}'
77 | self.imported = True
78 | return super().do_execute(code=bas_code, silent=silent, store_history=store_history,
79 | user_expressions=user_expressions, allow_stdin=allow_stdin)
80 |
81 |
--------------------------------------------------------------------------------
/basilisp_kernel/nrepl_server.lpy:
--------------------------------------------------------------------------------
1 | (ns basilisp-kernel.nrepl-server
2 | (:require [basilisp-nrepl-async.nrepl-server :as nr]
3 | [basilisp-nrepl-async.utils :as u]
4 | [basilisp.string :as str])
5 | (:import asyncio
6 | os))
7 |
8 | (defasync work-task
9 | ;; An async task to call `WORK-FN` every `INTERVAL-SEC`onds. It
10 | ;; exits when `SHUTDOWN?*` atom becomes truthy. It calls `SHUTDOWN`
11 | ;; on exit.
12 | [interval-sec work-fn shutdown?* shutdown!]
13 | (binding [*out* sys/stdout]
14 | (try
15 | (loop []
16 | (work-fn)
17 | (when-not @shutdown?*
18 | (await (asyncio/sleep 0.1))
19 | (recur)))
20 | (catch python/Exception e
21 | (println ::work-task-error :exiting (str e)))
22 | (finally (shutdown!)))
23 | (println ::work-task :exited)))
24 |
25 | (defn server-start
26 | "Starts the nrepl-server in async mode according to `OPTS`, using a
27 | asyncio task to schedule any pending client work every 100ms.
28 |
29 | `OPTS` is a map that can have the following keys. It defaults to {}.
30 |
31 | `:dir` The directory where the `.nrepl-port` file should be created
32 | at. It defaults to the current working directory if not given or
33 | empty.
34 |
35 | `:host` The interface address the server should be bound to. It
36 | defaults to 127.0.0.1 if not given or empty.
37 |
38 | `:port` The port number the server should listen to. It defaults to
39 | 0, which indicates a random available port number.
40 |
41 | It return the `server`, which is a map with the following keys
42 |
43 | `:host` The host interface to which the server is bound.
44 |
45 | `:nrepl-port-file` The path to the `.nrepl-port` file created by the
46 | server.
47 |
48 | `:port` The port on the host interface the server listens for client
49 | connections.
50 |
51 | `:shutdown!` A function to shutdown the server."
52 | ([]
53 | (server-start {}))
54 | ([{:keys [host port dir interval-sec] :as opts
55 | :or {port 0
56 | interval-sec 0.1}}]
57 | (let [host (if (or (nil? host) (empty? (str/trim host)))
58 | "127.0.0.1"
59 | host)
60 | nrepl-port-dir (if (or (nil? dir) (empty? (str/trim dir)))
61 | "."
62 | dir)]
63 |
64 | (if (not (os.path/isdir nrepl-port-dir))
65 | {:error (u/error-make [:nrepl-server-start :nrepl-port-dir-not-a-dir nrepl-port-dir])}
66 |
67 | (let [{:keys [error work-fn shutdown-fn] :as ret}
68 | (nr/server-start! {:async? true
69 | :host host
70 | :port port
71 | :nrepl-port-file (os.path/join nrepl-port-dir ".nrepl-port")})]
72 | (if error
73 | (binding [*out* sys/stderr]
74 | (println :server-start-error (u/error->str error))
75 | {:error error})
76 |
77 | (let [shutdown?* (volatile! false)
78 | shutdown! #(do (vreset! shutdown?* true)
79 | (shutdown-fn)
80 | :shut)
81 | loop (asyncio/get-running-loop)
82 | task (.create-task loop (work-task interval-sec work-fn shutdown?* shutdown!))]
83 | (-> (select-keys ret [:host :port :nrepl-port-file])
84 | (assoc :shutdown! shutdown!
85 | :_task task)))))))))
86 |
87 | (defn ^:inline server-shut
88 | "Convenience function to shutdown the `SERVER`."
89 | [server]
90 | ((:shutdown! server)))
91 |
--------------------------------------------------------------------------------
/bb.edn:
--------------------------------------------------------------------------------
1 | {:tasks
2 | {quickdoc {:doc "Invoke quickdoc"
3 | :extra-deps {io.github.borkdude/quickdoc {:git/sha "7e41f33d98e2ef697dd9ecdff8e84b779141c7a6"}}
4 | :task (exec 'quickdoc.api/quickdoc)
5 | :exec-args {:git/branch "master"
6 | :github/repo "https://github.com/ikappaki/basilisp-kernel"
7 | :source-paths ["basilisp_kernel/nrepl_server.lpy"]}}}}
8 |
--------------------------------------------------------------------------------
/hatch_build.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface
4 |
5 |
6 | import argparse
7 | import json
8 | import os
9 | import sys
10 | import shutil
11 |
12 | from jupyter_client.kernelspec import KernelSpecManager
13 | from tempfile import TemporaryDirectory
14 |
15 | kernel_json = {
16 | "argv": ["python", "-m", "basilisp_kernel", "-f", "{connection_file}"],
17 | "display_name": "Basilisp",
18 | "language": "clojure",
19 | }
20 |
21 | class CustomHook(BuildHookInterface):
22 | def initialize(self, version, build_data):
23 | here = os.path.abspath(os.path.dirname(__file__))
24 | sys.path.insert(0, here)
25 | prefix = os.path.join(here, 'data_kernelspec')
26 |
27 | with TemporaryDirectory() as td:
28 | os.chmod(td, 0o755) # Starts off as 700, not user readable
29 | with open(os.path.join(td, 'kernel.json'), 'w') as f:
30 | json.dump(kernel_json, f, sort_keys=True)
31 | print('Installing Jupyter kernel spec')
32 |
33 | # Requires logo files in kernel root directory
34 | cur_path = os.path.dirname(os.path.realpath(__file__))
35 | for logo in ["logo-32x32.png", "logo-64x64.png"]:
36 | try:
37 | shutil.copy(os.path.join(cur_path, logo), td)
38 | except FileNotFoundError:
39 | print("Custom logo files not found. Default logos will be used.")
40 |
41 | KernelSpecManager().install_kernel_spec(td, 'basilisp', user=False, prefix=prefix)
42 |
43 |
--------------------------------------------------------------------------------
/notebooks/clojure-flow-control.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "bb2d079a-a22f-4264-b163-9a25dd6a7b03",
6 | "metadata": {},
7 | "source": [
8 | "## Learn Clojure - Flow Control\n",
9 | "https://clojure.org/guides/learn/flow"
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "id": "0c183f97-d468-41cd-a86c-7442d11f464f",
15 | "metadata": {},
16 | "source": [
17 | "### Flow Control Expressions"
18 | ]
19 | },
20 | {
21 | "cell_type": "markdown",
22 | "id": "9372c969-eb5a-443f-8dcc-debf9e075853",
23 | "metadata": {},
24 | "source": [
25 | "#### if"
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "execution_count": 1,
31 | "id": "cda50450-57fd-4d6f-9e14-395584838599",
32 | "metadata": {},
33 | "outputs": [
34 | {
35 | "data": {
36 | "text/plain": [
37 | "'2 is even'"
38 | ]
39 | },
40 | "execution_count": 1,
41 | "metadata": {},
42 | "output_type": "execute_result"
43 | }
44 | ],
45 | "source": [
46 | "(str \"2 is \" (if (even? 2) \"even\" \"odd\"))"
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": 2,
52 | "id": "d5cd9ef1-d92b-4b9c-8a5e-d35659446a13",
53 | "metadata": {},
54 | "outputs": [],
55 | "source": [
56 | "(if (true? false) \"impossible!\") ;; else is optional"
57 | ]
58 | },
59 | {
60 | "cell_type": "markdown",
61 | "id": "753d03d9-1dfa-4a65-b3c3-42fbd8ed1982",
62 | "metadata": {},
63 | "source": [
64 | "#### Truth"
65 | ]
66 | },
67 | {
68 | "cell_type": "code",
69 | "execution_count": 3,
70 | "id": "32154053-95fc-47e2-80a4-908dd0fcceaa",
71 | "metadata": {},
72 | "outputs": [
73 | {
74 | "data": {
75 | "text/plain": [
76 | ":truthy"
77 | ]
78 | },
79 | "execution_count": 3,
80 | "metadata": {},
81 | "output_type": "execute_result"
82 | }
83 | ],
84 | "source": [
85 | "(if true :truthy :falsey)"
86 | ]
87 | },
88 | {
89 | "cell_type": "code",
90 | "execution_count": 4,
91 | "id": "bae69dc8-9488-43cd-9133-d057959babad",
92 | "metadata": {},
93 | "outputs": [
94 | {
95 | "data": {
96 | "text/plain": [
97 | ":truthy"
98 | ]
99 | },
100 | "execution_count": 4,
101 | "metadata": {},
102 | "output_type": "execute_result"
103 | }
104 | ],
105 | "source": [
106 | "(if (object.) :truthy :falsey) ; objects are true"
107 | ]
108 | },
109 | {
110 | "cell_type": "code",
111 | "execution_count": 5,
112 | "id": "e9c65744-b2eb-4444-8da9-60c5b1fef62d",
113 | "metadata": {},
114 | "outputs": [
115 | {
116 | "data": {
117 | "text/plain": [
118 | ":truthy"
119 | ]
120 | },
121 | "execution_count": 5,
122 | "metadata": {},
123 | "output_type": "execute_result"
124 | }
125 | ],
126 | "source": [
127 | "(if [] :truthy :falsey) ; empty collections are true"
128 | ]
129 | },
130 | {
131 | "cell_type": "code",
132 | "execution_count": 6,
133 | "id": "25bca075-b3f1-4516-b336-0a54aee551e3",
134 | "metadata": {},
135 | "outputs": [
136 | {
137 | "data": {
138 | "text/plain": [
139 | ":truthy"
140 | ]
141 | },
142 | "execution_count": 6,
143 | "metadata": {},
144 | "output_type": "execute_result"
145 | }
146 | ],
147 | "source": [
148 | "(if 0 :truthy :falsey) ; zero is true"
149 | ]
150 | },
151 | {
152 | "cell_type": "code",
153 | "execution_count": 7,
154 | "id": "8b48e579-2d40-467f-a7f5-3dfa7c357415",
155 | "metadata": {},
156 | "outputs": [
157 | {
158 | "data": {
159 | "text/plain": [
160 | ":falsey"
161 | ]
162 | },
163 | "execution_count": 7,
164 | "metadata": {},
165 | "output_type": "execute_result"
166 | }
167 | ],
168 | "source": [
169 | "(if false :truthy :falsey)"
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": 8,
175 | "id": "1f8685bc-1bf7-4b5e-81af-3f81f0652c0d",
176 | "metadata": {},
177 | "outputs": [
178 | {
179 | "data": {
180 | "text/plain": [
181 | ":falsey"
182 | ]
183 | },
184 | "execution_count": 8,
185 | "metadata": {},
186 | "output_type": "execute_result"
187 | }
188 | ],
189 | "source": [
190 | "(if nil :truthy :falsey)"
191 | ]
192 | },
193 | {
194 | "cell_type": "markdown",
195 | "id": "68821f7b-4d76-435a-a52a-903456a6a6b3",
196 | "metadata": {},
197 | "source": [
198 | "#### if and do"
199 | ]
200 | },
201 | {
202 | "cell_type": "code",
203 | "execution_count": 9,
204 | "id": "a76cff05-d4ca-4462-bde7-ebd018bc2b55",
205 | "metadata": {},
206 | "outputs": [
207 | {
208 | "name": "stdout",
209 | "output_type": "stream",
210 | "text": [
211 | "odd\n",
212 | "\n"
213 | ]
214 | },
215 | {
216 | "data": {
217 | "text/plain": [
218 | "False"
219 | ]
220 | },
221 | "execution_count": 9,
222 | "metadata": {},
223 | "output_type": "execute_result"
224 | }
225 | ],
226 | "source": [
227 | "(if (even? 5)\n",
228 | " (do (println \"even\")\n",
229 | " true)\n",
230 | " (do (println \"odd\")\n",
231 | " false))"
232 | ]
233 | },
234 | {
235 | "cell_type": "markdown",
236 | "id": "6d4a28f5-c46b-413f-8905-812dc1c0eaa3",
237 | "metadata": {},
238 | "source": [
239 | "#### when"
240 | ]
241 | },
242 | {
243 | "cell_type": "code",
244 | "execution_count": 10,
245 | "id": "28a5ce11-06a5-4690-a0b9-02fdcb8f6851",
246 | "metadata": {},
247 | "outputs": [],
248 | "source": [
249 | "(let [x 1] ;; change to a negative num to throw exception\n",
250 | " (when (neg? x)\n",
251 | " (throw (RuntimeError. (str \"x must be positive: \" x)))))"
252 | ]
253 | },
254 | {
255 | "cell_type": "markdown",
256 | "id": "6857b53f-9730-46cd-9dad-71fb78603142",
257 | "metadata": {},
258 | "source": [
259 | "#### cond"
260 | ]
261 | },
262 | {
263 | "cell_type": "code",
264 | "execution_count": 11,
265 | "id": "35d076f4-d51c-4756-bd0b-daee3fba739b",
266 | "metadata": {},
267 | "outputs": [
268 | {
269 | "data": {
270 | "text/plain": [
271 | "'x is less than 10'"
272 | ]
273 | },
274 | "execution_count": 11,
275 | "metadata": {},
276 | "output_type": "execute_result"
277 | }
278 | ],
279 | "source": [
280 | "(let [x 5]\n",
281 | " (cond\n",
282 | " (< x 2) \"x is less than 2\"\n",
283 | " (< x 10) \"x is less than 10\"))"
284 | ]
285 | },
286 | {
287 | "cell_type": "markdown",
288 | "id": "ab77af05-4be1-419b-bb31-b17d9412af98",
289 | "metadata": {},
290 | "source": [
291 | "#### cond and else"
292 | ]
293 | },
294 | {
295 | "cell_type": "code",
296 | "execution_count": 12,
297 | "id": "6763a4b1-d382-4dfb-abbb-d31a85f36ed2",
298 | "metadata": {},
299 | "outputs": [
300 | {
301 | "data": {
302 | "text/plain": [
303 | "'x is greater than or equal to 10'"
304 | ]
305 | },
306 | "execution_count": 12,
307 | "metadata": {},
308 | "output_type": "execute_result"
309 | }
310 | ],
311 | "source": [
312 | "(let [x 11]\n",
313 | " (cond\n",
314 | " (< x 2) \"x is less than 2\"\n",
315 | " (< x 10) \"x is less than 10\"\n",
316 | " :else \"x is greater than or equal to 10\"))"
317 | ]
318 | },
319 | {
320 | "cell_type": "markdown",
321 | "id": "777e4fd0-895a-40b1-b732-7e6117b92cc0",
322 | "metadata": {},
323 | "source": [
324 | "#### case"
325 | ]
326 | },
327 | {
328 | "cell_type": "code",
329 | "execution_count": 13,
330 | "id": "d3be4b71-63a5-4c2b-9e53-9851965808b0",
331 | "metadata": {},
332 | "outputs": [
333 | {
334 | "data": {
335 | "text/plain": [
336 | "#'user/foo"
337 | ]
338 | },
339 | "execution_count": 13,
340 | "metadata": {},
341 | "output_type": "execute_result"
342 | }
343 | ],
344 | "source": [
345 | "(defn foo [x]\n",
346 | " (case x\n",
347 | " 5 \"x is 5\"\n",
348 | " 10 \"x is 10\"))"
349 | ]
350 | },
351 | {
352 | "cell_type": "code",
353 | "execution_count": 14,
354 | "id": "f8996b49-da00-4d63-b8de-2bcae9e03222",
355 | "metadata": {},
356 | "outputs": [
357 | {
358 | "data": {
359 | "text/plain": [
360 | "'x is 10'"
361 | ]
362 | },
363 | "execution_count": 14,
364 | "metadata": {},
365 | "output_type": "execute_result"
366 | }
367 | ],
368 | "source": [
369 | "(foo 10)"
370 | ]
371 | },
372 | {
373 | "cell_type": "code",
374 | "execution_count": 15,
375 | "id": "699416a9-5085-48f7-ac57-3652db6ee38b",
376 | "metadata": {},
377 | "outputs": [
378 | {
379 | "data": {
380 | "text/plain": [
381 | ""
382 | ]
383 | },
384 | "execution_count": 15,
385 | "metadata": {},
386 | "output_type": "execute_result"
387 | }
388 | ],
389 | "source": [
390 | "#_(foo 11) ;; => ValueError: No case clause matches 11"
391 | ]
392 | },
393 | {
394 | "cell_type": "markdown",
395 | "id": "23a70b79-4f68-450a-97bd-f1d7676edb1a",
396 | "metadata": {},
397 | "source": [
398 | "#### case with else-expression"
399 | ]
400 | },
401 | {
402 | "cell_type": "code",
403 | "execution_count": 16,
404 | "id": "4b301b20-9955-49b0-b673-3452d6f83099",
405 | "metadata": {},
406 | "outputs": [
407 | {
408 | "data": {
409 | "text/plain": [
410 | "#'user/foo"
411 | ]
412 | },
413 | "execution_count": 16,
414 | "metadata": {},
415 | "output_type": "execute_result"
416 | }
417 | ],
418 | "source": [
419 | "(defn foo [x]\n",
420 | " (case x\n",
421 | " 5 \"x is 5\"\n",
422 | " 10 \"x is 10\"\n",
423 | " \"x isn't 5 or 10\"))"
424 | ]
425 | },
426 | {
427 | "cell_type": "code",
428 | "execution_count": 17,
429 | "id": "73477504-f023-4123-b700-0f8ee5e14d84",
430 | "metadata": {},
431 | "outputs": [
432 | {
433 | "data": {
434 | "text/plain": [
435 | "\"x isn't 5 or 10\""
436 | ]
437 | },
438 | "execution_count": 17,
439 | "metadata": {},
440 | "output_type": "execute_result"
441 | }
442 | ],
443 | "source": [
444 | "(foo 11)"
445 | ]
446 | },
447 | {
448 | "cell_type": "markdown",
449 | "id": "7b42df7a-9b1b-4e52-95ec-57012941a8a0",
450 | "metadata": {},
451 | "source": [
452 | "### Iteration and Side Effects"
453 | ]
454 | },
455 | {
456 | "cell_type": "markdown",
457 | "id": "f54855b1-0d22-491c-87d6-07ce7a9378af",
458 | "metadata": {},
459 | "source": [
460 | "#### dotimes"
461 | ]
462 | },
463 | {
464 | "cell_type": "code",
465 | "execution_count": 18,
466 | "id": "d20ff6c5-3022-4e3c-b801-b6e4b4e531da",
467 | "metadata": {},
468 | "outputs": [
469 | {
470 | "name": "stdout",
471 | "output_type": "stream",
472 | "text": [
473 | "0\n",
474 | "1\n",
475 | "2\n",
476 | "\n"
477 | ]
478 | }
479 | ],
480 | "source": [
481 | "(dotimes [i 3]\n",
482 | " (println i))"
483 | ]
484 | },
485 | {
486 | "cell_type": "markdown",
487 | "id": "2a090e89-6cd6-419f-a898-e87dc5ac6623",
488 | "metadata": {},
489 | "source": [
490 | "#### doseq"
491 | ]
492 | },
493 | {
494 | "cell_type": "code",
495 | "execution_count": 19,
496 | "id": "aea97718-8f2a-482f-9f6e-264991a229a2",
497 | "metadata": {},
498 | "outputs": [
499 | {
500 | "name": "stdout",
501 | "output_type": "stream",
502 | "text": [
503 | "0\n",
504 | "1\n",
505 | "2\n",
506 | "\n"
507 | ]
508 | }
509 | ],
510 | "source": [
511 | "(doseq [n (range 3)]\n",
512 | " (println n))"
513 | ]
514 | },
515 | {
516 | "cell_type": "markdown",
517 | "id": "f024b5a2-78be-4e45-97e0-e905cbdcabb3",
518 | "metadata": {},
519 | "source": [
520 | "#### doseq with multiple bindings"
521 | ]
522 | },
523 | {
524 | "cell_type": "code",
525 | "execution_count": 20,
526 | "id": "88ca0761-da8d-40a0-83b1-d14d67e0d624",
527 | "metadata": {},
528 | "outputs": [
529 | {
530 | "name": "stdout",
531 | "output_type": "stream",
532 | "text": [
533 | "[:a 0]\n",
534 | "[:a 1]\n",
535 | "[:a 2]\n",
536 | "[:b 0]\n",
537 | "[:b 1]\n",
538 | "[:b 2]\n",
539 | "\n"
540 | ]
541 | }
542 | ],
543 | "source": [
544 | "(doseq [letter [:a :b]\n",
545 | " number (range 3)] ; list of 0, 1, 2\n",
546 | " (prn [letter number]))"
547 | ]
548 | },
549 | {
550 | "cell_type": "markdown",
551 | "id": "c1b0d09d-3f68-4619-aa60-f8c2a7feeaee",
552 | "metadata": {},
553 | "source": [
554 | "### Clojure's for"
555 | ]
556 | },
557 | {
558 | "cell_type": "code",
559 | "execution_count": 21,
560 | "id": "48699c42-6361-43b8-92ba-35bb5881edb6",
561 | "metadata": {},
562 | "outputs": [
563 | {
564 | "data": {
565 | "text/plain": [
566 | "([:a 0] [:a 1] [:a 2] [:b 0] [:b 1] [:b 2])"
567 | ]
568 | },
569 | "execution_count": 21,
570 | "metadata": {},
571 | "output_type": "execute_result"
572 | }
573 | ],
574 | "source": [
575 | "(for [letter [:a :b]\n",
576 | " number (range 3)] ; list of 0, 1, 2\n",
577 | " [letter number])"
578 | ]
579 | },
580 | {
581 | "cell_type": "markdown",
582 | "id": "47816937-5efa-40f6-854c-50309fb0ec78",
583 | "metadata": {},
584 | "source": [
585 | "### Recursion"
586 | ]
587 | },
588 | {
589 | "cell_type": "markdown",
590 | "id": "9d9ab360-0151-49a6-99f2-847a2cbd7c97",
591 | "metadata": {},
592 | "source": [
593 | "#### loop and recur"
594 | ]
595 | },
596 | {
597 | "cell_type": "code",
598 | "execution_count": 22,
599 | "id": "97befb63-bdd4-41c3-ad90-79adca795b01",
600 | "metadata": {},
601 | "outputs": [
602 | {
603 | "data": {
604 | "text/plain": [
605 | "10"
606 | ]
607 | },
608 | "execution_count": 22,
609 | "metadata": {},
610 | "output_type": "execute_result"
611 | }
612 | ],
613 | "source": [
614 | "(loop [i 0]\n",
615 | " (if (< i 10)\n",
616 | " (recur (inc i))\n",
617 | " i))"
618 | ]
619 | },
620 | {
621 | "cell_type": "markdown",
622 | "id": "f61399e8-355c-4617-b240-38f3453401f6",
623 | "metadata": {},
624 | "source": [
625 | "### Exceptions"
626 | ]
627 | },
628 | {
629 | "cell_type": "markdown",
630 | "id": "a5fe00ec-3363-467f-90ed-32498cf1e87d",
631 | "metadata": {},
632 | "source": [
633 | "#### Exception handling"
634 | ]
635 | },
636 | {
637 | "cell_type": "code",
638 | "execution_count": 23,
639 | "id": "bcb10ea9-207a-4b43-8279-5d335352f817",
640 | "metadata": {},
641 | "outputs": [
642 | {
643 | "name": "stdout",
644 | "output_type": "stream",
645 | "text": [
646 | "cleanup\n",
647 | "\n"
648 | ]
649 | },
650 | {
651 | "data": {
652 | "text/plain": [
653 | "Fraction(2, 1)"
654 | ]
655 | },
656 | "execution_count": 23,
657 | "metadata": {},
658 | "output_type": "execute_result"
659 | }
660 | ],
661 | "source": [
662 | "(try\n",
663 | " (/ 2 1)\n",
664 | " (catch ArithmeticError e\n",
665 | " \"divide by zero\")\n",
666 | " (finally\n",
667 | " (println \"cleanup\")))\n",
668 | "\n",
669 | "(/ 2 1)"
670 | ]
671 | },
672 | {
673 | "cell_type": "markdown",
674 | "id": "1f2ba079-a4b8-411f-91cc-0492038fafe5",
675 | "metadata": {},
676 | "source": [
677 | "#### Throwing Exceptions"
678 | ]
679 | },
680 | {
681 | "cell_type": "code",
682 | "execution_count": 24,
683 | "id": "06902ddc-ac1c-4beb-ab39-5780896ac2cb",
684 | "metadata": {},
685 | "outputs": [
686 | {
687 | "data": {
688 | "text/plain": [
689 | "\"Exception('something went wrong')\""
690 | ]
691 | },
692 | "execution_count": 24,
693 | "metadata": {},
694 | "output_type": "execute_result"
695 | }
696 | ],
697 | "source": [
698 | "(try\n",
699 | " (throw (Exception. \"something went wrong\"))\n",
700 | " (catch Exception e (str e)))"
701 | ]
702 | },
703 | {
704 | "cell_type": "markdown",
705 | "id": "eb7bb565-7c8d-480d-a7d0-a321e29b0bf3",
706 | "metadata": {},
707 | "source": [
708 | "#### Exception with Clojure data"
709 | ]
710 | },
711 | {
712 | "cell_type": "code",
713 | "execution_count": 25,
714 | "id": "0daa69f5-3c18-407c-af1a-df2c1453912c",
715 | "metadata": {},
716 | "outputs": [
717 | {
718 | "name": "stdout",
719 | "output_type": "stream",
720 | "text": [
721 | "42\n",
722 | "\n"
723 | ]
724 | }
725 | ],
726 | "source": [
727 | "(try\n",
728 | " (throw (ex-info \"There was a problem\" {:detail 42}))\n",
729 | " (catch Exception e\n",
730 | " (prn (:detail (ex-data e)))))"
731 | ]
732 | },
733 | {
734 | "cell_type": "code",
735 | "execution_count": 26,
736 | "id": "6a3cff25-eaf4-4cd3-86b4-e8a01efad1b3",
737 | "metadata": {},
738 | "outputs": [
739 | {
740 | "data": {
741 | "text/plain": [
742 | "9"
743 | ]
744 | },
745 | "execution_count": 26,
746 | "metadata": {},
747 | "output_type": "execute_result"
748 | }
749 | ],
750 | "source": [
751 | "(let [f (python/open \"new\" ** :mode \"w\") ]\n",
752 | " (try\n",
753 | " (.write f \"some text\")\n",
754 | " (finally\n",
755 | " (.close f))))\n",
756 | "\n",
757 | ";; Can be written:\n",
758 | "(with-open [f (python/open \"new\" ** :mode \"w\")]\n",
759 | " (.write f \"some text\"))"
760 | ]
761 | }
762 | ],
763 | "metadata": {
764 | "kernelspec": {
765 | "display_name": "Basilisp",
766 | "language": "clojure",
767 | "name": "basilisp"
768 | },
769 | "language_info": {
770 | "file_extension": ".lpy",
771 | "mimetype": "text/x-clojure",
772 | "name": "clojure"
773 | }
774 | },
775 | "nbformat": 4,
776 | "nbformat_minor": 5
777 | }
778 |
--------------------------------------------------------------------------------
/notebooks/nb-plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikappaki/basilisp-kernel/b2dd50bd1830bd2d12fb554de34cc7f895140030/notebooks/nb-plot.png
--------------------------------------------------------------------------------
/notebooks/pandas-03-select.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "c970e277-6573-484e-b2a0-0812db69fb0d",
6 | "metadata": {},
7 | "source": [
8 | "## How do I select a subset of a DataFrame\n",
9 | "https://web.archive.org/web/20240318204924id_/https://pandas.pydata.org/pandas-docs/stable/getting_started/intro_tutorials/03_subset_data.html"
10 | ]
11 | },
12 | {
13 | "cell_type": "code",
14 | "execution_count": 24,
15 | "id": "317d4286-22bf-45e2-9d6b-492888a6f6ad",
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | ";; pip install pandas\n",
20 | "(import [pandas :as pd])\n",
21 | "nil"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": 2,
27 | "id": "5fa7ae4b-02db-40eb-9648-5a0f188790bb",
28 | "metadata": {},
29 | "outputs": [
30 | {
31 | "data": {
32 | "text/plain": [
33 | "#'user/titanic"
34 | ]
35 | },
36 | "execution_count": 2,
37 | "metadata": {},
38 | "output_type": "execute_result"
39 | }
40 | ],
41 | "source": [
42 | "(def titanic (pd/read_csv \"https://web.archive.org/web/20230319165431id_/https://raw.githubusercontent.com/pandas-dev/pandas/main/doc/data/titanic.csv\"))"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": 3,
48 | "id": "42844a8f-e77a-494b-a3b1-e911ec2bebb4",
49 | "metadata": {},
50 | "outputs": [
51 | {
52 | "data": {
53 | "text/html": [
54 | "\n",
55 | "\n",
68 | "
\n",
69 | " \n",
70 | " \n",
71 | " \n",
72 | " PassengerId \n",
73 | " Survived \n",
74 | " Pclass \n",
75 | " Name \n",
76 | " Sex \n",
77 | " Age \n",
78 | " SibSp \n",
79 | " Parch \n",
80 | " Ticket \n",
81 | " Fare \n",
82 | " Cabin \n",
83 | " Embarked \n",
84 | " \n",
85 | " \n",
86 | " \n",
87 | " \n",
88 | " 0 \n",
89 | " 1 \n",
90 | " 0 \n",
91 | " 3 \n",
92 | " Braund, Mr. Owen Harris \n",
93 | " male \n",
94 | " 22.0 \n",
95 | " 1 \n",
96 | " 0 \n",
97 | " A/5 21171 \n",
98 | " 7.2500 \n",
99 | " NaN \n",
100 | " S \n",
101 | " \n",
102 | " \n",
103 | " 1 \n",
104 | " 2 \n",
105 | " 1 \n",
106 | " 1 \n",
107 | " Cumings, Mrs. John Bradley (Florence Briggs Th... \n",
108 | " female \n",
109 | " 38.0 \n",
110 | " 1 \n",
111 | " 0 \n",
112 | " PC 17599 \n",
113 | " 71.2833 \n",
114 | " C85 \n",
115 | " C \n",
116 | " \n",
117 | " \n",
118 | " 2 \n",
119 | " 3 \n",
120 | " 1 \n",
121 | " 3 \n",
122 | " Heikkinen, Miss. Laina \n",
123 | " female \n",
124 | " 26.0 \n",
125 | " 0 \n",
126 | " 0 \n",
127 | " STON/O2. 3101282 \n",
128 | " 7.9250 \n",
129 | " NaN \n",
130 | " S \n",
131 | " \n",
132 | " \n",
133 | " 3 \n",
134 | " 4 \n",
135 | " 1 \n",
136 | " 1 \n",
137 | " Futrelle, Mrs. Jacques Heath (Lily May Peel) \n",
138 | " female \n",
139 | " 35.0 \n",
140 | " 1 \n",
141 | " 0 \n",
142 | " 113803 \n",
143 | " 53.1000 \n",
144 | " C123 \n",
145 | " S \n",
146 | " \n",
147 | " \n",
148 | " 4 \n",
149 | " 5 \n",
150 | " 0 \n",
151 | " 3 \n",
152 | " Allen, Mr. William Henry \n",
153 | " male \n",
154 | " 35.0 \n",
155 | " 0 \n",
156 | " 0 \n",
157 | " 373450 \n",
158 | " 8.0500 \n",
159 | " NaN \n",
160 | " S \n",
161 | " \n",
162 | " \n",
163 | " ... \n",
164 | " ... \n",
165 | " ... \n",
166 | " ... \n",
167 | " ... \n",
168 | " ... \n",
169 | " ... \n",
170 | " ... \n",
171 | " ... \n",
172 | " ... \n",
173 | " ... \n",
174 | " ... \n",
175 | " ... \n",
176 | " \n",
177 | " \n",
178 | " 886 \n",
179 | " 887 \n",
180 | " 0 \n",
181 | " 2 \n",
182 | " Montvila, Rev. Juozas \n",
183 | " male \n",
184 | " 27.0 \n",
185 | " 0 \n",
186 | " 0 \n",
187 | " 211536 \n",
188 | " 13.0000 \n",
189 | " NaN \n",
190 | " S \n",
191 | " \n",
192 | " \n",
193 | " 887 \n",
194 | " 888 \n",
195 | " 1 \n",
196 | " 1 \n",
197 | " Graham, Miss. Margaret Edith \n",
198 | " female \n",
199 | " 19.0 \n",
200 | " 0 \n",
201 | " 0 \n",
202 | " 112053 \n",
203 | " 30.0000 \n",
204 | " B42 \n",
205 | " S \n",
206 | " \n",
207 | " \n",
208 | " 888 \n",
209 | " 889 \n",
210 | " 0 \n",
211 | " 3 \n",
212 | " Johnston, Miss. Catherine Helen \"Carrie\" \n",
213 | " female \n",
214 | " NaN \n",
215 | " 1 \n",
216 | " 2 \n",
217 | " W./C. 6607 \n",
218 | " 23.4500 \n",
219 | " NaN \n",
220 | " S \n",
221 | " \n",
222 | " \n",
223 | " 889 \n",
224 | " 890 \n",
225 | " 1 \n",
226 | " 1 \n",
227 | " Behr, Mr. Karl Howell \n",
228 | " male \n",
229 | " 26.0 \n",
230 | " 0 \n",
231 | " 0 \n",
232 | " 111369 \n",
233 | " 30.0000 \n",
234 | " C148 \n",
235 | " C \n",
236 | " \n",
237 | " \n",
238 | " 890 \n",
239 | " 891 \n",
240 | " 0 \n",
241 | " 3 \n",
242 | " Dooley, Mr. Patrick \n",
243 | " male \n",
244 | " 32.0 \n",
245 | " 0 \n",
246 | " 0 \n",
247 | " 370376 \n",
248 | " 7.7500 \n",
249 | " NaN \n",
250 | " Q \n",
251 | " \n",
252 | " \n",
253 | "
\n",
254 | "
891 rows × 12 columns
\n",
255 | "
"
256 | ],
257 | "text/plain": [
258 | " PassengerId Survived Pclass \\\n",
259 | "0 1 0 3 \n",
260 | "1 2 1 1 \n",
261 | "2 3 1 3 \n",
262 | "3 4 1 1 \n",
263 | "4 5 0 3 \n",
264 | ".. ... ... ... \n",
265 | "886 887 0 2 \n",
266 | "887 888 1 1 \n",
267 | "888 889 0 3 \n",
268 | "889 890 1 1 \n",
269 | "890 891 0 3 \n",
270 | "\n",
271 | " Name Sex Age SibSp \\\n",
272 | "0 Braund, Mr. Owen Harris male 22.0 1 \n",
273 | "1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 \n",
274 | "2 Heikkinen, Miss. Laina female 26.0 0 \n",
275 | "3 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 \n",
276 | "4 Allen, Mr. William Henry male 35.0 0 \n",
277 | ".. ... ... ... ... \n",
278 | "886 Montvila, Rev. Juozas male 27.0 0 \n",
279 | "887 Graham, Miss. Margaret Edith female 19.0 0 \n",
280 | "888 Johnston, Miss. Catherine Helen \"Carrie\" female NaN 1 \n",
281 | "889 Behr, Mr. Karl Howell male 26.0 0 \n",
282 | "890 Dooley, Mr. Patrick male 32.0 0 \n",
283 | "\n",
284 | " Parch Ticket Fare Cabin Embarked \n",
285 | "0 0 A/5 21171 7.2500 NaN S \n",
286 | "1 0 PC 17599 71.2833 C85 C \n",
287 | "2 0 STON/O2. 3101282 7.9250 NaN S \n",
288 | "3 0 113803 53.1000 C123 S \n",
289 | "4 0 373450 8.0500 NaN S \n",
290 | ".. ... ... ... ... ... \n",
291 | "886 0 211536 13.0000 NaN S \n",
292 | "887 0 112053 30.0000 B42 S \n",
293 | "888 2 W./C. 6607 23.4500 NaN S \n",
294 | "889 0 111369 30.0000 C148 C \n",
295 | "890 0 370376 7.7500 NaN Q \n",
296 | "\n",
297 | "[891 rows x 12 columns]"
298 | ]
299 | },
300 | "execution_count": 3,
301 | "metadata": {},
302 | "output_type": "execute_result"
303 | }
304 | ],
305 | "source": [
306 | "titanic"
307 | ]
308 | },
309 | {
310 | "cell_type": "code",
311 | "execution_count": 4,
312 | "id": "2e64be7e-8d4f-4f7a-ace7-8e0d93086757",
313 | "metadata": {},
314 | "outputs": [
315 | {
316 | "data": {
317 | "text/html": [
318 | "\n",
319 | "\n",
332 | "
\n",
333 | " \n",
334 | " \n",
335 | " \n",
336 | " PassengerId \n",
337 | " Survived \n",
338 | " Pclass \n",
339 | " Name \n",
340 | " Sex \n",
341 | " Age \n",
342 | " SibSp \n",
343 | " Parch \n",
344 | " Ticket \n",
345 | " Fare \n",
346 | " Cabin \n",
347 | " Embarked \n",
348 | " \n",
349 | " \n",
350 | " \n",
351 | " \n",
352 | " 0 \n",
353 | " 1 \n",
354 | " 0 \n",
355 | " 3 \n",
356 | " Braund, Mr. Owen Harris \n",
357 | " male \n",
358 | " 22.0 \n",
359 | " 1 \n",
360 | " 0 \n",
361 | " A/5 21171 \n",
362 | " 7.2500 \n",
363 | " NaN \n",
364 | " S \n",
365 | " \n",
366 | " \n",
367 | " 1 \n",
368 | " 2 \n",
369 | " 1 \n",
370 | " 1 \n",
371 | " Cumings, Mrs. John Bradley (Florence Briggs Th... \n",
372 | " female \n",
373 | " 38.0 \n",
374 | " 1 \n",
375 | " 0 \n",
376 | " PC 17599 \n",
377 | " 71.2833 \n",
378 | " C85 \n",
379 | " C \n",
380 | " \n",
381 | " \n",
382 | " 2 \n",
383 | " 3 \n",
384 | " 1 \n",
385 | " 3 \n",
386 | " Heikkinen, Miss. Laina \n",
387 | " female \n",
388 | " 26.0 \n",
389 | " 0 \n",
390 | " 0 \n",
391 | " STON/O2. 3101282 \n",
392 | " 7.9250 \n",
393 | " NaN \n",
394 | " S \n",
395 | " \n",
396 | " \n",
397 | " 3 \n",
398 | " 4 \n",
399 | " 1 \n",
400 | " 1 \n",
401 | " Futrelle, Mrs. Jacques Heath (Lily May Peel) \n",
402 | " female \n",
403 | " 35.0 \n",
404 | " 1 \n",
405 | " 0 \n",
406 | " 113803 \n",
407 | " 53.1000 \n",
408 | " C123 \n",
409 | " S \n",
410 | " \n",
411 | " \n",
412 | " 4 \n",
413 | " 5 \n",
414 | " 0 \n",
415 | " 3 \n",
416 | " Allen, Mr. William Henry \n",
417 | " male \n",
418 | " 35.0 \n",
419 | " 0 \n",
420 | " 0 \n",
421 | " 373450 \n",
422 | " 8.0500 \n",
423 | " NaN \n",
424 | " S \n",
425 | " \n",
426 | " \n",
427 | "
\n",
428 | "
"
429 | ],
430 | "text/plain": [
431 | " PassengerId Survived Pclass \\\n",
432 | "0 1 0 3 \n",
433 | "1 2 1 1 \n",
434 | "2 3 1 3 \n",
435 | "3 4 1 1 \n",
436 | "4 5 0 3 \n",
437 | "\n",
438 | " Name Sex Age SibSp \\\n",
439 | "0 Braund, Mr. Owen Harris male 22.0 1 \n",
440 | "1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 \n",
441 | "2 Heikkinen, Miss. Laina female 26.0 0 \n",
442 | "3 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 \n",
443 | "4 Allen, Mr. William Henry male 35.0 0 \n",
444 | "\n",
445 | " Parch Ticket Fare Cabin Embarked \n",
446 | "0 0 A/5 21171 7.2500 NaN S \n",
447 | "1 0 PC 17599 71.2833 C85 C \n",
448 | "2 0 STON/O2. 3101282 7.9250 NaN S \n",
449 | "3 0 113803 53.1000 C123 S \n",
450 | "4 0 373450 8.0500 NaN S "
451 | ]
452 | },
453 | "execution_count": 4,
454 | "metadata": {},
455 | "output_type": "execute_result"
456 | }
457 | ],
458 | "source": [
459 | "(.head titanic)"
460 | ]
461 | },
462 | {
463 | "cell_type": "code",
464 | "execution_count": 5,
465 | "id": "6c7774ed-1a11-4ff8-98bf-3a9efd775fdc",
466 | "metadata": {},
467 | "outputs": [
468 | {
469 | "data": {
470 | "text/html": [
471 | "\n",
472 | "\n",
485 | "
\n",
486 | " \n",
487 | " \n",
488 | " \n",
489 | " PassengerId \n",
490 | " Survived \n",
491 | " Pclass \n",
492 | " Name \n",
493 | " Sex \n",
494 | " Age \n",
495 | " SibSp \n",
496 | " Parch \n",
497 | " Ticket \n",
498 | " Fare \n",
499 | " Cabin \n",
500 | " Embarked \n",
501 | " \n",
502 | " \n",
503 | " \n",
504 | " \n",
505 | " 886 \n",
506 | " 887 \n",
507 | " 0 \n",
508 | " 2 \n",
509 | " Montvila, Rev. Juozas \n",
510 | " male \n",
511 | " 27.0 \n",
512 | " 0 \n",
513 | " 0 \n",
514 | " 211536 \n",
515 | " 13.00 \n",
516 | " NaN \n",
517 | " S \n",
518 | " \n",
519 | " \n",
520 | " 887 \n",
521 | " 888 \n",
522 | " 1 \n",
523 | " 1 \n",
524 | " Graham, Miss. Margaret Edith \n",
525 | " female \n",
526 | " 19.0 \n",
527 | " 0 \n",
528 | " 0 \n",
529 | " 112053 \n",
530 | " 30.00 \n",
531 | " B42 \n",
532 | " S \n",
533 | " \n",
534 | " \n",
535 | " 888 \n",
536 | " 889 \n",
537 | " 0 \n",
538 | " 3 \n",
539 | " Johnston, Miss. Catherine Helen \"Carrie\" \n",
540 | " female \n",
541 | " NaN \n",
542 | " 1 \n",
543 | " 2 \n",
544 | " W./C. 6607 \n",
545 | " 23.45 \n",
546 | " NaN \n",
547 | " S \n",
548 | " \n",
549 | " \n",
550 | " 889 \n",
551 | " 890 \n",
552 | " 1 \n",
553 | " 1 \n",
554 | " Behr, Mr. Karl Howell \n",
555 | " male \n",
556 | " 26.0 \n",
557 | " 0 \n",
558 | " 0 \n",
559 | " 111369 \n",
560 | " 30.00 \n",
561 | " C148 \n",
562 | " C \n",
563 | " \n",
564 | " \n",
565 | " 890 \n",
566 | " 891 \n",
567 | " 0 \n",
568 | " 3 \n",
569 | " Dooley, Mr. Patrick \n",
570 | " male \n",
571 | " 32.0 \n",
572 | " 0 \n",
573 | " 0 \n",
574 | " 370376 \n",
575 | " 7.75 \n",
576 | " NaN \n",
577 | " Q \n",
578 | " \n",
579 | " \n",
580 | "
\n",
581 | "
"
582 | ],
583 | "text/plain": [
584 | " PassengerId Survived Pclass Name \\\n",
585 | "886 887 0 2 Montvila, Rev. Juozas \n",
586 | "887 888 1 1 Graham, Miss. Margaret Edith \n",
587 | "888 889 0 3 Johnston, Miss. Catherine Helen \"Carrie\" \n",
588 | "889 890 1 1 Behr, Mr. Karl Howell \n",
589 | "890 891 0 3 Dooley, Mr. Patrick \n",
590 | "\n",
591 | " Sex Age SibSp Parch Ticket Fare Cabin Embarked \n",
592 | "886 male 27.0 0 0 211536 13.00 NaN S \n",
593 | "887 female 19.0 0 0 112053 30.00 B42 S \n",
594 | "888 female NaN 1 2 W./C. 6607 23.45 NaN S \n",
595 | "889 male 26.0 0 0 111369 30.00 C148 C \n",
596 | "890 male 32.0 0 0 370376 7.75 NaN Q "
597 | ]
598 | },
599 | "execution_count": 5,
600 | "metadata": {},
601 | "output_type": "execute_result"
602 | }
603 | ],
604 | "source": [
605 | "(.tail titanic)"
606 | ]
607 | },
608 | {
609 | "cell_type": "code",
610 | "execution_count": 6,
611 | "id": "e8b485af-8801-419c-916d-727b5684b966",
612 | "metadata": {},
613 | "outputs": [
614 | {
615 | "name": "stdout",
616 | "output_type": "stream",
617 | "text": [
618 | "\n",
619 | "RangeIndex: 891 entries, 0 to 890\n",
620 | "Data columns (total 12 columns):\n",
621 | " # Column Non-Null Count Dtype \n",
622 | "--- ------ -------------- ----- \n",
623 | " 0 PassengerId 891 non-null int64 \n",
624 | " 1 Survived 891 non-null int64 \n",
625 | " 2 Pclass 891 non-null int64 \n",
626 | " 3 Name 891 non-null object \n",
627 | " 4 Sex 891 non-null object \n",
628 | " 5 Age 714 non-null float64\n",
629 | " 6 SibSp 891 non-null int64 \n",
630 | " 7 Parch 891 non-null int64 \n",
631 | " 8 Ticket 891 non-null object \n",
632 | " 9 Fare 891 non-null float64\n",
633 | " 10 Cabin 204 non-null object \n",
634 | " 11 Embarked 889 non-null object \n",
635 | "dtypes: float64(2), int64(5), object(5)\n",
636 | "memory usage: 83.7+ KB\n"
637 | ]
638 | }
639 | ],
640 | "source": [
641 | "(.info titanic)"
642 | ]
643 | },
644 | {
645 | "cell_type": "code",
646 | "execution_count": 7,
647 | "id": "70c75282-31a3-47a9-9a05-76eddf97dd9a",
648 | "metadata": {},
649 | "outputs": [
650 | {
651 | "data": {
652 | "text/plain": [
653 | "#'user/ages"
654 | ]
655 | },
656 | "execution_count": 7,
657 | "metadata": {},
658 | "output_type": "execute_result"
659 | }
660 | ],
661 | "source": [
662 | "(def ages (aget titanic \"Age\"))"
663 | ]
664 | },
665 | {
666 | "cell_type": "code",
667 | "execution_count": 8,
668 | "id": "170e9897-653e-46e4-985d-e03287a19e6a",
669 | "metadata": {},
670 | "outputs": [
671 | {
672 | "data": {
673 | "text/plain": [
674 | "0 22.0\n",
675 | "1 38.0\n",
676 | "2 26.0\n",
677 | "3 35.0\n",
678 | "4 35.0\n",
679 | " ... \n",
680 | "886 27.0\n",
681 | "887 19.0\n",
682 | "888 NaN\n",
683 | "889 26.0\n",
684 | "890 32.0\n",
685 | "Name: Age, Length: 891, dtype: float64"
686 | ]
687 | },
688 | "execution_count": 8,
689 | "metadata": {},
690 | "output_type": "execute_result"
691 | }
692 | ],
693 | "source": [
694 | "ages"
695 | ]
696 | },
697 | {
698 | "cell_type": "code",
699 | "execution_count": 9,
700 | "id": "b2b45abe-a548-4040-b641-009c97a7e020",
701 | "metadata": {},
702 | "outputs": [
703 | {
704 | "data": {
705 | "text/plain": [
706 | "(891,)"
707 | ]
708 | },
709 | "execution_count": 9,
710 | "metadata": {},
711 | "output_type": "execute_result"
712 | }
713 | ],
714 | "source": [
715 | "(.-shape ages)"
716 | ]
717 | },
718 | {
719 | "cell_type": "code",
720 | "execution_count": 10,
721 | "id": "2a6f9d1f-52fc-4533-bceb-0825eb4dff4d",
722 | "metadata": {},
723 | "outputs": [
724 | {
725 | "data": {
726 | "text/html": [
727 | "\n",
728 | "\n",
741 | "
\n",
742 | " \n",
743 | " \n",
744 | " \n",
745 | " Age \n",
746 | " Sex \n",
747 | " \n",
748 | " \n",
749 | " \n",
750 | " \n",
751 | " 0 \n",
752 | " 22.0 \n",
753 | " male \n",
754 | " \n",
755 | " \n",
756 | " 1 \n",
757 | " 38.0 \n",
758 | " female \n",
759 | " \n",
760 | " \n",
761 | " 2 \n",
762 | " 26.0 \n",
763 | " female \n",
764 | " \n",
765 | " \n",
766 | " 3 \n",
767 | " 35.0 \n",
768 | " female \n",
769 | " \n",
770 | " \n",
771 | " 4 \n",
772 | " 35.0 \n",
773 | " male \n",
774 | " \n",
775 | " \n",
776 | " ... \n",
777 | " ... \n",
778 | " ... \n",
779 | " \n",
780 | " \n",
781 | " 886 \n",
782 | " 27.0 \n",
783 | " male \n",
784 | " \n",
785 | " \n",
786 | " 887 \n",
787 | " 19.0 \n",
788 | " female \n",
789 | " \n",
790 | " \n",
791 | " 888 \n",
792 | " NaN \n",
793 | " female \n",
794 | " \n",
795 | " \n",
796 | " 889 \n",
797 | " 26.0 \n",
798 | " male \n",
799 | " \n",
800 | " \n",
801 | " 890 \n",
802 | " 32.0 \n",
803 | " male \n",
804 | " \n",
805 | " \n",
806 | "
\n",
807 | "
891 rows × 2 columns
\n",
808 | "
"
809 | ],
810 | "text/plain": [
811 | " Age Sex\n",
812 | "0 22.0 male\n",
813 | "1 38.0 female\n",
814 | "2 26.0 female\n",
815 | "3 35.0 female\n",
816 | "4 35.0 male\n",
817 | ".. ... ...\n",
818 | "886 27.0 male\n",
819 | "887 19.0 female\n",
820 | "888 NaN female\n",
821 | "889 26.0 male\n",
822 | "890 32.0 male\n",
823 | "\n",
824 | "[891 rows x 2 columns]"
825 | ]
826 | },
827 | "execution_count": 10,
828 | "metadata": {},
829 | "output_type": "execute_result"
830 | }
831 | ],
832 | "source": [
833 | "(aget titanic [\"Age\" \"Sex\"])"
834 | ]
835 | },
836 | {
837 | "cell_type": "code",
838 | "execution_count": 11,
839 | "id": "71a18d64-2bd4-4373-85a9-5ece2cd76129",
840 | "metadata": {},
841 | "outputs": [
842 | {
843 | "data": {
844 | "text/plain": [
845 | "#'user/above-35"
846 | ]
847 | },
848 | "execution_count": 11,
849 | "metadata": {},
850 | "output_type": "execute_result"
851 | }
852 | ],
853 | "source": [
854 | "(def above-35 (aget titanic (> (aget titanic \"Age\") 35)))"
855 | ]
856 | },
857 | {
858 | "cell_type": "code",
859 | "execution_count": 12,
860 | "id": "482e5a86-68a2-45ac-b8cf-0f777995e11c",
861 | "metadata": {},
862 | "outputs": [
863 | {
864 | "data": {
865 | "text/html": [
866 | "\n",
867 | "\n",
880 | "
\n",
881 | " \n",
882 | " \n",
883 | " \n",
884 | " PassengerId \n",
885 | " Survived \n",
886 | " Pclass \n",
887 | " Name \n",
888 | " Sex \n",
889 | " Age \n",
890 | " SibSp \n",
891 | " Parch \n",
892 | " Ticket \n",
893 | " Fare \n",
894 | " Cabin \n",
895 | " Embarked \n",
896 | " \n",
897 | " \n",
898 | " \n",
899 | " \n",
900 | " 1 \n",
901 | " 2 \n",
902 | " 1 \n",
903 | " 1 \n",
904 | " Cumings, Mrs. John Bradley (Florence Briggs Th... \n",
905 | " female \n",
906 | " 38.0 \n",
907 | " 1 \n",
908 | " 0 \n",
909 | " PC 17599 \n",
910 | " 71.2833 \n",
911 | " C85 \n",
912 | " C \n",
913 | " \n",
914 | " \n",
915 | " 6 \n",
916 | " 7 \n",
917 | " 0 \n",
918 | " 1 \n",
919 | " McCarthy, Mr. Timothy J \n",
920 | " male \n",
921 | " 54.0 \n",
922 | " 0 \n",
923 | " 0 \n",
924 | " 17463 \n",
925 | " 51.8625 \n",
926 | " E46 \n",
927 | " S \n",
928 | " \n",
929 | " \n",
930 | " 11 \n",
931 | " 12 \n",
932 | " 1 \n",
933 | " 1 \n",
934 | " Bonnell, Miss. Elizabeth \n",
935 | " female \n",
936 | " 58.0 \n",
937 | " 0 \n",
938 | " 0 \n",
939 | " 113783 \n",
940 | " 26.5500 \n",
941 | " C103 \n",
942 | " S \n",
943 | " \n",
944 | " \n",
945 | " 13 \n",
946 | " 14 \n",
947 | " 0 \n",
948 | " 3 \n",
949 | " Andersson, Mr. Anders Johan \n",
950 | " male \n",
951 | " 39.0 \n",
952 | " 1 \n",
953 | " 5 \n",
954 | " 347082 \n",
955 | " 31.2750 \n",
956 | " NaN \n",
957 | " S \n",
958 | " \n",
959 | " \n",
960 | " 15 \n",
961 | " 16 \n",
962 | " 1 \n",
963 | " 2 \n",
964 | " Hewlett, Mrs. (Mary D Kingcome) \n",
965 | " female \n",
966 | " 55.0 \n",
967 | " 0 \n",
968 | " 0 \n",
969 | " 248706 \n",
970 | " 16.0000 \n",
971 | " NaN \n",
972 | " S \n",
973 | " \n",
974 | " \n",
975 | "
\n",
976 | "
"
977 | ],
978 | "text/plain": [
979 | " PassengerId Survived Pclass \\\n",
980 | "1 2 1 1 \n",
981 | "6 7 0 1 \n",
982 | "11 12 1 1 \n",
983 | "13 14 0 3 \n",
984 | "15 16 1 2 \n",
985 | "\n",
986 | " Name Sex Age SibSp \\\n",
987 | "1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 \n",
988 | "6 McCarthy, Mr. Timothy J male 54.0 0 \n",
989 | "11 Bonnell, Miss. Elizabeth female 58.0 0 \n",
990 | "13 Andersson, Mr. Anders Johan male 39.0 1 \n",
991 | "15 Hewlett, Mrs. (Mary D Kingcome) female 55.0 0 \n",
992 | "\n",
993 | " Parch Ticket Fare Cabin Embarked \n",
994 | "1 0 PC 17599 71.2833 C85 C \n",
995 | "6 0 17463 51.8625 E46 S \n",
996 | "11 0 113783 26.5500 C103 S \n",
997 | "13 5 347082 31.2750 NaN S \n",
998 | "15 0 248706 16.0000 NaN S "
999 | ]
1000 | },
1001 | "execution_count": 12,
1002 | "metadata": {},
1003 | "output_type": "execute_result"
1004 | }
1005 | ],
1006 | "source": [
1007 | "(.head above-35)"
1008 | ]
1009 | },
1010 | {
1011 | "cell_type": "code",
1012 | "execution_count": 13,
1013 | "id": "90a244f9-0a23-447a-9208-f98a9da7bcf3",
1014 | "metadata": {},
1015 | "outputs": [
1016 | {
1017 | "data": {
1018 | "text/plain": [
1019 | "(217, 12)"
1020 | ]
1021 | },
1022 | "execution_count": 13,
1023 | "metadata": {},
1024 | "output_type": "execute_result"
1025 | }
1026 | ],
1027 | "source": [
1028 | "(.-shape above-35)"
1029 | ]
1030 | },
1031 | {
1032 | "cell_type": "code",
1033 | "execution_count": 14,
1034 | "id": "7e758359-a22c-4307-abf8-9a851a20a584",
1035 | "metadata": {},
1036 | "outputs": [
1037 | {
1038 | "data": {
1039 | "text/plain": [
1040 | "0 False\n",
1041 | "1 True\n",
1042 | "2 False\n",
1043 | "3 False\n",
1044 | "4 False\n",
1045 | " ... \n",
1046 | "886 False\n",
1047 | "887 False\n",
1048 | "888 False\n",
1049 | "889 False\n",
1050 | "890 False\n",
1051 | "Name: Age, Length: 891, dtype: bool"
1052 | ]
1053 | },
1054 | "execution_count": 14,
1055 | "metadata": {},
1056 | "output_type": "execute_result"
1057 | }
1058 | ],
1059 | "source": [
1060 | "(> (aget titanic \"Age\") 35)"
1061 | ]
1062 | },
1063 | {
1064 | "cell_type": "code",
1065 | "execution_count": 15,
1066 | "id": "84f41bd2-beda-4c3f-b2a7-d47912a83fec",
1067 | "metadata": {},
1068 | "outputs": [
1069 | {
1070 | "data": {
1071 | "text/plain": [
1072 | "#'user/class-23"
1073 | ]
1074 | },
1075 | "execution_count": 15,
1076 | "metadata": {},
1077 | "output_type": "execute_result"
1078 | }
1079 | ],
1080 | "source": [
1081 | "(def class-23 (aget titanic (.isin (aget titanic \"Pclass\") [2 3])))"
1082 | ]
1083 | },
1084 | {
1085 | "cell_type": "code",
1086 | "execution_count": 16,
1087 | "id": "89eed3be-92da-47fe-ad15-e9f3997f08e0",
1088 | "metadata": {},
1089 | "outputs": [
1090 | {
1091 | "data": {
1092 | "text/html": [
1093 | "\n",
1094 | "\n",
1107 | "
\n",
1108 | " \n",
1109 | " \n",
1110 | " \n",
1111 | " PassengerId \n",
1112 | " Survived \n",
1113 | " Pclass \n",
1114 | " Name \n",
1115 | " Sex \n",
1116 | " Age \n",
1117 | " SibSp \n",
1118 | " Parch \n",
1119 | " Ticket \n",
1120 | " Fare \n",
1121 | " Cabin \n",
1122 | " Embarked \n",
1123 | " \n",
1124 | " \n",
1125 | " \n",
1126 | " \n",
1127 | " 0 \n",
1128 | " 1 \n",
1129 | " 0 \n",
1130 | " 3 \n",
1131 | " Braund, Mr. Owen Harris \n",
1132 | " male \n",
1133 | " 22.0 \n",
1134 | " 1 \n",
1135 | " 0 \n",
1136 | " A/5 21171 \n",
1137 | " 7.2500 \n",
1138 | " NaN \n",
1139 | " S \n",
1140 | " \n",
1141 | " \n",
1142 | " 2 \n",
1143 | " 3 \n",
1144 | " 1 \n",
1145 | " 3 \n",
1146 | " Heikkinen, Miss. Laina \n",
1147 | " female \n",
1148 | " 26.0 \n",
1149 | " 0 \n",
1150 | " 0 \n",
1151 | " STON/O2. 3101282 \n",
1152 | " 7.9250 \n",
1153 | " NaN \n",
1154 | " S \n",
1155 | " \n",
1156 | " \n",
1157 | " 4 \n",
1158 | " 5 \n",
1159 | " 0 \n",
1160 | " 3 \n",
1161 | " Allen, Mr. William Henry \n",
1162 | " male \n",
1163 | " 35.0 \n",
1164 | " 0 \n",
1165 | " 0 \n",
1166 | " 373450 \n",
1167 | " 8.0500 \n",
1168 | " NaN \n",
1169 | " S \n",
1170 | " \n",
1171 | " \n",
1172 | " 5 \n",
1173 | " 6 \n",
1174 | " 0 \n",
1175 | " 3 \n",
1176 | " Moran, Mr. James \n",
1177 | " male \n",
1178 | " NaN \n",
1179 | " 0 \n",
1180 | " 0 \n",
1181 | " 330877 \n",
1182 | " 8.4583 \n",
1183 | " NaN \n",
1184 | " Q \n",
1185 | " \n",
1186 | " \n",
1187 | " 7 \n",
1188 | " 8 \n",
1189 | " 0 \n",
1190 | " 3 \n",
1191 | " Palsson, Master. Gosta Leonard \n",
1192 | " male \n",
1193 | " 2.0 \n",
1194 | " 3 \n",
1195 | " 1 \n",
1196 | " 349909 \n",
1197 | " 21.0750 \n",
1198 | " NaN \n",
1199 | " S \n",
1200 | " \n",
1201 | " \n",
1202 | "
\n",
1203 | "
"
1204 | ],
1205 | "text/plain": [
1206 | " PassengerId Survived Pclass Name Sex \\\n",
1207 | "0 1 0 3 Braund, Mr. Owen Harris male \n",
1208 | "2 3 1 3 Heikkinen, Miss. Laina female \n",
1209 | "4 5 0 3 Allen, Mr. William Henry male \n",
1210 | "5 6 0 3 Moran, Mr. James male \n",
1211 | "7 8 0 3 Palsson, Master. Gosta Leonard male \n",
1212 | "\n",
1213 | " Age SibSp Parch Ticket Fare Cabin Embarked \n",
1214 | "0 22.0 1 0 A/5 21171 7.2500 NaN S \n",
1215 | "2 26.0 0 0 STON/O2. 3101282 7.9250 NaN S \n",
1216 | "4 35.0 0 0 373450 8.0500 NaN S \n",
1217 | "5 NaN 0 0 330877 8.4583 NaN Q \n",
1218 | "7 2.0 3 1 349909 21.0750 NaN S "
1219 | ]
1220 | },
1221 | "execution_count": 16,
1222 | "metadata": {},
1223 | "output_type": "execute_result"
1224 | }
1225 | ],
1226 | "source": [
1227 | "(.head class-23)"
1228 | ]
1229 | },
1230 | {
1231 | "cell_type": "code",
1232 | "execution_count": 17,
1233 | "id": "7ad1815d-2554-4f3c-a1a1-b3f5a86bf7a9",
1234 | "metadata": {},
1235 | "outputs": [
1236 | {
1237 | "data": {
1238 | "text/plain": [
1239 | "0 3\n",
1240 | "1 1\n",
1241 | "2 3\n",
1242 | "3 1\n",
1243 | "4 3\n",
1244 | " ..\n",
1245 | "886 2\n",
1246 | "887 1\n",
1247 | "888 3\n",
1248 | "889 1\n",
1249 | "890 3\n",
1250 | "Name: Pclass, Length: 891, dtype: int64"
1251 | ]
1252 | },
1253 | "execution_count": 17,
1254 | "metadata": {},
1255 | "output_type": "execute_result"
1256 | }
1257 | ],
1258 | "source": [
1259 | "(operator/or (aget titanic \"Pclass\") (aget titanic \"Pclass\"))"
1260 | ]
1261 | },
1262 | {
1263 | "cell_type": "code",
1264 | "execution_count": 18,
1265 | "id": "fc74c4c9-4fba-4861-9d49-aaf472918faa",
1266 | "metadata": {},
1267 | "outputs": [
1268 | {
1269 | "data": {
1270 | "text/plain": [
1271 | "#'user/age-no-na"
1272 | ]
1273 | },
1274 | "execution_count": 18,
1275 | "metadata": {},
1276 | "output_type": "execute_result"
1277 | }
1278 | ],
1279 | "source": [
1280 | "(def age-no-na (aget titanic (.notna (aget titanic \"Age\"))))"
1281 | ]
1282 | },
1283 | {
1284 | "cell_type": "code",
1285 | "execution_count": 19,
1286 | "id": "6042413f-ade5-4ef7-9e66-c018067d623d",
1287 | "metadata": {},
1288 | "outputs": [
1289 | {
1290 | "data": {
1291 | "text/plain": [
1292 | "(714, 12)"
1293 | ]
1294 | },
1295 | "execution_count": 19,
1296 | "metadata": {},
1297 | "output_type": "execute_result"
1298 | }
1299 | ],
1300 | "source": [
1301 | "(.-shape age-no-na)"
1302 | ]
1303 | },
1304 | {
1305 | "cell_type": "code",
1306 | "execution_count": 20,
1307 | "id": "737bb875-2669-4c89-bf4d-4633c08b1841",
1308 | "metadata": {},
1309 | "outputs": [
1310 | {
1311 | "data": {
1312 | "text/plain": [
1313 | "1 Cumings, Mrs. John Bradley (Florence Briggs Th...\n",
1314 | "6 McCarthy, Mr. Timothy J\n",
1315 | "11 Bonnell, Miss. Elizabeth\n",
1316 | "13 Andersson, Mr. Anders Johan\n",
1317 | "15 Hewlett, Mrs. (Mary D Kingcome) \n",
1318 | " ... \n",
1319 | "865 Bystrom, Mrs. (Karolina)\n",
1320 | "871 Beckwith, Mrs. Richard Leonard (Sallie Monypeny)\n",
1321 | "873 Vander Cruyssen, Mr. Victor\n",
1322 | "879 Potter, Mrs. Thomas Jr (Lily Alexenia Wilson)\n",
1323 | "885 Rice, Mrs. William (Margaret Norton)\n",
1324 | "Name: Name, Length: 217, dtype: object"
1325 | ]
1326 | },
1327 | "execution_count": 20,
1328 | "metadata": {},
1329 | "output_type": "execute_result"
1330 | }
1331 | ],
1332 | "source": [
1333 | "(aget (.loc titanic) (> (aget titanic \"Age\") 35) \"Name\")"
1334 | ]
1335 | },
1336 | {
1337 | "cell_type": "code",
1338 | "execution_count": 21,
1339 | "id": "d4e2d719-0da8-4a3a-996a-fffbfab03a45",
1340 | "metadata": {},
1341 | "outputs": [
1342 | {
1343 | "data": {
1344 | "text/html": [
1345 | "\n",
1346 | "\n",
1359 | "
\n",
1360 | " \n",
1361 | " \n",
1362 | " \n",
1363 | " Pclass \n",
1364 | " Name \n",
1365 | " Sex \n",
1366 | " \n",
1367 | " \n",
1368 | " \n",
1369 | " \n",
1370 | " 9 \n",
1371 | " 2 \n",
1372 | " Nasser, Mrs. Nicholas (Adele Achem) \n",
1373 | " female \n",
1374 | " \n",
1375 | " \n",
1376 | " 10 \n",
1377 | " 3 \n",
1378 | " Sandstrom, Miss. Marguerite Rut \n",
1379 | " female \n",
1380 | " \n",
1381 | " \n",
1382 | " 11 \n",
1383 | " 1 \n",
1384 | " Bonnell, Miss. Elizabeth \n",
1385 | " female \n",
1386 | " \n",
1387 | " \n",
1388 | " 12 \n",
1389 | " 3 \n",
1390 | " Saundercock, Mr. William Henry \n",
1391 | " male \n",
1392 | " \n",
1393 | " \n",
1394 | " 13 \n",
1395 | " 3 \n",
1396 | " Andersson, Mr. Anders Johan \n",
1397 | " male \n",
1398 | " \n",
1399 | " \n",
1400 | " 14 \n",
1401 | " 3 \n",
1402 | " Vestrom, Miss. Hulda Amanda Adolfina \n",
1403 | " female \n",
1404 | " \n",
1405 | " \n",
1406 | " 15 \n",
1407 | " 2 \n",
1408 | " Hewlett, Mrs. (Mary D Kingcome) \n",
1409 | " female \n",
1410 | " \n",
1411 | " \n",
1412 | " 16 \n",
1413 | " 3 \n",
1414 | " Rice, Master. Eugene \n",
1415 | " male \n",
1416 | " \n",
1417 | " \n",
1418 | " 17 \n",
1419 | " 2 \n",
1420 | " Williams, Mr. Charles Eugene \n",
1421 | " male \n",
1422 | " \n",
1423 | " \n",
1424 | " 18 \n",
1425 | " 3 \n",
1426 | " Vander Planke, Mrs. Julius (Emelia Maria Vande... \n",
1427 | " female \n",
1428 | " \n",
1429 | " \n",
1430 | " 19 \n",
1431 | " 3 \n",
1432 | " Masselmani, Mrs. Fatima \n",
1433 | " female \n",
1434 | " \n",
1435 | " \n",
1436 | " 20 \n",
1437 | " 2 \n",
1438 | " Fynney, Mr. Joseph J \n",
1439 | " male \n",
1440 | " \n",
1441 | " \n",
1442 | " 21 \n",
1443 | " 2 \n",
1444 | " Beesley, Mr. Lawrence \n",
1445 | " male \n",
1446 | " \n",
1447 | " \n",
1448 | " 22 \n",
1449 | " 3 \n",
1450 | " McGowan, Miss. Anna \"Annie\" \n",
1451 | " female \n",
1452 | " \n",
1453 | " \n",
1454 | " 23 \n",
1455 | " 1 \n",
1456 | " Sloper, Mr. William Thompson \n",
1457 | " male \n",
1458 | " \n",
1459 | " \n",
1460 | " 24 \n",
1461 | " 3 \n",
1462 | " Palsson, Miss. Torborg Danira \n",
1463 | " female \n",
1464 | " \n",
1465 | " \n",
1466 | "
\n",
1467 | "
"
1468 | ],
1469 | "text/plain": [
1470 | " Pclass Name Sex\n",
1471 | "9 2 Nasser, Mrs. Nicholas (Adele Achem) female\n",
1472 | "10 3 Sandstrom, Miss. Marguerite Rut female\n",
1473 | "11 1 Bonnell, Miss. Elizabeth female\n",
1474 | "12 3 Saundercock, Mr. William Henry male\n",
1475 | "13 3 Andersson, Mr. Anders Johan male\n",
1476 | "14 3 Vestrom, Miss. Hulda Amanda Adolfina female\n",
1477 | "15 2 Hewlett, Mrs. (Mary D Kingcome) female\n",
1478 | "16 3 Rice, Master. Eugene male\n",
1479 | "17 2 Williams, Mr. Charles Eugene male\n",
1480 | "18 3 Vander Planke, Mrs. Julius (Emelia Maria Vande... female\n",
1481 | "19 3 Masselmani, Mrs. Fatima female\n",
1482 | "20 2 Fynney, Mr. Joseph J male\n",
1483 | "21 2 Beesley, Mr. Lawrence male\n",
1484 | "22 3 McGowan, Miss. Anna \"Annie\" female\n",
1485 | "23 1 Sloper, Mr. William Thompson male\n",
1486 | "24 3 Palsson, Miss. Torborg Danira female"
1487 | ]
1488 | },
1489 | "execution_count": 21,
1490 | "metadata": {},
1491 | "output_type": "execute_result"
1492 | }
1493 | ],
1494 | "source": [
1495 | "(aget (.iloc titanic) #py ((slice 9 25) (slice 2 5)))"
1496 | ]
1497 | },
1498 | {
1499 | "cell_type": "code",
1500 | "execution_count": 22,
1501 | "id": "8f497c17-11c0-4d47-a07a-20b57d9131b5",
1502 | "metadata": {},
1503 | "outputs": [
1504 | {
1505 | "data": {
1506 | "text/plain": [
1507 | "'anonymous'"
1508 | ]
1509 | },
1510 | "execution_count": 22,
1511 | "metadata": {},
1512 | "output_type": "execute_result"
1513 | }
1514 | ],
1515 | "source": [
1516 | "(aset (.iloc titanic) #py ((slice 0 3) 3) \"anonymous\")"
1517 | ]
1518 | },
1519 | {
1520 | "cell_type": "code",
1521 | "execution_count": 23,
1522 | "id": "bfea0826-a064-43d5-8b11-a1b1333e7bb2",
1523 | "metadata": {},
1524 | "outputs": [
1525 | {
1526 | "data": {
1527 | "text/html": [
1528 | "\n",
1529 | "\n",
1542 | "
\n",
1543 | " \n",
1544 | " \n",
1545 | " \n",
1546 | " PassengerId \n",
1547 | " Survived \n",
1548 | " Pclass \n",
1549 | " Name \n",
1550 | " Sex \n",
1551 | " Age \n",
1552 | " SibSp \n",
1553 | " Parch \n",
1554 | " Ticket \n",
1555 | " Fare \n",
1556 | " Cabin \n",
1557 | " Embarked \n",
1558 | " \n",
1559 | " \n",
1560 | " \n",
1561 | " \n",
1562 | " 0 \n",
1563 | " 1 \n",
1564 | " 0 \n",
1565 | " 3 \n",
1566 | " anonymous \n",
1567 | " male \n",
1568 | " 22.0 \n",
1569 | " 1 \n",
1570 | " 0 \n",
1571 | " A/5 21171 \n",
1572 | " 7.2500 \n",
1573 | " NaN \n",
1574 | " S \n",
1575 | " \n",
1576 | " \n",
1577 | " 1 \n",
1578 | " 2 \n",
1579 | " 1 \n",
1580 | " 1 \n",
1581 | " anonymous \n",
1582 | " female \n",
1583 | " 38.0 \n",
1584 | " 1 \n",
1585 | " 0 \n",
1586 | " PC 17599 \n",
1587 | " 71.2833 \n",
1588 | " C85 \n",
1589 | " C \n",
1590 | " \n",
1591 | " \n",
1592 | " 2 \n",
1593 | " 3 \n",
1594 | " 1 \n",
1595 | " 3 \n",
1596 | " anonymous \n",
1597 | " female \n",
1598 | " 26.0 \n",
1599 | " 0 \n",
1600 | " 0 \n",
1601 | " STON/O2. 3101282 \n",
1602 | " 7.9250 \n",
1603 | " NaN \n",
1604 | " S \n",
1605 | " \n",
1606 | " \n",
1607 | " 3 \n",
1608 | " 4 \n",
1609 | " 1 \n",
1610 | " 1 \n",
1611 | " Futrelle, Mrs. Jacques Heath (Lily May Peel) \n",
1612 | " female \n",
1613 | " 35.0 \n",
1614 | " 1 \n",
1615 | " 0 \n",
1616 | " 113803 \n",
1617 | " 53.1000 \n",
1618 | " C123 \n",
1619 | " S \n",
1620 | " \n",
1621 | " \n",
1622 | " 4 \n",
1623 | " 5 \n",
1624 | " 0 \n",
1625 | " 3 \n",
1626 | " Allen, Mr. William Henry \n",
1627 | " male \n",
1628 | " 35.0 \n",
1629 | " 0 \n",
1630 | " 0 \n",
1631 | " 373450 \n",
1632 | " 8.0500 \n",
1633 | " NaN \n",
1634 | " S \n",
1635 | " \n",
1636 | " \n",
1637 | "
\n",
1638 | "
"
1639 | ],
1640 | "text/plain": [
1641 | " PassengerId Survived Pclass \\\n",
1642 | "0 1 0 3 \n",
1643 | "1 2 1 1 \n",
1644 | "2 3 1 3 \n",
1645 | "3 4 1 1 \n",
1646 | "4 5 0 3 \n",
1647 | "\n",
1648 | " Name Sex Age SibSp Parch \\\n",
1649 | "0 anonymous male 22.0 1 0 \n",
1650 | "1 anonymous female 38.0 1 0 \n",
1651 | "2 anonymous female 26.0 0 0 \n",
1652 | "3 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 \n",
1653 | "4 Allen, Mr. William Henry male 35.0 0 0 \n",
1654 | "\n",
1655 | " Ticket Fare Cabin Embarked \n",
1656 | "0 A/5 21171 7.2500 NaN S \n",
1657 | "1 PC 17599 71.2833 C85 C \n",
1658 | "2 STON/O2. 3101282 7.9250 NaN S \n",
1659 | "3 113803 53.1000 C123 S \n",
1660 | "4 373450 8.0500 NaN S "
1661 | ]
1662 | },
1663 | "execution_count": 23,
1664 | "metadata": {},
1665 | "output_type": "execute_result"
1666 | }
1667 | ],
1668 | "source": [
1669 | "(.head titanic)"
1670 | ]
1671 | }
1672 | ],
1673 | "metadata": {
1674 | "kernelspec": {
1675 | "display_name": "Basilisp",
1676 | "language": "clojure",
1677 | "name": "basilisp"
1678 | },
1679 | "language_info": {
1680 | "file_extension": ".lpy",
1681 | "mimetype": "text/x-clojure",
1682 | "name": "clojure"
1683 | }
1684 | },
1685 | "nbformat": 4,
1686 | "nbformat_minor": 5
1687 | }
1688 |
--------------------------------------------------------------------------------
/notebooks/python-interop.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "24cec1b7-f4c8-4a53-a70f-b8dae962e426",
6 | "metadata": {},
7 | "source": [
8 | "## Python Interop\n",
9 | "https://basilisp.readthedocs.io/en/latest/pyinterop.html"
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "id": "5a33fecd-a4c3-4dba-b639-2e2d58c30da6",
15 | "metadata": {},
16 | "source": [
17 | "### Python builtins"
18 | ]
19 | },
20 | {
21 | "cell_type": "code",
22 | "execution_count": 1,
23 | "id": "245f8361-8aa7-4797-8179-5e9d755e3f2f",
24 | "metadata": {},
25 | "outputs": [
26 | {
27 | "data": {
28 | "text/plain": [
29 | "1"
30 | ]
31 | },
32 | "execution_count": 1,
33 | "metadata": {},
34 | "output_type": "execute_result"
35 | }
36 | ],
37 | "source": [
38 | "(python/abs -1)"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "id": "a4f0de4a-473f-430f-8982-57b1fa890ae1",
44 | "metadata": {},
45 | "source": [
46 | "### Importing modules"
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": 2,
52 | "id": "bb133bf3-4014-41f5-b9c4-6475db6382aa",
53 | "metadata": {},
54 | "outputs": [
55 | {
56 | "data": {
57 | "text/plain": [
58 | "False"
59 | ]
60 | },
61 | "execution_count": 2,
62 | "metadata": {},
63 | "output_type": "execute_result"
64 | }
65 | ],
66 | "source": [
67 | "(import [os.path :as path])\n",
68 | "(path/exists \"tests.txt\")"
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "id": "47f09168-7310-4391-8ce3-455b318b8fca",
74 | "metadata": {},
75 | "source": [
76 | "### Referencing Module Members"
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": 3,
82 | "id": "d0823d69-19e8-412c-94ed-72ac425162a0",
83 | "metadata": {},
84 | "outputs": [
85 | {
86 | "data": {
87 | "text/plain": [
88 | "datetime.datetime(2030, 1, 1, 0, 0)"
89 | ]
90 | },
91 | "execution_count": 3,
92 | "metadata": {},
93 | "output_type": "execute_result"
94 | }
95 | ],
96 | "source": [
97 | "(import datetime)\n",
98 | ";; fromisoformat is a class method of datetime\n",
99 | "(datetime.datetime/fromisoformat \"2030-01-01\")"
100 | ]
101 | },
102 | {
103 | "cell_type": "markdown",
104 | "id": "1b082632-1052-4936-a2fd-f50cd2aa1469",
105 | "metadata": {},
106 | "source": [
107 | "### Accessing Object Methods and Properties"
108 | ]
109 | },
110 | {
111 | "cell_type": "code",
112 | "execution_count": 4,
113 | "id": "5dae5dcc-30f3-4de6-9abc-c0b35ba08e85",
114 | "metadata": {},
115 | "outputs": [
116 | {
117 | "data": {
118 | "text/plain": [
119 | "'2030-01-01'"
120 | ]
121 | },
122 | "execution_count": 4,
123 | "metadata": {},
124 | "output_type": "execute_result"
125 | }
126 | ],
127 | "source": [
128 | "(def date (datetime.datetime/fromisoformat \"2030-01-01\"))\n",
129 | "(. date strftime \"%Y-%m-%d\")"
130 | ]
131 | },
132 | {
133 | "cell_type": "code",
134 | "execution_count": 5,
135 | "id": "d037b257-d404-4b52-b03e-d8030f2ae07c",
136 | "metadata": {},
137 | "outputs": [
138 | {
139 | "data": {
140 | "text/plain": [
141 | "'2030-01-01'"
142 | ]
143 | },
144 | "execution_count": 5,
145 | "metadata": {},
146 | "output_type": "execute_result"
147 | }
148 | ],
149 | "source": [
150 | "(.strftime date \"%Y-%m-%d\")"
151 | ]
152 | },
153 | {
154 | "cell_type": "code",
155 | "execution_count": 6,
156 | "id": "3d954ecf-eb94-4437-a6bc-f5582ddf317d",
157 | "metadata": {},
158 | "outputs": [
159 | {
160 | "data": {
161 | "text/plain": [
162 | "2030"
163 | ]
164 | },
165 | "execution_count": 6,
166 | "metadata": {},
167 | "output_type": "execute_result"
168 | }
169 | ],
170 | "source": [
171 | "(.- date year) "
172 | ]
173 | },
174 | {
175 | "cell_type": "code",
176 | "execution_count": 7,
177 | "id": "a0f97754-8b5a-43aa-b2d8-d0e91b7afd90",
178 | "metadata": {},
179 | "outputs": [
180 | {
181 | "data": {
182 | "text/plain": [
183 | "2030"
184 | ]
185 | },
186 | "execution_count": 7,
187 | "metadata": {},
188 | "output_type": "execute_result"
189 | }
190 | ],
191 | "source": [
192 | "(.-year date)"
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "id": "1f0995ed-5c48-40ab-8fb3-b155f972e9ff",
198 | "metadata": {},
199 | "source": [
200 | "### Keyword Arguments"
201 | ]
202 | },
203 | {
204 | "cell_type": "code",
205 | "execution_count": 8,
206 | "id": "d39b9bff-494f-4aca-a3de-09ebbc79715d",
207 | "metadata": {},
208 | "outputs": [
209 | {
210 | "data": {
211 | "text/plain": [
212 | "datetime.datetime(2010, 1, 5, 0, 0)"
213 | ]
214 | },
215 | "execution_count": 8,
216 | "metadata": {},
217 | "output_type": "execute_result"
218 | }
219 | ],
220 | "source": [
221 | "(.replace date ** :year 2010 :day 5)"
222 | ]
223 | },
224 | {
225 | "cell_type": "markdown",
226 | "id": "12a13a0d-b63c-4042-86ef-0d89ee2b6de6",
227 | "metadata": {},
228 | "source": [
229 | "### Basilisp Functions with Keyword Arguments"
230 | ]
231 | },
232 | {
233 | "cell_type": "code",
234 | "execution_count": 9,
235 | "id": "0c3e5dd9-2ea2-49b6-a077-136b6d75b000",
236 | "metadata": {},
237 | "outputs": [
238 | {
239 | "data": {
240 | "text/plain": [
241 | "30"
242 | ]
243 | },
244 | "execution_count": 9,
245 | "metadata": {},
246 | "output_type": "execute_result"
247 | }
248 | ],
249 | "source": [
250 | ";; :apply strategy (keyword arguments are applied to the fn)\n",
251 | "(defn ^{:kwargs :apply} area-calculate \n",
252 | " [scale-factor & {:keys [length width] :as kwargs}]\n",
253 | " (* scale-factor (* length width)))\n",
254 | "\n",
255 | "(area-calculate 2 ** :length 5 :width 3)"
256 | ]
257 | },
258 | {
259 | "cell_type": "code",
260 | "execution_count": 10,
261 | "id": "bf6c46c4-393a-4c8d-b157-bcb4fdef03e2",
262 | "metadata": {},
263 | "outputs": [
264 | {
265 | "data": {
266 | "text/plain": [
267 | "45"
268 | ]
269 | },
270 | "execution_count": 10,
271 | "metadata": {},
272 | "output_type": "execute_result"
273 | }
274 | ],
275 | "source": [
276 | ";; :collect strategy (keyword arguments are passed as a last argument map)\n",
277 | "(defn ^{:kwargs :collect} area-calculate-2 \n",
278 | " [scale-factor {:keys [length width] :as kwargs}]\n",
279 | " (* scale-factor (* length width)))\n",
280 | "\n",
281 | "(area-calculate-2 3 ** :length 5 :width 3)"
282 | ]
283 | },
284 | {
285 | "cell_type": "markdown",
286 | "id": "4b1404c1-442b-4d0b-90c7-b9782463614b",
287 | "metadata": {},
288 | "source": [
289 | "### Type Hinting"
290 | ]
291 | },
292 | {
293 | "cell_type": "code",
294 | "execution_count": 11,
295 | "id": "c86c51a2-5476-4186-9c3a-d3460bdc05eb",
296 | "metadata": {},
297 | "outputs": [
298 | {
299 | "data": {
300 | "text/plain": [
301 | "128"
302 | ]
303 | },
304 | "execution_count": 11,
305 | "metadata": {},
306 | "output_type": "execute_result"
307 | }
308 | ],
309 | "source": [
310 | "(def ^python/str s \"a string\")\n",
311 | "\n",
312 | "(defn upper\n",
313 | " ^python/str [^python/str s]\n",
314 | " (.upper s))\n",
315 | "\n",
316 | "(let [^python/int i 64]\n",
317 | " (* i 2))"
318 | ]
319 | }
320 | ],
321 | "metadata": {
322 | "kernelspec": {
323 | "display_name": "Basilisp",
324 | "language": "clojure",
325 | "name": "basilisp"
326 | },
327 | "language_info": {
328 | "file_extension": ".lpy",
329 | "mimetype": "text/x-clojure",
330 | "name": "clojure"
331 | }
332 | },
333 | "nbformat": 4,
334 | "nbformat_minor": 5
335 | }
336 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "hatchling>=1.10.0", "ipykernel"
4 | ]
5 | build-backend = "hatchling.build"
6 |
7 | [project]
8 | name = "basilisp-kernel"
9 | dynamic = [
10 | "version",
11 | ]
12 | description = "Basilisp kernel for Jupyter"
13 | readme = "README.md"
14 | license = { file = "LICENSE" }
15 | authors = [
16 | { name = "ikappaki"}
17 | ]
18 | classifiers = [
19 | "License :: OSI Approved :: BSD License",
20 | "Programming Language :: Python :: 3",
21 | ]
22 | requires-python = ">=3.9"
23 | dependencies = [
24 | "ipykernel",
25 | "jupyter_client",
26 | "basilisp>=0.3.2",
27 | "basilisp-nrepl-async>=0.1.0",
28 | ]
29 |
30 | [project.optional-dependencies]
31 | test = [
32 | "jupyter_kernel_test",
33 | ]
34 |
35 | [tool.hatch.envs.test]
36 | dependencies = [
37 | "basilisp-pprint",
38 | "jupyter_client",
39 | "jupyter_kernel_test",
40 | "nbclient",
41 | "nbformat",
42 | "pytest",
43 | ]
44 |
45 | [project.urls]
46 | Homepage = "https://github.com/ikappaki/basilisp-kernel"
47 |
48 | [tool.hatch.version]
49 | path = "basilisp_kernel/__init__.py"
50 |
51 | # Used to call hatch_build.py
52 | [tool.hatch.build.hooks.custom]
53 |
54 |
55 | [tool.hatch.build.targets.sdist]
56 | include = [
57 | "/basilisp_kernel",
58 | ]
59 |
60 | [tool.hatch.build.targets.wheel.shared-data]
61 | "data_kernelspec/share" = "share"
62 |
--------------------------------------------------------------------------------
/test_basilisp.py:
--------------------------------------------------------------------------------
1 | """
2 | Example use of jupyter_kernel_test, with tests for the default python3 kernel
3 | (IPyKernel). This includes all the currently available tests.
4 | """
5 |
6 | import unittest
7 |
8 | import jupyter_kernel_test as jkt
9 |
10 |
11 | class BasilispKernelTests(jkt.KernelTests):
12 |
13 | # REQUIRED
14 |
15 | # the kernel to be tested
16 | # this is the normally the name of the directory containing the
17 | # kernel.json file - you should be able to do
18 | # `jupyter console --kernel KERNEL_NAME`
19 | kernel_name = "basilisp"
20 |
21 | # Everything else is OPTIONAL
22 |
23 | # the name of the language the kernel executes
24 | # checked against language_info.name in kernel_info_reply
25 | language_name = "clojure"
26 |
27 | # the normal file extension (including the leading dot) for this language
28 | # checked against language_info.file_extension in kernel_info_reply
29 | file_extension = ".lpy"
30 |
31 | # code which should write the exact string `hello, world` to STDOUT
32 | code_hello_world = "(println \"hello, world\")"
33 |
34 | # code which should cause (any) text to be written to STDERR
35 | code_stderr = "(binding [*out* *err*] (println \"hi\"))"
36 |
37 | # samples for the autocompletion functionality
38 | # for each dictionary, `text` is the input to try and complete, and
39 | # `matches` the list of all complete matching strings which should be found
40 | completion_samples = [
41 | {
42 | "text": "[abc print",
43 | "matches": {"printf", "println", "println-str", "print", "print-str"},
44 | },
45 | {
46 | "text": "(import sys)\n(sys/int",
47 | "matches": {"sys/intern", "sys/int_info"},
48 | },
49 |
50 | ]
51 |
52 | # code which should generate a (user-level) error in the kernel, and send
53 | # a traceback to the client
54 | code_generate_error = "(throw (Exception. \"based\"))"
55 |
56 | # a statement or block of code which generates a result (which is shown
57 | # as Out[n] instead of just something printed to stdout)
58 | # running each `code` should cause `result` to be displayed (note that the
59 | # result here will always be a string representation of whatever the actual
60 | # result type is - be careful of string formatting)
61 | code_execute_result = [
62 | {"code": "(+ 1 2 3)", "result": "6"},
63 | {"code": "(map #(* % %) (range 1 4))", "result": "(1 4 9)"},
64 | ]
65 |
66 | def test_basilisp_kernel_exceptions(self):
67 | reply, output_msgs = self.execute_helper(code='#lmn')
68 | self.assertEqual("SyntaxError", output_msgs[0]['content']['ename'])
69 | self.assertIn("message: No data reader found for tag #lmn", output_msgs[0]['content']['evalue'])
70 |
71 | reply, output_msgs = self.execute_helper(code='(xyz)')
72 | self.assertEqual("CompilerException", output_msgs[0]['content']['ename'])
73 | self.assertIn("message: unable to resolve symbol 'xyz' in this context", output_msgs[0]['content']['evalue'])
74 |
75 | reply, output_msgs = self.execute_helper(code='(/ 2 0)')
76 | self.assertEqual("Exception", output_msgs[0]['content']['ename'])
77 | self.assertIn("Traceback (most recent call last):", output_msgs[0]['content']['evalue'])
78 |
79 | # code which generates some sort of rich output
80 | # for each `code` input a single rich display object with the specified
81 | # `mime` type should be sent to the frontend
82 | # note that this expects a `display_data` message rather than
83 | # `execute_result`; this test might be a little too inflexible in some cases
84 | # code_display_data = [
85 | # {
86 | # "code": "(import [IPython.display :as d]) (println (d/display (d/HTML \"A \")))",
87 | # # "code": "from IPython.display import HTML, display; display(HTML('test '))",
88 | # "mime": "text/html",
89 | # },
90 | # {
91 | # "code": "from IPython.display import Math, display; display(Math('\\frac{1}{2}'))",
92 | # "mime": "text/latex",
93 | # },
94 | # ]
95 |
96 |
97 | if __name__ == "__main__":
98 | unittest.main()
99 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikappaki/basilisp-kernel/b2dd50bd1830bd2d12fb554de34cc7f895140030/tests/__init__.py
--------------------------------------------------------------------------------
/tests/basilisp_kernel/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikappaki/basilisp-kernel/b2dd50bd1830bd2d12fb554de34cc7f895140030/tests/basilisp_kernel/__init__.py
--------------------------------------------------------------------------------
/tests/basilisp_kernel/integration/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikappaki/basilisp-kernel/b2dd50bd1830bd2d12fb554de34cc7f895140030/tests/basilisp_kernel/integration/__init__.py
--------------------------------------------------------------------------------
/tests/basilisp_kernel/integration/notebook_test.lpy:
--------------------------------------------------------------------------------
1 | (ns tests.basilisp-kernel.integration.notebook-test
2 | (:require
3 | [basilisp-kernel.nrepl-server :as nr]
4 | [tests.basilisp-kernel.nrepl-server-test :as nt]
5 | [basilisp.io :as bio]
6 | [basilisp.contrib.bencode :as bc]
7 | [basilisp-pprint.pprint :as pp]
8 | [basilisp.test :refer [deftest are is testing use-fixtures]]
9 | [basilisp.test.fixtures :as fixtures :refer [*tempdir*]])
10 | (:import [jupyter_client :as jc]
11 | nbformat
12 | nbclient))
13 |
14 | (use-fixtures :each fixtures/tempdir)
15 |
16 | (defn notebook-make
17 | "Writes the `FORMS` to a `nbformat.v4/new-notebook` and returns it."
18 | [forms]
19 | (let [nb (nbformat.v4/new-notebook)]
20 | (doseq [form forms]
21 | (-> nb
22 | .-cells
23 | (.append (nbformat.v4/new-code-cell (str form)))))
24 | nb))
25 |
26 | (deftest notebook-execute-test
27 | (let [nb (notebook-make ['(+ 4 9)])
28 | km (jc/KernelManager ** :kernel-name "basilisp")
29 | client (nbclient/NotebookClient nb km)]
30 |
31 | (try
32 | (with [_ (.setup-kernel client)]
33 | (let [{:keys [cells metadata] :as all} (py->lisp (.execute client))]
34 | (is (= {:language_info
35 | {:mimetype "text/x-clojure",
36 | :name "clojure",
37 | :file_extension ".lpy"}}
38 | metadata))
39 |
40 | (is (= [{:cell_type "code",
41 | :outputs
42 | [{:metadata {},
43 | :output_type "execute_result",
44 | :execution_count 1,
45 | :data {(keyword nil "text/plain") "13"}}]
46 | :execution_count 1,
47 | :source "(+ 4 9)",}]
48 | (for [cell cells]
49 | (reduce dissoc cell [:id :metadata]))))))
50 | (finally
51 | (when (.is-alive km) (.shutdown-kernel km ** :now true))))))
52 | #_(pp/pprint (notebook-execute-test))
53 |
54 | (def ^:dynamic *notebook*
55 | "It will be bound to the new notebook that `with-notebook` created."
56 | nil)
57 |
58 | (def ^:dynamic *client*
59 | "It Will be bound to the `nbclient/NotebookClient` connected to the
60 | Notebook that `with-notebook` created."
61 | nil)
62 |
63 |
64 | (defmacro with-notebook
65 | "Writes `CELLS` to a new `*notebook*` loaded in a `basilisp` kernel
66 | connected to a NoteBook `*client*`. It executes `BODY` in that
67 | context."
68 | [cells & body]
69 | `(let [nb# (notebook-make ~cells)
70 | km# (jc/KernelManager ~'** :kernel-name "basilisp")
71 | client# (nbclient/NotebookClient nb# km#)]
72 |
73 | (try
74 | (with [_ (.setup-kernel client#)]
75 | (binding [*notebook* nb#
76 | *client* client#]
77 | ~@body))
78 | (finally
79 | (when (.is-alive km#) (.shutdown-kernel km# ~'** :now true))))))
80 |
81 | #_(pp/pprint (macroexpand-1 '(with-notebook [])))
82 |
83 | (deftest with-notebook-test
84 | (with-notebook '[(+ 19 20)]
85 | (is *notebook*)
86 | (is *client*)
87 | (let [{:keys [cells]} (py->lisp (.execute *client*))]
88 | (is (= {(keyword nil "text/plain") "39"} (get-in cells [0 :outputs 0 :data])) cells))))
89 | #_(pp/pprint (with-notebook-test))
90 |
91 | (defn code-output-text-get
92 | [cell]
93 | (get-in cell [:outputs 0 :data (keyword nil "text/plain")]))
94 |
95 | (defn cell-output-get
96 | "Returns the `CELL`'s :outputs in the following format according to their `output_type`:
97 |
98 | ```
99 | \"stream\" {:name name1 :text text1} ... -> :stream {:name1 text :name2 text
100 |
101 | \"execute_result\" {:data {:text/plain text}} -> :result {:text text}
102 | ```"
103 | [cell]
104 | (reduce (fn [acc {:keys [output_type] :as output}]
105 | ;; (pp/pprint output)
106 | (case output_type
107 | "stream"
108 | (let [{nm :name text :text} output]
109 | (assoc-in acc [:stream nm] text))
110 | "execute_result"
111 | (let [text (get-in output [:data (keyword nil "text/plain")])]
112 | (assoc-in acc [:result :text] text))
113 | ))
114 | {} (:outputs cell)))
115 |
116 | (deftest basilisp-notebook-test
117 | (testing "multiple cells with cross references"
118 | (with-notebook '[(+ 19 20)
119 | (def abc (atom 5))
120 | @abc]
121 | (let [{:keys [cells]} (py->lisp (.execute *client*))]
122 | (are [i result] (= result (cell-output-get (aget cells i)))
123 | 0 {:result {:text "39"}}
124 | 1 {:result {:text "#'user/abc"}}
125 | 2 {:result {:text "5"}})))))
126 | #_(pp/pprint (basilisp-notebook-test))
127 |
128 |
129 | (deftest nrepl-server-test
130 | (testing "starting server in notebook and remote changing atom"
131 | (with-notebook (concat [(list 'def 'tempdir *tempdir*)]
132 | '[(require '[basilisp-kernel.nrepl-server :as bkns])
133 | (def value* (atom 0))
134 | (def server (bkns/server-start {:dir tempdir}))
135 | (:port server)
136 | (deref value*)
137 |
138 | (deref value*)])
139 | (let [cells (.-cells *notebook*)
140 | [out-tempdir _out-req out-value* out-server out-port out-value :as executed]
141 | (map-indexed (fn [i cell]
142 | (-> (.execute-cell *client* cell ** :cell-index i)
143 | py->lisp
144 | cell-output-get))
145 | (butlast cells))]
146 | (is (= {:text "#'user/tempdir"} (:result out-tempdir)) executed)
147 | (is (= {:text "#'user/value*"} (:result out-value*)) executed)
148 | (let [port-txt (get-in out-port [:result :text])
149 | port (int port-txt)]
150 | (is (int? port) out-port)
151 | (nt/with-connect port
152 | (is (= ["done"] (nt/client-do nt/*nrepl-client* 1 :clone)))
153 | (is (= {:value (pr-str *tempdir*) :status ["done"]} (nt/client-do nt/*nrepl-client* 2 :eval "tempdir")))
154 | (is (= {:value "5" :status ["done"]} (nt/client-do nt/*nrepl-client* 2 :eval "(+ 2 3)")))
155 |
156 | (is (= {:text "0"} (:result out-value)))
157 |
158 | (is (= {:value "1" :status ["done"]} (nt/client-do nt/*nrepl-client* 3 :eval "(swap! value* inc)")))
159 |
160 | (let [out-value-after (-> (.execute-cell *client* (last cells) ** :cell-index 6)
161 | py->lisp
162 | cell-output-get)]
163 | (is (= {:text "1"} (:result out-value-after)) out-value-after))
164 | )
165 | )
166 | )
167 | )))
168 | #_(pp/pprint (nrepl-server-test))
169 |
170 |
--------------------------------------------------------------------------------
/tests/basilisp_kernel/nrepl_server_test.lpy:
--------------------------------------------------------------------------------
1 | (ns tests.basilisp-kernel.nrepl-server-test
2 | (:require
3 | [basilisp-kernel.nrepl-server :as nr]
4 | [basilisp.io :as bio]
5 | [basilisp.contrib.bencode :as bc]
6 | ;; [basilisp-pprint.pprint :as pp]
7 | [basilisp.test :refer [deftest are is testing use-fixtures]]
8 | [basilisp.test.fixtures :as fixtures :refer [*tempdir*]]
9 | )
10 | (:import asyncio
11 | os
12 | socket))
13 |
14 | (use-fixtures :each fixtures/tempdir)
15 |
16 | (defn client-make!
17 | "Creates and returns an nREPL client."
18 | []
19 | (let [sock (socket/socket socket/AF_INET socket/SOCK_STREAM)]
20 | {:sock sock
21 | :backlog* (atom {:items [] :fraction nil})
22 | :last-id* (atom 0)}))
23 |
24 | (defn client-close!
25 | "Closes the nREPL `CLIENT` connection."
26 | [client]
27 | (.close (:sock client)))
28 |
29 | (defn client-send!
30 | "Sends `VALUE` (a map) to the nREPL server from the given `CLIENT`."
31 | [client value]
32 | (let [{:keys [sock last-id*]} client
33 | id (swap! last-id* inc)
34 | v (bc/encode (assoc value :id id))]
35 | (.sendall sock v)))
36 |
37 | (defn client-recv!
38 | "Receives data from the server via `client`. Processes any backlog or
39 | incoming data, decodes it, and returns the first item. Updates the
40 | backlog with remaining data."
41 | [client]
42 | (let [{:keys [sock backlog*]} client
43 | zero-bytes #b ""]
44 | (loop [{:keys [items fraction]} @backlog*]
45 | (if-let [item (first items)]
46 | (do (reset! backlog* {:items (drop 1 items) :fraction fraction})
47 | item)
48 |
49 | (let [data (.recv sock 8192)]
50 | (if (= data zero-bytes)
51 | (throw (python/IOError (str [:client-recv!-error :socket-closed])))
52 | (let [data (if fraction (+ fraction data) data)
53 | [items-d remaining :as response] (bc/decode-all data {:keywordize-keys true
54 | :string-fn #(.decode % "utf-8")})
55 | items (concat items items-d)
56 | [item & _items-left] items]
57 | (recur {:items items :fraction remaining}))))))))
58 |
59 | (def ^:dynamic *nrepl-client*
60 | "Will be bound to the nREPL client `with-connect` is connected to the
61 | nREPL server with."
62 | nil)
63 |
64 | (defmacro with-connect
65 | "A macro that establishes a connection to the specified `port` on
66 | the local interface using a new nREPL client. It binds the client to
67 | `*nrepl-client*` and executes the `body` forms within this context."
68 | [port & body]
69 | `(let [{sock# :sock :as client#} (client-make!)]
70 | (if-not ~port
71 | (throw (python/ValueError [:with-connect-error :invalid-port ~port]))
72 |
73 | (try
74 | (binding [*nrepl-client* client#]
75 | (.connect sock# (python/tuple ["127.0.0.1" ~port]))
76 | (.settimeout sock# 1)
77 | ~@body)
78 | (finally
79 | (client-close! client#))))))
80 |
81 | (defn client-do
82 | [client id op & args]
83 | (case op
84 | :clone
85 | (do (client-send! client {:id id :op "clone"})
86 | (let [{:keys [status]} (client-recv! client)]
87 | status))
88 | :eval
89 | (do (client-send! client {:id id :op "eval" :code (first args)})
90 | (let [{:keys [value]} (client-recv! client)
91 | {:keys [status ex] :as all}
92 | (client-recv! client)]
93 | (cond-> {:value value :status status}
94 | ex
95 | (assoc :ex ex))))))
96 |
97 | (defasync server-run
98 | [opts server*]
99 | (let [{:keys [_task] :as server} (nr/server-start opts)]
100 | (deliver server* server)
101 | (await _task)))
102 |
103 |
104 | (deftest server-run-test
105 | (let [server* (promise)
106 | fut (future
107 | (asyncio/run (server-run {} server*)))
108 | {:keys [error host nrepl-port-file port shutdown!] :as server} (deref server* 1000 ::timed-out)]
109 | (is (not= ::timed-out server))
110 | (is (nil? error) error)
111 | (is (= host "127.0.0.1"))
112 | (is (os.path/exists nrepl-port-file ))
113 | (is port server)
114 | (is shutdown! server)
115 | (is (not= :timed-out (deref server* 100 :timed-out)))
116 | (try
117 | (with-connect port
118 | (is *nrepl-client*)
119 | (is (= ["done"] (client-do *nrepl-client* 1 :clone)))
120 | (is (= {:value "4" :status ["done"]} (client-do *nrepl-client* 2 :eval "(+ 1 3)")))
121 | (nr/server-shut server)
122 | (is (thrown? python/IOError (client-do *nrepl-client* 3 :eval "(+ 1 4)"))))
123 | (finally
124 | (when shutdown! (shutdown!))))))
125 |
126 | #_(pp/pprint (server-run-test))
127 |
128 | (def ^:dynamic *nrepl-server*
129 | nil)
130 |
131 | (defmacro with-server
132 | [opts & body]
133 | `(let [server*# (promise)
134 | fut (future
135 | (asyncio/run (server-run ~opts server*#)))
136 | {port# :port
137 | shutdown!# :shutdown! :as server#}
138 | (deref server*# 1000 :timed-out)]
139 | (try
140 | (binding [*nrepl-server* @server*#]
141 | ~@body)
142 | (finally (when shutdown!# (shutdown!#))))))
143 |
144 | (deftest with-server-test
145 | (with-server {}
146 | (let [{:keys [error port]} *nrepl-server*]
147 | (is (nil? error) error)
148 | (with-connect port
149 | (is (= ["done"] (client-do *nrepl-client* 1 :clone)))
150 | (is (= {:value "4" :status ["done"]} (client-do *nrepl-client* 2 :eval "(+ 1 3)")))))))
151 |
152 | #_(with-server-test)
153 |
154 | (deftest nrepl-server-async-test
155 | (testing "host option"
156 | (with-server {:host "0.0.0.0"}
157 | (let [{:keys [host]} *nrepl-server*]
158 | (is (= "0.0.0.0" host)))))
159 |
160 | (testing "port option"
161 | (with-server {:port -1}
162 | (let [{:keys [error ]} *nrepl-server*]
163 | (is (= "(:server-make-error OverflowError('bind(): port must be 0-65535.'))" (str error))))))
164 |
165 | (testing "nrepl-port-dir option"
166 | (with-server {:dir *tempdir*}
167 | (let [{:keys [nrepl-port-file]} *nrepl-server*]
168 | (is (= (str (bio/path *tempdir* ".nrepl-port")) nrepl-port-file))))))
169 |
170 | #_(pp/pprint (nrepl-server-async-test))
171 |
--------------------------------------------------------------------------------