Skip to content

Commit f099764

Browse files
authored
Support requiring namespaces as aliases only (#840)
Fixes #664
1 parent b03c5c2 commit f099764

File tree

4 files changed

+81
-18
lines changed

4 files changed

+81
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
* Added support for `thrown-with-msg?` assertions to `basilisp.test/is` (#831)
1717
* Added support for reading scientific notation literals, octal and hex integer literals, and arbitrary base (2-36) integer literals (#769)
1818
* Added support for passing trailing maps to functions which accept Basilisp keyword arguments (#663)
19+
* Added support for loading namespaces as an alias only (#664)
1920

2021
### Changed
2122
* Optimize calls to Python's `operator` module into their corresponding native operators (#754)

docs/concepts.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Concepts
1010
Seqs
1111
----
1212

13+
TBD
14+
1315
.. _macros:
1416

1517
Macros

src/basilisp/core.lpy

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4723,6 +4723,9 @@
47234723
Optional keys:
47244724

47254725
- ``:as ns-alias`` a symbol which will alias the Namespace when required (if given)
4726+
- ``:as-alias ns-alias`` a symbol which will alias the Namespace when required; if
4727+
the namespace does not exist, it will be created as an empty namespace; if it does
4728+
exist, it will not be loaded
47264729
- ``:refer [sym1, sym2]`` a sequence of symbols naming Vars to refer
47274730
- ``:refer :all`` if every Var should be referred
47284731
- ``:only [sym1, sym2]`` a sequence of symbols naming Vars to refer
@@ -4761,6 +4764,9 @@
47614764
;; rewrote the namespace on require.
47624765
(when-let [original-ns (:original-namespace libspec)]
47634766
(.add-alias requiring-ns (the-ns required-ns-sym) original-ns))
4767+
;; If an `:as-alias` is requested, apply that as well.
4768+
(when-let [as-alias (:as-alias libspec)]
4769+
(.add-alias requiring-ns (the-ns required-ns-sym) as-alias))
47644770
;; Reset the namespace to the requiring namespace, since it was likely changed
47654771
;; during the require process
47664772
(set! *ns* requiring-ns)))
@@ -4812,6 +4818,9 @@
48124818
Vector libspec arguments must be one of:
48134819

48144820
- ``:as name`` which will alias the imported namespace to the symbol name
4821+
- ``:as-alias name`` which will alias the namespace to the symbol name but not
4822+
require the namespace, which can be useful for namespaces used primarily for
4823+
keywords; the namespace need not exist at all; can be combined with ``:as``
48154824
- ``:refer [& syms]`` which will refer syms in the local namespace directly
48164825
- ``:refer :all`` which will refer all symbols from the namespace directly
48174826

@@ -4820,22 +4829,27 @@
48204829
[& args]
48214830
(let [current-ns *ns*]
48224831
(doseq [libspec (map require-libspec args)]
4823-
(require-lib current-ns libspec)
4824-
4825-
;; Add refers
4826-
(let [new-ns (the-ns (:namespace libspec))
4827-
refer-opt (:refer libspec)]
4828-
(cond
4829-
(= :all refer-opt)
4830-
(.refer-all current-ns new-ns)
4832+
(if (and (:as-alias libspec) (not (:as libspec)))
4833+
(let [alias-target (or (find-ns (:namespace libspec))
4834+
(create-ns (:namespace libspec)))]
4835+
(.add-alias current-ns alias-target (:as-alias libspec)))
4836+
(do
4837+
(require-lib current-ns libspec)
4838+
4839+
;; Add refers
4840+
(let [new-ns (the-ns (:namespace libspec))
4841+
refer-opt (:refer libspec)]
4842+
(cond
4843+
(= :all refer-opt)
4844+
(.refer-all current-ns new-ns)
48314845

4832-
(seq refer-opt)
4833-
(let [new-ns-interns (ns-interns new-ns)]
4834-
(doseq [var-sym refer-opt]
4835-
(let [var (get new-ns-interns var-sym)]
4836-
(.add-refer current-ns var-sym var))))
4846+
(seq refer-opt)
4847+
(let [new-ns-interns (ns-interns new-ns)]
4848+
(doseq [var-sym refer-opt]
4849+
(let [var (get new-ns-interns var-sym)]
4850+
(.add-refer current-ns var-sym var))))
48374851

4838-
:else nil)))
4852+
:else nil)))))
48394853
nil))
48404854

48414855
(defn refer
@@ -4926,7 +4940,25 @@
49264940
[basilisp.string :as str])
49274941
(:use
49284942
[basilisp.set :only [intersection]])
4929-
(:import inspect))"
4943+
(:import inspect))
4944+
4945+
Flags and contents of each of the various sections are detailed further at each of
4946+
their respective function definitions.
4947+
4948+
- ``:refer-basilisp`` (``:refer-clojure`` is also accepted) controls how names from
4949+
:lpy:ns:`basilisp.core` are exposed in the namespace. Refer to :lpy:fn:`refer` for
4950+
usage.
4951+
- ``:require`` imports other Basilisp namespaces. Refer to :lpy:fn:`require` for
4952+
usage.
4953+
- ``:use`` is a variant of ``:require``, although ``:require`` is preferred in most
4954+
cases. See :lpy:fn:`use` for usage.
4955+
- ``:import`` imports Python modules and packages. Refer to :lpy:fn:`import` for
4956+
usage.
4957+
4958+
Use of the ``ns`` macro to control requires and imports is recommended in all cases.
4959+
The various functions that ``ns`` delegates to and whose documentation are referred
4960+
to above are generally useful for interactive usage at the REPL but not appropriate
4961+
for usage in a larger application."
49304962
[name & opts]
49314963
(when-not (and (symbol? name) (nil? (namespace name)))
49324964
(throw (ex-info "Namespace name must be a non-namespaced symbol"
@@ -4966,8 +4998,8 @@
49664998
~@imports)))
49674999

