Add description and examples for Exceptions
authorJon Speicher <jon.speicher@gmail.com>
Sat, 27 Jul 2013 23:52:44 +0000 (19:52 -0400)
committerW. Trevor King <wking@tremily.us>
Sat, 9 Nov 2013 18:27:52 +0000 (10:27 -0800)
python/sw_engineering/SoftwareEngineering.ipynb

index bdfd76321e981cbfa5e70d9e0b68f85e04d950ff..de261006d3785461845eb686f87b051cfa5d0402 100644 (file)
@@ -21,7 +21,8 @@
       "* Refactoring\n",
       "* Testing\n",
       "* Nose\n",
-      "* Test-driven development"
+      "* Test-driven development\n",
+      "* Exceptions"
      ]
     },
     {
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 1
+     "prompt_number": 4
     },
     {
      "cell_type": "markdown",
      "outputs": [
       {
        "output_type": "pyout",
-       "prompt_number": 2,
+       "prompt_number": 5,
        "text": [
         "117"
        ]
       }
      ],
-     "prompt_number": 2
+     "prompt_number": 5
     },
     {
      "cell_type": "markdown",
        ]
       }
      ],
-     "prompt_number": 3
+     "prompt_number": 6
     },
     {
      "cell_type": "markdown",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 4
+     "prompt_number": 7
     },
     {
      "cell_type": "markdown",
        ]
       }
      ],
-     "prompt_number": 5
+     "prompt_number": 8
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "output_type": "pyout",
-       "prompt_number": 6,
+       "prompt_number": 9,
        "text": [
         "(['2011-04-22', '2011-04-23', '2011-04-23', '2011-04-23', '2011-04-23'],\n",
         " ['21:06', '14:12', '10:24', '20:08', '18:46'],\n",
        ]
       }
      ],
-     "prompt_number": 6
+     "prompt_number": 9
     },
     {
      "cell_type": "markdown",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 7
+     "prompt_number": 10
     },
     {
      "cell_type": "markdown",
      "outputs": [
       {
        "output_type": "pyout",
-       "prompt_number": 8,
+       "prompt_number": 11,
        "text": [
         "117"
        ]
       }
      ],
-     "prompt_number": 8
+     "prompt_number": 11
     },
     {
      "cell_type": "markdown",
        ]
       }
      ],
-     "prompt_number": 9
+     "prompt_number": 12
     },
     {
      "cell_type": "markdown",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 10
+     "prompt_number": 13
     },
     {
      "cell_type": "markdown",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 12
+     "prompt_number": 14
     },
     {
      "cell_type": "markdown",
        "output_type": "pyerr",
        "traceback": [
         "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
-        "\u001b[0;32m<ipython-input-13-2d797333ff4c>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
-        "\u001b[0;32m<ipython-input-13-2d797333ff4c>\u001b[0m in \u001b[0;36mtest_add_two_plus_two_equals_four\u001b[0;34m()\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m     \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+        "\u001b[0;32m<ipython-input-15-2d797333ff4c>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;32m<ipython-input-15-2d797333ff4c>\u001b[0m in \u001b[0;36mtest_add_two_plus_two_equals_four\u001b[0;34m()\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m     \u001b[0;32massert\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0madd_two_plus_two\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"2 + 2 didn't equal 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0mtest_add_two_plus_two_equals_four\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
         "\u001b[0;31mAssertionError\u001b[0m: 2 + 2 didn't equal 4"
        ]
       }
      ],
-     "prompt_number": 13
+     "prompt_number": 15
     },
     {
      "cell_type": "markdown",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 14
+     "prompt_number": 16
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "html": [
-        "<div id=\"ipython_nose_d38e10d9d55741bd9baa798e352d80ea\"></div>"
+        "<div id=\"ipython_nose_5e87717b84c447ad93143bde900e1715\"></div>"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea = $(\"#ipython_nose_d38e10d9d55741bd9baa798e352d80ea\");"
+        "document.ipython_nose_5e87717b84c447ad93143bde900e1715 = $(\"#ipython_nose_5e87717b84c447ad93143bde900e1715\");"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_5e87717b84c447ad93143bde900e1715.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea.append($(\"<span>F</span>\"));"
+        "document.ipython_nose_5e87717b84c447ad93143bde900e1715.append($(\"<span>E</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "delete document.ipython_nose_d38e10d9d55741bd9baa798e352d80ea;"
+        "document.ipython_nose_5e87717b84c447ad93143bde900e1715.append($(\"<span>F</span>\"));"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "delete document.ipython_nose_5e87717b84c447ad93143bde900e1715;"
        ],
        "output_type": "display_data"
       },
         "    </script>\n",
         "    \n",
         "    <div class=\"noseresults\">\n",
-        "      <div class=\"nosebar fail leftmost\" style=\"width: 50%\">\n",
+        "      <div class=\"nosebar fail leftmost\" style=\"width: 66%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
         "      <div class=\"nosebar skip\" style=\"width: 0%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      <div class=\"nosebar pass rightmost\" style=\"width: 50%\">\n",
+        "      <div class=\"nosebar pass rightmost\" style=\"width: 34%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      1/2 tests passed; 1 failed\n",
+        "      1/3 tests passed; 2 failed\n",
+        "    </div>\n",
+        "    \n",
+        "    <div class=\"nosefailure\">\n",
+        "        <div class=\"nosefailbanner\">\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_empty_function_raises_value_error</span>\n",
+        "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
+        "        </div>\n",
+        "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"&lt;<a href=\"#ipython-input-2-bc417f346267\">ipython-input-2-bc417f346267</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([2])\")\n",
+        "                .attr(\"id\", \"ipython-input-2-bc417f346267\");\n",
+        "            </script>&gt;\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "NameError: global name 'mean' is not defined\n",
+        "</pre>\n",
         "    </div>\n",
         "    \n",
         "    <div class=\"nosefailure\">\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"&lt;<a href=\"#ipython-input-13-2d797333ff4c\">ipython-input-13-2d797333ff4c</a><script>\n",
-        "            $(\"div.prompt.input_prompt:contains([13])\")\n",
-        "                .attr(\"id\", \"ipython-input-13-2d797333ff4c\");\n",
+        "  File \"&lt;<a href=\"#ipython-input-15-2d797333ff4c\">ipython-input-15-2d797333ff4c</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([15])\")\n",
+        "                .attr(\"id\", \"ipython-input-15-2d797333ff4c\");\n",
         "            </script>&gt;\", line 5, in test_add_two_plus_two_equals_four\n",
         "    assert 4 == add_two_plus_two(), \"2 + 2 didn't equal 4\"\n",
         "AssertionError: 2 + 2 didn't equal 4\n",
         "    "
        ],
        "output_type": "pyout",
-       "prompt_number": 15,
+       "prompt_number": 18,
        "text": [
-        "1/2 tests passed; 1 failed\n",
+        "1/3 tests passed; 2 failed\n",
+        "========\n",
+        "__main__.test_mean_of_empty_function_raises_value_error\n",
+        "========\n",
+        "Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"<ipython-input-2-bc417f346267>\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "NameError: global name 'mean' is not defined\n",
+        "\n",
         "========\n",
         "__main__.test_add_two_plus_two_equals_four\n",
         "========\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"<ipython-input-13-2d797333ff4c>\", line 5, in test_add_two_plus_two_equals_four\n",
+        "  File \"<ipython-input-15-2d797333ff4c>\", line 5, in test_add_two_plus_two_equals_four\n",
         "    assert 4 == add_two_plus_two(), \"2 + 2 didn't equal 4\"\n",
         "AssertionError: 2 + 2 didn't equal 4\n",
         "\n"
        ]
       }
      ],
-     "prompt_number": 15
+     "prompt_number": 18
     },
     {
      "cell_type": "markdown",
        ]
       }
      ],
