Skip to content

Commit cd51030

Browse files
authored
Synchronize basilisp.main.init to allow repeat invocations (#1247)
Fixes #1242
1 parent 1cab066 commit cd51030

File tree

4 files changed

+32
-8
lines changed

4 files changed

+32
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
* `basilisp.core/str` now delegates to the builtin Python `str` in all cases except for customizing the string output for builtin Python types (#1237)
1919
* Optimised mainstream seq-consuming functions by coercing their inputs into `seq` upfront (#1234)
2020
* Renamed `awith` and `afor` to `with-async` and `for-async` for improved clarity (#1248)
21+
* `basilisp.main.init` will only initialize the runtime environment on the first invocation (#1242)
2122

2223
### Fixed
2324
* Fix a bug where protocols with methods with leading hyphens in the could not be defined (#1230)

docs/gettingstarted.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,16 @@ Given a Basilisp entrypoint function ``main`` (taking no arguments) in the ``pro
120120
If you were to place this in a module such as ``myproject.main``, you could easily configure a `setuptools entry point <https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_ (or any analog with another build tool) to point to that script directly, effectively launching you directly to Basilisp code.
121121

122122
For more sophisticated projects which may not have a direct or wrappable entrypoint, you can initialize the Basilisp runtime directly by calling :py:func:`basilisp.main.init` with no arguments.
123-
This may be a better fit for a project using something like Django, where the entrypoint is dictated by Django.
124-
In that case, you could use a hook such as Django's ``AppConfig.ready()``.
123+
A natural placement for this function call would be in the root ``__init__.py`` for a package, where you can freely import and initialize Basilisp.
124+
125+
.. code-block:: python
126+
127+
import basilisp.main
128+
129+
basilisp.main.init()
130+
131+
You could also initialize Basilisp in a framework such as Django, where the entrypoint is dictated by the framework.
132+
For example, you could use a hook such as Django's ``AppConfig.ready()``.
125133

126134
.. code-block:: python
127135

src/basilisp/main.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import importlib
22
import os
33
import sysconfig
4+
import threading
45
from pathlib import Path
56
from typing import Optional
67

@@ -11,8 +12,11 @@
1112
from basilisp.lang.typing import CompilerOpts
1213
from basilisp.lang.util import munge
1314

15+
_INIT_LOCK = threading.Lock()
16+
_runtime_is_initialized = False
1417

15-
def init(opts: Optional[CompilerOpts] = None) -> None:
18+
19+
def init(opts: Optional[CompilerOpts] = None, force_reload: bool = False) -> None:
1620
"""
1721
Initialize the runtime environment for Basilisp code evaluation.
1822
@@ -22,12 +26,22 @@ def init(opts: Optional[CompilerOpts] = None) -> None:
2226
2327
If you want to execute a Basilisp file which is stored in a well-formed package
2428
or module structure, you probably want to use :py:func:`bootstrap`.
29+
30+
``init()`` may be called more than once. Only the first invocation will initialize
31+
the runtime unless ``force_reload=True``.
2532
"""
26-
logconfig.configure_root_logger()
27-
runtime.init_ns_var()
28-
runtime.bootstrap_core(opts if opts is not None else compiler_opts())
29-
importer.hook_imports()
30-
importlib.import_module("basilisp.core")
33+
global _runtime_is_initialized
34+
35+
with _INIT_LOCK:
36+
if _runtime_is_initialized and not force_reload:
37+
return
38+
39+
logconfig.configure_root_logger()
40+
runtime.init_ns_var()
41+
runtime.bootstrap_core(opts if opts is not None else compiler_opts())
42+
importer.hook_imports()
43+
importlib.import_module("basilisp.core")
44+
_runtime_is_initialized = True
3145

3246

3347
def bootstrap(

tests/basilisp/cli_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class CapturedIO:
8383
@pytest.fixture
8484
def run_cli(monkeypatch, capsys, cap_lisp_io):
8585
def _run_cli(args: Sequence[str], input: Optional[str] = None):
86+
monkeypatch.setattr("basilisp.main._runtime_is_initialized", False)
8687
if input is not None:
8788
monkeypatch.setattr(
8889
"sys.stdin", io.TextIOWrapper(io.BytesIO(input.encode("utf-8")))

0 commit comments

Comments
 (0)