49685000
(defn requiring-resolve
4969-
"Resolve the namespaced symbol ``sym`` as by ``resolve``\\. If resolution fails,
4970-
attempts to require ``sym`` 's namespace (as by ``require``\\) before resolving
5001+
"Resolve the namespaced symbol ``sym`` as by :lpy:fn:`resolve`\\. If resolution fails,
5002+
attempts to require ``sym`` 's namespace (as by :lpy:fn:`require`\\) before resolving
49715003
again."
49725004
[sym]
49735005
(if (qualified-symbol? sym)

tests/basilisp/namespace_test.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from basilisp.lang import runtime as runtime
99
from basilisp.lang import symbol as sym
1010
from basilisp.lang.runtime import Namespace, NamespaceMap, Var
11-
from tests.basilisp.helpers import get_or_create_ns
11+
from tests.basilisp.helpers import CompileFn, get_or_create_ns
1212

1313

1414
@pytest.fixture
@@ -272,6 +272,34 @@ def test_alias(ns_cache: atom.Atom[NamespaceMap]):
272272
assert None is ns1.get_alias(sym.symbol("n2"))
273273

274274

275+
class TestRequireAsAlias:
276+
@pytest.fixture
277+
def test_ns(self) -> str:
278+
return "basilisp.require-as-alias-test"
279+
280+
@pytest.fixture
281+
def compiler_file_path(self) -> str:
282+
return "require_as_alias_test"
283+
284+
def test_requires_and_allows_aliasing(self, lcompile: CompileFn):
285+
lcompile("(require '[basilisp.json :as-alias json])")
286+
assert lcompile("::json/some-kw") == kw.keyword("some-kw", ns="basilisp.json")
287+
288+
def test_can_be_combined_with_normal_require(self, lcompile: CompileFn):
289+
lcompile("(require '[basilisp.json :as json :as-alias json-alias])")
290+
assert lcompile("::json/some-kw") == kw.keyword("some-kw", ns="basilisp.json")
291+
assert lcompile("::json-alias/some-kw") == kw.keyword(
292+
"some-kw", ns="basilisp.json"
293+
)
294+
295+
def test_need_not_be_real_ns(self, lcompile: CompileFn):
296+
lcompile("(require '[basilisp.require-alias-test-ns :as-alias test-ns])")
297+
assert lcompile("::test-ns/some-kw") == kw.keyword(
298+
"some-kw", ns="basilisp.require-alias-test-ns"
299+
)
300+
assert lcompile("(ns-publics 'basilisp.require-alias-test-ns)") == lmap.m()
301+
302+
275303
class TestCompletion:
276304
@pytest.fixture
277305
def ns(self) -> Namespace:

0 commit comments

Comments
 (0)