Skip to content

Commit

Permalink
Merge pull request #2586 from Kodiologist/hy-underscore-names
Browse files Browse the repository at this point in the history
Fixes for `help` and JIT imports
  • Loading branch information
Kodiologist committed May 14, 2024
2 parents 7596d27 + db54faf commit 3ba2c87
Show file tree
Hide file tree
Showing 13 changed files with 49 additions and 33 deletions.
12 changes: 4 additions & 8 deletions hy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def __getattr__(self, s):
# to be loaded if they're not needed.

_jit_imports = dict(
pyops=["hy.pyops", None],
read="hy.reader",
read_many="hy.reader",
mangle="hy.reader",
Expand All @@ -50,18 +51,13 @@ def __getattr__(self, s):


def __getattr__(k):
if k == "pyops":
global pyops
import hy.pyops

pyops = hy.pyops
return pyops

if k not in _jit_imports:
raise AttributeError(f"module {__name__!r} has no attribute {k!r}")

v = _jit_imports[k]
module, original_name = v if isinstance(v, list) else (v, k)
import importlib
module = importlib.import_module(module)

globals()[k] = getattr(importlib.import_module(module), original_name)
globals()[k] = getattr(module, original_name) if original_name else module
return globals()[k]
15 changes: 11 additions & 4 deletions hy/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,16 @@ def getdoc(object):
`inspect.getcomments` for an object defined in Hy code, which would try
to parse the Hy as Python. The implementation is based on Python
3.12.3's `getdoc`."""
result = pydoc._getdoc(object) or (
None
if inspect.getfile(object).endswith('.hy')
else inspect.getcomments(object))
result = pydoc._getdoc(object)
if not result:
can_get_comments = True
try:
file_path = inspect.getfile(object)
except TypeError:
None
else:
can_get_comments = not file_path.endswith('.hy')
if can_get_comments:
result = inspect.getcomments(object)
return result and re.sub('^ *\n', '', result.rstrip()) or ''
pydoc.getdoc = getdoc
4 changes: 1 addition & 3 deletions hy/reader/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ def read_many(stream, filename="<string>", reader=None, skip_shebang=False):
.. warning::
Thanks to reader macros, reading can execute arbitrary code. Don't read untrusted
input.
"""
input."""

if isinstance(stream, str):
stream = StringIO(stream)
Expand Down
2 changes: 1 addition & 1 deletion hy/reader/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ def from_reader(cls, message, reader):

class PrematureEndOfInput(LexException):
"Raised when input ends unexpectedly during parsing."
pass
__module__ = 'hy'
2 changes: 2 additions & 0 deletions hy/reader/hy_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ class HyReader(Reader):
When ``use_current_readers`` is true, initialize this reader
with all reader macros from the calling module."""

__module__ = 'hy'

###
# Components necessary for Reader implementation
###
Expand Down
2 changes: 2 additions & 0 deletions hy/reader/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class Reader(metaclass=ReaderMeta):
A read-only (line, column) tuple indicating the current cursor
position of the source being read"""

__module__ = 'hy'

def __init__(self):
self._source = None
self._filename = None
Expand Down
5 changes: 2 additions & 3 deletions hy/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ class REPL(code.InteractiveConsole):
Note that as with :func:`code.interact`, changes to local variables inside the
REPL are not propagated back to the original scope."""

__module__ = 'hy'

def __init__(self, spy=False, spy_delimiter=('-' * 30), output_fn=None, locals=None, filename="<stdin>", allow_incomplete=True):

# Create a proper module for this REPL so that we can obtain it easily
Expand Down Expand Up @@ -460,6 +462,3 @@ def banner(self):
pyversion=platform.python_version(),
os=platform.system(),
)


REPL.__module__ = "hy" # Print as `hy.REPL` instead of `hy.repl.REPL`.
4 changes: 2 additions & 2 deletions tests/compilers/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from hy.compiler import hy_compile
from hy.errors import HyError, HyLanguageError
from hy.reader import read_many
from hy.reader.exceptions import LexException, PrematureEndOfInput
from hy.reader.exceptions import LexException


def _ast_spotcheck(arg, root, secondary):
Expand Down Expand Up @@ -466,7 +466,7 @@ def test_compile_error():

def test_for_compile_error():
"""Ensure we get compile error in tricky 'for' cases"""
with pytest.raises(PrematureEndOfInput) as excinfo:
with pytest.raises(hy.PrematureEndOfInput) as excinfo:
can_compile("(fn [] (for)")
assert excinfo.value.msg.startswith("Premature end of input")

Expand Down
5 changes: 2 additions & 3 deletions tests/importer/test_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from hy.errors import HyLanguageError, hy_exc_handler
from hy.importer import HyLoader
from hy.reader import read_many
from hy.reader.exceptions import PrematureEndOfInput


def test_basics():
Expand Down Expand Up @@ -183,7 +182,7 @@ def unlink(filename):

source.write_text("(setv a 11 (setv b (// 20 1))")

with pytest.raises(PrematureEndOfInput):
with pytest.raises(hy.PrematureEndOfInput):
reload(mod)

mod = sys.modules.get(TESTFN)
Expand Down Expand Up @@ -266,7 +265,7 @@ def test_filtered_importlib_frames(capsys):
spec = importlib.util.spec_from_loader(testLoader.name, testLoader)
mod = importlib.util.module_from_spec(spec)

with pytest.raises(PrematureEndOfInput) as execinfo:
with pytest.raises(hy.PrematureEndOfInput) as execinfo:
testLoader.exec_module(mod)

hy_exc_handler(execinfo.type, execinfo.value, execinfo.tb)
Expand Down
8 changes: 8 additions & 0 deletions tests/native_tests/other.hy
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@
(assert (= (pydoc.getdoc pydoc-hy.C2) "")))
; `pydoc` shouldn't try to get a comment from Hy code, since it
; doesn't know how to parse Hy.


(defn test-help-class-attr []
"Our tampering with `pydoc` shouldn't cause `help` to raise
`TypeError` on classes with non-method attributes."
(defclass C []
(setv attribute 1))
(help C))
8 changes: 3 additions & 5 deletions tests/native_tests/reader_macros.hy
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
types
contextlib [contextmanager]
hy.errors [HyMacroExpansionError]
hy.reader [HyReader]
hy.reader.exceptions [PrematureEndOfInput]

pytest)

Expand Down Expand Up @@ -55,7 +53,7 @@
(defn test-bad-reader-macro-name []
(with [(pytest.raises HyMacroExpansionError)]
(eval-module "(defreader :a-key '1)"))
(with [(pytest.raises PrematureEndOfInput)]
(with [(pytest.raises hy.PrematureEndOfInput)]
(eval-module "# _ 3")))

(defn test-get-macro []
Expand Down Expand Up @@ -123,7 +121,7 @@
(with [(pytest.raises hy.errors.HySyntaxError)]
(hy.read tag)))
;; but they should be installed in the module
(hy.eval '(setv reader (hy.reader.HyReader :use-current-readers True)) :module module)
(hy.eval '(setv reader (hy.HyReader :use-current-readers True)) :module module)
(setv reader module.reader)
(for [[s val] [["#r" 5]
["#test-read" 4]
Expand All @@ -132,7 +130,7 @@

;; passing a reader explicitly should work as expected
(with [module (temp-module "<test>")]
(setv reader (HyReader))
(setv reader (hy.HyReader))
(defn eval1 [s]
(hy.eval (hy.read s :reader reader) :module module))
(eval1 "(defreader fbaz 32)")
Expand Down
2 changes: 1 addition & 1 deletion tests/test_bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ def req_err(x):
r' File "(?:<string>|string-[0-9a-f]+)", line 1\n'
r' \(print "\n'
r" \^\n"
r"hy.reader.exceptions.PrematureEndOfInput"
r"hy.PrematureEndOfInput"
)
assert re.search(peoi_re, error)

Expand Down
13 changes: 10 additions & 3 deletions tests/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest

from hy import PrematureEndOfInput
from hy.errors import hy_exc_handler
from hy.models import (
Bytes,
Expand All @@ -20,7 +21,7 @@
Symbol,
)
from hy.reader import read_many
from hy.reader.exceptions import LexException, PrematureEndOfInput
from hy.reader.exceptions import LexException


def tokenize(*args, **kwargs):
Expand Down Expand Up @@ -86,7 +87,7 @@ def test_lex_single_quote_err():
' File "<string>", line 1',
" '",
" ^",
"hy.reader.exceptions.PrematureEndOfInput: Premature end of input while attempting to parse one form",
"hy.PrematureEndOfInput: Premature end of input while attempting to parse one form",
],
)

Expand Down Expand Up @@ -660,7 +661,7 @@ def test_lex_exception_filtering(capsys):
' File "<string>", line 2',
" (foo",
" ^",
"hy.reader.exceptions.PrematureEndOfInput: Premature end of input while attempting to parse one form",
"hy.PrematureEndOfInput: Premature end of input while attempting to parse one form",
],
)

Expand Down Expand Up @@ -702,3 +703,9 @@ def test_shebang():
assert tokenize('#!/usr/bin/env hy\n5')
assert (tokenize('#!/usr/bin/env hy\n5', skip_shebang = True) ==
[Integer(5)])


def test_reader_class_reprs():
"Don't show internal modules in which these classes are defined."
assert repr(hy.Reader) == "<class 'hy.Reader'>"
assert repr(hy.HyReader) == "<class 'hy.HyReader'>"

0 comments on commit 3ba2c87

Please sign in to comment.