Skip to content

Commit

Permalink
Merge pull request #172 from ceball/explicitly_include_or_exclude_cell
Browse files Browse the repository at this point in the history
Celltest always generated for non-empty code cell, and always runs; defaults to %cell if test is empty
  • Loading branch information
ceball authored Jun 12, 2020
2 parents 0abb622 + 9784f7b commit a5cd54e
Show file tree
Hide file tree
Showing 13 changed files with 661 additions and 141 deletions.
2 changes: 1 addition & 1 deletion js/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ export class CelltestsWidget extends Widget {
let tests = this.currentActiveCell.model.metadata.get("tests") as string[];
let s = "";
if (tests === undefined || tests.length === 0) {
tests = ["# Use %cell to execute the cell\n"];
tests = ["# Use %cell to mark where the cell should be inserted, or add a line comment \"# no %cell\" to deliberately skip the cell\n", "%cell\n"];
}
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < tests.length; i++) {
Expand Down
56 changes: 50 additions & 6 deletions nbcelltests/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def extract_extrametadata(notebook, override=None, noqa_regex=None):
base['noqa'] = set()

for c in notebook.cells:
if c['cell_type'] != 'code' or is_empty(c['source']):
if c['cell_type'] != 'code' or empty_ast(c['source']):
continue

base['cell_lines'].append(0)
Expand All @@ -124,7 +124,7 @@ def extract_extrametadata(notebook, override=None, noqa_regex=None):
noqa_match = noqa_regex.match(line)
if noqa_match:
base['noqa'].add(noqa_match.group(1))
if not is_empty(line):
if not empty_ast(line):
base['lines'] += 1
base['cell_lines'][-1] += 1
if cell_injected_into_test(get_test(c)):
Expand All @@ -142,7 +142,20 @@ def get_test(cell):
return lines2source(cell.get('metadata', {}).get('tests', []))


def is_empty(source):
def empty_ast(source):
"""
Whether the supplied source string has an empty ast.
>>> empty_ast(" ")
True
>>> empty_ast("pass")
False
>>> empty_ast("# hello")
True
"""
try:
parsed = ast.parse(source)
except SyntaxError:
Expand All @@ -156,7 +169,25 @@ def is_empty(source):
return len(parsed.body) == 0


def only_whitespace(source):
"""
Whether the supplied source string contains only whitespace.
>>> only_whitespace(" ")
True
>>> only_whitespace("pass")
False
>>> only_whitespace("# hello")
False
"""
return len(source.strip()) == 0


# TODO drop the "token" part
CELL_INJ_TOKEN = r"%cell"
CELL_SKIP_TOKEN = r"# no %cell"


def source2lines(source):
Expand All @@ -181,10 +212,23 @@ def get_cell_inj_span(test_line):


def cell_injected_into_test(test_source):
"""
Return True if the corresponding cell is injected into the test,
False if the cell is deliberately not injected, or None if there
is no deliberate command either way.
"""
inject = False
run = None
for test_line in source2lines(test_source):
if get_cell_inj_span(test_line) is not None:
return True
return False
if inject is False and get_cell_inj_span(test_line) is not None:
inject = True
elif run is None and test_line.strip().startswith(CELL_SKIP_TOKEN):
run = False

if run is False and inject:
raise ValueError("'%s' and '%s' are mutually exclusive but both were supplied:\n%s" % (CELL_SKIP_TOKEN, CELL_INJ_TOKEN, test_source))

return inject or run


def get_coverage(metadata):
Expand Down
39 changes: 39 additions & 0 deletions nbcelltests/tests/_broken_magics.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"%magics2\n",
"raise\n",
"class X: pass"
]
}
],
"metadata": {
"celltests": {
"cell_coverage": 50
},
"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": 4
}
8 changes: 2 additions & 6 deletions nbcelltests/tests/_cell_counting.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tests": [
"assert x == 1"
]
"tests": []
},
"outputs": [],
"source": [
"x = 1"
]
"source": []
},
{
"cell_type": "markdown",
Expand Down
15 changes: 6 additions & 9 deletions nbcelltests/tests/_cell_coverage.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tests": [
"assert x == 1\n",
"class X: pass"
]
},
"metadata": {},
"outputs": [],
"source": [
"# nothing"
Expand Down Expand Up @@ -51,8 +46,10 @@
"execution_count": 3,
"metadata": {
"tests": [
"assert x == 3\n",
"%magics1"
"x = 4\n",
"assert x == 4\n",
"%dirs\n",
"# no %cell"
]
},
"outputs": [],
Expand All @@ -77,7 +74,7 @@
"metadata": {},
"outputs": [],
"source": [
"%magics2\n",
"%dirs\n",
"class X: pass"
]
}
Expand Down
82 changes: 82 additions & 0 deletions nbcelltests/tests/_cell_not_injected_or_mocked.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tests": [
"# no %cell"
]
},
"outputs": [],
"source": [
"x = 1\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# nothing to see here"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"x = 1"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"tests": [
"%cell"
]
},
"outputs": [],
"source": [
"x = 1"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"tests": [
"x = 17"
]
},
"outputs": [],
"source": [
"x = 1"
]
}
],
"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": 4
}
43 changes: 41 additions & 2 deletions nbcelltests/tests/_cumulative_run.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"execution_count": 1,
"metadata": {
"tests": [
"pass"
"# no %cell"
]
},
"outputs": [],
Expand Down Expand Up @@ -68,7 +68,46 @@
"source": [
"pass"
]
}
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"tests": [
"\n"
]
},
"outputs": [],
"source": [
"y = 10"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"tests": [
"# no %cell"
]
},
"outputs": [],
"source": [
"a = 1"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"tests": [
"%cell\n"
]
},
"outputs": [],
"source": [
"z = 1"
]
}
],
"metadata": {
"kernelspec": {
Expand Down
47 changes: 47 additions & 0 deletions nbcelltests/tests/_empty_cell_with_test.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"source": [],
"outputs": [],
"metadata": {
"tests": [
"# empty test"
]
}
},
{
"cell_type": "code",
"execution_count": 2,
"source": [],
"outputs": [],
"metadata": {
"tests": [
"pass # not empty test"
]
}
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.6 64-bit ('celltestsui': conda)",
"language": "python",
"name": "python37664bitcelltestsuiconda549e14be7f784a2fbde278c18c8be829"
},
"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": 4
}
Loading

0 comments on commit a5cd54e

Please sign in to comment.