Skip to content

Add "lpn" checking in _ListPlot #1245

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

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
18 changes: 17 additions & 1 deletion mathics/builtin/drawing/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,12 @@ class _ListPlot(Builtin, ABC):
attributes = A_PROTECTED | A_READ_PROTECTED

messages = {
"joind": "Value of option Joined -> `1` is not True or False.",
"lpn": "`1` is not a list of numbers or pairs of numbers.",
"prng": (
"Value of option PlotRange -> `1` is not All, Automatic or "
"an appropriate list of range specifications."
),
"joind": "Value of option Joined -> `1` is not True or False.",
}

use_log_scale = False
Expand All @@ -309,6 +310,21 @@ def eval(self, points, evaluation: Evaluation, options: dict):
)
)

points = points.evaluate(evaluation)
if not isinstance(points, ListExpression):
evaluation.message(class_name, "lpn", points)
return

if not all(
element.is_numeric(evaluation)
or isinstance(element, ListExpression)
or (1 <= len(element.elements) <= 2)
or (len(element.elements) == 1 and isinstance(element[0], ListExpression))
for element in points.elements
):
evaluation.message(class_name, "lpn", points)
return

# If "points" is a literal value with a Python representation,
# it has a ".value" attribute with a non-None value. So here,
# we don't have to eval_N().to_python().
Expand Down
9 changes: 7 additions & 2 deletions mathics/core/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1410,8 +1410,13 @@ def rules():
if not isinstance(result, EvalMixin):
return result, False
if result.sameQ(new):
new._timestamp_cache(evaluation)
return new, False
# Even though result and new may be the same,
# new can be a Expression[SymbolConstant: System`List...]
# while "result' might be ListExpression!?
# So make sure to use "result", not "new".
if isinstance(result, Expression):
result._timestamp_cache(evaluation)
return result, False
else:
return result, True

Expand Down
14 changes: 8 additions & 6 deletions mathics/eval/drawing/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,19 +201,21 @@ def eval_ListPlot(
[xx for xx, yy in plot_groups], [xx for xx, yy in plot_groups], x_range
)
plot_groups = [plot_groups]
elif all(isinstance(line, list) for line in plot_groups):
if not all(isinstance(line, list) for line in plot_groups):
elif all(isinstance(line, (list, tuple)) for line in plot_groups):
if not all(isinstance(line, (list, tuple)) for line in plot_groups):
return

# He have a list of plot groups
if all(
isinstance(point, list) and len(point) == 2
for plot_group in plot_groups
isinstance(point, (list, tuple)) and len(point) == 2
for _ in plot_groups
for point in plot_groups
):
pass
elif not is_discrete_plot and all(
not isinstance(point, list) for line in plot_groups for point in line
not isinstance(point, (list, tuple))
for line in plot_groups
for point in line
):
# FIXME: is this right?
y_min = min(plot_groups)[0]
Expand All @@ -229,7 +231,7 @@ def eval_ListPlot(
# Split into plot segments
plot_groups = [[plot_group] for plot_group in plot_groups]
if isinstance(x_range, (list, tuple)):
x_min, m_max = x_range
x_min, x_max = x_range
y_min, y_max = y_range

for lidx, plot_group in enumerate(plot_groups):
Expand Down
37 changes: 32 additions & 5 deletions test/builtin/drawing/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,40 @@
Unit tests from mathics.builtin.drawing.plot
"""

import sys
import time
from test.helper import check_evaluation, evaluate
from test.helper import check_evaluation

import pytest


def test__listplot():
"""tests for module builtin.drawing.plot._ListPlot"""
for str_expr, msgs, str_expected, fail_msg in (
(
"ListPlot[5]",
("5 is not a list of numbers or pairs of numbers.",),
"ListPlot[5]",
"ListPlot with invalid list of point",
),
(
"ListLinePlot[{{}, {{1., 1.}}, {{1., 2.}}, {}}]",
(
"{{}, {{1., 1.}}, {{1., 2.}}, {}} is not a list of numbers or pairs of numbers.",
),
"ListLinePlot[{{}, {{1., 1.}}, {{1., 2.}}, {}}]",
"ListLinePlot with invalid list of point",
),
):
check_evaluation(
str_expr,
str_expected,
to_string_expr=True,
to_string_expected=True,
hold_expected=True,
failure_message=fail_msg,
expected_messages=msgs,
)


@pytest.mark.parametrize(
("str_expr", "msgs", "str_expected", "fail_msg"),
[
Expand Down Expand Up @@ -159,8 +186,8 @@
),
],
)
def test_private_doctests_plot(str_expr, msgs, str_expected, fail_msg):
"""builtin.drawing.plot"""
def test_plot(str_expr, msgs, str_expected, fail_msg):
"""tests for module builtin.drawing.plot"""
check_evaluation(
str_expr,
str_expected,
Expand Down
2 changes: 1 addition & 1 deletion test/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def evaluate(str_expr: str):
def check_evaluation(
str_expr: Optional[str],
str_expected: Optional[str] = None,
failure_message: str = "",
failure_message: Optional[str] = "",
hold_expected: bool = False,
to_string_expr: Optional[bool] = True,
to_string_expected: bool = True,
Expand Down
Loading