Skip to content

Commit e437ea5

Browse files
authored
Nested module imports are no longer obscured by their parent module (#360)
1 parent 5b88673 commit e437ea5

File tree

4 files changed

+38
-15
lines changed

4 files changed

+38
-15
lines changed

src/basilisp/lang/compiler/generator.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,11 +1171,10 @@ def _import_to_py_ast(ctx: GeneratorContext, node: Import) -> GeneratedPyAST:
11711171
deps: List[ast.AST] = []
11721172
for alias in node.aliases:
11731173
safe_name = munge(alias.name)
1174-
safe_alias = munge(alias.alias)
11751174

11761175
try:
11771176
module = importlib.import_module(safe_name)
1178-
if alias.name != alias.alias:
1177+
if alias.alias is not None:
11791178
ctx.add_import(sym.symbol(alias.name), module, sym.symbol(alias.alias))
11801179
else:
11811180
ctx.add_import(sym.symbol(alias.name), module)
@@ -1184,17 +1183,22 @@ def _import_to_py_ast(ctx: GeneratorContext, node: Import) -> GeneratedPyAST:
11841183
f"Python module '{alias.name}' not found", node.form, node
11851184
) from e
11861185

1186+
py_import_alias = (
1187+
munge(alias.alias)
1188+
if alias.alias is not None
1189+
else safe_name.split(".", maxsplit=1)[0]
1190+
)
11871191
deps.append(
11881192
ast.Assign(
1189-
targets=[ast.Name(id=safe_alias, ctx=ast.Store())],
1193+
targets=[ast.Name(id=py_import_alias, ctx=ast.Store())],
11901194
value=ast.Call(
11911195
func=_load_attr("builtins.__import__"),
11921196
args=[ast.Str(safe_name)],
11931197
keywords=[],
11941198
),
11951199
)
11961200
)
1197-
last = ast.Name(id=safe_alias, ctx=ast.Load())
1201+
last = ast.Name(id=py_import_alias, ctx=ast.Load())
11981202

11991203
# Note that we add this import to the live running system in the above
12001204
# calls to `ctx.add_import`, however, since we compile and cache Python

src/basilisp/lang/compiler/nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ class Import(Node[SpecialForm]):
404404
class ImportAlias(Node[Union[sym.Symbol, vec.Vector]]):
405405
form: Union[sym.Symbol, vec.Vector]
406406
name: str
407-
alias: str
407+
alias: Optional[str]
408408
env: NodeEnv
409409
children: Collection[kw.Keyword] = vec.Vector.empty()
410410
op: NodeOp = NodeOp.IMPORT_ALIAS

