Skip to content

Commit d52b03a

Browse files
authored
Demunge namespaces specified via Python import machinery (#217)
1 parent 0b9f7b4 commit d52b03a

File tree

5 files changed

+61
-6
lines changed

5 files changed

+61
-6
lines changed

src/basilisp/importer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import basilisp.compiler as compiler
1010
import basilisp.lang.runtime as runtime
1111
import basilisp.reader as reader
12+
from basilisp.lang.util import demunge
1213

1314

1415
class BasilispImporter(MetaPathFinder, SourceLoader):
@@ -83,7 +84,8 @@ def exec_module(self, module):
8384
# a blank module. If we do not replace the module here with the module we are
8485
# generating, then we will not be able to use advanced compilation features such
8586
# as direct Python variable access to functions and other def'ed values.
86-
ns: runtime.Namespace = runtime.set_current_ns(fullname).value
87+
ns_name = demunge(fullname)
88+
ns: runtime.Namespace = runtime.set_current_ns(ns_name).value
8789
ns.module = module
8890

8991
forms = reader.read_file(filename, resolver=runtime.resolve_alias)
@@ -95,7 +97,7 @@ def exec_module(self, module):
9597
#
9698
# Later on, we can probably remove this and just use the 'ns macro to auto-refer
9799
# all 'basilisp.core values into the current namespace.
98-
runtime.Namespace.add_default_import(fullname)
100+
runtime.Namespace.add_default_import(ns_name)
99101

100102

101103
def hook_imports():

src/basilisp/lang/util.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import uuid
66
from decimal import Decimal
77
from fractions import Fraction
8-
from typing import Pattern
8+
from typing import Pattern, Match
99

1010
import dateutil.parser as dateparser
1111

@@ -73,6 +73,24 @@ def munge(s: str, allow_builtins: bool = False) -> str:
7373
return new_s
7474

7575

76+
_DEMUNGE_PATTERN = re.compile(r"(__[A-Z]+__)")
77+
_DEMUNGE_REPLACEMENTS = {v: k for k, v in _MUNGE_REPLACEMENTS.items()}
78+
79+
80+
def demunge(s: str) -> str:
81+
"""Replace munged string components with their original
82+
representation."""
83+
84+
def demunge_replacer(match: Match) -> str:
85+
full_match = match.group(0)
86+
replacement = _DEMUNGE_REPLACEMENTS.get(full_match, None)
87+
if replacement:
88+
return replacement
89+
return full_match
90+
91+
return re.sub(_DEMUNGE_PATTERN, demunge_replacer, s).replace('_', '-')
92+
93+
7694
# Use an atomically incremented integer as a suffix for all
7795
# user-defined function and variable names compiled into Python
7896
# code so no conflicts occur

tests/basilisp/importer_test.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
import importlib
2+
import os.path
13
import sys
4+
import tempfile
25
from unittest.mock import patch
36

7+
import _pytest.pytester as pytester
8+
49
import basilisp.importer as importer
10+
import basilisp.lang.runtime as runtime
11+
import basilisp.lang.symbol as sym
512

613

714
def importer_counter():
@@ -22,3 +29,21 @@ def test_hook_imports():
2229
assert 1 == importer_counter()
2330
importer.hook_imports()
2431
assert 1 == importer_counter()
32+
33+
34+
def test_demunged_import(testdir: pytester.Testdir):
35+
with tempfile.TemporaryDirectory() as tmpdir:
36+
tmp_module = os.path.join(tmpdir, 'long__AMP__namespace_name__PLUS__with___LT__punctuation__GT__.lpy')
37+
with open(tmp_module, mode='w') as module:
38+
code = """
39+
(ns long&namespace-name+with-<punctuation>)
40+
"""
41+
module.write(code)
42+
43+
with patch('sys.path', new=[tmpdir]), \
44+
patch('sys.meta_path', new=[importer.BasilispImporter()]):
45+
importlib.import_module('long__AMP__namespace_name__PLUS__with___LT__punctuation__GT__')
46+
47+
assert runtime.Namespace.get(sym.symbol('long&namespace-name+with-<punctuation>')) is not None
48+
assert runtime.Namespace.get(
49+
sym.symbol('long__AMP__namespace_name__PLUS__with___LT__punctuation__GT__')) is None

tests/basilisp/langutil_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from basilisp.lang.util import demunge, _MUNGE_REPLACEMENTS
2+
3+
4+
def test_demunge():
5+
for v, munged in _MUNGE_REPLACEMENTS.items():
6+
assert demunge(munged) == v
7+
8+
assert "-->--" == demunge("____GT____")
9+
assert "--init--" == demunge("__init__")
10+
assert "random--V--" == demunge("random__V__")
11+
assert "hi-how-are-you?" == demunge("hi_how_are_you__Q__")
12+
assert "hi-how-are-you----" == demunge("hi_how_are_you____")

tests/basilisp/testrunner_test.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import _pytest.pytester as pytester
22

33

4-
# TODO: fix namespace after importer namespace issue resolved
5-
# (https://github.com/chrisrink10/basilisp/issues/206)
64
def test_testrunner(testdir: pytester.Testdir):
75
code = """
8-
(ns test_fixture
6+
(ns test-fixture
97
(:require
108
[basilisp.test :refer [deftest is]]))
119

0 commit comments

Comments
 (0)