{
 "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
}
