Skip to content

Commit ff2f585

Browse files
authored
Merge branch 'master' into pretty_print
2 parents 4af37ff + 275aa03 commit ff2f585

File tree

8 files changed

+199
-70
lines changed

8 files changed

+199
-70
lines changed

mathics/builtin/datentime.py

+3-54
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@
3434
)
3535
from mathics.core.expression import Expression
3636
from mathics.core.list import ListExpression
37-
from mathics.core.symbols import Symbol, SymbolNull
37+
from mathics.core.symbols import Symbol
3838
from mathics.core.systemsymbols import (
3939
SymbolAborted,
40+
SymbolAbsoluteTime,
4041
SymbolAutomatic,
4142
SymbolInfinity,
4243
SymbolRowBox,
@@ -109,11 +110,9 @@ def total_seconds(td):
109110
else:
110111
total_seconds = timedelta.total_seconds
111112

112-
SymbolAbsoluteTime = Symbol("AbsoluteTime")
113113
SymbolDateObject = Symbol("DateObject")
114114
SymbolDateString = Symbol("DateString")
115115
SymbolGregorian = Symbol("Gregorian")
116-
SymbolPause = Symbol("Pause")
117116

118117

119118
class _Date:
@@ -1026,39 +1025,6 @@ def eval(self, year, evaluation):
10261025
return ListExpression(year, Integer(month), Integer(day))
10271026

10281027

1029-
class Pause(Builtin):
1030-
"""
1031-
<url>:WMA link:https://reference.wolfram.com/language/ref/Pause.html</url>
1032-
1033-
<dl>
1034-
<dt>'Pause[n]'
1035-
<dd>pauses for $n$ seconds.
1036-
</dl>
1037-
1038-
>> Pause[0.5]
1039-
"""
1040-
1041-
messages = {
1042-
"numnm": (
1043-
"Non-negative machine-sized number expected at " "position 1 in `1`."
1044-
),
1045-
}
1046-
1047-
summary_text = "pause for a number of seconds"
1048-
1049-
def eval(self, n, evaluation):
1050-
"Pause[n_]"
1051-
sleeptime = n.to_python()
1052-
if not isinstance(sleeptime, (int, float)) or sleeptime < 0:
1053-
evaluation.message(
1054-
"Pause", "numnm", Expression(SymbolPause, from_python(n))
1055-
)
1056-
return
1057-
1058-
time.sleep(sleeptime)
1059-
return SymbolNull
1060-
1061-
10621028
class SystemTimeZone(Predefined):
10631029
"""
10641030
<url>
@@ -1103,7 +1069,7 @@ def evaluate(self, evaluation):
11031069
return Expression(SymbolDateObject.evaluate(evaluation))
11041070

11051071

1106-
if sys.platform != "win32" and not hasattr(sys, "pyston_version_info"):
1072+
if sys.platform != "emscripten":
11071073

11081074
class TimeConstrained(Builtin):
11091075
"""
@@ -1124,23 +1090,6 @@ class TimeConstrained(Builtin):
11241090
the state of the Mathics3 kernel.
11251091
"""
11261092

1127-
# FIXME: these tests sometimes cause SEGVs which probably means
1128-
# that TimeConstraint has bugs.
1129-
1130-
# Consider testing via unit tests.
1131-
# >> TimeConstrained[Integrate[Sin[x]^1000000,x],1]
1132-
# = $Aborted
1133-
1134-
# >> TimeConstrained[Integrate[Sin[x]^1000000,x], 1, Integrate[Cos[x],x]]
1135-
# = Sin[x]
1136-
1137-
# >> s=TimeConstrained[Integrate[Sin[x] ^ 3, x], a]
1138-
# : Number of seconds a is not a positive machine-sized number or Infinity.
1139-
# = TimeConstrained[Integrate[Sin[x] ^ 3, x], a]
1140-
1141-
# >> a=1; s
1142-
# = Cos[x] (-5 + Cos[2 x]) / 6
1143-
11441093
attributes = A_HOLD_ALL | A_PROTECTED
11451094
messages = {
11461095
"timc": (

mathics/builtin/procedural.py

+45-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
environment.
1717
"""
1818

19+
import time
1920

2021
from mathics.core.attributes import (
2122
A_HOLD_ALL,
@@ -24,6 +25,7 @@
2425
A_READ_PROTECTED,
2526
)
2627
from mathics.core.builtin import BinaryOperator, Builtin, IterationFunction
28+
from mathics.core.convert.python import from_python
2729
from mathics.core.evaluation import Evaluation
2830
from mathics.core.expression import Expression
2931
from mathics.core.interrupt import (
@@ -34,7 +36,7 @@
3436
WLThrowInterrupt,
3537
)
3638
from mathics.core.symbols import Symbol, SymbolFalse, SymbolNull, SymbolTrue
37-
from mathics.core.systemsymbols import SymbolMatchQ
39+
from mathics.core.systemsymbols import SymbolMatchQ, SymbolPause
3840
from mathics.eval.patterns import match
3941

4042
SymbolWhich = Symbol("Which")
@@ -438,6 +440,48 @@ def eval(self, evaluation: Evaluation):
438440
raise AbortInterrupt
439441

440442

443+
class Pause(Builtin):
444+
"""
445+
<url>:WMA link:https://reference.wolfram.com/language/ref/Pause.html</url>
446+
447+
<dl>
448+
<dt>'Pause[n]'
449+
<dd>pauses for at least $n$ seconds.
450+
</dl>
451+
452+
>> Pause[0.5]
453+
"""
454+
455+
messages = {
456+
"numnm": (
457+
"Non-negative machine-sized number expected at " "position 1 in `1`."
458+
),
459+
}
460+
461+
summary_text = "pause for a number of seconds"
462+
463+
# Number of timeout polls per second that we perform in looking
464+
# for a timeout.
465+
PAUSE_TICKS_PER_SECOND = 1000
466+
467+
def eval(self, n, evaluation: Evaluation):
468+
"Pause[n_]"
469+
sleeptime = n.to_python()
470+
if not isinstance(sleeptime, (int, float)) or sleeptime < 0:
471+
evaluation.message(
472+
"Pause", "numnm", Expression(SymbolPause, from_python(n))
473+
)
474+
return
475+
476+
steps = int(self.PAUSE_TICKS_PER_SECOND * sleeptime)
477+
for _ in range(steps):
478+
time.sleep(1 / self.PAUSE_TICKS_PER_SECOND)
479+
if evaluation.timeout:
480+
return SymbolNull
481+
482+
return SymbolNull
483+
484+
441485
class Return(Builtin):
442486
"""
443487
<url>:WMA link:

mathics/core/builtin.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
from mathics.core.convert.expression import to_expression
5959
from mathics.core.convert.op import ascii_operator_to_symbol
6060
from mathics.core.convert.python import from_bool
61-
from mathics.core.convert.sympy import from_sympy, to_numeric_sympy_args
61+
from mathics.core.convert.sympy import from_sympy
6262
from mathics.core.definitions import Definition, Definitions
6363
from mathics.core.evaluation import Evaluation
6464
from mathics.core.exceptions import MessageException
@@ -91,6 +91,7 @@
9191
from mathics.eval.numbers.numbers import cancel
9292
from mathics.eval.numerify import numerify
9393
from mathics.eval.scoping import dynamic_scoping
94+
from mathics.eval.sympy import eval_sympy
9495

9596
try:
9697
import ujson
@@ -582,14 +583,7 @@ def eval(self, z, evaluation: Evaluation):
582583
# converted to python and the result is converted from sympy
583584
#
584585
# "%(name)s[z__]"
585-
sympy_args = to_numeric_sympy_args(z, evaluation)
586-
if self.sympy_name is None:
587-
return
588-
sympy_fn = getattr(sympy, self.sympy_name)
589-
try:
590-
return from_sympy(tracing.run_sympy(sympy_fn, *sympy_args))
591-
except Exception:
592-
return
586+
return eval_sympy(self, z, evaluation)
593587

594588
def get_constant(self, precision, evaluation, have_mpmath=False):
595589
try:

mathics/core/evaluation.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def run_with_timeout_and_stack(request, timeout, evaluation):
131131
if thread.is_alive():
132132
evaluation.timeout = True
133133
while thread.is_alive():
134+
time.sleep(0.001)
134135
pass
135136
evaluation.timeout = False
136137
evaluation.stopped = False
@@ -140,7 +141,7 @@ def run_with_timeout_and_stack(request, timeout, evaluation):
140141
if success:
141142
return result
142143
else:
143-
raise result[0].with_traceback(result[1], result[2])
144+
raise result[1].with_traceback(result[2])
144145

145146

146147
class _Out(KeyComparable):

mathics/core/systemsymbols.py

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@
191191
SymbolPath = Symbol("System`$Path")
192192
SymbolPattern = Symbol("System`Pattern")
193193
SymbolPatternTest = Symbol("System`PatternTest")
194+
SymbolPause = Symbol("System`Pause")
194195
SymbolPi = Symbol("System`Pi")
195196
SymbolPiecewise = Symbol("System`Piecewise")
196197
SymbolPlot = Symbol("System`Plot")

mathics/docpipeline.py

+3
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,9 @@ def main():
938938
test_pipeline.print_and_log(
939939
f"Test evaluation took {datetime.now() - start_time} seconds"
940940
)
941+
test_pipeline.print_and_log(
942+
f"Test finished at {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}"
943+
)
941944

942945
if args.show_statistics:
943946
show_lru_cache_statistics()

mathics/eval/sympy.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""
2+
Evaluation of SymPy functions
3+
"""
4+
5+
import sys
6+
from queue import Queue
7+
from threading import Thread
8+
from typing import Optional
9+
10+
import sympy
11+
12+
import mathics.eval.tracing as tracing
13+
from mathics.core.convert.sympy import from_sympy, to_numeric_sympy_args
14+
from mathics.core.element import BaseElement
15+
from mathics.core.evaluation import Evaluation
16+
17+
18+
def eval_sympy_unconstrained(
19+
self, z: BaseElement, evaluation: Evaluation
20+
) -> Optional[BaseElement]:
21+
"""
22+
Evaluate element `z` converting it to SymPy and back to Mathics3.
23+
If an exception is raised we return None.
24+
25+
This version is called not-wrapped in a thread on systems like
26+
emscripten that do not support Python-style threading.
27+
"""
28+
sympy_args = to_numeric_sympy_args(z, evaluation)
29+
if self.sympy_name is None:
30+
return
31+
sympy_fn = getattr(sympy, self.sympy_name)
32+
try:
33+
return from_sympy(tracing.run_sympy(sympy_fn, *sympy_args))
34+
except Exception:
35+
return
36+
37+
38+
def eval_sympy_with_timeout(
39+
self, z: BaseElement, evaluation: Evaluation
40+
) -> Optional[BaseElement]:
41+
"""
42+
Evaluate an element `z` converting it to SymPy,
43+
and back to Mathics3.
44+
If an exception is raised we return None.
45+
46+
This version is run in a thread, and checked for evaluation timeout.
47+
"""
48+
49+
if evaluation.timeout is None:
50+
return eval_sympy_unconstrained(self, z, evaluation)
51+
52+
def _thread_target(queue) -> None:
53+
try:
54+
result = eval_sympy_unconstrained(self, z, evaluation)
55+
queue.put((True, result))
56+
except BaseException:
57+
exc_info = sys.exc_info()
58+
queue.put((False, exc_info))
59+
60+
queue = Queue(maxsize=1) # stores the result or exception
61+
62+
thread = Thread(target=_thread_target, args=(queue,))
63+
thread.start()
64+
while thread.is_alive():
65+
thread.join(0.001)
66+
if evaluation.timeout:
67+
# I can kill the thread.
68+
# just leave it...
69+
return None
70+
71+
# pick the result and return
72+
success, result = queue.get()
73+
if success:
74+
return result
75+
else:
76+
raise result[1].with_traceback(result[2])
77+
78+
79+
# Common top-level evaluation SymPy "eval" function:
80+
eval_sympy = (
81+
eval_sympy_unconstrained
82+
if sys.platform in ("emscripten",)
83+
else eval_sympy_with_timeout
84+
)

0 commit comments

Comments
 (0)