Skip to content

Commit e5469d0

Browse files
committed
ensure plots are well-formatted
1 parent 834f74a commit e5469d0

File tree

3 files changed

+57
-31
lines changed

3 files changed

+57
-31
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import ast
2+
from typing import Union
3+
4+
5+
def base_obj(node: ast.Call) -> Union[str, None]:
6+
"""If this is a method call, return the name of the base object it was called on"""
7+
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
8+
return node.func.value.id
9+
else:
10+
return None
11+
12+
13+
class PlotChecker(ast.NodeVisitor):
14+
def visit_Call(self, node):
15+
if base_obj(node) == "px":
16+
args = [kw.arg for kw in node.keywords]
17+
method = node.func.attr
18+
19+
if method == "get_trendline_results":
20+
# `title` not applicable
21+
return
22+
23+
assert "title" in args, f"call to `{method}()` missing a `title`"
24+
25+
26+
def check_plots(source: str):
27+
tree = ast.parse(source)
28+
checker = PlotChecker()
29+
checker.visit(tree)
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
11
"""https://github.com/gradescope/gradescope-utils/tree/master/gradescope_utils/autograder_utils#readme"""
22

3-
from unittest import TestCase
43
import os
4+
from glob import glob
5+
from unittest import TestCase
6+
57
from gradescope_utils.autograder_utils.decorators import number
68

9+
from tests.helpers import check_plots
10+
11+
12+
def get_ext(filename):
13+
return os.path.splitext(filename)[1]
14+
715

816
class TestFiles(TestCase):
917
@number("1.1")
1018
def test_notebook_and_py_file(self):
1119
"""There should be exactly one notebook and one Python file submitted"""
1220

1321
files = os.listdir("/autograder/submission")
14-
extensions = [os.path.splitext(filename)[1] for filename in files]
22+
extensions = [get_ext(filename) for filename in files]
1523
extensions.sort()
1624
self.assertListEqual(extensions, [".ipynb", ".py"], f"Files submitted: {', '.join(files)}")
25+
26+
@number("1.2")
27+
def test_plots(self):
28+
"""Plots should be well-formatted"""
29+
30+
scripts = glob("/autograder/submission/*.py")
31+
for filename in scripts:
32+
with open(filename) as f:
33+
source = f.read()
34+
35+
check_plots(source)

extras/test_notebooks.py

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import ast
2-
from glob import glob
31
import os
4-
from typing import Union
2+
import re
3+
from glob import glob
4+
5+
import pytest
56
from markdown_it import MarkdownIt
67
from markdown_it.token import Token
7-
import pytest
8-
import re
8+
9+
from .autograder.source.tests.helpers import check_plots
910
from .lib.nb_helper import get_tags, is_h1, is_markdown, is_python, read_notebook
1011

1112

@@ -109,27 +110,6 @@ def test_links(file):
109110
check_link(child, token)
110111

111112

112-
def base_obj(node: ast.Call) -> Union[str, None]:
113-
"""If this is a method call, return the name of the base object it was called on"""
114-
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
115-
return node.func.value.id
116-
else:
117-
return None
118-
119-
120-
class PlotChecker(ast.NodeVisitor):
121-
def visit_Call(self, node):
122-
if base_obj(node) == "px":
123-
args = [kw.arg for kw in node.keywords]
124-
method = node.func.attr
125-
126-
if method == "get_trendline_results":
127-
# `title` not applicable
128-
return
129-
130-
assert "title" in args, f"call to `{method}()` missing a `title`"
131-
132-
133113
@pytest.mark.parametrize("file", notebooks)
134114
def test_chart_titles(file):
135115
"""Make sure all charts have titles"""
@@ -140,9 +120,7 @@ def test_chart_titles(file):
140120
if "skip-plot-check" in tags or not is_python(cell):
141121
continue
142122

143-
tree = ast.parse(cell.source)
144-
checker = PlotChecker()
145-
checker.visit(tree)
123+
check_plots(cell.source)
146124

147125

148126
def num_lines(output):

0 commit comments

Comments
 (0)