Return from the else clause of a try form

I overhauled the documentation of `try` while I was editing it.
This commit is contained in:
Kodi Arfer 2017-05-13 21:35:12 -04:00 committed by Ryan Gonzalez
parent 81d89c9d12
commit dffa2811e6
4 changed files with 65 additions and 29 deletions

3
NEWS
View File

@ -26,6 +26,9 @@ Changes from 0.12.1
* `(** a b c d)` is now equivalent to `(** a (** b (** c d)))`, * `(** a b c d)` is now equivalent to `(** a (** b (** c d)))`,
not `(** (** (** a b) c) d)` not `(** (** (** a b) c) d)`
* `setv` always returns None * `setv` always returns None
* When a `try` form executes an `else` clause, the return value for the
`try` form is taken from `else` instead of the `try` body. For example,
`(try 1 (except [ValueError] 2) (else 3))` returns `3`.
* xor: If exactly one argument is true, return it * xor: If exactly one argument is true, return it
* hy.core.reserved is now hy.extra.reserved * hy.core.reserved is now hy.extra.reserved

View File

@ -1538,23 +1538,37 @@ or no arguments to re-raise the last ``Exception``.
try try
--- ---
The ``try`` form is used to start a ``try`` / ``except`` block. The form is The ``try`` form is used to catch exceptions (``except``) and run cleanup
used as follows: actions (``finally``).
.. code-block:: clj .. code-block:: clj
(try (try
(error-prone-function) (error-prone-function)
(except [e ZeroDivisionError] (print "Division by zero")) (except [ZeroDivisionError]
(else (print "no errors")) (print "Division by zero"))
(finally (print "all done"))) (except [[IndexError KeyboardInterrupt]]
(print "Index error or Ctrl-C"))
(except [e ValueError]
(print "ValueError:" (repr e)))
(except [e [TabError PermissionError ReferenceError]]
(print "Some sort of error:" (repr e)))
(else
(print "No errors"))
(finally
(print "All done")))
``try`` must contain at least one ``except`` block, and may optionally include The first argument of ``try`` is its body. (To put more than one form in the
an ``else`` or ``finally`` block. If an error is raised with a matching except body, use ``do``.) Then comes any number of ``except`` clauses, then optionally
block during the execution of ``error-prone-function``, that ``except`` block an ``else`` clause, then optionally a ``finally`` clause. If an exception is
will be executed. If no errors are raised, the ``else`` block is executed. The raised with a matching ``except`` clause during the execution of the body, that
``finally`` block will be executed last regardless of whether or not an error ``except`` clause will be executed. If no exceptions are raised, the ``else``
was raised. clause is executed. The ``finally`` clause will be executed last regardless of
whether an exception was raised.
The return value of ``try`` is the last form of the ``except`` clause that was
run, or the last form of ``else`` if no exception was raised, or the ``try``
body if there is no ``else`` clause.
unless unless

View File

@ -786,13 +786,6 @@ class HyASTCompiler(object):
returnable = Result(expr=expr_name, temp_variables=[expr_name, name], returnable = Result(expr=expr_name, temp_variables=[expr_name, name],
contains_yield=body.contains_yield) contains_yield=body.contains_yield)
body += ast.Assign(targets=[name],
value=body.force_expr,
lineno=expr.start_line,
col_offset=expr.start_column)
body = body.stmts
if not all(expr): if not all(expr):
raise HyTypeError(expr, "Empty list not allowed in `try'") raise HyTypeError(expr, "Empty list not allowed in `try'")
handler_results = Result() handler_results = Result()
@ -803,13 +796,22 @@ class HyASTCompiler(object):
handlers.append(handler_results.stmts.pop()) handlers.append(handler_results.stmts.pop())
orelse = [] orelse = []
if expr and expr[0][0] == HySymbol("else"): if expr and expr[0][0] == HySymbol("else"):
orelse = self.try_except_helper(expr.pop(0), HySymbol("else")) orelse = self._compile_branch(expr.pop(0)[1:])
orelse += ast.Assign(targets=[name],
value=orelse.force_expr,
lineno=expr.start_line,
col_offset=expr.start_column)
orelse += orelse.expr_as_stmt()
orelse = orelse.stmts
finalbody = [] finalbody = []
if expr and expr[0][0] == HySymbol("finally"): if expr and expr[0][0] == HySymbol("finally"):
finalbody = self.try_except_helper(expr.pop(0), HySymbol("finally")) finalbody = self._compile_branch(expr.pop(0)[1:])
finalbody += finalbody.expr_as_stmt()
finalbody = finalbody.stmts
if expr: if expr:
if expr[0][0] in ("except", "else", "finally"): if expr[0][0] in ("except", "else", "finally"):
raise HyTypeError(expr, "Incorrect order of `except'/`else'/`finally' in `try'") raise HyTypeError(expr, "Incorrect order "
"of `except'/`else'/`finally' in `try'")
raise HyTypeError(expr, "Unknown expression in `try'") raise HyTypeError(expr, "Unknown expression in `try'")
# Using (else) without (except) is verboten! # Using (else) without (except) is verboten!
@ -831,6 +833,14 @@ class HyASTCompiler(object):
ret = handler_results ret = handler_results
body += body.expr_as_stmt() if orelse else ast.Assign(
targets=[name],
value=body.force_expr,
lineno=expr.start_line,
col_offset=expr.start_column)
body = body.stmts or [ast.Pass(lineno=expr.start_line,
col_offset=expr.start_column)]
if PY3: if PY3:
# Python 3.3 features a merge of TryExcept+TryFinally into Try. # Python 3.3 features a merge of TryExcept+TryFinally into Try.
return ret + ast.Try( return ret + ast.Try(
@ -867,11 +877,6 @@ class HyASTCompiler(object):
body=body, body=body,
orelse=orelse) + returnable orelse=orelse) + returnable
def try_except_helper(self, hy_obj, symbol):
x = self._compile_branch(hy_obj[1:])
x += x.expr_as_stmt()
return x.stmts
@builds("except") @builds("except")
def magic_internal_form(self, expr): def magic_internal_form(self, expr):
raise HyTypeError(expr, raise HyTypeError(expr,

View File

@ -1174,20 +1174,34 @@
(assert (= foo 4))) (assert (= foo 4)))
#@(pytest.mark.xfail
(defn test-try-else-return [] (defn test-try-else-return []
"NATIVE: test that we can return from the `else` clause of a `try`" "NATIVE: test that we can return from the `else` clause of a `try`"
; https://github.com/hylang/hy/issues/798 ; https://github.com/hylang/hy/issues/798
(assert (= "ef" ((fn [] (assert (= "ef" ((fn []
(try (+ "a" "b") (try (+ "a" "b")
(except [NameError] (+ "c" "d")) (except [NameError] (+ "c" "d"))
(else (+ "e" "f"))))))) (else (+ "e" "f")))))))
(setv foo (setv foo
(try (+ "A" "B") (try (+ "A" "B")
(except [NameError] (+ "C" "D")) (except [NameError] (+ "C" "D"))
(else (+ "E" "F")))) (else (+ "E" "F"))))
(assert (= foo "EF")))) (assert (= foo "EF"))
; Check that the lvalue isn't assigned in the main `try` body
; there's an `else`.
(setv x 1)
(setv y 0)
(setv x
(try (+ "G" "H")
(except [NameError] (+ "I" "J"))
(else
(setv y 1)
(assert (= x 1))
(+ "K" "L"))))
(assert (= x "KL"))
(assert (= y 1)))
(defn test-require [] (defn test-require []
"NATIVE: test requiring macros from python code" "NATIVE: test requiring macros from python code"