-     "prompt_number": 16
+     "prompt_number": 19
     },
     {
      "cell_type": "markdown",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 17
+     "prompt_number": 20
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "html": [
-        "<div id=\"ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0\"></div>"
+        "<div id=\"ipython_nose_510c086cad7b476795575f3b317d1255\"></div>"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_510c086cad7b476795575f3b317d1255 = $(\"#ipython_nose_510c086cad7b476795575f3b317d1255\");"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0 = $(\"#ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0\");"
+        "document.ipython_nose_510c086cad7b476795575f3b317d1255.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_510c086cad7b476795575f3b317d1255.append($(\"<span>E</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_510c086cad7b476795575f3b317d1255.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "delete document.ipython_nose_b902caf7914c4fb3b2efaf1c540ce8e0;"
+        "delete document.ipython_nose_510c086cad7b476795575f3b317d1255;"
        ],
        "output_type": "display_data"
       },
         "    </script>\n",
         "    \n",
         "    <div class=\"noseresults\">\n",
-        "      <div class=\"nosebar fail leftmost\" style=\"width: 0%\">\n",
+        "      <div class=\"nosebar fail leftmost\" style=\"width: 33%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
         "      <div class=\"nosebar skip\" style=\"width: 0%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      <div class=\"nosebar pass rightmost\" style=\"width: 100%\">\n",
+        "      <div class=\"nosebar pass rightmost\" style=\"width: 67%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      2/2 tests passed\n",
+        "      2/3 tests passed; 1 failed\n",
+        "    </div>\n",
+        "    \n",
+        "    <div class=\"nosefailure\">\n",
+        "        <div class=\"nosefailbanner\">\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_empty_function_raises_value_error</span>\n",
+        "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
+        "        </div>\n",
+        "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"&lt;<a href=\"#ipython-input-2-bc417f346267\">ipython-input-2-bc417f346267</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([2])\")\n",
+        "                .attr(\"id\", \"ipython-input-2-bc417f346267\");\n",
+        "            </script>&gt;\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "NameError: global name 'mean' is not defined\n",
+        "</pre>\n",
         "    </div>\n",
         "    "
        ],
        "output_type": "pyout",
-       "prompt_number": 18,
+       "prompt_number": 21,
        "text": [
-        "2/2 tests passed\n"
+        "2/3 tests passed; 1 failed\n",
+        "========\n",
+        "__main__.test_mean_of_empty_function_raises_value_error\n",
+        "========\n",
+        "Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"<ipython-input-2-bc417f346267>\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "NameError: global name 'mean' is not defined\n",
+        "\n"
        ]
       }
      ],
