Skip to content

Instantly share code, notes, and snippets.

@juntyr
Created February 2, 2024 11:43
Show Gist options
  • Save juntyr/e78a21c9705c0392c35b466fe7ff00d8 to your computer and use it in GitHub Desktop.
Save juntyr/e78a21c9705c0392c35b466fe7ff00d8 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"kernelspec": {
"name": "python",
"display_name": "Python (Pyodide)",
"language": "python"
},
"language_info": {
"codemirror_mode": {
"name": "python",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8"
}
},
"nbformat_minor": 4,
"nbformat": 4,
"cells": [
{
"cell_type": "markdown",
"source": "# Brief example of type-checking Python code using `mypy`",
"metadata": {}
},
{
"cell_type": "markdown",
"source": "## Install `mypy` and register a custom `%%typecheck` magic in this JupyterLite notebook",
"metadata": {}
},
{
"cell_type": "code",
"source": "%pip install mypy\n%pip install mypy_extensions\n\n\"\"\"\nAdd mypy type-checking cell magic to jupyter/ipython.\n\nSave this script to your ipython profile's startup directory.\n\nIPython's directories can be found via `ipython locate [profile]` to find the current ipython directory and ipython profile directory, respectively.\n\nFor example, this file could exist on a path like this on mac:\n\n/Users/yourusername/.ipython/profile_default/startup/typecheck.py\n\nwhere /Users/yourusername/.ipython/profile_default/ is the ipython directory for\nthe default profile.\n\nThe line magic is called \"typecheck\" to avoid namespace conflict with the mypy\npackage.\n\nhttps://gist.github.com/knowsuchagency/f7b2203dd613756a45f816d6809f01a6\n\n\"\"\"\n\nimport typing_extensions\n\nfrom IPython.core.magic import register_cell_magic\n\n\n@register_cell_magic\ndef typecheck(line, cell):\n \"\"\"\n Run the following cell though mypy.\n\n Any parameters that would normally be passed to the mypy cli\n can be passed on the first line, with the exception of the\n -c flag we use to pass the code from the cell we want to execute\n\n i.e.\n\n %%typecheck --ignore-missing-imports\n ...\n ...\n ...\n\n mypy stdout and stderr will print prior to output of cell. If there are no conflicts,\n nothing will be printed by mypy.\n \"\"\"\n\n from IPython import get_ipython\n from mypy import api\n\n # inserting a newline at the beginning of the cell\n # ensures mypy's output matches the the line\n # numbers in jupyter\n\n cell = '\\n' + cell\n\n mypy_result = api.run(['-c', cell] + line.split())\n\n if mypy_result[0]: # print mypy stdout\n print(mypy_result[0])\n\n if mypy_result[1]: # print mypy stderr\n print(mypy_result[1])\n\n shell = get_ipython()\n shell.run_cell(cell)",
"metadata": {
"trusted": true,
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"execution_count": 1
},
{
"cell_type": "markdown",
"source": "## a) Define an untyped function and call it correctly",
"metadata": {}
},
{
"cell_type": "code",
"source": "def takes(x):\n return x + \"b\"\n\ntakes(\"a\")",
"metadata": {
"trusted": true
},
"outputs": [
{
"execution_count": 2,
"output_type": "execute_result",
"data": {
"text/plain": "'ab'"
},
"metadata": {}
}
],
"execution_count": 2
},
{
"cell_type": "markdown",
"source": "## b) Define an untyped function and call it with the wrong type - runtime `TypeError`",
"metadata": {}
},
{
"cell_type": "code",
"source": "def takes(x):\n return x + \"b\"\n\ntakes(3)",
"metadata": {
"trusted": true
},
"outputs": [
{
"ename": "<class 'TypeError'>",
"evalue": "unsupported operand type(s) for +: 'int' and 'str'",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[3], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtakes\u001b[39m(x):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m x \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m----> 4\u001b[0m \u001b[43mtakes\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n",
"Cell \u001b[0;32mIn[3], line 2\u001b[0m, in \u001b[0;36mtakes\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtakes\u001b[39m(x):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mb\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\n",
"\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'int' and 'str'"
],
"output_type": "error"
}
],
"execution_count": 3
},
{
"cell_type": "markdown",
"source": "## c) Type the function and use `mypy` to catch the type error before execution",
"metadata": {}
},
{
"cell_type": "code",
"source": "%%typecheck\ndef takes(x: str):\n return x + \"b\"\n\ntakes(3)",
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"text": "<string>:5: error: Argument 1 to \"takes\" has incompatible type \"int\"; expected \"str\" [arg-type]\nFound 1 error in 1 file (checked 1 source file)\n\n",
"output_type": "stream"
},
{
"ename": "<class 'TypeError'>",
"evalue": "unsupported operand type(s) for +: 'int' and 'str'",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[4], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtakes\u001b[39m(x: \u001b[38;5;28mstr\u001b[39m):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m x \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m----> 4\u001b[0m \u001b[43mtakes\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n",
"Cell \u001b[0;32mIn[4], line 2\u001b[0m, in \u001b[0;36mtakes\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtakes\u001b[39m(x: \u001b[38;5;28mstr\u001b[39m):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mb\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\n",
"\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'int' and 'str'"
],
"output_type": "error"
}
],
"execution_count": 4
},
{
"cell_type": "code",
"source": "",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment