# 错误处理

> 原文： [https://www.fuzzingbook.org/html/ExpectError.html](https://www.fuzzingbook.org/html/ExpectError.html)

本笔记本中的代码有助于处理错误。 通常，笔记本代码中的错误会导致代码执行停止； 而笔记本代码中的无限循环会导致笔记本无休止地运行。 本笔记本提供了两个类来帮助解决这些问题。

**前提条件**

*   该笔记本需要对Python的高级概念有一些了解，特别是
    *   类
    *   Python `with`语句
    *   追踪
    *   测量时间
    *   例外情况

## 内容提要

要使用本章中提供的代码来[，请编写](Importing.html)

```py
>>> from [fuzzingbook.ExpectError](ExpectError.html) import <identifier>

```

然后利用以下功能。

`ExpectError`类允许您捕获和报告异常，但可以恢复执行。 这在笔记本中很有用，因为它们通常会在引发异常后立即中断执行。 它的典型用法是与`with`子句结合使用：

```py
>>> with ExpectError():
>>>     x = 1 / 0
Traceback (most recent call last):
  File "<ipython-input-13-264328004f25>", line 2, in <module>
    x = 1 / 0
ZeroDivisionError: division by zero (expected)

```

`ExpectTimeout`类允许您在指定的时间后中断执行。 这对于中断可能永远运行的代码很有用。

```py
>>> with ExpectTimeout(5):
>>>     long_running_test()
Start
0 seconds have passed
1 seconds have passed
2 seconds have passed
3 seconds have passed

Traceback (most recent call last):
  File "<ipython-input-14-7e5136e65261>", line 2, in <module>
    long_running_test()
  File "<ipython-input-10-8d0f8e53f106>", line 5, in long_running_test
    print(i, "seconds have passed")
  File "<ipython-input-10-8d0f8e53f106>", line 5, in long_running_test
    print(i, "seconds have passed")
  File "<ipython-input-9-a28a583f0630>", line 16, in check_time
    raise TimeoutError
TimeoutError (expected)

```

异常和相关的回溯将显示为错误消息。 如果您不想这样做，请使用以下关键字选项：

*   可以将`print_traceback`（默认为True）设置为`False`，以避免打印回溯
*   可以将`mute`（默认为False）设置为`True`，以完全避免任何输出。

## 捕获错误

类`ExpectError`允许表达某些代码产生异常。 典型用法如下：

```py
from [ExpectError](ExpectError.html) import ExpectError

with ExpectError():
    function_that_is_supposed_to_fail()

```

如果发生异常，则以标准错误打印； 然而，执行仍在继续。

```py
import [fuzzingbook_utils](https://github.com/uds-se/fuzzingbook/tree/master/notebooks/fuzzingbook_utils)

```

```py
import [traceback](https://docs.python.org/3/library/traceback.html)
import [sys](https://docs.python.org/3/library/sys.html)

```

```py
class ExpectError(object):
    def __init__(self, print_traceback=True, mute=False):
        self.print_traceback = print_traceback
        self.mute = mute

    # Begin of `with` block
    def __enter__(self):
        return self

    # End of `with` block
    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is None:
            # No exception
            return

        # An exception occurred
        if self.print_traceback:
            lines = ''.join(
                traceback.format_exception(
                    exc_type,
                    exc_value,
                    tb)).strip()
        else:
            lines = traceback.format_exception_only(
                exc_type, exc_value)[-1].strip()

        if not self.mute:
            print(lines, "(expected)", file=sys.stderr)
        return True  # Ignore it

```

这是一个例子：

```py
def fail_test():
    # Trigger an exception
    x = 1 / 0

```

```py
with ExpectError():
    fail_test()

```

```py
Traceback (most recent call last):
  File "<ipython-input-5-67c629a2a842>", line 2, in <module>
    fail_test()
  File "<ipython-input-4-2e8a6dbc7b2c>", line 3, in fail_test
    x = 1 / 0
ZeroDivisionError: division by zero (expected)

```

```py
with ExpectError(print_traceback=False):
    fail_test()

```

```py
ZeroDivisionError: division by zero (expected)

```

## 捕获超时

类`ExpectTimeout(seconds)`允许表达某些代码可能运行很长时间或无限长的时间。 因此，执行将在`seconds`秒后中断。 典型用法如下：

```py
from [ExpectError](ExpectError.html) import ExpectTimeout

with ExpectTimeout(2) as t:
    function_that_is_supposed_to_hang()

```

如果发生异常，则以标准错误打印（如`ExpectError`一样）； 然而，执行仍在继续。

如果需要取消`with`块内的超时，则`t.cancel()`可以解决问题。

该实现使用`sys.settrace()`，因为这似乎是实现超时的最可移植的方法。 但是，它不是很有效。 而且，它仅适用于Python代码的单独几行，不会中断长时间运行的系统功能。

```py
import [sys](https://docs.python.org/3/library/sys.html)
import [time](https://docs.python.org/3/library/time.html)

```

```py
try:
    # Should be defined in Python 3
    x = TimeoutError
except:
    # For Python 2
    class TimeoutError(Exception):
        def __init__(self, value="Timeout"):
            self.value = value

        def __str__(self):
            return repr(self.value)

```

```py
class ExpectTimeout(object):
    def __init__(self, seconds, print_traceback=True, mute=False):
        self.seconds_before_timeout = seconds
        self.original_trace_function = None
        self.end_time = None
        self.print_traceback = print_traceback
        self.mute = mute

    # Tracing function
    def check_time(self, frame, event, arg):
        if self.original_trace_function is not None:
            self.original_trace_function(frame, event, arg)

        current_time = time.time()
        if current_time >= self.end_time:
            raise TimeoutError

        return self.check_time

    # Begin of `with` block
    def __enter__(self):
        start_time = time.time()
        self.end_time = start_time + self.seconds_before_timeout

        self.original_trace_function = sys.gettrace()
        sys.settrace(self.check_time)
        return self

    # End of `with` block
    def __exit__(self, exc_type, exc_value, tb):
        self.cancel()

        if exc_type is None:
            return

        # An exception occurred
        if self.print_traceback:
            lines = ''.join(
                traceback.format_exception(
                    exc_type,
                    exc_value,
                    tb)).strip()
        else:
            lines = traceback.format_exception_only(
                exc_type, exc_value)[-1].strip()

        if not self.mute:
            print(lines, "(expected)", file=sys.stderr)
        return True  # Ignore it

    def cancel(self):
        sys.settrace(self.original_trace_function)

```

Here's an example:

```py
def long_running_test():
    print("Start")
    for i in range(10):
        time.sleep(1)
        print(i, "seconds have passed")
    print("End")

```

```py
with ExpectTimeout(5, print_traceback=False):
    long_running_test()

```

```py
Start
0 seconds have passed
1 seconds have passed
2 seconds have passed
3 seconds have passed

```

```py
TimeoutError (expected)

```

请注意，可以嵌套多个超时。

```py
with ExpectTimeout(5):
    with ExpectTimeout(3):
        long_running_test()
    long_running_test()

```

```py
Start
0 seconds have passed
1 seconds have passed

```

```py
Traceback (most recent call last):
  File "<ipython-input-12-d87d5d50b60e>", line 3, in <module>
    long_running_test()
  File "<ipython-input-10-8d0f8e53f106>", line 5, in long_running_test
    print(i, "seconds have passed")
  File "<ipython-input-10-8d0f8e53f106>", line 5, in long_running_test
    print(i, "seconds have passed")
  File "<ipython-input-9-a28a583f0630>", line 16, in check_time
    raise TimeoutError
TimeoutError (expected)

```

```py
Start
0 seconds have passed

```

```py
Traceback (most recent call last):
  File "<ipython-input-12-d87d5d50b60e>", line 4, in <module>
    long_running_test()
  File "<ipython-input-10-8d0f8e53f106>", line 5, in long_running_test
    print(i, "seconds have passed")
  File "<ipython-input-10-8d0f8e53f106>", line 5, in long_running_test
    print(i, "seconds have passed")
  File "<ipython-input-9-a28a583f0630>", line 16, in check_time
    raise TimeoutError
TimeoutError (expected)

```

就是这样-伙计们-尽情享受吧！

>