Skip to content

Commit

Permalink
Merge pull request #2411 from Kodiologist/misc
Browse files Browse the repository at this point in the history
Miscellany
  • Loading branch information
Kodiologist committed Feb 18, 2023
2 parents 352351e + 0bd5505 commit 1cc9c8b
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 125 deletions.
9 changes: 9 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@
Unreleased
=============================

Breaking Changes
------------------------------
* The `py` macro now dedents the input like `pys`.

Bug Fixes
------------------------------
* Fixed an installation failure in some situations when version lookup
fails.

New Features
------------------------------
* `nonlocal` and `global` can now be called with no arguments, in which
case they're no-ops.

0.26.0 (released 2023-02-08)
=============================

Expand Down
21 changes: 9 additions & 12 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,12 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.
(assert (= 1 2) "one should equal two")
; AssertionError: one should equal two

.. hy:macro:: (global [sym #* syms])
.. hy:macro:: (global [#* syms])
``global`` compiles to a :py:keyword:`global` statement, which declares one
or more names as referring to global (i.e., module-level) variables. The
arguments are symbols; at least one is required. The return value is always
``None``. ::
arguments are symbols; with no arguments, ``global`` has no effect. The
return value is always ``None``. ::

(setv a 1 b 10)
(print a b) ; => 1 10
Expand Down Expand Up @@ -741,7 +741,7 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.

(del foo (get mydict "mykey") myobj.myattr)

.. hy:macro:: (nonlocal [sym #* syms])
.. hy:macro:: (nonlocal [#* syms])
As :hy:func:`global`, but the result is a :py:keyword:`nonlocal` statement.

Expand All @@ -762,12 +762,14 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.
code that's only defined at run-time, try the standard Python function
:func:`eval`.

The code string is dedented with :func:`textwrap.dedent` before parsing,
which allows you to indent the code to match the surrounding Hy code when
Python would otherwise forbid this, but beware that significant leading
whitespace in embedded string literals will be removed.

Python code need not syntactically round-trip if you use ``hy2py`` on a Hy
program that uses ``py`` or ``pys``. For example, comments will be removed.


.. _pys-specialform:

.. hy:macro:: (pys [string])
As :hy:func:`py <py>`, but the code can consist of zero or more statements,
Expand All @@ -777,11 +779,6 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.
(pys "myvar = 5")
(print "myvar is" myvar)

The code string is dedented with :func:`textwrap.dedent` before parsing,
which allows you to indent the code to match the surrounding Hy code when
Python would otherwise forbid this, but beware that significant leading
whitespace in embedded string literals will be removed.

.. hy:macro:: (quasiquote [form])
``quasiquote`` allows you to quote a form, but also selectively evaluate
Expand Down
4 changes: 2 additions & 2 deletions hy/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,8 @@ def __init__(self, module, filename=None, source=None):

# Hy expects this to be present, so we prep the module for Hy
# compilation.
self.module.__dict__.setdefault("__macros__", {})
self.module.__dict__.setdefault("__reader_macros__", {})
self.module.__dict__.setdefault("_hy_macros", {})
self.module.__dict__.setdefault("_hy_reader_macros", {})

self.scope = ScopeGlobal(self)

Expand Down
6 changes: 3 additions & 3 deletions hy/completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ def __init__(self, namespace={}):
self.namespace = namespace
self.path = [builtins.__dict__, namespace]

namespace.setdefault("__macros__", {})
namespace.setdefault("__reader_macros__", {})
namespace.setdefault("_hy_macros", {})
namespace.setdefault("_hy_reader_macros", {})

self.path.append(namespace["__macros__"])
self.path.append(namespace["_hy_macros"])

def attr_matches(self, text):
# Borrowed from IPython's completer
Expand Down
12 changes: 6 additions & 6 deletions hy/core/macros.hy
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
~@body)))
(eval-when-compile
(setv (get hy.&reader.reader-table ~dispatch-key)
(get __reader_macros__ ~dispatch-key)))))
(get _hy_reader_macros ~dispatch-key)))))