-     "prompt_number": 18
+     "prompt_number": 21
     },
     {
      "cell_type": "markdown",
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "def test_mean_of_zero():\n",
+      "def test_mean_of_zero_is_zero():\n",
       "    assert 0 == mean([0])"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 19
+     "prompt_number": 22
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "***\n",
+      "**Aside: Test names**\n",
+      "\n",
+      "Naming tests well will help you to quickly resolve bugs that may crop up when running your tests. Imagine that you have 100 tests for the `mean` function. When you run your test suite, would you rather see a failure in `test_mean37` or in `test_mean_of_zero_is_zero`?\n",
+      "***"
+     ]
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "html": [
-        "<div id=\"ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197\"></div>"
+        "<div id=\"ipython_nose_880d0b5c63014ac78937ccad69cf89d2\"></div>"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_880d0b5c63014ac78937ccad69cf89d2 = $(\"#ipython_nose_880d0b5c63014ac78937ccad69cf89d2\");"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197 = $(\"#ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197\");"
+        "document.ipython_nose_880d0b5c63014ac78937ccad69cf89d2.append($(\"<span>E</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197.append($(\"<span>E</span>\"));"
+        "document.ipython_nose_880d0b5c63014ac78937ccad69cf89d2.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_880d0b5c63014ac78937ccad69cf89d2.append($(\"<span>E</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_880d0b5c63014ac78937ccad69cf89d2.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "delete document.ipython_nose_a3c3d168eb2c4a988da0f4a492cbd197;"
+        "delete document.ipython_nose_880d0b5c63014ac78937ccad69cf89d2;"
        ],
        "output_type": "display_data"
       },
         "    </script>\n",
         "    \n",
         "    <div class=\"noseresults\">\n",
-        "      <div class=\"nosebar fail leftmost\" style=\"width: 33%\">\n",
+        "      <div class=\"nosebar fail leftmost\" style=\"width: 50%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
         "      <div class=\"nosebar skip\" style=\"width: 0%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      <div class=\"nosebar pass rightmost\" style=\"width: 67%\">\n",
+        "      <div class=\"nosebar pass rightmost\" style=\"width: 50%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      2/3 tests passed; 1 failed\n",
+        "      2/4 tests passed; 2 failed\n",
         "    </div>\n",
         "    \n",
         "    <div class=\"nosefailure\">\n",
         "        <div class=\"nosefailbanner\">\n",
-        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_zero</span>\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_zero_is_zero</span>\n",
         "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
         "        </div>\n",
         "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"&lt;<a href=\"#ipython-input-19-481e1c58db22\">ipython-input-19-481e1c58db22</a><script>\n",
-        "            $(\"div.prompt.input_prompt:contains([19])\")\n",
-        "                .attr(\"id\", \"ipython-input-19-481e1c58db22\");\n",
-        "            </script>&gt;\", line 2, in test_mean_of_zero\n",
+        "  File \"&lt;<a href=\"#ipython-input-22-ee2ab148c1b0\">ipython-input-22-ee2ab148c1b0</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([22])\")\n",
+        "                .attr(\"id\", \"ipython-input-22-ee2ab148c1b0\");\n",
+        "            </script>&gt;\", line 2, in test_mean_of_zero_is_zero\n",
         "    assert 0 == mean([0])\n",
         "NameError: global name 'mean' is not defined\n",
         "</pre>\n",
         "    </div>\n",
+        "    \n",
+        "    <div class=\"nosefailure\">\n",
+        "        <div class=\"nosefailbanner\">\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_empty_function_raises_value_error</span>\n",
+        "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
+        "        </div>\n",
+        "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"&lt;<a href=\"#ipython-input-2-bc417f346267\">ipython-input-2-bc417f346267</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([2])\")\n",
+        "                .attr(\"id\", \"ipython-input-2-bc417f346267\");\n",
+        "            </script>&gt;\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "NameError: global name 'mean' is not defined\n",
+        "</pre>\n",
+        "    </div>\n",
         "    "
        ],
        "output_type": "pyout",
-       "prompt_number": 20,
+       "prompt_number": 23,
        "text": [
-        "2/3 tests passed; 1 failed\n",
+        "2/4 tests passed; 2 failed\n",
         "========\n",
-        "__main__.test_mean_of_zero\n",
+        "__main__.test_mean_of_zero_is_zero\n",
         "========\n",
         "Traceback (most recent call last):\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"<ipython-input-19-481e1c58db22>\", line 2, in test_mean_of_zero\n",
+        "  File \"<ipython-input-22-ee2ab148c1b0>\", line 2, in test_mean_of_zero_is_zero\n",
         "    assert 0 == mean([0])\n",
         "NameError: global name 'mean' is not defined\n",
+        "\n",
+        "========\n",
+        "__main__.test_mean_of_empty_function_raises_value_error\n",
+        "========\n",
+        "Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"<ipython-input-2-bc417f346267>\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "NameError: global name 'mean' is not defined\n",
         "\n"
        ]
       }
      ],
