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)))`,
not `(** (** (** a b) c) d)`
* `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
* hy.core.reserved is now hy.extra.reserved

View File

@ -1538,23 +1538,37 @@ or no arguments to re-raise the last ``Exception``.
try
---
The ``try`` form is used to start a ``try`` / ``except`` block. The form is
used as follows:
The ``try`` form is used to catch exceptions (``except``) and run cleanup
actions (``finally``).
.. code-block:: clj
(try
(error-prone-function)
(except [e ZeroDivisionError] (print "Division by zero"))
(else (print "no errors"))
(finally (print "all done")))
(error-prone-function)
(except [ZeroDivisionError]
(print "Division by zero"))
(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
an ``else`` or ``finally`` block. If an error is raised with a matching except
block during the execution of ``error-prone-function``, that ``except`` block
will be executed. If no errors are raised, the ``else`` block is executed. The
``finally`` block will be executed last regardless of whether or not an error
was raised.
The first argument of ``try`` is its body. (To put more than one form in the
body, use ``do``.) Then comes any number of ``except`` clauses, then optionally
an ``else`` clause, then optionally a ``finally`` clause. If an exception is
raised with a matching ``except`` clause during the execution of the body, that
``except`` clause will be executed. If no exceptions are raised, the ``else``
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

View File

@ -786,13 +786,6 @@ class HyASTCompiler(object):
returnable = Result(expr=expr_name, temp_variables=[expr_name, name],
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):
raise HyTypeError(expr, "Empty list not allowed in `try'")
handler_results = Result()
@ -803,13 +796,22 @@ class HyASTCompiler(object):
handlers.append(handler_results.stmts.pop())
orelse = []
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 = []
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[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'")
# Using (else) without (except) is verboten!
@ -831,6 +833,14 @@ class HyASTCompiler(object):
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:
# Python 3.3 features a merge of TryExcept+TryFinally into Try.
return ret + ast.Try(
@ -867,11 +877,6 @@ class HyASTCompiler(object):
body=body,
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")
def magic_internal_form(self, expr):
raise HyTypeError(expr,

View File

@ -1174,20 +1174,34 @@
(assert (= foo 4)))
#@(pytest.mark.xfail
(defn test-try-else-return []
"NATIVE: test that we can return from the `else` clause of a `try`"
; https://github.com/hylang/hy/issues/798
(assert (= "ef" ((fn []
(try (+ "a" "b")
(except [NameError] (+ "c" "d"))
(else (+ "e" "f")))))))
(setv foo
(try (+ "A" "B")
(except [NameError] (+ "C" "D"))
(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 []
"NATIVE: test requiring macros from python code"