{ "cells": [ { "cell_type": "markdown", "id": "radical-beast", "metadata": {}, "source": [ "\n", "\n", "

Debugging Principles and Techniques

\n", "\n", "\n", "\n", "\n", "---\n", "\n", "## Lesson Objectives\n", "\n", "*After this lesson, you will be able to...*\n", "\n", "* Troubleshoot common types of errors.\n", "* Implement basic exception mitigation.\n", "* Troubleshoot logic errors.\n", "\n", "---\n", "\n", "## Discussion: Error Messages\n", "\n", "\n", "Have you found a shiny red error message before? What do you think has happened here?\n", "\n", "" ] }, { "cell_type": "code", "execution_count": null, "id": "optimum-terror", "metadata": {}, "outputs": [], "source": [ "x = 5\n", "y = 0\n", "\n", "x / y" ] }, { "cell_type": "markdown", "id": "homeless-module", "metadata": {}, "source": [ "---\n", "\n", "## Making Errors Into Friends\n", "\n", "On the surface, errors are frustrating! However, we'll walk through some common ones. You'll see:\n", "\n", "- Errors sometimes say exactly what's wrong.\n", "- Some errors have very common causes.\n", "- Errors may say exactly how to fix the issue.\n", "- Python errors are very helpful and have clear messages.\n", "\n", "\n", "\n", "---\n", "\n", "## We Do: IndexError\n", "\n", "Let's debug this code together.\n", "\n", "**Protip**: Index errors typically happen when you attempt to access a list index that doesn't exist." ] }, { "cell_type": "code", "execution_count": null, "id": "weekly-listing", "metadata": {}, "outputs": [], "source": [ "race_runners = [\"Yuna\", \"Bill\", \"Hyun\"]\n", "\n", "first_place = race_runners[1]\n", "second_place = race_runners[2]\n", "third_place = race_runners[3]\n", "\n", "print(\"The winners are:\", first_place, second_place, third_place)" ] }, { "cell_type": "markdown", "id": "failing-level", "metadata": {}, "source": [ "\n", "\n", "\n", "---\n", "\n", "## You Do: Fix a NameError\n", "\n", "Directions: Fix it!\n", "\n", "*Hints*:\n", "- Run the code to get the error.\n", "- What kind of error is it? What is the error message?" ] }, { "cell_type": "code", "execution_count": null, "id": "discrete-stone", "metadata": {}, "outputs": [], "source": [ "# Get a number between 2 and 8.\n", "my_nums = 5\n", "\n", "# Print the number\n", "print(my_num)\n" ] }, { "cell_type": "markdown", "id": "sharing-storage", "metadata": {}, "source": [ "- We most commonly get a `NameError` if we use a variable:\n", " * Without defining it.\n", " * *Before* defining it\n", "\n", "---\n", "\n", "## KeyError\n", "\n", "Accessing a key in a dictionary that doesn't exist.\n", "\n", "Commonly caused by:\n", "- A misspelling.\n", "- Mixing uppercase and lowercase.\n", "\n", "The error message tells you exactly what key is missing!" ] }, { "cell_type": "code", "execution_count": null, "id": "quantitative-tournament", "metadata": {}, "outputs": [], "source": [ "my_favorites = {\n", " \"Food\": \"Lobster Rolls\",\n", " \"Song\": \"Bohemian Rhapsody\",\n", " \"Flower\": \"Iris\",\n", " \"Band\": \"Tom Petty & the Heartbreakers\",\n", " \"Color\": \"Green\",\n", " \"Movie\": \"The Princess Bride\",\n", " \"Programming Language\": \"Python\"\n", "}\n", "\n", "# This is okay!\n", "print(\"My favorite color is\", my_favorites[\"Color\"])\n", "\n", "# This is NOT okay! (Case sensitivity!)\n", "print(\"My favorite color is\", my_favorites[\"color\"])\n", "\n", "# This is NOT okay! (Key doesn't exist)\n", "print(\"My favorite restaurant is\", my_favorites[\"Restaurant\"])" ] }, { "cell_type": "markdown", "id": "subtle-population", "metadata": {}, "source": [ "\n", "---\n", "\n", "## AttributeError\n", "\n", "* More general than `KeyError`, but the same idea.\n", "* Accessing an attribute (e.g., function or property) that doesn't exist" ] }, { "cell_type": "code", "execution_count": null, "id": "advisory-composer", "metadata": {}, "outputs": [], "source": [ "class Dog():\n", " def __init__(self, name):\n", " self.name = name\n", "\n", " def bark(self):\n", " print(\"Bark!\")\n", "\n", "# Declare a new dog instance\n", "my_dog = Dog(\"Fido\")\n", "\n", "# Call the bark method\n", "my_dog.bark() # OK!\n", "\n", "# Call the run method\n", "my_dog.run() # AttributeError!" ] }, { "cell_type": "markdown", "id": "domestic-threshold", "metadata": {}, "source": [ "\n", "---\n", "\n", "## Discussion: SyntaxError\n", "\n", "Let's run the code together. What happens? How can we fix it?" ] }, { "cell_type": "code", "execution_count": null, "id": "educational-mongolia", "metadata": {}, "outputs": [], "source": [ "my_age = 13\n", "\n", "if my_age = 18:\n", " print(\"I may vote.\")\n", "else:\n", " print(\"I may not vote.\")" ] }, { "cell_type": "markdown", "id": "qualified-miniature", "metadata": {}, "source": [ "\n", "\n", "---\n", "\n", "## Discussion: TypeError\n", "\n", "`TypeError` and its message tell us:\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "partial-howard", "metadata": {}, "outputs": [], "source": [ "my_num = 5 + \"10\"\n", "print(my_num)\n", "# TypeError: unsupported operand type(s) for +: 'int' and 'str'" ] }, { "cell_type": "markdown", "id": "precious-aberdeen", "metadata": {}, "source": [ "\n", "What do we learn from this error message? Have you learned a way to fix this?\n", "\n", "**Fun Fact**: Some languages, like JavaScript, let this code run (breaking something!).\n", "\n", "---\n", "\n", "## IndentationError\n", "\n", "May be caused by:\n", "\n", "* Not enough indentation\n", "* Mismatched indentation\n", "* Mixing tabs and spaces!" ] }, { "cell_type": "code", "execution_count": null, "id": "coupled-jacob", "metadata": {}, "outputs": [], "source": [ "my_age = 13\n", "\n", "if my_age == 16:\n", " print(\"I may drive.\")\n", "else:\n", "print(\"I may not drive.\")" ] }, { "cell_type": "markdown", "id": "limited-relevance", "metadata": {}, "source": [ "\n", "\n", "---\n", "\n", "## ValueError\n", "\n", "Most commonly caused by trying to convert a bad string into a number.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "threaded-caution", "metadata": {}, "outputs": [], "source": [ "# This is okay!\n", "my_num = int(\"10\")\n", "\n", "# This throws a ValueError\n", "my_num = int(\"Moose\")" ] }, { "cell_type": "markdown", "id": "fantastic-martin", "metadata": {}, "source": [ "\n", "---\n", "\n", "## RuntimeError\n", "\n", "The worst error to see!\n", "\n", "* When no other error type fits.\n", "* You need to rely on the error message content.\n", "* May be used for custom errors.\n", "\n", "**Example**: `RuntimeError` is like if I said to you:\n", "\n", "\n", "\n", "`Please eat the piano`\n", "\n", "\n", "You can understand what's being asked, but can't actually do that!\n", "\n", "---\n", "\n", "## Quick Review\n", "\n", "There are many types of errors in Python!\n", "\n", "Usually, the error has a name or description that says exactly what's wrong.\n", "\n", "Think about `IndentationError` or `IndexError` - what went wrong?\n", "\n", "Sometimes, you'll see `RuntimeError`. Python throws us this if something is broken but it can't say specifically what - like `Please eat the piano`. Revisit your code and see what might have happened.\n", "\n", "**Next Up:** A list of common errors, then ways to prevent errors.\n", "\n", "---\n", "\n", "## List of Common Errors\n", "\n", "This chart's for you to refer to later - don't memorize it now!\n", "\n", "| Error Type | Most Common Cause |\n", "| --- | ---|\n", "| `AttributeError` | Attempting to access a non-existent attribute |\n", "| `KeyError` | Attempting to access a non-existent key in a dict |\n", "| `ImportError` | A module you tried to import doesn't exist |\n", "| `IndexError` | You attempted to access a list element that doesn't exist |\n", "| `IndentationError` | Indenting code in an invalid way |\n", "| `IOError` | Accessing a file that doesn't exist |\n", "| `NameError` | Attempting to use a module you haven't imported/installed |\n", "| `OverflowError` | You made a number larger than the maximum size |\n", "| `RuntimeError` | The error doesn't fit into any other category |\n", "| `SyntaxError` | A typo, such as forgetting a colon |\n", "| `TypeError` | Using two different types in an incompatible way |\n", "| `ValueError` | When you are trying to convert bad keyboard input to a number |\n", "| `ZeroDivisionError` | Dividing By Zero |\n", "\n", "\n", "---\n", "\n", "## Discussion: Throwing Errors\n", "\n", "Sometimes, we might have code that we expect to throw an error.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "interesting-digit", "metadata": {}, "outputs": [], "source": [ "# The user might not give us a number!\n", "my_num = int(input(\"Please give me a number:\"))" ] }, { "cell_type": "markdown", "id": "adult-protection", "metadata": {}, "source": [ "\n", "What if the user types a string like \"Moose\"?\n", "\n", "- This causes a `ValueError` - we'll be trying to make an int out of a string \"Moose\".\n", "- We can anticipate and prepare for it!\n", "\n", "---\n", "\n", "## Try-Except\n", "\n", "A `Try`-`Except` block is the way we can catch errors in Python. We can catch:\n", "\n", "- One error (`except ValueError:`)\n", "- Multiple errors (`except (ValueError, KeyError):`)\n", "- Any/every error (`except:`)\n", "\n", "Always try to specify the error, if possible!" ] }, { "cell_type": "code", "execution_count": null, "id": "northern-petite", "metadata": {}, "outputs": [], "source": [ "my_num = None\n", "\n", "while my_num is None:\n", " try:\n", " my_num = int(input(\"Please give me a number:\"))\n", " except ValueError as err:\n", " print(\"That was not good input, please try again!\")\n", " print(\"Error was\", err)\n", "\n", "print(\"Thanks for typing the number\", my_num)" ] }, { "cell_type": "markdown", "id": "everyday-lingerie", "metadata": {}, "source": [ "\n", "\n", "---\n", "\n", "## Discussion: Switching Gears\n", "\n", "Not every programming error is caught by an error message!\n", "\n", "* Can anyone say what is wrong with this code?\n", "* What might happen if you run it?\n", "\n", "**Do not try to run the below code**.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "veterinary-porcelain", "metadata": {}, "outputs": [], "source": [ "# my_num = 1\n", "\n", "# while my_num < 10:\n", "# print(my_num)\n", "# my_num + 1" ] }, { "cell_type": "markdown", "id": "dietary-amsterdam", "metadata": {}, "source": [ "\n", "---\n", "\n", "## Discussion: Another Infinite Loop\n", "\n", "It's easy to accidentally make an infinite loop. What's the problem here?\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "suburban-socket", "metadata": {}, "outputs": [], "source": [ "# am_hungry = True\n", "# fridge_has_food = True\n", "\n", "# while am_hungry or fridge_has_food:\n", "# print(\"Opening the fridge!\")\n", "# am_hungry = False" ] }, { "cell_type": "markdown", "id": "practical-nashville", "metadata": {}, "source": [ "\n", "---\n", "\n", "## Infinite Infinite Loops!\n", "\n", "Most common infinite loops are a result of:\n", "\n", "* A `while` loop's condition never becomes `False`.\n", "* Forgetting to increment a counter variable.\n", "* Logic inside the loop that restarts the loop.\n", "* Bad logic in a `while` loop's condition (e.g., putting `or` instead of `and`)\n", "\n", "Be careful to check your end conditions!\n", "\n", "If you find your program running endlessly, interrrupt or stop the kernel to stop it!\n", "\n", "\n", "\n", "---\n", "\n", "## Discussion: Logic Error\n", "\n", "\n", "Here, we want to find the average of `8` and `10`. The answer should be `9`, because `8 + 10 == 18`, then `18 / 2 == 9`\n", "\n", "What happened and why?" ] }, { "cell_type": "code", "execution_count": null, "id": "private-victor", "metadata": {}, "outputs": [], "source": [ "x = 8\n", "y = 10\n", "average = x + y / 2\n", "print(average)" ] }, { "cell_type": "markdown", "id": "consolidated-remark", "metadata": {}, "source": [ "\n", "\n", "---\n", "\n", "## Quick Review: Common Errors\n", "\n", "- If you expect an error, use a try/except block:\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "involved-nylon", "metadata": {}, "outputs": [], "source": [ "my_num = None\n", "\n", "while my_num is None:\n", " try:\n", " my_num = int(input(\"Please give me a number:\"))\n", " except ValueError as err:\n", " print(\"That was not good input, please try again!\")\n", " print(\"Error was\", err)\n", "\n", "print(\"Thanks for typing the number\", my_num)" ] }, { "cell_type": "markdown", "id": "collaborative-philippines", "metadata": {}, "source": [ "\n", "- Logic problems are common but won't throw a helpful error. Always check end conditions on your `while` loops!\n", "\n", "---\n", "\n", "## Print Statements for Sanity Checks\n", "\n", "**Pro Tip**: If something is wonky and you don't know why, starting `print`ing.\n", "\n", "* Use `print` statements on each line to peek at the values.\n", "* Remember to remove debugging statements once the problem is solved!\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ongoing-competition", "metadata": {}, "outputs": [], "source": [ "x = 8\n", "y = 10\n", "get_average = x + y / 2\n", "print(\"get_average is\", get_average) # Print out what this equals (it's wrong!)\n", "testing_sum = x + y # To figure out why, break it down.\n", "print(\"testing_sum is\", testing_sum) # Print out each step.\n", "testing_average = testing_average / 2\n", "print(\"testing_average is\", testing_average) # The individual math test works\n", "# We know there must be a problem with the logic in \"average\"" ] }, { "cell_type": "markdown", "id": "theoretical-specific", "metadata": {}, "source": [ "\n", "When your programs become very complex, adding `print` statements will be a great help.\n", "\n", "---\n", "\n", "## You Do: Wrapping it Up\n", "\n", "\n", "Can you fix the code below?" ] }, { "cell_type": "code", "execution_count": null, "id": "upper-delight", "metadata": {}, "outputs": [], "source": [ "new_phone = Phone(5214)\n", "\n", "class Phone:\n", " def __init__(self, phone_number):\n", " self.number = phone_number\n", "\n", " def call(self, other_number):\n", " print(\"Calling from\" self.number, \"to\", other_number)\n", "\n", " def text(self, other_number, msg):\n", " print(\"Sending text from\", self.number, \"to\", other_number\n", " print(msg)\n", "\n", "test_phone = Phone()\n", "test_phone.call(515)\n", "test_phone.text(int(\"text 141\"), \"Hi!\")\"" ] }, { "cell_type": "markdown", "id": "improved-czech", "metadata": {}, "source": [ "\n", "\n", "\n", "---\n", "\n", "## Summary and Q&A\n", "\n", "* Python has many common built-in errors.\n", "* Use `try`-`except` syntax to catch an expected error.\n", "* Logic issues don't throw errors, so be careful!\n", "* Use `print` statements to walk through your code line-by-line.\n", "\n", "---\n", "\n", "## Additional Resources\n", "\n", "* [List of Built-In Errors](https://www.tutorialspoint.com/python/standard_exceptions.htm)\n", "* [Error Flowchart PDF](https://www.dropbox.com/s/cqsxfws52gulkyx/drawing.pdf)\n", "* [Try-Except Documentation](http://www.pythonforbeginners.com/error-handling/python-try-and-except)\n", "- [A deep dive into try/except clauses](https://jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/)\n", "- To get advanced, add [logging](https://fangpenlin.com/posts/2012/08/26/good-logging-practice-in-python/) to your code.\n", "- To get very advanced, include [unit tests](http://www.diveintopython.net/unit_testing/index.html); the [pytest module](http://pythontesting.net/framework/pytest/pytest-introduction/) is great.\n" ] } ], "metadata": { "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.7.6" } }, "nbformat": 4, "nbformat_minor": 5 }