-     "prompt_number": 20
+     "prompt_number": 23
     },
     {
      "cell_type": "code",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 21
+     "prompt_number": 24
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "html": [
-        "<div id=\"ipython_nose_bbb38260de4a452086abb22e7db22dfd\"></div>"
+        "<div id=\"ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9\"></div>"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd = $(\"#ipython_nose_bbb38260de4a452086abb22e7db22dfd\");"
+        "document.ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9 = $(\"#ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9\");"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd.append($(\"<span>E</span>\"));"
+        "document.ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9.append($(\"<span>E</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_bbb38260de4a452086abb22e7db22dfd.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9.append($(\"<span>E</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "delete document.ipython_nose_bbb38260de4a452086abb22e7db22dfd;"
+        "document.ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9.append($(\"<span>.</span>\"));"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "delete document.ipython_nose_c2735c7e3d0c4677b88a8a5cd4cdf4a9;"
        ],
        "output_type": "display_data"
       },
         "    </script>\n",
         "    \n",
         "    <div class=\"noseresults\">\n",
-        "      <div class=\"nosebar fail leftmost\" style=\"width: 33%\">\n",
+        "      <div class=\"nosebar fail leftmost\" style=\"width: 50%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
         "      <div class=\"nosebar skip\" style=\"width: 0%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      <div class=\"nosebar pass rightmost\" style=\"width: 67%\">\n",
+        "      <div class=\"nosebar pass rightmost\" style=\"width: 50%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      2/3 tests passed; 1 failed\n",
+        "      2/4 tests passed; 2 failed\n",
         "    </div>\n",
         "    \n",
         "    <div class=\"nosefailure\">\n",
         "        <div class=\"nosefailbanner\">\n",
-        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_zero</span>\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_zero_is_zero</span>\n",
         "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
         "        </div>\n",
         "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"&lt;<a href=\"#ipython-input-19-481e1c58db22\">ipython-input-19-481e1c58db22</a><script>\n",
-        "            $(\"div.prompt.input_prompt:contains([19])\")\n",
-        "                .attr(\"id\", \"ipython-input-19-481e1c58db22\");\n",
-        "            </script>&gt;\", line 2, in test_mean_of_zero\n",
+        "  File \"&lt;<a href=\"#ipython-input-22-ee2ab148c1b0\">ipython-input-22-ee2ab148c1b0</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([22])\")\n",
+        "                .attr(\"id\", \"ipython-input-22-ee2ab148c1b0\");\n",
+        "            </script>&gt;\", line 2, in test_mean_of_zero_is_zero\n",
         "    assert 0 == mean([0])\n",
         "TypeError: mean() takes no arguments (1 given)\n",
         "</pre>\n",
         "    </div>\n",
+        "    \n",
+        "    <div class=\"nosefailure\">\n",
+        "        <div class=\"nosefailbanner\">\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_empty_function_raises_value_error</span>\n",
+        "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
+        "        </div>\n",
+        "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"&lt;<a href=\"#ipython-input-2-bc417f346267\">ipython-input-2-bc417f346267</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([2])\")\n",
+        "                .attr(\"id\", \"ipython-input-2-bc417f346267\");\n",
+        "            </script>&gt;\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "TypeError: mean() takes no arguments (1 given)\n",
+        "</pre>\n",
+        "    </div>\n",
         "    "
        ],
        "output_type": "pyout",
-       "prompt_number": 22,
+       "prompt_number": 25,
        "text": [
-        "2/3 tests passed; 1 failed\n",
+        "2/4 tests passed; 2 failed\n",
         "========\n",
-        "__main__.test_mean_of_zero\n",
+        "__main__.test_mean_of_zero_is_zero\n",
         "========\n",
         "Traceback (most recent call last):\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"<ipython-input-19-481e1c58db22>\", line 2, in test_mean_of_zero\n",
+        "  File \"<ipython-input-22-ee2ab148c1b0>\", line 2, in test_mean_of_zero_is_zero\n",
         "    assert 0 == mean([0])\n",
         "TypeError: mean() takes no arguments (1 given)\n",
+        "\n",
+        "========\n",
+        "__main__.test_mean_of_empty_function_raises_value_error\n",
+        "========\n",
+        "Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"<ipython-input-2-bc417f346267>\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "TypeError: mean() takes no arguments (1 given)\n",
         "\n"
        ]
       }
      ],
