You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

785 lines
21 KiB

{
"cells": [
{
"cell_type": "markdown",
"id": "radical-beast",
"metadata": {},
"source": [
"<!--\n",
"title: Debugging Principles and Techniques\n",
"type: lesson\n",
"duration: \"00:40\"\n",
"creator: Brandi Butler\n",
"-->\n",
"\n",
"<h1>Debugging Principles and Techniques</h1>\n",
"\n",
"<!--\n",
"\n",
"\n",
"## Overview\n",
"This lesson is meant to give students the skills needed to debug their own code. There is a mix of demo, You Do, and We Do to keep it fresh while running through many common errors. We throw a lot of errors at them very quickly - the idea is to get them comfortable recognizing errors and solving them, not to memorize them.\n",
"\n",
"Then, it guides students in how to use `Try`-`Except` blocks when errors are expected/unavoidable. Last, some time is spent teaching students how to handle situations that are problematic, but don't usually throw errors specifically, including endless loops, off-by-one errors, and logical errors.\n",
"\n",
"\n",
"#### Helpful Table of Errors\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",
"## Learning Objectives\n",
"In this lesson, students will:\n",
"\n",
"* Troubleshoot common types of errors.\n",
"* Implement basic exception mitigation.\n",
"* Troubleshoot logic errors.\n",
"\n",
"## Duration\n",
"40 minutes\n",
"\n",
"## Suggested Agenda\n",
"\n",
"| Time | Activity |\n",
"| --- | --- |\n",
"| 0:00 - 0:03 | Welcome |\n",
"| 0:04 - 0:16 | Recognizing Errors |\n",
"| 0:17 - 0:23 | Exception Handling |\n",
"| 0:24 - 0:37 | Untyped Errors |\n",
"| 0:38 - 0:40 | Summary |\n",
"\n",
"\n",
"## In Class: Materials\n",
"- Projector\n",
"- Internet connection\n",
"- Python3\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",
"<!-- ![](https://s3.amazonaws.com/ga-instruction/assets/python-fundamentals/ZeroDivisionError.png) -->"
]
},
{
"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",
"<!-- ![](https://s3.amazonaws.com/ga-instruction/assets/python-fundamentals/ZeroDivisionError.png) -->\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",
"</aside>\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": [
"</aside>\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": [
"</aside>\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": [
"</aside>\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": [
"</aside>\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",
"</aside>\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
}