(defmacro doc [symbol]
Expand All @@ -129,10 +129,10 @@
(setv mangled (hy.mangle symbol))
(setv builtins (hy.gensym "builtins"))
`(do (import builtins :as ~builtins)
(help (or (.get __macros__ ~mangled)
(.get __reader_macros__ ~mangled)
(.get (. ~builtins __macros__) ~mangled)
(.get (. ~builtins __reader_macros__) ~mangled)
(help (or (.get _hy_macros ~mangled)
(.get _hy_reader_macros ~mangled)
(.get (. ~builtins _hy_macros) ~mangled)
(.get (. ~builtins _hy_reader_macros) ~mangled)
(raise (NameError f"macro {~symbol !r} is not defined"))))))


Expand Down Expand Up @@ -192,4 +192,4 @@
(let [sym (hy.gensym)]
`(eval-and-compile
(for [~sym ~(lfor name names (hy.mangle name))]
(when (in ~sym __macros__) (del (get __macros__ ~sym)))))))
(when (in ~sym _hy_macros) (del (get _hy_macros ~sym)))))))
7 changes: 5 additions & 2 deletions hy/core/result_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def compile_inline_python(compiler, expr, root, code):
try:
o = asty.parse(
expr,
textwrap.dedent(code) if exec_mode else code,
textwrap.dedent(code),
compiler.filename,
"exec" if exec_mode else "eval",
).body
Expand Down Expand Up @@ -486,8 +486,11 @@ def compile_assign(
return result


@pattern_macro(["global", "nonlocal"], [oneplus(SYM)])
@pattern_macro(["global", "nonlocal"], [many(SYM)])
def compile_global_or_nonlocal(compiler, expr, root, syms):
if not syms:
return asty.Pass(expr)

node = asty.Global if root == "global" else asty.Nonlocal
ret = node(expr, names=[mangle(s) for s in syms])

Expand Down
44 changes: 22 additions & 22 deletions hy/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def macro(name):

def reader_macro(name, fn):
fn = rename_function(fn, name)
inspect.getmodule(fn).__dict__.setdefault("__reader_macros__", {})[name] = fn
inspect.getmodule(fn).__dict__.setdefault("_hy_reader_macros", {})[name] = fn


def pattern_macro(names, pattern, shadow=None):
Expand Down Expand Up @@ -87,8 +87,8 @@ def install_macro(name, fn, module_of):
name = mangle(name)
fn = rename_function(fn, name)
calling_module = inspect.getmodule(module_of)
macros_obj = calling_module.__dict__.setdefault("__macros__", {})
if name in getattr(builtins, "__macros__", {}):
macros_obj = calling_module.__dict__.setdefault("_hy_macros", {})
if name in getattr(builtins, "_hy_macros", {}):
warnings.warn(
(
f"{name} already refers to: `{name}` in module: `builtins`,"
Expand Down Expand Up @@ -179,15 +179,15 @@ def require_reader(source_module, target_module, assignments):
if not inspect.ismodule(source_module):
source_module = import_module_from_string(source_module, target_module)

source_macros = source_module.__dict__.setdefault("__reader_macros__", {})
target_macros = target_namespace.setdefault("__reader_macros__", {})
source_macros = source_module.__dict__.setdefault("_hy_reader_macros", {})
target_macros = target_namespace.setdefault("_hy_reader_macros", {})

assignments = (
source_macros.keys() if assignments == "ALL" else map(mangle, assignments)
)

for name in assignments:
if name in source_module.__reader_macros__:
if name in source_module._hy_reader_macros:
target_macros[name] = source_macros[name]
else:
raise HyRequireError(f"Could not require name {name} from {source_module}")
Expand All @@ -198,12 +198,12 @@ def require_reader(source_module, target_module, assignments):
def enable_readers(module, reader, names):
_, namespace = derive_target_module(module, inspect.stack()[1][0])
names = (
namespace["__reader_macros__"].keys() if names == "ALL" else map(mangle, names)
namespace["_hy_reader_macros"].keys() if names == "ALL" else map(mangle, names)
)
for name in names:
if name not in namespace["__reader_macros__"]:
if name not in namespace["_hy_reader_macros"]:
raise NameError(f"reader {name} is not defined")
reader.reader_table[name] = namespace["__reader_macros__"][name]
reader.reader_table[name] = namespace["_hy_reader_macros"][name]


def require(source_module, target_module, assignments, prefix=""):
Expand Down Expand Up @@ -241,14 +241,14 @@ def require(source_module, target_module, assignments, prefix=""):
if not inspect.ismodule(source_module):
source_module = import_module_from_string(source_module, target_module)

source_macros = source_module.__dict__.setdefault("__macros__", {})
source_macros = source_module.__dict__.setdefault("_hy_macros", {})
source_exports = getattr(
source_module,
"_hy_export_macros",
[k for k in source_macros.keys() if not k.startswith("_")],
)

if not source_module.__macros__:
if not source_module._hy_macros:
if assignments in ("ALL", "EXPORTS"):
return False
for name, alias in assignments:
Expand All @@ -267,7 +267,7 @@ def require(source_module, target_module, assignments, prefix=""):
)
return True

target_macros = target_namespace.setdefault("__macros__", {})
target_macros = target_namespace.setdefault("_hy_macros", {})

if prefix:
prefix += "."
Expand All @@ -287,7 +287,7 @@ def require(source_module, target_module, assignments, prefix=""):
if unmangle(alias).startswith("#")
else prefix + alias
)
if _name in source_module.__macros__:
if _name in source_module._hy_macros:
target_macros[alias] = source_macros[_name]
else:
raise HyRequireError(
Expand All @@ -303,19 +303,19 @@ def load_macros(module):
It is an error to call this on any module in `hy.core`.
"""
builtin_macros = EXTRA_MACROS
module.__macros__ = {}
module.__reader_macros__ = {}
module._hy_macros = {}
module._hy_reader_macros = {}

for builtin_mod_name in builtin_macros:
builtin_mod = importlib.import_module(builtin_mod_name)

# This may overwrite macros in the module.
if hasattr(builtin_mod, "__macros__"):
module.__macros__.update(getattr(builtin_mod, "__macros__", {}))
if hasattr(builtin_mod, "_hy_macros"):
module._hy_macros.update(getattr(builtin_mod, "_hy_macros", {}))

if hasattr(builtin_mod, "__reader_macros__"):
module.__reader_macros__.update(
getattr(builtin_mod, "__reader_macros__", {})
if hasattr(builtin_mod, "_hy_reader_macros"):
module._hy_reader_macros.update(
getattr(builtin_mod, "_hy_reader_macros", {})
)


Expand Down Expand Up @@ -406,9 +406,9 @@ def macroexpand(tree, module, compiler=None, once=False, result_ok=True):
# Choose the first namespace with the macro.
m = next(
(
mod.__macros__[fn]
mod._hy_macros[fn]
for mod in expr_modules
if fn in getattr(mod, "__macros__", ())
if fn in getattr(mod, "_hy_macros", ())
),
None,
)
Expand Down
4 changes: 2 additions & 2 deletions hy/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ def __init__(

super().__init__()

if hasattr(self.module, "__reader_macros__"):
if hasattr(self.module, "_hy_reader_macros"):
enable_readers(
self.module, self.reader, self.module.__reader_macros__.keys()
self.module, self.reader, self.module._hy_reader_macros.keys()
)

self.flags |= hy_ast_compile_flags
Expand Down
4 changes: 2 additions & 2 deletions hy/reserved.hy
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
(defn macros []
"Return a frozenset of Hy's core macro names."
(frozenset (map hy.unmangle (+
(list (.keys hy.core.result_macros.__macros__))
(list (.keys hy.core.macros.__macros__))))))
(list (.keys hy.core.result_macros._hy_macros))
(list (.keys hy.core.macros._hy_macros))))))

(defn names []
"Return a frozenset of reserved symbol names.
Expand Down
Loading

0 comments on commit 1cc9c8b

Please sign in to comment.