-     "prompt_number": 22
+     "prompt_number": 25
     },
     {
      "cell_type": "code",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 23
+     "prompt_number": 26
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "html": [
-        "<div id=\"ipython_nose_183426a0661b43479529049b993ae958\"></div>"
+        "<div id=\"ipython_nose_1e1bd17442294222a80f6cc462ac3176\"></div>"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_1e1bd17442294222a80f6cc462ac3176 = $(\"#ipython_nose_1e1bd17442294222a80f6cc462ac3176\");"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_183426a0661b43479529049b993ae958 = $(\"#ipython_nose_183426a0661b43479529049b993ae958\");"
+        "document.ipython_nose_1e1bd17442294222a80f6cc462ac3176.append($(\"<span>F</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_183426a0661b43479529049b993ae958.append($(\"<span>F</span>\"));"
+        "document.ipython_nose_1e1bd17442294222a80f6cc462ac3176.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_183426a0661b43479529049b993ae958.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_1e1bd17442294222a80f6cc462ac3176.append($(\"<span>F</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_183426a0661b43479529049b993ae958.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_1e1bd17442294222a80f6cc462ac3176.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "delete document.ipython_nose_183426a0661b43479529049b993ae958;"
+        "delete document.ipython_nose_1e1bd17442294222a80f6cc462ac3176;"
        ],
        "output_type": "display_data"
       },
         "    </script>\n",
         "    \n",
         "    <div class=\"noseresults\">\n",
-        "      <div class=\"nosebar fail leftmost\" style=\"width: 33%\">\n",
+        "      <div class=\"nosebar fail leftmost\" style=\"width: 50%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
         "      <div class=\"nosebar skip\" style=\"width: 0%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      <div class=\"nosebar pass rightmost\" style=\"width: 67%\">\n",
+        "      <div class=\"nosebar pass rightmost\" style=\"width: 50%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      2/3 tests passed; 1 failed\n",
+        "      2/4 tests passed; 2 failed\n",
         "    </div>\n",
         "    \n",
         "    <div class=\"nosefailure\">\n",
         "        <div class=\"nosefailbanner\">\n",
-        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_zero</span>\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_zero_is_zero</span>\n",
         "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
         "        </div>\n",
         "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"&lt;<a href=\"#ipython-input-19-481e1c58db22\">ipython-input-19-481e1c58db22</a><script>\n",
-        "            $(\"div.prompt.input_prompt:contains([19])\")\n",
-        "                .attr(\"id\", \"ipython-input-19-481e1c58db22\");\n",
-        "            </script>&gt;\", line 2, in test_mean_of_zero\n",
+        "  File \"&lt;<a href=\"#ipython-input-22-ee2ab148c1b0\">ipython-input-22-ee2ab148c1b0</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([22])\")\n",
+        "                .attr(\"id\", \"ipython-input-22-ee2ab148c1b0\");\n",
+        "            </script>&gt;\", line 2, in test_mean_of_zero_is_zero\n",
         "    assert 0 == mean([0])\n",
         "AssertionError\n",
         "</pre>\n",
         "    </div>\n",
+        "    \n",
+        "    <div class=\"nosefailure\">\n",
+        "        <div class=\"nosefailbanner\">\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_empty_function_raises_value_error</span>\n",
+        "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
+        "        </div>\n",
+        "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 67, in newfunc\n",
+        "    raise AssertionError(message)\n",
+        "AssertionError: test_mean_of_empty_function_raises_value_error() did not raise ValueError\n",
+        "</pre>\n",
+        "    </div>\n",
         "    "
        ],
        "output_type": "pyout",
-       "prompt_number": 24,
+       "prompt_number": 27,
        "text": [
-        "2/3 tests passed; 1 failed\n",
+        "2/4 tests passed; 2 failed\n",
         "========\n",
-        "__main__.test_mean_of_zero\n",
+        "__main__.test_mean_of_zero_is_zero\n",
         "========\n",
         "Traceback (most recent call last):\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
         "    testMethod()\n",
         "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
         "    self.test(*self.arg)\n",
-        "  File \"<ipython-input-19-481e1c58db22>\", line 2, in test_mean_of_zero\n",
+        "  File \"<ipython-input-22-ee2ab148c1b0>\", line 2, in test_mean_of_zero_is_zero\n",
         "    assert 0 == mean([0])\n",
         "AssertionError\n",
+        "\n",
+        "========\n",
+        "__main__.test_mean_of_empty_function_raises_value_error\n",
+        "========\n",
+        "Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 67, in newfunc\n",
+        "    raise AssertionError(message)\n",
+        "AssertionError: test_mean_of_empty_function_raises_value_error() did not raise ValueError\n",
         "\n"
        ]
       }
      ],
-     "prompt_number": 24
+     "prompt_number": 27
     },
     {
      "cell_type": "code",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 25
+     "prompt_number": 28
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "html": [
-        "<div id=\"ipython_nose_6748615aba7a4a3abecfdbad96ac3251\"></div>"
+        "<div id=\"ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493\"></div>"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493 = $(\"#ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493\");"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251 = $(\"#ipython_nose_6748615aba7a4a3abecfdbad96ac3251\");"
+        "document.ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493.append($(\"<span>F</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "delete document.ipython_nose_6748615aba7a4a3abecfdbad96ac3251;"
+        "delete document.ipython_nose_fe9e7f6480d54f919bfa6edad2ce0493;"
        ],
        "output_type": "display_data"
       },
         "    </script>\n",
         "    \n",
         "    <div class=\"noseresults\">\n",
-        "      <div class=\"nosebar fail leftmost\" style=\"width: 0%\">\n",
+        "      <div class=\"nosebar fail leftmost\" style=\"width: 25%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
         "      <div class=\"nosebar skip\" style=\"width: 0%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      <div class=\"nosebar pass rightmost\" style=\"width: 100%\">\n",
+        "      <div class=\"nosebar pass rightmost\" style=\"width: 75%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      3/3 tests passed\n",
+        "      3/4 tests passed; 1 failed\n",
+        "    </div>\n",
+        "    \n",
+        "    <div class=\"nosefailure\">\n",
+        "        <div class=\"nosefailbanner\">\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_empty_function_raises_value_error</span>\n",
+        "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
+        "        </div>\n",
+        "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 67, in newfunc\n",
+        "    raise AssertionError(message)\n",
+        "AssertionError: test_mean_of_empty_function_raises_value_error() did not raise ValueError\n",
+        "</pre>\n",
         "    </div>\n",
         "    "
        ],
        "output_type": "pyout",
-       "prompt_number": 26,
+       "prompt_number": 29,
        "text": [
-        "3/3 tests passed\n"
+        "3/4 tests passed; 1 failed\n",
+        "========\n",
+        "__main__.test_mean_of_empty_function_raises_value_error\n",
+        "========\n",
+        "Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 67, in newfunc\n",
+        "    raise AssertionError(message)\n",
+        "AssertionError: test_mean_of_empty_function_raises_value_error() did not raise ValueError\n",
+        "\n"
        ]
       }
      ],
