Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

### Fixed
* Fix a bug where protocols with methods with leading hyphens in the could not be defined (#1230)
Expand Down
12 changes: 10 additions & 2 deletions docs/gettingstarted.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,16 @@ Given a Basilisp entrypoint function ``main`` (taking no arguments) in the ``pro
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.

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.
This may be a better fit for a project using something like Django, where the entrypoint is dictated by Django.
In that case, you could use a hook such as Django's ``AppConfig.ready()``.
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.

.. code-block:: python

import basilisp.main

basilisp.main.init()

You could also initialize Basilisp in a framework such as Django, where the entrypoint is dictated by the framework.
For example, you could use a hook such as Django's ``AppConfig.ready()``.

.. code-block:: python

Expand Down
26 changes: 20 additions & 6 deletions src/basilisp/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import importlib
import os
import sysconfig
import threading
from pathlib import Path
from typing import Optional

Expand All @@ -11,8 +12,11 @@
from basilisp.lang.typing import CompilerOpts
from basilisp.lang.util import munge

_INIT_LOCK = threading.Lock()
_runtime_is_initialized = False

def init(opts: Optional[CompilerOpts] = None) -> None:

def init(opts: Optional[CompilerOpts] = None, force_reload: bool = False) -> None:
"""
Initialize the runtime environment for Basilisp code evaluation.

Expand All @@ -22,12 +26,22 @@ def init(opts: Optional[CompilerOpts] = None) -> None:

If you want to execute a Basilisp file which is stored in a well-formed package
or module structure, you probably want to use :py:func:`bootstrap`.

``init()`` may be called more than once. Only the first invocation will initialize
the runtime unless ``force_reload=True``.
"""
logconfig.configure_root_logger()
runtime.init_ns_var()
runtime.bootstrap_core(opts if opts is not None else compiler_opts())
importer.hook_imports()
importlib.import_module("basilisp.core")
global _runtime_is_initialized

with _INIT_LOCK:
if _runtime_is_initialized and not force_reload:
return

logconfig.configure_root_logger()
runtime.init_ns_var()
runtime.bootstrap_core(opts if opts is not None else compiler_opts())
importer.hook_imports()
importlib.import_module("basilisp.core")
_runtime_is_initialized = True


def bootstrap(
Expand Down
1 change: 1 addition & 0 deletions tests/basilisp/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class CapturedIO:
@pytest.fixture
def run_cli(monkeypatch, capsys, cap_lisp_io):
def _run_cli(args: Sequence[str], input: Optional[str] = None):
monkeypatch.setattr("basilisp.main._runtime_is_initialized", False)
if input is not None:
monkeypatch.setattr(
"sys.stdin", io.TextIOWrapper(io.BytesIO(input.encode("utf-8")))
Expand Down