Skip to content

Commit 6f60171

Browse files
authored
Merge branch 'master' into blank_patterns
2 parents 0e88e3a + 90d6767 commit 6f60171

File tree

2 files changed

+153
-1
lines changed

2 files changed

+153
-1
lines changed

mathics/core/util.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from platform import python_implementation
1010
from typing import Optional
1111

12+
from mathics.core.symbols import Symbol
13+
1214
IS_PYPY = python_implementation() == "PyPy"
1315

1416

@@ -113,3 +115,29 @@ def subranges(
113115
items[start : start + length],
114116
(items[:start], items[start + length :]),
115117
)
118+
119+
120+
def print_expression_tree(expr, indent="", marker=lambda expr: ""):
121+
"""
122+
Print a Mathics Expression as an indented tree.
123+
Caller may supply a marker function that computes a marker
124+
to be displayed in the tree for the given node.
125+
"""
126+
if isinstance(expr, Symbol):
127+
print(f"{indent}{marker(expr)}{expr}")
128+
elif not hasattr(expr, "elements"):
129+
print(f"{indent}{marker(expr)}{expr.get_head()} {expr}")
130+
else:
131+
print(f"{indent}{marker(expr)}{expr.head}")
132+
for elt in expr.elements:
133+
print_expression_tree(elt, indent + " ", marker=marker)
134+
135+
136+
def print_sympy_tree(expr, indent=""):
137+
"""Print a SymPy Expression as an indented tree"""
138+
if expr.args:
139+
print(f"{indent}{expr.func.__name__}")
140+
for i, arg in enumerate(expr.args):
141+
print_sympy_tree(arg, indent + " ")
142+
else:
143+
print(f"{indent}{expr.func.__name__}({str(expr)})")

test/builtin/drawing/test_plot.py

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
Unit tests from mathics.builtin.drawing.plot
44
"""
55

6-
from test.helper import check_evaluation
6+
from test.helper import check_evaluation, session
77

88
import pytest
99

10+
from mathics.core.util import print_expression_tree
11+
1012

1113
def test__listplot():
1214
"""tests for module builtin.drawing.plot._ListPlot"""
@@ -201,3 +203,125 @@ def test_plot(str_expr, msgs, str_expected, fail_msg):
201203
failure_message=fail_msg,
202204
expected_messages=msgs,
203205
)
206+
207+
208+
#
209+
# Call plotting functions and examine the structure of the output
210+
# In case of error trees are printed with an embedded >>> marker showing location of error
211+
#
212+
213+
214+
def print_expression_tree_with_marker(expr):
215+
print_expression_tree(expr, marker=lambda expr: getattr(expr, "_marker", ""))
216+
217+
218+
def check_structure(result, expected):
219+
"""Check that expected is a prefix of result at every node"""
220+
221+
def error(msg):
222+
result._marker = "RESULT >>> "
223+
expected._marker = "EXPECTED >>> "
224+
raise AssertionError(msg)
225+
226+
# do the heads match?
227+
if result.get_head() != expected.get_head():
228+
error("heads don't match")
229+
230+
# does the structure match?
231+
if hasattr(expected, "elements"):
232+
if not hasattr(result, "elements"):
233+
error("expected elements but result has none")
234+
for i, e in enumerate(expected.elements):
235+
if len(result.elements) <= i:
236+
error("result has too few elements")
237+
check_structure(result.elements[i], e)
238+
else:
239+
if str(result) != str(expected):
240+
error("leaves don't match")
241+
242+
243+
def eval_and_check_structure(str_expr, str_expected):
244+
expr = session.parse(str_expr)
245+
result = expr.evaluate(session.evaluation)
246+
expected = session.parse(str_expected)
247+
try:
248+
check_structure(result, expected)
249+
except AssertionError as oops:
250+
print(f"\nERROR: {oops} (error is marked with >>> below)")
251+
print("=== result:")
252+
print_expression_tree_with_marker(result)
253+
print("=== expected:")
254+
print_expression_tree_with_marker(expected)
255+
raise
256+
257+
258+
def test_plot3d_default():
259+
eval_and_check_structure(
260+
"""
261+
Plot3D[
262+
x+y,
263+
{x,0,1}, {y,0,1},
264+
PlotPoints->{2,2},
265+
MaxRecursion->0
266+
]
267+
""",
268+
"""
269+
Graphics3D[
270+
{
271+
Polygon[{{0.0,0.0,0.0}, {0.0,0.5,0.5}, {0.5,0.0,0.5}}],
272+
Polygon[{{}}]
273+
},
274+
AspectRatio -> 1,
275+
Axes -> True,
276+
AxesStyle -> {},
277+
Background -> Automatic,
278+
BoxRatios -> {1, 1, 0.4},
279+
ImageSize -> Automatic,
280+
LabelStyle -> {},
281+
PlotRange -> Automatic,
282+
PlotRangePadding -> Automatic,
283+
TicksStyle -> {}
284+
]
285+
""",
286+
)
287+
288+
289+
def test_plot3d_nondefault():
290+
eval_and_check_structure(
291+
"""
292+
Plot3D[
293+
x+y,
294+
{x,0,1}, {y,0,1},
295+
PlotPoints->{2,2},
296+
MaxRecursion->0
297+
AspectRatio -> 0.5,
298+
Axes -> False,
299+
AxesStyle -> {Red,Blue},
300+
Background -> Green,
301+
BoxRatios -> {10, 10, 1},
302+
ImageSize -> {200,200},
303+
LabelStyle -> Red,
304+
PlotRange -> {0,1},
305+
PlotRangePadding -> {1,2},
306+
TicksStyle -> {Purple,White}
307+
]
308+
""",
309+
"""
310+
Graphics3D[
311+
{
312+
Polygon[{{0.0,0.0,0.0}, {0.0,0.5,0.5}, {0.5,0.0,0.5}}],
313+
Polygon[{{}}]
314+
},
315+
AspectRatio -> 1, (* TODO: is not passed through apparently - or is my misunderstanding? *)
316+
Axes -> False,
317+
AxesStyle -> {RGBColor[1,0,0],RGBColor[0,0,1]},
318+
Background -> RGBColor[0,1,0],
319+
BoxRatios -> {10, 10, 1},
320+
ImageSize -> {200,200},
321+
LabelStyle -> RGBColor[1,0,0],
322+
PlotRange -> {0,1},
323+
PlotRangePadding -> {1,2},
324+
TicksStyle -> {RGBColor[0.5,0,0.5],GrayLevel[1]}
325+
]
326+
""",
327+
)

0 commit comments

Comments
 (0)