-     "prompt_number": 26
+     "prompt_number": 29
     },
     {
      "cell_type": "markdown",
      "source": [
       "## Results\n",
       "\n",
-      "You probably wound up with something like this."
+      "When I first wrote this code, I wound up with something like this:"
      ]
     },
     {
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 1
+     "prompt_number": 30
     },
     {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
-      "How good was your testing?"
+      "How good was my testing?"
      ]
     },
     {
      "outputs": [
       {
        "output_type": "pyout",
-       "prompt_number": 2,
+       "prompt_number": 31,
        "text": [
         "0.0"
        ]
       }
      ],
-     "prompt_number": 2
+     "prompt_number": 31
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "output_type": "pyout",
-       "prompt_number": 5,
+       "prompt_number": 32,
        "text": [
         "1.0"
        ]
       }
      ],
-     "prompt_number": 5
+     "prompt_number": 32
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "output_type": "pyout",
-       "prompt_number": 3,
+       "prompt_number": 33,
        "text": [
         "3.0"
        ]
       }
      ],
-     "prompt_number": 3
+     "prompt_number": 33
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "output_type": "pyout",
-       "prompt_number": 4,
+       "prompt_number": 34,
        "text": [
         "4.566666666666666"
        ]
       }
      ],
-     "prompt_number": 4
+     "prompt_number": 34
     },
     {
      "cell_type": "code",
        "output_type": "pyerr",
        "traceback": [
         "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
-        "\u001b[0;32m<ipython-input-6-e9e2222d800d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
-        "\u001b[0;32m<ipython-input-1-16ccb79ef6d8>\u001b[0m in \u001b[0;36mmean\u001b[0;34m(numbers)\u001b[0m\n\u001b[1;32m      4\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0mnumber\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m         \u001b[0mtotal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtotal\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;32m<ipython-input-35-e9e2222d800d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;32m<ipython-input-30-16ccb79ef6d8>\u001b[0m in \u001b[0;36mmean\u001b[0;34m(numbers)\u001b[0m\n\u001b[1;32m      4\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0mnumber\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m         \u001b[0mtotal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtotal\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
         "\u001b[0;31mZeroDivisionError\u001b[0m: float division by zero"
        ]
       }
      ],
