Initialize the return variable of `with`

This commit is contained in:
Kodi Arfer 2017-08-19 07:35:41 -07:00
parent f5ee5f4ee5
commit 8d40a68232
4 changed files with 49 additions and 2 deletions

2
NEWS
View File

@ -27,6 +27,8 @@ Changes from 0.13.0
* `exec` now works under Python 2
* No TypeError from multi-arity defn returning values evaluating to None
* try form now possible in defmacro/deftag
* Fixed a crash when `with` suppresses an exception. `with` now returns
`None` in this case.
[ Misc. Improvements ]
* `read`, `read_str`, and `eval` are exposed and documented as top-level

View File

@ -1678,6 +1678,13 @@ screen. The file is automatically closed after it has been processed.
(with [f (open "NEWS")] (print (.read f)))
``with`` returns the value of its last form, unless it suppresses an exception
(because the context manager's ``__exit__`` method returned true), in which
case it returns ``None``. So, the previous example could also be written
.. code-block:: clj
(print (with [f (open "NEWS")] (.read f)))
with-decorator
--------------

View File

@ -1472,6 +1472,16 @@ class HyASTCompiler(object):
value=body.force_expr,
lineno=expr.start_line,
col_offset=expr.start_column)
# Initialize the tempvar to None in case the `with` exits
# early with an exception.
initial_assign = ast.Assign(targets=[name],
value=ast.Name(
id=ast_str("None"),
ctx=ast.Load(),
lineno=expr.start_line,
col_offset=expr.start_column),
lineno=expr.start_line,
col_offset=expr.start_column)
the_with = ast.With(context_expr=ctx.force_expr,
lineno=expr.start_line,
@ -1483,7 +1493,7 @@ class HyASTCompiler(object):
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
optional_vars=thing)]
ret = ctx + the_with
ret = Result(stmts = [initial_assign]) + ctx + the_with
ret.contains_yield = ret.contains_yield or body.contains_yield
# And make our expression context our temp variable
expr_name = ast.Name(id=ast_str(var), arg=ast_str(var),
@ -1491,7 +1501,10 @@ class HyASTCompiler(object):
lineno=expr.start_line,
col_offset=expr.start_column)
ret += Result(expr=expr_name, temp_variables=[expr_name, name])
ret += Result(expr=expr_name)
# We don't give the Result any temp_vars because we don't want
# Result.rename to touch `name`. Otherwise, initial_assign will
# clobber any preexisting value of the renamed-to variable.
return ret

View File

@ -43,3 +43,28 @@
(assert (= t1 1))
(assert (= t2 2))
(assert (= t3 3))))
(defclass SuppressZDE [object]
(defn --enter-- [self])
(defn --exit-- [self exc-type exc-value traceback]
(and (not (none? exc-type)) (issubclass exc-type ZeroDivisionError))))
(defn test-exception-suppressing-with []
; https://github.com/hylang/hy/issues/1320
(setv x (with [(SuppressZDE)] 5))
(assert (= x 5))
(setv y (with [(SuppressZDE)] (/ 1 0)))
(assert (none? y))
(setv z (with [(SuppressZDE)] (/ 1 0) 5))
(assert (none? z))
(defn f [] (with [(SuppressZDE)] (/ 1 0)))
(assert (none? (f)))
(setv w 7 l [])
(setv w (with [(SuppressZDE)] (.append l w) (/ 1 0) 5))
(assert (none? w))
(assert (= l [7])))