Repository: hylang/hy

Issue:
Remove internal `importlib` frames from traceback
Python 3.x uses `_call_with_frames_removed` to remove internal frames from tracebacks.  I tried it in #1687, but couldn't get it to work; might be due to this [Python issue](https://bugs.python.org/issue23773).  
Regardless, it would be good to remove those frames somehow.


Discussion:
as an example:
```python
>>> import a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/alliejo/dev/langs/hy/a.py", line 1
    print('hi"
              ^
SyntaxError: EOL while scanning string literal
```
```clojure
=> (import a)
Traceback (most recent call last):
  File "stdin-99c0d4eb813cb56c622500ab4696f5bbb3cb3660", line 1, in <module>
    (import a)
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 786, in exec_module
  File "<frozen importlib._bootstrap_external>", line 923, in get_code
  File "/home/alliejo/dev/langs/hy/a.hy", line 1
    (print 'hi"
              ^
hy.lex.exceptions.PrematureEndOfInput: Partial string literal
```

Resolving patch:
diff --git a/hy/_compat.py b/hy/_compat.py
--- a/hy/_compat.py
+++ b/hy/_compat.py
@@ -1,9 +1,11 @@
 import sys
+import platform
 
 PY3_7 = sys.version_info >= (3, 7)
 PY3_8 = sys.version_info >= (3, 8)
 PY3_9 = sys.version_info >= (3, 9)
 PY3_10 = sys.version_info >= (3, 10)
+PYPY = platform.python_implementation() == "PyPy"
 
 
 if not PY3_9:
diff --git a/hy/errors.py b/hy/errors.py
--- a/hy/errors.py
+++ b/hy/errors.py
@@ -8,6 +8,7 @@
 from colorama import Fore
 from contextlib import contextmanager
 from hy import _initialize_env_var
+from hy._compat import PYPY
 
 _hy_filter_internal_errors = _initialize_env_var('HY_FILTER_INTERNAL_ERRORS',
                                                  True)
@@ -238,6 +239,13 @@ def _module_filter_name(module_name):
                                       'rply'])
                       if m is not None}
 
+# We can't derive these easily from just their module names due
+# to missing magic attributes in internal importlib modules
+_tb_hidden_modules.update(
+    f"<builtin>/frozen {x}" if PYPY else f"<frozen {x}>"
+    for x in ("importlib._bootstrap", "importlib._bootstrap_external")
+)
+
 
 def hy_exc_filter(exc_type, exc_value, exc_traceback):
     """Produce exceptions print-outs with all frames originating from the


Test patch:
diff --git a/tests/importer/test_importer.py b/tests/importer/test_importer.py
--- a/tests/importer/test_importer.py
+++ b/tests/importer/test_importer.py
@@ -12,7 +12,7 @@
 
 import hy
 from hy.lex import hy_parse
-from hy.errors import HyLanguageError
+from hy.errors import HyLanguageError, hy_exc_handler
 from hy.lex.exceptions import PrematureEndOfInput
 from hy.compiler import hy_eval, hy_compile
 from hy.importer import HyLoader
@@ -291,3 +291,18 @@ def test_hy_python_require():
     # https://github.com/hylang/hy/issues/1911
     test = "(do (require tests.resources.macros [test-macro]) (test-macro) blah)"
     assert hy.eval(hy.read_str(test)) == 1
+
+
+def test_filtered_importlib_frames(capsys):
+    path = os.getcwd() + "/tests/resources/importer/compiler_error.hy"
+    testLoader = HyLoader("tests.resources.importer.compiler_error", path)
+    spec = importlib.util.spec_from_loader(testLoader.name, testLoader)
+    mod = importlib.util.module_from_spec(spec)
+
+    with pytest.raises(PrematureEndOfInput) as execinfo:
+        testLoader.exec_module(mod)
+
+    hy_exc_handler(execinfo.type, execinfo.value, execinfo.tb)
+    captured_w_filtering = capsys.readouterr()[-1].strip()
+
+    assert "importlib._" not in captured_w_filtering
diff --git a/tests/resources/importer/compiler_error.hy b/tests/resources/importer/compiler_error.hy
new file mode 100644
--- /dev/null
+++ b/tests/resources/importer/compiler_error.hy
@@ -0,0 +1,2 @@
+;; This should fail at compile time
+(print "hello)