{ "cells": [ { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "# Error Handling\n", "\n", "The code in this notebook helps with handling errors. Normally, an error in notebook code causes the execution of the code to stop; while an infinite loop in notebook code causes the notebook to run without end. This notebook provides two classes to help address these concerns." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "**Prerequisites**\n", "\n", "* This notebook needs some understanding on advanced concepts in Python, notably \n", " * classes\n", " * the Python `with` statement\n", " * tracing\n", " * measuring time\n", " * exceptions" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Catching Errors\n", "\n", "The class `ExpectError` allows to express that some code produces an exception. A typical usage looks as follows:\n", "\n", "```Python\n", "from ExpectError import ExpectError\n", "\n", "with ExpectError():\n", " function_that_is_supposed_to_fail()\n", "```\n", "\n", "If an exception occurs, it is printed on standard error; yet, execution continues." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import fuzzingbook_utils" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import traceback\n", "import sys" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class ExpectError(object):\n", " def __init__(self, print_traceback=True, mute=False):\n", " self.print_traceback = print_traceback\n", " self.mute = mute\n", "\n", " # Begin of `with` block\n", " def __enter__(self):\n", " return self\n", "\n", " # End of `with` block\n", " def __exit__(self, exc_type, exc_value, tb):\n", " if exc_type is None:\n", " # No exception\n", " return\n", "\n", " # An exception occurred\n", " if self.print_traceback:\n", " lines = ''.join(\n", " traceback.format_exception(\n", " exc_type,\n", " exc_value,\n", " tb)).strip()\n", " else:\n", " lines = traceback.format_exception_only(\n", " exc_type, exc_value)[-1].strip()\n", "\n", " if not self.mute:\n", " print(lines, \"(expected)\", file=sys.stderr)\n", " return True # Ignore it" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Here's an example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def fail_test():\n", " # Trigger an exception\n", " x = 1 / 0" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "with ExpectError():\n", " fail_test()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "with ExpectError(print_traceback=False):\n", " fail_test()" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Catching Timeouts\n", "\n", "The class `ExpectTimeout(seconds)` allows to express that some code may run for a long or infinite time; execution is thus interrupted after `seconds` seconds. A typical usage looks as follows:\n", "\n", "```Python\n", "from ExpectError import ExpectTimeout\n", "\n", "with ExpectTimeout(2) as t:\n", " function_that_is_supposed_to_hang()\n", "```\n", "\n", "If an exception occurs, it is printed on standard error (as with `ExpectError`); yet, execution continues.\n", "\n", "Should there be a need to cancel the timeout within the `with` block, `t.cancel()` will do the trick.\n", "\n", "The implementation uses `sys.settrace()`, as this seems to be the most portable way to implement timeouts. It is not very efficient, though. Also, it only works on individual lines of Python code and will not interrupt a long-running system function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import sys\n", "import time" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "try:\n", " # Should be defined in Python 3\n", " x = TimeoutError\n", "except:\n", " # For Python 2\n", " class TimeoutError(Exception):\n", " def __init__(self, value=\"Timeout\"):\n", " self.value = value\n", "\n", " def __str__(self):\n", " return repr(self.value)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class ExpectTimeout(object):\n", " def __init__(self, seconds, print_traceback=True, mute=False):\n", " self.seconds_before_timeout = seconds\n", " self.original_trace_function = None\n", " self.end_time = None\n", " self.print_traceback = print_traceback\n", " self.mute = mute\n", "\n", " # Tracing function\n", " def check_time(self, frame, event, arg):\n", " if self.original_trace_function is not None:\n", " self.original_trace_function(frame, event, arg)\n", "\n", " current_time = time.time()\n", " if current_time >= self.end_time:\n", " raise TimeoutError\n", "\n", " return self.check_time\n", "\n", " # Begin of `with` block\n", " def __enter__(self):\n", " start_time = time.time()\n", " self.end_time = start_time + self.seconds_before_timeout\n", "\n", " self.original_trace_function = sys.gettrace()\n", " sys.settrace(self.check_time)\n", " return self\n", "\n", " # End of `with` block\n", " def __exit__(self, exc_type, exc_value, tb):\n", " self.cancel()\n", "\n", " if exc_type is None:\n", " return\n", "\n", " # An exception occurred\n", " if self.print_traceback:\n", " lines = ''.join(\n", " traceback.format_exception(\n", " exc_type,\n", " exc_value,\n", " tb)).strip()\n", " else:\n", " lines = traceback.format_exception_only(\n", " exc_type, exc_value)[-1].strip()\n", "\n", " if not self.mute:\n", " print(lines, \"(expected)\", file=sys.stderr)\n", " return True # Ignore it\n", "\n", " def cancel(self):\n", " sys.settrace(self.original_trace_function)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Here's an example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def long_running_test():\n", " print(\"Start\")\n", " for i in range(10):\n", " time.sleep(1)\n", " print(i, \"seconds have passed\")\n", " print(\"End\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "with ExpectTimeout(5, print_traceback=False):\n", " long_running_test()" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "Note that it is possible to nest multiple timeouts." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "with ExpectTimeout(5):\n", " with ExpectTimeout(3):\n", " long_running_test()\n", " long_running_test()" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "That's it, folks – enjoy!" ] } ], "metadata": { "ipub": { "bibliography": "fuzzingbook.bib", "toc": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true }, "toc-autonumbering": false }, "nbformat": 4, "nbformat_minor": 2 }