Skip to content

Commit 249d5ea

Browse files
authored
Even more error checking DRYness (#1468)
DRY error checking in `Round[]` and `Sign[]`.
1 parent 5aa90ec commit 249d5ea

File tree

4 files changed

+76
-27
lines changed

4 files changed

+76
-27
lines changed

mathics/builtin/numeric.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Complex,
2020
Integer,
2121
Integer0,
22+
Integer1,
2223
IntegerM1,
2324
Number,
2425
Rational,
@@ -41,6 +42,7 @@
4142
SymbolDivide,
4243
SymbolFalse,
4344
SymbolMachinePrecision,
45+
SymbolPlus,
4446
SymbolTimes,
4547
SymbolTrue,
4648
)
@@ -707,17 +709,31 @@ class Round(Builtin):
707709

708710
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
709711

712+
# Set checking that the number of arguments required is one or two.
713+
eval_error = Builtin.generic_argument_error
714+
expected_args = (1, 2)
715+
716+
# For now, we handle Rounding Complex numnbers as rules.
717+
# In the future consider folding this into the code.
710718
rules = {
711-
"Round[expr_?NumericQ]": "Round[Re[expr], 1] + I * Round[Im[expr], 1]",
719+
"Round[expr_Complex]": "Round[Re[expr], 1] + I * Round[Im[expr], 1]",
712720
"Round[expr_Complex, k_?RealValuedNumberQ]": (
713721
"Round[Re[expr], k] + I * Round[Im[expr], k]"
714722
),
715723
}
716724

717725
summary_text = "find closest integer or multiple of"
718726

719-
def eval(self, expr, k, evaluation: Evaluation):
720-
"Round[expr_?NumericQ, k_?NumericQ]"
727+
def eval_one_arg(self, expr, evaluation: Evaluation):
728+
"Round[expr_]"
729+
return self.eval_two_args(expr, Integer1, evaluation)
730+
731+
def eval_two_args(self, expr, k, evaluation: Evaluation):
732+
"Round[expr_, k_]"
733+
734+
if not expr.is_numeric(evaluation):
735+
# We can't evaluate, so keep the symbolic representation.
736+
return
721737

722738
n = Expression(SymbolDivide, expr, k).round_to_float(
723739
evaluation, permit_complex=True
@@ -760,20 +776,20 @@ class Sign(SympyFunction):
760776
761777
"""
762778

763-
summary_text = "complex sign of a number"
764-
sympy_name = "sign"
765-
# mpmath_name = 'sign'
766-
767779
attributes = A_LISTABLE | A_NUMERIC_FUNCTION | A_PROTECTED
768780

769-
messages = {
770-
"argx": "Sign called with `1` arguments; 1 argument is expected.",
771-
}
781+
# Set checking that the number of arguments required is one.
782+
eval_error = Builtin.generic_argument_error
783+
expected_args = 1
772784

773785
rules = {
774786
"Sign[Power[a_, b_]]": "Power[Sign[a], b]",
775787
}
776788

789+
summary_text = "complex sign of a number"
790+
sympy_name = "sign"
791+
# mpmath_name = 'sign'
792+
777793
def eval(self, x, evaluation: Evaluation):
778794
"Sign[x_]"
779795
result = eval_Sign(x)
@@ -787,10 +803,6 @@ def eval(self, x, evaluation: Evaluation):
787803
# Unhandled cases. Use sympy
788804
return super(Sign, self).eval(x, evaluation)
789805

790-
def eval_error(self, x, seqs, evaluation: Evaluation):
791-
"Sign[x_, seqs__]"
792-
evaluation.message("Sign", "argx", Integer(len(seqs.get_sequence()) + 1))
793-
794806

795807
class UnitStep(Builtin):
796808
"""

mathics/builtin/specialfns/elliptic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class EllipticE(SympyFunction):
5353

5454
attributes = A_NUMERIC_FUNCTION | A_PROTECTED
5555

56-
# Set checking that the number of arguments required is two or three.
56+
# Set checking that the number of arguments required is one or two.
5757
eval_error = Builtin.generic_argument_error
5858
expected_args = (1, 2)
5959

mathics/core/builtin.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,12 +439,12 @@ def contextify_form_name(f):
439439
makeboxes_def.add_rule(rule)
440440

441441
# This method is used to produce generic argument mismatch errors
442-
# (tags: argx, argr, argrx, argt, argtu) for builtin functions that define this
443-
# as an eval method. e.g. For example for Sqrt[a, b] (one
444-
# argument expected) or Subtract[a] (two arguments expected) It
445-
# assumes each builtin defines "expected_args" for the correct
446-
# number of arguments to give. See class
447-
# mathics.builtins.basic.Sqrt for how to set up.
442+
# (tags: "argx", "argr", "argrx", "argt", or "argtu") for builtin
443+
# functions that define this as an eval method. e.g. For example
444+
# for Sqrt[a, b] (one argument expected) or Subtract[a] (two
445+
# arguments expected) It assumes each builtin defines
446+
# "expected_args" for the correct number of arguments to give.
447+
# See class mathics.builtins.basic.Sqrt for how to set up.
448448
def generic_argument_error(self, invalid, evaluation: Evaluation):
449449
"%(name)s[invalid___]"
450450

test/builtin/test_numeric.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,6 @@ def test_realvalued():
118118
None,
119119
),
120120
("Sign[1 - 4*I] == (1/17 - 4 I/17) Sqrt[17]", None, "True", None),
121-
(
122-
"Sign[4, 5, 6]",
123-
("Sign called with 3 arguments; 1 argument is expected.",),
124-
"Sign[4, 5, 6]",
125-
None,
126-
),
127121
('Sign["20"]', None, "Sign[20]", None),
128122
],
129123
)
@@ -138,3 +132,46 @@ def test_private_doctests_numeric(str_expr, msgs, str_expected, fail_msg):
138132
failure_message=fail_msg,
139133
expected_messages=msgs,
140134
)
135+
136+
137+
@pytest.mark.parametrize(
138+
("str_expr", "msgs", "assert_fail_msg"),
139+
[
140+
(
141+
"Round[a, b]",
142+
None,
143+
"Round with one symbolic argument should not give an error message",
144+
),
145+
(
146+
"Round[a, b]",
147+
None,
148+
"Round with two symbolic arguments should not give an error message",
149+
),
150+
(
151+
"Round[a, b, c]",
152+
("Round called with 3 arguments; 1 or 2 arguments are expected.",),
153+
"Round wrong number of arguments",
154+
),
155+
(
156+
"Sign[x]",
157+
None,
158+
"Sign with one symbolic argument should not give an error message",
159+
),
160+
(
161+
"Sign[4, 5, 6]",
162+
("Sign called with 3 arguments; 1 argument is expected.",),
163+
"Sign wrong number of arguments",
164+
),
165+
],
166+
)
167+
def test_wrong_number_of_arguments(str_expr, msgs, assert_fail_msg):
168+
""" """
169+
check_evaluation(
170+
str_expr,
171+
str_expr,
172+
to_string_expr=True,
173+
to_string_expected=True,
174+
hold_expected=True,
175+
failure_message=assert_fail_msg,
176+
expected_messages=msgs,
177+
)

0 commit comments

Comments
 (0)