Skip to content

Commit d2e2885

Browse files
committed
Add the :hy pragma
1 parent ae0de08 commit d2e2885

File tree

5 files changed

+62
-5
lines changed

5 files changed

+62
-5
lines changed

NEWS.rst

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ Unreleased
55

66
Supports Python 3.9 – Python 3.x
77

8+
New Features
9+
------------------------------
10+
* New pragma `hy`.
11+
812
1.0.0 ("Afternoon Review", released 2024-09-22)
913
======================================================================
1014

docs/api.rst

+10-2
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,17 @@ Fundamentals
169169
The effect of each pragma is locally scoped to its containing function,
170170
class, or comprehension form (other than ``for``), if there is one.
171171

172-
Only one pragma is currently implemented:
172+
These pragmata are currently implemented:
173173

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

176184
- ``:warn-on-core-shadow``: If true (the default), :hy:func:`defmacro` and
177185
:hy:func:`require` will raise a warning at compile-time if you define a macro

hy/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
__version__ = 'unreleased'
22
nickname = None
3+
last_version = '1.0.0'
4+
# This is used by `(pragma :hy …)` to guess whether an unreleased
5+
# version of Hy is new enough. In a released version, it's simply
6+
# equal to `__version__`.
37

48

59
def _initialize_env_var(env_var, default_val):

hy/core/result_macros.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
import ast
1212
import textwrap
1313
from contextlib import nullcontext
14-
from itertools import dropwhile
14+
from itertools import dropwhile, zip_longest
1515

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

18+
from hy import last_version
1819
from hy.compat import PY3_11, PY3_12
1920
from hy.compiler import Result, asty, mkexpr
2021
from hy.errors import HyEvalError, HyInternalError, HyTypeError
@@ -165,11 +166,31 @@ def compile_inline_python(compiler, expr, root, code):
165166
@pattern_macro("pragma", [many(KEYWORD + FORM)])
166167
def compile_pragma(compiler, expr, root, kwargs):
167168
for kw, value in kwargs:
168-
if kw == Keyword("warn-on-core-shadow"):
169+
170+
if kw == Keyword("hy"):
171+
min_version = compiler.eval(value)
172+
if not isinstance(min_version, str):
173+
raise compiler._syntax_error(value, "The version given to the pragma `:hy` must be a string")
174+
parts = min_version.split('.')
175+
if not all(p.isdigit() for p in parts):
176+
raise compiler._syntax_error(value, "The string given to the pragma `:hy` must be a dot-separated sequence of integers")
177+
for have, need in zip_longest(
178+
map(int, last_version.split('.')),
179+
map(int, parts)):
180+
if need is None:
181+
break
182+
if have is None or have < need:
183+
raise compiler._syntax_error(kw, f"Hy version {min_version} or later required")
184+
if have > need:
185+
break
186+
187+
elif kw == Keyword("warn-on-core-shadow"):
169188
compiler.local_state_stack[-1]['warn_on_core_shadow'] = (
170189
bool(compiler.eval(value)))
190+
171191
else:
172192
raise compiler._syntax_error(kw, f"Unknown pragma `{kw}`. Perhaps it's implemented by a newer version of Hy.")
193+
173194
return Result()
174195

175196

tests/native_tests/other.hy

+21-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,27 @@
66
importlib
77
pydoc
88
pytest
9-
hy.errors [HyLanguageError])
9+
hy.errors [HyLanguageError HySyntaxError])
10+
11+
12+
(defn test-pragma-hy []
13+
14+
(pragma :hy "1")
15+
(pragma :hy "1.0")
16+
(pragma :hy "1.0.0")
17+
(pragma :hy "0.28.0")
18+
19+
(eval-when-compile (setv a-version "1.0"))
20+
(pragma :hy a-version)
21+
22+
(defn bad [v msg]
23+
(with [e (pytest.raises HySyntaxError)]
24+
(hy.eval `(pragma :hy ~v)))
25+
(assert (in msg e.value.msg)))
26+
(bad "5" "version 5 or later required")
27+
(bad "1.99.1" "version 1.99.1 or later required")
28+
(bad 5 "must be a string")
29+
(bad "Afternoon Review" "must be a dot-separated sequence of integers"))
1030

1131

1232
(defn test-illegal-assignments []

0 commit comments

Comments
 (0)