src/basilisp/lang/compiler/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ def _import_ast( # pylint: disable=too-many-branches
995995
for f in form.rest:
996996
if isinstance(f, sym.Symbol):
997997
module_name = f
998-
module_alias = module_name.name.split(".", maxsplit=1)[0]
998+
module_alias = None
999999
elif isinstance(f, vec.Vector):
10001000
if len(f) != 3:
10011001
raise ParserException(

tests/basilisp/compiler_test.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,19 @@ def test_ns() -> str:
4444

4545

4646
@pytest.fixture
47-
def ns(test_ns: str) -> runtime.Namespace:
47+
def test_ns_sym(test_ns: str) -> sym.Symbol:
48+
return sym.symbol(test_ns)
49+
50+
51+
@pytest.fixture
52+
def ns(test_ns: str, test_ns_sym: sym.Symbol) -> runtime.Namespace:
4853
runtime.init_ns_var(which_ns=runtime.CORE_NS)
54+
runtime.Namespace.get_or_create(test_ns_sym)
4955
with runtime.ns_bindings(test_ns) as ns:
50-
yield ns
56+
try:
57+
yield ns
58+
finally:
59+
runtime.Namespace.remove(test_ns_sym)
5160

5261

5362
@pytest.fixture
@@ -902,7 +911,7 @@ def test_macro_expansion(ns: runtime.Namespace):
902911

903912

904913
class TestIf:
905-
def test_if_number_of_elems(self, ns: runtime.Namespace):
914+
def test_if_number_of_elems(self):
906915
with pytest.raises(compiler.CompilerException):
907916
lcompile("(if)")
908917

@@ -925,7 +934,7 @@ def test_if(self, ns: runtime.Namespace):
925934
"""
926935
assert "YELLING" == lcompile(code)
927936

928-
def test_truthiness(self, ns: runtime.Namespace):
937+
def test_truthiness(self):
929938
# Valid false values
930939
assert kw.keyword("b") == lcompile("(if false :a :b)")
931940
assert kw.keyword("b") == lcompile("(if nil :a :b)")
@@ -1007,7 +1016,7 @@ def test_import_module_must_exist(self, ns: runtime.Namespace):
10071016
with pytest.raises(ImportError):
10081017
lcompile("(import* real.fake.module)")
10091018

1010-
def test_import_resolves_within_do_block(self):
1019+
def test_import_resolves_within_do_block(self, ns: runtime.Namespace):
10111020
import time
10121021

10131022
assert time.perf_counter == lcompile("(do (import* time)) time/perf-counter")
@@ -1041,6 +1050,16 @@ def test_multi_import(self, ns: runtime.Namespace):
10411050
)
10421051
)
10431052

1053+
def test_nested_imports_visible_with_parent(self, ns: runtime.Namespace):
1054+
import collections.abc
1055+
1056+
assert [collections.OrderedDict, collections.abc.Sized] == lcompile(
1057+
"""
1058+
(import* collections collections.abc)
1059+
#py [collections/OrderedDict collections.abc/Sized]
1060+
"""
1061+
)
1062+
10441063

10451064
class TestPythonInterop:
10461065
def test_interop_is_valid_type(self, ns: runtime.Namespace):
@@ -1368,22 +1387,22 @@ def test_loop_with_recur(self, ns: runtime.Namespace):
13681387

13691388

13701389
class TestQuote:
1371-
def test_quoted_list(self, ns: runtime.Namespace):
1390+
def test_quoted_list(self):
13721391
assert lcompile("'()") == llist.l()
13731392
assert lcompile("'(str)") == llist.l(sym.symbol("str"))
13741393
assert lcompile("'(str 3)") == llist.l(sym.symbol("str"), 3)
13751394
assert lcompile("'(str 3 :feet-deep)") == llist.l(
13761395
sym.symbol("str"), 3, kw.keyword("feet-deep")
13771396
)
13781397

1379-
def test_quoted_map(self, ns: runtime.Namespace):
1398+
def test_quoted_map(self):
13801399
assert lcompile("'{}") == lmap.Map.empty()
13811400
assert lcompile("'{:a 2}") == lmap.map({kw.keyword("a"): 2})
13821401
assert lcompile('\'{:a 2 "str" s}') == lmap.map(
13831402
{kw.keyword("a"): 2, "str": sym.symbol("s")}
13841403
)
13851404

1386-
def test_quoted_set(self, ns: runtime.Namespace):
1405+
def test_quoted_set(self):
13871406
assert lcompile("'#{}") == lset.Set.empty()
13881407
assert lcompile("'#{:a 2}") == lset.s(kw.keyword("a"), 2)
13891408
assert lcompile('\'#{:a 2 "str"}') == lset.s(kw.keyword("a"), 2, "str")
@@ -1650,7 +1669,7 @@ def test_set_cannot_assign_fn_arg_local(self):
16501669
with pytest.raises(compiler.CompilerException):
16511670
lcompile("(fn [a b] (set! a :c))")
16521671

1653-
def test_set_cannot_assign_non_dynamic_var(self):
1672+
def test_set_cannot_assign_non_dynamic_var(self, ns: runtime.Namespace):
16541673
with pytest.raises(compiler.CompilerException):
16551674
lcompile(
16561675
"""

0 commit comments

Comments
 (0)