Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more tests to pydemo.hy #2637

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ New Features
Bug Fixes
------------------------------
* Fixed a crash from using an empty string in a `(. …)` expression.
* `(except [[]] …)` now catches no exceptions, rather than being treated like
`(except [] …)`, which catches all exceptions.
* `(except [e []] …)` is now translated to Python correctly by `hy2py`.

1.0.0 ("Afternoon Review", released 2024-09-22)
======================================================================
Expand Down
2 changes: 2 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,8 @@ Exception-handling
``except ETYPE as VAR:``
- ``[VAR [ETYPE1 ETYPE2 …]]`` to catch any of the named types and bind it to
``VAR``, like Python's ``except ETYPE1, ETYPE2, … as VAR:``
- ``[[]]`` or ``[VAR []]`` to catch no exceptions, like Python's
``except ():``.

The return value of ``try`` is the last form evaluated among the main body,
``except`` forms, ``except*`` forms, and ``else``.
Expand Down
2 changes: 1 addition & 1 deletion docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ hy2py
.. warning::
``hy2py`` can execute arbitrary code (via macros, :hy:func:`eval-when-compile`, etc.). Don't give it untrusted input.


Hy has no built-in capacity to translate Python to Hy, but see `py2hy <https://github.com/hylang/py2hy>`__.

.. _hyc:

Expand Down
2 changes: 2 additions & 0 deletions docs/interop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ You can embed Python code directly into a Hy program with the macros
tools like :func:`eval` or :func:`exec` to execute or manipulate Python code in
strings.

To translate Python code to Hy, see `py2hy <https://github.com/hylang/py2hy>`__.

.. _using-hy-from-python:

Using Hy from Python
Expand Down
5 changes: 3 additions & 2 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ This code returns ``176``. Why? You can see the infix equivalent with the
command ``echo "(- (* (+ 1 3 88) 2) 8)" | hy2py``, which returns the Python
code corresponding to the given Hy code. Or you can pass the ``--spy`` option to
Hy when starting the interactive read-eval-print loop (REPL), which shows the
Python equivalent of each input line before the result. The infix equivalent in
this case is:
Python equivalent of each input line before the result. (To translate in the
other direction, from Python to Hy, try the external program `py2hy
<https://github.com/hylang/py2hy>`__.) The infix equivalent in this case is:

.. code-block:: python

Expand Down
30 changes: 17 additions & 13 deletions hy/core/result_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -1449,20 +1449,24 @@ def compile_try_expression(compiler, expr, root, body, catchers, orelse, finalbo
)

name = None
if len(exceptions) == 2:
name = mangle(compiler._nonconst(exceptions[0]))

exceptions_list = exceptions[-1] if exceptions else List()
if isinstance(exceptions_list, List):
if len(exceptions_list):
# [FooBar BarFoo] → catch Foobar and BarFoo exceptions
elts, types, _ = compiler._compile_collect(exceptions_list)
types += asty.Tuple(exceptions_list, elts=elts, ctx=ast.Load())
else:
# [] → all exceptions caught
types = Result()
if len(exceptions) == 0:
exceptions = "ALL"
elif len(exceptions) == 1:
[exceptions] = exceptions
else:
[name, exceptions] = exceptions
name = mangle(compiler._nonconst(name))

if exceptions == "ALL":
# Catch all exceptions.
types = Result()
elif isinstance(exceptions, List):
# [FooBar BarFoo] → Catch Foobar and BarFoo exceptions.
elts, types, _ = compiler._compile_collect(exceptions)
types += asty.Tuple(exceptions, elts=elts, ctx=ast.Load())
else:
types = compiler.compile(exceptions_list)
# A single exception type.
types = compiler.compile(exceptions)

# Create a "fake" scope for the exception variable.
# See: https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
Expand Down
4 changes: 4 additions & 0 deletions tests/native_tests/try.hy
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
(setv out ["ccc" (type e)]))
(except [e [KeyError AttributeError]]
(setv out ["ddd" (type e)]))
(except [[]]
(assert False))
(except [e []]
(assert False))
(except []
(setv out ["eee" None]))
(else
Expand Down
7 changes: 4 additions & 3 deletions tests/native_tests/with.hy
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
asyncio
unittest.mock [Mock]
pytest
tests.resources [async-test AsyncWithTest async-exits])
tests.resources [async-test]
tests.resources.pydemo [AsyncWithTest async-exits])

(defn test-context []
(with [fd (open "tests/resources/text.txt" "r")] (assert fd))
Expand Down Expand Up @@ -39,7 +40,7 @@
(assert (= exits [4 3 2 1])))

(defn [async-test] test-single-with-async []
(setv (cut async-exits) [])
(.clear async-exits)
(setv out [])
(asyncio.run
((fn :async []
Expand All @@ -49,7 +50,7 @@
(assert (= async-exits [1])))

(defn [async-test] test-quince-with-async []
(setv (cut async-exits) [])
(.clear async-exits)
(setv out [])
(asyncio.run
((fn :async []
Expand Down
20 changes: 0 additions & 20 deletions tests/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,3 @@ def function_with_a_dash():
async_test = pytest.mark.skipif(
not can_test_async, reason="`asyncio.run` not implemented"
)


async_exits = []
class AsyncWithTest:
def __init__(self, val):
self.val = val

async def __aenter__(self):
return self.val

async def __aexit__(self, exc_type, exc, traceback):
async_exits.append(self.val)


async def async_loop(items):
import asyncio

for x in items:
yield x
await asyncio.sleep(0)
89 changes: 65 additions & 24 deletions tests/resources/pydemo.hy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
;; This Hy module is intended to concisely demonstrate all of
;; Python's major syntactic features for the purpose of testing hy2py.
;; It also tries out macros and reader macros to ensure they work with
;; hy2py.
;; hy2py. It's used as part of Hy's test suite as well as py2hy's.
"This is a module docstring."

(setv mystring (* "foo" 3))
Expand Down Expand Up @@ -53,6 +53,8 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
(setv mylambda (fn [x] (+ x "z")))
(setv annotated-lambda-ret (fn #^ int [] 1))
(setv annotated-lambda-params (fn [#^ int a * #^ str [b "hello world!"]] #(a b)))
(setv #^ list annotated-assignment [3])
#^ tuple bare-annotation

(setv fstring1 f"hello {(+ 1 1)} world")
(setv p "xyzzy")
Expand All @@ -78,6 +80,14 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
(+= if-block "c")
(+= if-block "d")))

(if (setx mysetx "mx")
(do
(+= mysetx "a")
(+= mysetx "b"))
(do
(+= mysetx "c")
(+= mysetx "d")))

(setv counter 4)
(setv while-block "")
(while counter
Expand All @@ -102,25 +112,6 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
(for [x ["fo" "fi" "fu"]]
(setv for-block (+ x for-block)))

(try
(assert (= 1 0))
(except [_ AssertionError]
(setv caught-assertion True))
(finally
(setv ran-finally True)))

(try
(raise (ValueError "payload"))
(except [e ValueError]
(setv myraise (str e))))

(try
1
(except [e ValueError]
(raise))
(else
(setv ran-try-else True)))

(defn fun [a b [c 9] [from 10] #* args #** kwargs]
"function docstring"
[a b c from args (sorted (.items kwargs))])
Expand All @@ -136,7 +127,8 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little

(defn generator []
(for [x "abc"]
(yield x)))
(yield x))
(yield :from "xyz"))
(setv myyield (list (generator)))

(defn [(fn [f] (setv f.newattr "hello") f)] mydecorated [])
Expand All @@ -147,6 +139,34 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
(+= myglobal 1))
(set-global)

(defn nonlocal-outer []
(setv mynonlocal 400)
(defn f []
(nonlocal mynonlocal)
(+= mynonlocal 1))
(f)
mynonlocal)

(setv finally-values [])
(defn mytry [error-type]
(try
(when error-type
(raise (error-type "payload")))
(except [ZeroDivisionError]
"zero-div")
(except [e [ValueError TypeError]]
["vt" (type e) e.args])
(except [[]]
"never")
(except [e []]
"never2")
(except []
"other")
(else
"else")
(finally
(.append finally-values 1))))

(defclass C1 []) ; Force the creation of a `pass` statement.

(defclass C2 [C1]
Expand All @@ -157,10 +177,19 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
(import contextlib [closing])
(setv closed [])
(defclass Closeable []
(defn close [self] (.append closed self.x)))
(defn __init__ [self [x None]]
(setv self.x x))
(defn close [self]
(.append closed self.x)))
(with [(closing (Closeable))])
(with [c1 (closing (Closeable)) c2 (closing (Closeable))]
(setv c1.x "v1")
(setv c2.x "v2"))
(with [
_ (closing (Closeable "a1"))
c3 (closing (Closeable))
_ (closing (Closeable "a2"))]
(setv c3.x "v3"))
(setv closed1 (.copy closed))

(pys "
Expand All @@ -174,8 +203,7 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
(setv py-accum (py "''.join(map(str, pys_accum))"))

(defn :async coro []
(import asyncio
tests.resources [AsyncWithTest async-loop])
(import asyncio)
(await (asyncio.sleep 0))
(setv values ["a"])
(with [:async t (AsyncWithTest "b")]
Expand All @@ -186,6 +214,19 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
:async item (async-loop ["e" "f"])
item))
values)
(defclass AsyncWithTest []
(defn __init__ [self val]
(setv self.val val))
(defn :async __aenter__ [self]
self.val)
(defn :async __aexit__ [self exc-type exc traceback]
(.append async-exits self.val)))
(setv async-exits [])
(defn :async async-loop [items]
(import asyncio)
(for [x items]
(yield x)
(await (asyncio.sleep 0))))

(defmacro macaroni [expr]
`[~expr ~expr])
Expand Down
Loading