Skip to content

Commit 6f3e5a3

Browse files
A port of the nbb nrepl-server to basilisp (#723)
Hi, could you please review patch to port [nbb](https://github.com/babashka/nbb)'s build in nrepl-server to basilisp contrib. It addresses #412. `nbb` has the same EPL-1.0 license as `basilisp` so I believe using its code as the base for this PR should not cause licensing issues. The nbb `bencode` module which is a dependency was ported as well. Extensive tests were written for both. The server has been tested to work with both [CIDER](https://github.com/clojure-emacs/cider) and [Calva](https://github.com/BetterThanTomorrow/calva) in VS-Code. The server can be invoked with `baslisp nrepl-server`: ``` $ poetry run basilisp nrepl-server -h usage: basilisp.cmd nrepl-server [-h] [--host HOST] [--port PORT] [--port-filepath PORT_FILEPATH] Start the nREPL server. optional arguments: -h, --help show this help message and exit --host HOST the interface address to bind to, defaults to 127.0.0.1. --port PORT the port to connect to, defaults to 0 (random available port). --port-filepath PORT_FILEPATH the file path where the server port number is output to, defaults to ".nrepl-port". ``` Several fixes were required to make it work, which are included as separate commits in this PR prior to the main bencode and nrepl-server commits. They address the following issues - ~~Fixed issue with sort-* fns returning an error on empty seqs (#716)~~ - Fix issue with `ns` being unavail after `in-ns` during `eval` (#718) - Fixed issue with import modules aliasing using ns eval (#719) - Fixed issue with `ns-resolve` throwing error on macros (#720) support for the `basilisp.stacktrace/print-cause-trace` is also added because it is requested by CIDER when an exception is thrown to show to the user, which is essential, it partially addresses #721. In addition, two issues were observed while running the tests in CI - ~~The 1.6.0 was released few days ago and complains for an additional type error in the generator, which is now ignored with this patch~~ ``` Traceback (most recent call last): #... py311-mypy: commands[0]> mypy --config-file=C:\src\basilisp/pyproject.toml -p basilisp src\basilisp\lang\compiler\generator.py:3530: error: Argument 1 to "fields" has incompatible type "type[IType]"; expected an attrs class [misc] src\basilisp\lang\compiler\generator.py:3530: note: Error code "misc" not covered by "type: ignore" comment ``` - The nrepl-server tests which are using threads were failing due to trying to release an rlock which has been already released whose essence is captured in #722, and is fixed with this patch as described in the ticket by using `with self._lock.gen_rlock()` instead of a single instance of `with self._rlock()`. The error was ``` File "/home/runner/work/basilisp/basilisp/.tox/py39/lib/python3.9/site-packages/basilisp/lang/runtime.py", line 706, in find return v File "/home/runner/work/basilisp/basilisp/.tox/py39/lib/python3.9/site-packages/readerwriterlock/rwlock.py", line 49, in __exit__ self.release() File "/home/runner/work/basilisp/basilisp/.tox/py39/lib/python3.9/site-packages/readerwriterlock/rwlock.py", line 344, in release if not self.v_locked: raise RELEASE_ERR_CLS(RELEASE_ERR_MSG) RuntimeError: release unlocked lock ``` I understand this could be a lot to go through and there likely to be multiple iterations while I update the code with your feedback, of which I'm looking forward to. I have not written a nrepl-server section for the manual yet. I plan to do this after a successful review, and will also update the changelog. Thanks --------- Co-authored-by: ikappaki <[email protected]> Co-authored-by: Chris Rink <[email protected]>
1 parent 88b0275 commit 6f3e5a3

File tree

12 files changed

+1205
-5
lines changed

12 files changed

+1205
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
* Added support for Python 3.12 (#734)
1212
* Added a default reader conditional for the current platform (`windows`, `darwin`, `linux`, etc.) (#692)
1313
* Added support for `bencode` binary encoding (part of #412)
14+
* Ported nbb's nrepl-server module to basilisp (#412).
1415

1516
### Changed
1617
* Basilisp now supports PyTest 7.0+ (#660)

docs/api/contrib/nrepl-server.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
basilisp.contrib.nrepl-server
2+
=============================
3+
4+
.. toctree::
5+
:maxdepth: 2
6+
:caption: Contents:
7+
8+
.. autonamespace:: basilisp.contrib.nrepl-server
9+
:members:
10+
:undoc-members:

docs/cli.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,30 @@ The builtin REPL supports basic code completion suggestions, syntax highlighting
3232

3333
You can exit the REPL by entering an end-of-file ("EOF") character by pressing Ctrl+D at your keyboard.
3434

35+
.. _start_an_nREPL_session:
36+
37+
Start an nREPL Session
38+
----------------------
39+
40+
Basilisp's CLI incorporates an nREPL server adapted from `nbb <https://github.com/babashka/nbb>`_.
41+
42+
To start the server from the command line use the following command
43+
44+
.. code-block:: bash
45+
46+
basilisp nrepl-server
47+
# => nREPL server started on port 50407 on host 127.0.0.1 - nrepl://127.0.0.1:50407
48+
49+
You can then establish a connection from your IDE to the server address.
50+
51+
- from `Emacs`, using `CIDER <https://github.com/clojure-emacs/cider>`_
52+
53+
`M-x cider-connect-clj`
54+
55+
- from `Visual Studio Code`, using `Calva <https://calva.io/>`_
56+
57+
`REPL` -> `Connect to a running REPL in your project` -> `Generic`
58+
3559
.. _run_basilisp_code:
3660

3761
Run Basilisp Code

docs/differencesfromclojure.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ REPL
8888
----
8989

9090
Basilisp's REPL experience closely matches that of Clojure's.
91-
Basilisp does not currently support any programmatic REPL server such as `nREPL <https://nrepl.org/nrepl/index.html>`_ or pREPL, though support is planned (see `#412 <https://github.com/basilisp-lang/basilisp/issues/412>`_).
9291

9392
.. _evaluation_differences:
9493

docs/features.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ Basilisp is still young and so lacks many features that more mature languages an
2020
There are many such planned features that will hopefully improve the ergonomics of the project for new developers.
2121
A few of those features include:
2222

23-
* Built-in `nREPL <https://nrepl.org/nrepl/usage/server.html>`_ server
2423
* Project management tooling to facility dependency management/locking and packaging
2524
* Compatibility with the platform-agnostic components of Clojure
2625

27-
See the GitHub `issues <https://github.com/basilisp-lang/basilisp/issues>`_ and `projects <https://github.com/basilisp-lang/basilisp/projects?type=beta>`_ pages for more details.
26+
See the GitHub `issues <https://github.com/basilisp-lang/basilisp/issues>`_ and `projects <https://github.com/basilisp-lang/basilisp/projects?type=beta>`_ pages for more details.

src/basilisp/cli.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,56 @@ def _wrapped_subcommand(subparsers: "argparse._SubParsersAction"):
244244
return _wrap_add_subcommand
245245

246246

247+
def nrepl_server(
248+
_,
249+
args: argparse.Namespace,
250+
):
251+
opts = compiler.compiler_opts()
252+
basilisp.init(opts)
253+
254+
ctx = compiler.CompilerContext(filename=REPL_INPUT_FILE_PATH, opts=opts)
255+
eof = object()
256+
257+
ns = runtime.Namespace.get_or_create(runtime.CORE_NS_SYM)
258+
host = runtime.lrepr(args.host)
259+
port = args.port
260+
port_filepath = runtime.lrepr(args.port_filepath)
261+
eval_str(
262+
(
263+
"(require '[basilisp.contrib.nrepl-server :as nr])"
264+
f"(nr/start-server! {{:host {host} :port {port} :nrepl-port-file {port_filepath}}})"
265+
),
266+
ctx,
267+
ns,
268+
eof,
269+
)
270+
271+
272+
@_subcommand(
273+
"nrepl-server",
274+
help="start the nREPL server",
275+
description="Start the nREPL server.",
276+
handler=nrepl_server,
277+
)
278+
def _add_nrepl_server_subcommand(parser: argparse.ArgumentParser) -> None:
279+
parser.add_argument(
280+
"--host",
281+
default="127.0.0.1",
282+
help="the interface address to bind to, defaults to 127.0.0.1.",
283+
)
284+
parser.add_argument(
285+
"--port",
286+
default=0,
287+
type=int,
288+
help="the port to connect to, defaults to 0 (random available port).",
289+
)
290+
parser.add_argument(
291+
"--port-filepath",
292+
default=".nrepl-port",
293+
help='the file path where the server port number is output to, defaults to ".nrepl-port".',
294+
)
295+
296+
247297
def repl(
248298
_,
249299
args: argparse.Namespace,
@@ -445,6 +495,7 @@ def invoke_cli(args: Optional[Sequence[str]] = None) -> None:
445495
)
446496

447497
subparsers = parser.add_subparsers(help="sub-commands")
498+
_add_nrepl_server_subcommand(subparsers)
448499
_add_repl_subcommand(subparsers)
449500
_add_run_subcommand(subparsers)
450501
_add_test_subcommand(subparsers)

src/basilisp/contrib/bencode.lpy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
;; Decoding logic adapted from
2+
;; https://github.com/babashka/nbb/blob/bca8b5017a06768eb35d02a2d6233ca9c6c2f692/src/nbb/impl/bencode.cljs
13
(ns basilisp.contrib.bencode
24
(:require
35
[basilisp.string :as str]))

0 commit comments

Comments
 (0)