Skip to content

Commit

Permalink
Add the :hy pragma
Browse files Browse the repository at this point in the history
  • Loading branch information
Kodiologist committed Oct 24, 2024
1 parent ae0de08 commit d2e2885
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 5 deletions.
4 changes: 4 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Unreleased

Supports Python 3.9 – Python 3.x

New Features
------------------------------
* New pragma `hy`.

1.0.0 ("Afternoon Review", released 2024-09-22)
======================================================================

Expand Down
12 changes: 10 additions & 2 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,17 @@ Fundamentals
The effect of each pragma is locally scoped to its containing function,
class, or comprehension form (other than ``for``), if there is one.

Only one pragma is currently implemented:
These pragmata are currently implemented:

.. _warn-on-core-shadow:
- ``:hy``: Set this to a string giving a Hy version number or prefix thereof,
such as "1.1.0" or "1", to raise a compile-time error if the currently
executing version of Hy isn't at least this new. If you're writing a
package, you should still declare the required version of Hy in ``setup.py``
or ``pyproject.toml`` or whatever, because ``pip`` won't look for ``(pragma
:hy …)`` calls. In the future, this pragma may also switch on features of Hy
that were introduced in or before the given version.

.. _warn-on-core-shadow:

- ``:warn-on-core-shadow``: If true (the default), :hy:func:`defmacro` and
:hy:func:`require` will raise a warning at compile-time if you define a macro
Expand Down
4 changes: 4 additions & 0 deletions hy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
__version__ = 'unreleased'
nickname = None
last_version = '1.0.0'
# This is used by `(pragma :hy …)` to guess whether an unreleased
# version of Hy is new enough. In a released version, it's simply
# equal to `__version__`.


def _initialize_env_var(env_var, default_val):
Expand Down
25 changes: 23 additions & 2 deletions hy/core/result_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
import ast
import textwrap
from contextlib import nullcontext
from itertools import dropwhile
from itertools import dropwhile, zip_longest

from funcparserlib.parser import finished, forward_decl, many, maybe, oneplus, some

from hy import last_version
from hy.compat import PY3_11, PY3_12
from hy.compiler import Result, asty, mkexpr
from hy.errors import HyEvalError, HyInternalError, HyTypeError
Expand Down Expand Up @@ -165,11 +166,31 @@ def compile_inline_python(compiler, expr, root, code):
@pattern_macro("pragma", [many(KEYWORD + FORM)])
def compile_pragma(compiler, expr, root, kwargs):
for kw, value in kwargs:
if kw == Keyword("warn-on-core-shadow"):

if kw == Keyword("hy"):
min_version = compiler.eval(value)
if not isinstance(min_version, str):
raise compiler._syntax_error(value, "The version given to the pragma `:hy` must be a string")
parts = min_version.split('.')
if not all(p.isdigit() for p in parts):
raise compiler._syntax_error(value, "The string given to the pragma `:hy` must be a dot-separated sequence of integers")
for have, need in zip_longest(
map(int, last_version.split('.')),
map(int, parts)):
if need is None:
break
if have is None or have < need:
raise compiler._syntax_error(kw, f"Hy version {min_version} or later required")
if have > need:
break

elif kw == Keyword("warn-on-core-shadow"):
compiler.local_state_stack[-1]['warn_on_core_shadow'] = (
bool(compiler.eval(value)))

else:
raise compiler._syntax_error(kw, f"Unknown pragma `{kw}`. Perhaps it's implemented by a newer version of Hy.")

return Result()


Expand Down
22 changes: 21 additions & 1 deletion tests/native_tests/other.hy
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,27 @@
importlib
pydoc
pytest
hy.errors [HyLanguageError])
hy.errors [HyLanguageError HySyntaxError])


(defn test-pragma-hy []

(pragma :hy "1")
(pragma :hy "1.0")
(pragma :hy "1.0.0")
(pragma :hy "0.28.0")

(eval-when-compile (setv a-version "1.0"))
(pragma :hy a-version)

(defn bad [v msg]
(with [e (pytest.raises HySyntaxError)]
(hy.eval `(pragma :hy ~v)))
(assert (in msg e.value.msg)))
(bad "5" "version 5 or later required")
(bad "1.99.1" "version 1.99.1 or later required")
(bad 5 "must be a string")
(bad "Afternoon Review" "must be a dot-separated sequence of integers"))


(defn test-illegal-assignments []
Expand Down

0 comments on commit d2e2885

Please sign in to comment.