Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add context manager for failed tests #126

Merged
merged 6 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fastcore/_nbdev.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"TEST_IMAGE": "00_test.ipynb",
"TEST_IMAGE_BW": "00_test.ipynb",
"test_fig_exists": "00_test.ipynb",
"TestFail": "00_test.ipynb",
"defaults": "01_foundation.ipynb",
"copy_func": "01_foundation.ipynb",
"patch_to": "01_foundation.ipynb",
Expand Down
17 changes: 15 additions & 2 deletions fastcore/test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/00_test.ipynb (unless otherwise specified).

__all__ = ['test_fail', 'test', 'nequals', 'test_eq', 'test_eq_type', 'test_ne', 'is_close', 'test_close', 'test_is',
'test_shuffled', 'test_stdout', 'test_warns', 'TEST_IMAGE', 'TEST_IMAGE_BW', 'test_fig_exists']
'test_shuffled', 'test_stdout', 'test_warns', 'TEST_IMAGE', 'TEST_IMAGE_BW', 'test_fig_exists', 'TestFail']

# Cell
from .imports import *
from .utils import *
from collections import Counter
from contextlib import redirect_stdout

Expand Down Expand Up @@ -95,4 +96,16 @@ def test_warns(f, show=False):
# Cell
def test_fig_exists(ax):
"Test there is a figure displayed in `ax`"
assert ax and len(ax.figure.canvas.tostring_argb())
assert ax and len(ax.figure.canvas.tostring_argb())

# Cell
class TestFail:
"A context manager to allow you to test if an exception (ex) is raised. Optionally, search the exception's error message with a regex."
def __init__(self, ex:Exception, regex:str=None):store_attr()
def __enter__(self): pass
def __exit__(self, type, value, traceback):
if isinstance(value, self.ex):
if self.regex:
assert re.search(self.regex, ' '.join(value.args)) is not None, f"Did not find regex:{self.regex} in Exception message."
return True
else: raise TypeError(f"An error of {self.ex} was not raised.")
45 changes: 42 additions & 3 deletions nbs/00_test.ipynb

Large diffs are not rendered by default.

66 changes: 54 additions & 12 deletions nbs/02_utils.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@
{
"data": {
"text/plain": [
"<__main__._t at 0x7f945b39e070>"
"<__main__._t at 0x7fbd57805430>"
]
},
"execution_count": null,
Expand Down Expand Up @@ -2171,7 +2171,7 @@
{
"data": {
"text/plain": [
"['h', 'g', 'f', 'b', 'd', 'e', 'a', 'c']"
"['h', 'c', 'g', 'd', 'a', 'e', 'b', 'f']"
]
},
"execution_count": null,
Expand Down Expand Up @@ -2964,7 +2964,7 @@
{
"data": {
"text/plain": [
"(Path('../fastcore/all.py'), Path('00_test.ipynb'))"
"(Path('../fastcore/__init__.py'), Path('00_test.ipynb'))"
]
},
"execution_count": null,
Expand Down Expand Up @@ -3513,7 +3513,7 @@
{
"data": {
"text/plain": [
"8"
"4"
]
},
"execution_count": null,
Expand Down Expand Up @@ -3645,6 +3645,47 @@
" return functools.update_wrapper(_f, f)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`typed` validates argument types at **runtime**. This is in contrast to [MyPy](http://mypy-lang.org/) which only offers static type checking.\n",
"\n",
"For example, a `TypeError` will be raised if we try to pass an integer into the first argument of the below function: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"@typed\n",
"def discount(price:int, pct:float): \n",
" return (1-pct) * price\n",
"\n",
"with TestFail(TypeError): discount(100.0, .1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also optionally allow multiple types by enumarating the types in a tuple as illustrated below:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def discount(price:(int,float), pct:float): \n",
" return (1-pct) * price\n",
"\n",
"assert 90.0 == discount(100.0, .1)"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -3654,11 +3695,12 @@
"@typed\n",
"def foo(a:int, b:str='a'): return a\n",
"test_eq(foo(1, '2'), 1)\n",
"test_fail(partial(foo, 1, 2))\n",
"\n",
"with TestFail(TypeError): foo(1,2)\n",
"\n",
"@typed\n",
"def foo()->str: return 1\n",
"test_fail(partial(foo))\n",
"with TestFail(TypeError): foo()\n",
"\n",
"@typed\n",
"def foo()->str: return '1'\n",
Expand Down Expand Up @@ -3791,7 +3833,7 @@
"text/markdown": [
"<h4 id=\"ProcessPoolExecutor\" class=\"doc_header\"><code>class</code> <code>ProcessPoolExecutor</code><a href=\"\" class=\"source_link\" style=\"float:right\">[source]</a></h4>\n",
"\n",
"> <code>ProcessPoolExecutor</code>(**`max_workers`**=*`64`*, **`on_exc`**=*`print`*, **`pause`**=*`0`*, **\\*\\*`kwargs`**) :: [`ProcessPoolExecutor`](/utils.html#ProcessPoolExecutor)\n",
"> <code>ProcessPoolExecutor</code>(**`max_workers`**=*`4`*, **`on_exc`**=*`print`*, **`pause`**=*`0`*, **\\*\\*`kwargs`**) :: [`ProcessPoolExecutor`](/utils.html#ProcessPoolExecutor)\n",
"\n",
"Same as Python's ProcessPoolExecutor, except can pass `max_workers==0` for serial execution"
],
Expand Down Expand Up @@ -3922,11 +3964,11 @@
"name": "stdout",
"output_type": "stream",
"text": [
"0 2020-10-08 18:38:18.973557\n",
"1 2020-10-08 18:38:19.223553\n",
"2 2020-10-08 18:38:19.475253\n",
"3 2020-10-08 18:38:19.725614\n",
"4 2020-10-08 18:38:19.976216\n"
"1 2020-10-12 21:15:11.361062\n",
"0 2020-10-12 21:15:11.612469\n",
"2 2020-10-12 21:15:11.863159\n",
"3 2020-10-12 21:15:12.113859\n",
"4 2020-10-12 21:15:12.366331\n"
]
}
],
Expand Down