Skip to content

Commit

Permalink
Fix statement pull-out in multi-item with
Browse files Browse the repository at this point in the history
  • Loading branch information
Kodiologist committed Sep 17, 2024
1 parent 2d16a26 commit ae844e8
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 4 deletions.
2 changes: 2 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Bug Fixes
------------------------------
* Fixed a crash on Python 3.12.6.
* Keyword objects can now be compared to each other with `<` etc.
* The order of evaluation in multi-item `with`\s now matches that of
nested one-item `with`\s.
* Fixed a bug in which the REPL misinterpreted the symbol `pass`.

0.29.0 (released 2024-05-20)
Expand Down
25 changes: 21 additions & 4 deletions hy/core/result_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -1116,22 +1116,39 @@ def compile_with_expression(compiler, expr, root, args, body):
expr, targets=[name], value=asty.Constant(expr, value=None)
)

[args] = args
ret = Result(stmts=[initial_assign])
items = []
was_async = None
cbody = None
for i, (is_async, variable, ctx) in enumerate(args[0]):
for i, (is_async, variable, ctx) in enumerate(args):
is_async = bool(is_async)
if was_async is None:
was_async = is_async
elif is_async != was_async:
# We're compiling a `with` that mixes synchronous and
# asynchronous context managers. Python doesn't support
# this directly, so start a new `with` inside the body.
cbody = compile_with_expression(compiler, expr, root, [args[0][i:]], body)
cbody = compile_with_expression(compiler, expr, root, [args[i:]], body)
break
ctx = compiler.compile(ctx)
ret += ctx
if not isinstance(ctx, Result):
# In a non-recursive call, `ctx` has not yet been compiled,
# and thus is not yet a `Result`.
ctx = compiler.compile(ctx)
if i == 0:
ret += ctx
elif ctx.stmts:
# We need to include some statements as part of this
# context manager, but this `with` already has at
# least one prior context manager. So, put our
# statements in the body and then start a new `with`.
cbody = ctx + compile_with_expression(
compiler,
expr,
root,
[((is_async, variable, ctx), *args[i + 1:])],
body)
break
variable = (
None
if variable == Symbol("_")
Expand Down
15 changes: 15 additions & 0 deletions tests/native_tests/with.hy
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(import
asyncio
unittest.mock [Mock]
pytest
tests.resources [async-test AsyncWithTest async-exits])

Expand Down Expand Up @@ -106,3 +107,17 @@
(setv w (with [(SuppressZDE)] (.append l w) (/ 1 0) 5))
(assert (is w None))
(assert (= l [7])))

(defn test-statements []

(setv m (Mock))
(with [t (do (m) (WithTest 2))]
(setv out t))
(assert (= m.call-count 1))
(assert (= out 2))

; https://github.com/hylang/hy/issues/2605
(with [t1 (WithTest 1) t2 (do (setv foo t1) (WithTest 2))]
(setv out [t1 t2]))
(assert (= out [1 2]))
(assert (= foo 1)))

0 comments on commit ae844e8

Please sign in to comment.