-     "prompt_number": 6
+     "prompt_number": 35
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "***\n",
+      "# Exceptions\n",
+      "***\n",
+      "\n",
+      "An empty list has a length of zero. The loop doesn't iterate, so total stays at 0.0, which is what we initialized it to, and the code happily attempts to divide zero by zero, resulting in an error.\n",
+      "\n",
+      "`ZeroDivisionError` is called an \"exception\". Our code can raise exceptions as well, and we can even define our own exceptions if we want. While this exception works perfectly fine, it could be a bit more clear, and I obviously missed a test case, which means I missed implementing a piece of beneficial behavior in my `mean` function. So we need to add a test, and we need to update our `mean` function to die more gracefully in the event of an empty list. We'll start with the test."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import nose.tools\n",
+      "\n",
+      "@nose.tools.raises(ValueError)\n",
+      "def test_mean_of_empty_function_raises_value_error():\n",
+      "    mean([])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 36
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "import nose.tools as nt"
+      "%nose"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [
       {
-       "output_type": "stream",
-       "stream": "stdout",
+       "html": [
+        "<div id=\"ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab\"></div>"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab = $(\"#ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab\");"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab.append($(\"<span>.</span>\"));"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab.append($(\"<span>.</span>\"));"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab.append($(\"<span>E</span>\"));"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "document.ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab.append($(\"<span>.</span>\"));"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "javascript": [
+        "delete document.ipython_nose_5ceb566dc2db4a1cbddaa43327f83fab;"
+       ],
+       "output_type": "display_data"
+      },
+      {
+       "html": [
+        "    <style type=\"text/css\">\n",
+        "        span.nosefailedfunc {\n",
+        "            font-family: monospace;\n",
+        "            font-weight: bold;\n",
+        "        }\n",
+        "        div.noseresults {\n",
+        "            width: 100%;\n",
+        "        }\n",
+        "        div.nosebar {\n",
+        "            float: left;\n",
+        "            padding: 1ex 0px 1ex 0px;\n",
+        "        }\n",
+        "        div.nosebar.fail {\n",
+        "            background: #ff3019; /* Old browsers */\n",
+        "            /* FF3.6+ */\n",
+        "            background: -moz-linear-gradient(top, #ff3019 0%, #cf0404 100%);\n",
+        "            /* Chrome,Safari4+ */\n",
+        "            background: -webkit-gradient(linear, left top, left bottom,\n",
+        "                                         color-stop(0%,#ff3019),\n",
+        "                                         color-stop(100%,#cf0404));\n",
+        "            /* Chrome10+,Safari5.1+ */\n",
+        "            background: -webkit-linear-gradient(top, #ff3019 0%,#cf0404 100%);\n",
+        "            /* Opera 11.10+ */\n",
+        "            background: -o-linear-gradient(top, #ff3019 0%,#cf0404 100%);\n",
+        "            /* IE10+ */\n",
+        "            background: -ms-linear-gradient(top, #ff3019 0%,#cf0404 100%);\n",
+        "            /* W3C */\n",
+        "            background: linear-gradient(to bottom, #ff3019 0%,#cf0404 100%);\n",
+        "        }\n",
+        "        div.nosebar.pass {\n",
+        "            background: #52b152;\n",
+        "            background: -moz-linear-gradient(top, #52b152 1%, #008a00 100%);\n",
+        "            background: -webkit-gradient(linear, left top, left bottom,\n",
+        "                                         color-stop(1%,#52b152),\n",
+        "                                         color-stop(100%,#008a00));\n",
+        "            background: -webkit-linear-gradient(top, #52b152 1%,#008a00 100%);\n",
+        "            background: -o-linear-gradient(top, #52b152 1%,#008a00 100%);\n",
+        "            background: -ms-linear-gradient(top, #52b152 1%,#008a00 100%);\n",
+        "            background: linear-gradient(to bottom, #52b152 1%,#008a00 100%);\n",
+        "        }\n",
+        "        div.nosebar.skip {\n",
+        "            background: #f1e767;\n",
+        "            background: -moz-linear-gradient(top, #f1e767 0%, #feb645 100%);\n",
+        "            background: -webkit-gradient(linear, left top, left bottom,\n",
+        "                                         color-stop(0%,#f1e767),\n",
+        "                                         color-stop(100%,#feb645));\n",
+        "            background: -webkit-linear-gradient(top, #f1e767 0%,#feb645 100%);\n",
+        "            background: -o-linear-gradient(top, #f1e767 0%,#feb645 100%);\n",
+        "            background: -ms-linear-gradient(top, #f1e767 0%,#feb645 100%);\n",
+        "            background: linear-gradient(to bottom, #f1e767 0%,#feb645 100%);\n",
+        "        }\n",
+        "        div.nosebar.leftmost {\n",
+        "            border-radius: 4px 0 0 4px;\n",
+        "        }\n",
+        "        div.nosebar.rightmost {\n",
+        "            border-radius: 0 4px 4px 0;\n",
+        "        }\n",
+        "        div.nosefailbanner {\n",
+        "            border-radius: 4px 0 0 4px;\n",
+        "            border-left: 10px solid #cf0404;\n",
+        "            padding: 0.5ex 0em 0.5ex 1em;\n",
+        "            margin-top: 1ex;\n",
+        "            margin-bottom: 0px;\n",
+        "        }\n",
+        "        div.nosefailbanner.expanded {\n",
+        "            border-radius: 4px 4px 0 0;\n",
+        "            border-top: 10px solid #cf0404;\n",
+        "        }\n",
+        "        pre.nosetraceback {\n",
+        "            border-radius: 0 4px 4px 4px;\n",
+        "            border-left: 10px solid #cf0404;\n",
+        "            padding: 1em;\n",
+        "            margin-left: 0px;\n",
+        "            margin-top: 0px;\n",
+        "            display: none;\n",
+        "        }\n",
+        "    </style>\n",
+        "    \n",
+        "    <script>\n",
+        "        setTimeout(function () {\n",
+        "            $('.nosefailtoggle').bind(\n",
+        "                'click',\n",
+        "                function () {\n",
+        "                    $(\n",
+        "                        $(this)\n",
+        "                            .parent().toggleClass('expanded')\n",
+        "                            .parent()\n",
+        "                            .children()\n",
+        "                            .filter('.nosetraceback')\n",
+        "                    ).toggle();\n",
+        "                }\n",
+        "            );},\n",
+        "            0);\n",
+        "    </script>\n",
+        "    \n",
+        "    <div class=\"noseresults\">\n",
+        "      <div class=\"nosebar fail leftmost\" style=\"width: 25%\">\n",
+        "          &nbsp;\n",
+        "      </div>\n",
+        "      <div class=\"nosebar skip\" style=\"width: 0%\">\n",
+        "          &nbsp;\n",
+        "      </div>\n",
+        "      <div class=\"nosebar pass rightmost\" style=\"width: 75%\">\n",
+        "          &nbsp;\n",
+        "      </div>\n",
+        "      3/4 tests passed; 1 failed\n",
+        "    </div>\n",
+        "    \n",
+        "    <div class=\"nosefailure\">\n",
+        "        <div class=\"nosefailbanner\">\n",
+        "          failed: <span class=\"nosefailedfunc\">__main__.test_mean_of_empty_function_raises_value_error</span>\n",
+        "            [<a class=\"nosefailtoggle\" href=\"#\">toggle traceback</a>]\n",
+        "        </div>\n",
+        "        <pre class=\"nosetraceback\">Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"&lt;<a href=\"#ipython-input-36-bc417f346267\">ipython-input-36-bc417f346267</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([36])\")\n",
+        "                .attr(\"id\", \"ipython-input-36-bc417f346267\");\n",
+        "            </script>&gt;\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "  File \"&lt;<a href=\"#ipython-input-30-16ccb79ef6d8\">ipython-input-30-16ccb79ef6d8</a><script>\n",
+        "            $(\"div.prompt.input_prompt:contains([30])\")\n",
+        "                .attr(\"id\", \"ipython-input-30-16ccb79ef6d8\");\n",
+        "            </script>&gt;\", line 6, in mean\n",
+        "    return total/float(len(numbers))\n",
+        "ZeroDivisionError: float division by zero\n",
+        "</pre>\n",
+        "    </div>\n",
+        "    "
+       ],
+       "output_type": "pyout",
+       "prompt_number": 37,
        "text": [
-        "9.0\n",
-        "56\n"
+        "3/4 tests passed; 1 failed\n",
+        "========\n",
+        "__main__.test_mean_of_empty_function_raises_value_error\n",
+        "========\n",
+        "Traceback (most recent call last):\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/unittest/case.py\", line 331, in run\n",
+        "    testMethod()\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/case.py\", line 197, in runTest\n",
+        "    self.test(*self.arg)\n",
+        "  File \"/Users/Jon/Applications/anaconda/lib/python2.7/site-packages/nose/tools/nontrivial.py\", line 60, in newfunc\n",
+        "    func(*arg, **kw)\n",
+        "  File \"<ipython-input-36-bc417f346267>\", line 5, in test_mean_of_empty_function_raises_value_error\n",
+        "    mean([])\n",
+        "  File \"<ipython-input-30-16ccb79ef6d8>\", line 6, in mean\n",
+        "    return total/float(len(numbers))\n",
+        "ZeroDivisionError: float division by zero\n",
+        "\n"
        ]
       }
      ],
-     "prompt_number": 31
+     "prompt_number": 37
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "def test_mean1():\n",
-      "    m = calc_mean([1, 2, 3])\n",
-      "    assert m == 2\n",
-      "    \n",
-      "def test_mean2():\n",
-      "    m = calc_mean([1])\n",
-      "    assert m == 1\n",
-      "\n",
-      "def test_mean3():\n",
-      "    m = calc_mean([3.4, 3.5, 3.6])\n",
-      "    assert m == 3.5\n",
-      "\n",
-      "@nt.raises(ValueError)\n",
-      "def test_mean4():\n",
-      "    m = calc_mean([])"
+      "def mean(numbers):\n",
+      "    '''Returns the mean of the provided list of numbers.'''\n",
+      "    if len(numbers) == 0:\n",
+      "        raise ValueError, \"Empty list received by mean\"\n",
+      "        \n",
+      "    total = 0.0\n",
+      "    for number in numbers:\n",
+      "        total = total + number\n",
+      "    return total/float(len(numbers))"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 28
+     "prompt_number": 39
     },
     {
      "cell_type": "code",
      "outputs": [
       {
        "html": [
-        "<div id=\"ipython_nose_78c80018c7cf4f79834c36da78624ba0\"></div>"
+        "<div id=\"ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0\"></div>"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_78c80018c7cf4f79834c36da78624ba0 = $(\"#ipython_nose_78c80018c7cf4f79834c36da78624ba0\");"
+        "document.ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0 = $(\"#ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0\");"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_78c80018c7cf4f79834c36da78624ba0.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_78c80018c7cf4f79834c36da78624ba0.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_78c80018c7cf4f79834c36da78624ba0.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_78c80018c7cf4f79834c36da78624ba0.append($(\"<span>.</span>\"));"
+        "document.ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0.append($(\"<span>.</span>\"));"
        ],
        "output_type": "display_data"
       },
       {
        "javascript": [
-        "document.ipython_nose_78c80018c7cf4f79834c36da78624ba0.append($(\"<span>.</span>\"));"
-       ],
-       "output_type": "display_data"
-      },
-      {
-       "javascript": [
-        "delete document.ipython_nose_78c80018c7cf4f79834c36da78624ba0;"
+        "delete document.ipython_nose_cd36649ad3264c26a9a1ce96cf0984c0;"
        ],
        "output_type": "display_data"
       },
         "      <div class=\"nosebar pass rightmost\" style=\"width: 100%\">\n",
         "          &nbsp;\n",
         "      </div>\n",
-        "      5/5 tests passed\n",
+        "      4/4 tests passed\n",
         "    </div>\n",
         "    "
        ],
        "output_type": "pyout",
-       "prompt_number": 31,
+       "prompt_number": 40,
        "text": [
-        "5/5 tests passed\n"
+        "4/4 tests passed\n"
        ]
       }
      ],
-     "prompt_number": 31
+     "prompt_number": 40
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "mean([])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "ename": "ValueError",
+       "evalue": "Empty list received by mean",
+       "output_type": "pyerr",
+       "traceback": [
+        "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
+        "\u001b[0;32m<ipython-input-47-e9e2222d800d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+        "\u001b[0;32m<ipython-input-39-9120caace96b>\u001b[0m in \u001b[0;36mmean\u001b[0;34m(numbers)\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0;34m'''Returns the mean of the provided list of numbers.'''\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"Empty list received by mean\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      6\u001b[0m     \u001b[0mtotal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+        "\u001b[0;31mValueError\u001b[0m: Empty list received by mean"
+       ]
+      }
+     ],
+     "prompt_number": 47
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "***\n",
+      "**Aside: What are exceptions good for?**\n",
+      "\n",
+      "In addition to providing nice error messages, exceptions can be used to allow your program to handle unexpected - or *exceptional* - conditions gracefully. Python provides *exception handling*, which allows your code to respond to exceptions if they occur.  Consider this example:\n",
+      "\n",
+      "    while not success:\n",
+      "        numbers = read_numbers_from_keyboard()\n",
+      "        try:\n",
+      "            result = mean(numbers)\n",
+      "            success = True\n",
+      "        except ValueError:\n",
+      "            print 'Please try again'\n",
+      "\n",
+      "***"
+     ]
     },
     {
      "cell_type": "code",