From 65b09b9dd9492194eb13759a2213e18a2a8e1a8b Mon Sep 17 00:00:00 2001 From: mmatera Date: Wed, 8 Jun 2022 20:03:06 -0300 Subject: [PATCH 1/9] improve TimeConstrained implementation for working with pyston and windows removing condition for win32 --- CHANGES.rst | 2 +- mathics/builtin/datentime.py | 120 +++++++++++++++++---------------- mathics/builtin/inout.py | 1 + mathics/core/evaluation.py | 38 ++++++++++- test/builtin/test_datentime.py | 53 ++++++++++----- 5 files changed, 138 insertions(+), 76 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cafca41ce..e7dfa9c4e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,7 +13,7 @@ Enhancements * In assignment to messages associated with symbols, the attribute ``Protected`` is not having into account, following the standard in WMA. With this and the above change, Combinatorical 2.0 works as written. * ``Share[]`` performs an explicit call to the Python garbage collection and returns the amount of memory free. * Improving the compatibility of ``TeXForm`` and ``MathMLForm`` outputs with WMA. MatML tags around numbers appear as "" tags instead of "", except in the case of ``InputForm`` expressions. In TeXForm some quotes around strings have been removed to conform to WMA. It is not clear whether this is the correct behavior. - +* ``TimeConstrained`` now works on pyston. Documentation diff --git a/mathics/builtin/datentime.py b/mathics/builtin/datentime.py index 4892ac118..ea7340aa0 100644 --- a/mathics/builtin/datentime.py +++ b/mathics/builtin/datentime.py @@ -25,6 +25,7 @@ from mathics.core.symbols import Symbol, SymbolList from mathics.core.systemsymbols import ( SymbolAborted, + SymbolFailed, SymbolInfinity, SymbolNull, SymbolRowBox, @@ -1040,14 +1041,21 @@ class Pause(Builtin): def apply(self, n, evaluation): "Pause[n_]" - sleeptime = n.to_python() - if not isinstance(sleeptime, (int, float)) or sleeptime < 0: + sleeptime = float(n.to_python()) + if not isinstance(sleeptime, float) or sleeptime < 0: evaluation.message( "Pause", "numnm", Expression(SymbolPause, from_python(n)) ) return - - time.sleep(sleeptime) + # This checks each 10ms if the evaluation + # was stopped. + while sleeptime > 0.01: + sleeptime = sleeptime - 0.01 + time.sleep(0.01) + if evaluation.timeout: + return SymbolNull + if sleeptime > 0: + time.sleep(sleeptime) return SymbolNull @@ -1088,72 +1096,70 @@ def evaluate(self, evaluation): return Expression(SymbolDateObject.evaluate(evaluation)) -if sys.platform != "win32" and not hasattr(sys, "pyston_version_info"): - - class TimeConstrained(Builtin): - r""" -
-
'TimeConstrained[$expr$, $t$]' -
'evaluates $expr$, stopping after $t$ seconds.' +class TimeConstrained(Builtin): + r""" +
+
'TimeConstrained[$expr$, $t$]' +
'evaluates $expr$, stopping after $t$ seconds.' -
'TimeConstrained[$expr$, $t$, $failexpr$]' -
'returns $failexpr$ if the time constraint is not met.' -
+
'TimeConstrained[$expr$, $t$, $failexpr$]' +
'returns $failexpr$ if the time constraint is not met.' +
- Possible issues: for certain time-consuming functions (like simplify) - which are based on sympy or other libraries, it is possible that - the evaluation continues after the timeout. However, at the end of the evaluation, the function will return '$Aborted' and the results will not affect - the state of the \Mathics kernel. + Possible issues: for certain time-consuming functions (like simplify) + which are based on sympy or other libraries, it is possible that + the evaluation continues after the timeout. However, at the end of the evaluation, the function will return '$Aborted' and the results will not affect + the state of the \Mathics kernel. - """ - # FIXME: these tests sometimes cause SEGVs which probably means - # that TimeConstraint has bugs. + # FIXME: these tests sometimes cause SEGVs which probably means + # that TimeConstraint has bugs. - # Consider testing via unit tests. - # >> TimeConstrained[Integrate[Sin[x]^1000000,x],1] - # = $Aborted + # Consider testing via unit tests. + >> TimeConstrained[Pause[.5];x,.1] + = $Aborted - # >> TimeConstrained[Integrate[Sin[x]^1000000,x], 1, Integrate[Cos[x],x]] - # = Sin[x] + >> TimeConstrained[Pause[.5];Integrate[Sin[x],x], .1, Integrate[Cos[x],x]] + = Sin[x] - # >> s=TimeConstrained[Integrate[Sin[x] ^ 3, x], a] - # : Number of seconds a is not a positive machine-sized number or Infinity. - # = TimeConstrained[Integrate[Sin[x] ^ 3, x], a] + >> s=TimeConstrained[Integrate[Sin[x] ^ 3, x], a] + : Number of seconds a is not a positive machine-sized number or Infinity. + = TimeConstrained[Integrate[Sin[x] ^ 3, x], a] - # >> a=1; s - # = Cos[x] (-5 + Cos[2 x]) / 6 + >> a=.5; s + = Cos[x] (-3 + Cos[x] ^ 2) / 3 + """ - attributes = hold_all | protected - messages = { - "timc": "Number of seconds `1` is not a positive machine-sized number or Infinity.", - } + attributes = hold_all | protected + messages = { + "timc": "Number of seconds `1` is not a positive machine-sized number or Infinity.", + } - summary_text = "run a command for at most a specified time" + summary_text = "run a command for at most a specified time" - def apply_2(self, expr, t, evaluation): - "TimeConstrained[expr_, t_]" - return self.apply_3(expr, t, SymbolAborted, evaluation) + def apply_2(self, expr, t, evaluation): + "TimeConstrained[expr_, t_]" + return self.apply_3(expr, t, SymbolAborted, evaluation) - def apply_3(self, expr, t, failexpr, evaluation): - "TimeConstrained[expr_, t_, failexpr_]" - t = t.evaluate(evaluation) - if not t.is_numeric(evaluation): - evaluation.message("TimeConstrained", "timc", t) - return - try: - t = float(t.to_python()) - evaluation.timeout_queue.append((t, datetime.now().timestamp())) - request = lambda: expr.evaluate(evaluation) - res = run_with_timeout_and_stack(request, t, evaluation) - except TimeoutInterrupt: - evaluation.timeout_queue.pop() - return failexpr.evaluate(evaluation) - except: - evaluation.timeout_queue.pop() - raise + def apply_3(self, expr, t, failexpr, evaluation): + "TimeConstrained[expr_, t_, failexpr_]" + t = t.evaluate(evaluation) + if not t.is_numeric(evaluation): + evaluation.message("TimeConstrained", "timc", t) + return + try: + t = float(t.to_python()) + evaluation.timeout_queue.append((t, datetime.now().timestamp())) + request = lambda: expr.evaluate(evaluation) + res = run_with_timeout_and_stack(request, t, evaluation) + except TimeoutInterrupt: + evaluation.timeout_queue.pop() + return failexpr.evaluate(evaluation) + except: evaluation.timeout_queue.pop() - return res + raise + evaluation.timeout_queue.pop() + return res class TimeZone(Predefined): diff --git a/mathics/builtin/inout.py b/mathics/builtin/inout.py index 2d2413383..2e0aa0758 100644 --- a/mathics/builtin/inout.py +++ b/mathics/builtin/inout.py @@ -2017,6 +2017,7 @@ class General(Builtin): "invalidargs": "Invalid arguments.", "notboxes": "`1` is not a valid box structure.", "pyimport": '`1`[] is not available. Python module "`2`" is not installed.', + "warn": "`1`", } summary_text = "general-purpose messages" diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index 6e3a1b503..c8554e9fb 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -65,6 +65,38 @@ def _thread_target(request, queue) -> None: queue.put((False, exc_info)) +def kill_thread(thread) -> bool: + """ + Tries to kill a thread. + If successful, returns True; otherwise, False. + """ + # See https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/ + + from ctypes import pythonapi, py_object, c_long + + thread_id = None + # First, look for the thread id + if hasattr(thread, "_thread_id"): + thread_id = thread._thread_id + else: + import threading + + for id, thr in threading._active.items(): + if thr is thread: + thread_id = id + if thread_id is None: + # The thread does not exists anymore. Our work has been done. + return True + + result = pythonapi.PyThreadState_SetAsyncExc( + c_long(thread_id), py_object(SystemExit) + ) + if result == 1: + return True + pythonapi.PyThreadState_SetAsyncExc(c_long(thread_id), None) + return False + + # MAX_RECURSION_DEPTH gives the maximum value allowed for $RecursionLimit. it's usually set to its # default settings.DEFAULT_MAX_RECURSION_DEPTH. @@ -128,10 +160,10 @@ def run_with_timeout_and_stack(request, timeout, evaluation): thread.join(timeout) if thread.is_alive(): evaluation.timeout = True - while thread.is_alive(): - pass - evaluation.timeout = False + if not kill_thread(thread): + evaluation.message("General", "warn", "thread couldn't be stopped.") evaluation.stopped = False + evaluation.timeout = False raise TimeoutInterrupt() success, result = queue.get() diff --git a/test/builtin/test_datentime.py b/test/builtin/test_datentime.py index 9430a923f..44cdeef11 100644 --- a/test/builtin/test_datentime.py +++ b/test/builtin/test_datentime.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from test.helper import check_evaluation, evaluate +from mathics.core.symbols import Symbol import pytest import sys @@ -7,26 +8,48 @@ import time -@pytest.mark.skipif( - sys.platform in ("win32",) or hasattr(sys, "pyston_version_info"), - reason="TimeConstrained needs to be rewritten", -) -def test_timeremaining(): - str_expr = "TimeConstrained[1+2; TimeRemaining[], 0.9]" - result = evaluate(str_expr) - assert result is None or 0 < result.to_python() < 9 - - -@pytest.mark.skip(reason="TimeConstrained needs to be rewritten") -def test_timeconstrained1(): - # +def test_timeconstrained_assignment_1(): + # This test str_expr1 = "a=1.; TimeConstrained[Do[Pause[.1];a=a+1,{1000}],1]" result = evaluate(str_expr1) str_expected = "$Aborted" expected = evaluate(str_expected) assert result == expected time.sleep(1) - assert evaluate("a").to_python() == 10 + # if all the operations where instantaneous, then the + # value of ``a`` should be 10. However, in macOS, ``a`` + # just reach 3... + assert evaluate("a").to_python() <= 10 + + +def test_timeconstrained_assignment_2(): + # This test checks if the assignment is really aborted + # if the RHS exceeds the wall time. + str_expr1 = "a=1.; TimeConstrained[a=(Pause[.2];2.),.1]" + result = evaluate(str_expr1) + str_expected = "$Aborted" + expected = evaluate(str_expected) + assert result == expected + time.sleep(0.2) + assert evaluate("a").to_python() == 1.0 + + +def test_timeconstrained_sympy(): + # This test tries to run a large and onerous calculus that runs + # in sympy (outside the control of Mathics). + # If the behaviour is the right one, the evaluation + # is interrupted before it saturates memory and raise a SIGEV + # exception. + str_expr = "TimeConstrained[Integrate[Sin[x]^1000000, x], 0.9]" + result = evaluate(str_expr) + + assert result is None or result == Symbol("$Aborted") + + +def test_timeremaining(): + str_expr = "TimeConstrained[1+2; TimeRemaining[], 0.9]" + result = evaluate(str_expr) + assert result is None or 0 < result.to_python() < 0.9 def test_datelist(): @@ -55,7 +78,7 @@ def test_datelist(): check_evaluation(str_expr, str_expected) -def test_datestring(): +def test_datestring2(): for str_expr, str_expected in ( ## Check Leading 0s # ( From b22b6bb171b3bdc9c7f8d2bf9c95f46aa5a7505c Mon Sep 17 00:00:00 2001 From: mmatera Date: Thu, 9 Jun 2022 11:07:39 -0300 Subject: [PATCH 2/9] better use process --- mathics/core/evaluation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index c8554e9fb..864b7fb40 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -6,6 +6,8 @@ import os import sys + +# maybe better use process from threading import Thread, stack_size as set_thread_stack_size from typing import Tuple From 4c3275379bb84ff439299679aabd79c37c59b310 Mon Sep 17 00:00:00 2001 From: mmatera Date: Thu, 9 Jun 2022 11:59:27 -0300 Subject: [PATCH 3/9] thread->process --- mathics/core/evaluation.py | 86 +++++++++----------------------------- 1 file changed, 20 insertions(+), 66 deletions(-) diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index 864b7fb40..f06c9e3a1 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from queue import Queue import time @@ -8,7 +7,9 @@ import sys # maybe better use process -from threading import Thread, stack_size as set_thread_stack_size +from threading import stack_size as set_thread_stack_size +from multiprocessing import Process, Queue + from typing import Tuple @@ -58,47 +59,6 @@ SymbolPost = Symbol("System`$Post") -def _thread_target(request, queue) -> None: - try: - result = request() - queue.put((True, result)) - except BaseException: - exc_info = sys.exc_info() - queue.put((False, exc_info)) - - -def kill_thread(thread) -> bool: - """ - Tries to kill a thread. - If successful, returns True; otherwise, False. - """ - # See https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/ - - from ctypes import pythonapi, py_object, c_long - - thread_id = None - # First, look for the thread id - if hasattr(thread, "_thread_id"): - thread_id = thread._thread_id - else: - import threading - - for id, thr in threading._active.items(): - if thr is thread: - thread_id = id - if thread_id is None: - # The thread does not exists anymore. Our work has been done. - return True - - result = pythonapi.PyThreadState_SetAsyncExc( - c_long(thread_id), py_object(SystemExit) - ) - if result == 1: - return True - pythonapi.PyThreadState_SetAsyncExc(c_long(thread_id), None) - return False - - # MAX_RECURSION_DEPTH gives the maximum value allowed for $RecursionLimit. it's usually set to its # default settings.DEFAULT_MAX_RECURSION_DEPTH. @@ -137,33 +97,27 @@ def run_with_timeout_and_stack(request, timeout, evaluation): # MATHICS_MAX_RECURSION_DEPTH. if it is set, we always use a thread, even if timeout is None, in # order to be able to set the thread stack size. - if MAX_RECURSION_DEPTH > settings.DEFAULT_MAX_RECURSION_DEPTH: - set_thread_stack_size(python_stack_size(MAX_RECURSION_DEPTH)) - elif timeout is None: + # if MAX_RECURSION_DEPTH > settings.DEFAULT_MAX_RECURSION_DEPTH: + # set_thread_stack_size(python_stack_size(MAX_RECURSION_DEPTH)) + # elif timeout is None: + if timeout is None: return request() + def _process_target(request, queue) -> None: + try: + result = request() + queue.put((True, result)) + except BaseException: + exc_info = sys.exc_info() + queue.put((False, exc_info)) + queue = Queue(maxsize=1) # stores the result or exception - thread = Thread(target=_thread_target, args=(request, queue)) - thread.start() - - # Thead join(timeout) can leave zombie threads (we are the parent) - # when a time out occurs, but the thread hasn't terminated. See - # https://docs.python.org/3/library/multiprocessing.shared_memory.html - # for a detailed discussion of this. - # - # To reduce this problem, we make use of specific properties of - # the Mathics evaluator: if we set "evaluation.timeout", the - # next call to "Expression.evaluate" in the thread will finish it - # immediately. - # - # However this still will not terminate long-running processes - # in Sympy or or libraries called by Mathics that might hang or run - # for a long time. - thread.join(timeout) - if thread.is_alive(): + process = Process(target=_process_target, args=(request, queue)) + process.start() + process.join(timeout) + if process.is_alive(): evaluation.timeout = True - if not kill_thread(thread): - evaluation.message("General", "warn", "thread couldn't be stopped.") + process.terminate() evaluation.stopped = False evaluation.timeout = False raise TimeoutInterrupt() From 5661d063dc4a12980b48f10e3fab42560b75bc6d Mon Sep 17 00:00:00 2001 From: mmatera Date: Thu, 9 Jun 2022 12:12:05 -0300 Subject: [PATCH 4/9] thread->process --- mathics/core/evaluation.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index f06c9e3a1..80abb5f75 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -88,6 +88,15 @@ def set_python_recursion_limit(n) -> None: raise OverflowError +def _process_target(request, queue) -> None: + try: + result = request() + queue.put((True, result)) + except BaseException: + exc_info = sys.exc_info() + queue.put((False, exc_info)) + + def run_with_timeout_and_stack(request, timeout, evaluation): """ interrupts evaluation after a given time period. Provides a suitable stack environment. @@ -103,14 +112,6 @@ def run_with_timeout_and_stack(request, timeout, evaluation): if timeout is None: return request() - def _process_target(request, queue) -> None: - try: - result = request() - queue.put((True, result)) - except BaseException: - exc_info = sys.exc_info() - queue.put((False, exc_info)) - queue = Queue(maxsize=1) # stores the result or exception process = Process(target=_process_target, args=(request, queue)) process.start() From 7be47954517f0030a28269d5f289270458bd0aac Mon Sep 17 00:00:00 2001 From: mmatera Date: Thu, 9 Jun 2022 12:20:34 -0300 Subject: [PATCH 5/9] as a funciton --- mathics/builtin/datentime.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mathics/builtin/datentime.py b/mathics/builtin/datentime.py index ea7340aa0..82f0532ef 100644 --- a/mathics/builtin/datentime.py +++ b/mathics/builtin/datentime.py @@ -1150,7 +1150,10 @@ def apply_3(self, expr, t, failexpr, evaluation): try: t = float(t.to_python()) evaluation.timeout_queue.append((t, datetime.now().timestamp())) - request = lambda: expr.evaluate(evaluation) + + def request(): + expr.evaluate(evaluation) + res = run_with_timeout_and_stack(request, t, evaluation) except TimeoutInterrupt: evaluation.timeout_queue.pop() From 78260ef6a440fb27d66a3f9f61a506efadc724f4 Mon Sep 17 00:00:00 2001 From: mmatera Date: Thu, 9 Jun 2022 12:52:51 -0300 Subject: [PATCH 6/9] adjusting walltimes --- mathics/builtin/datentime.py | 2 +- test/builtin/test_datentime.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mathics/builtin/datentime.py b/mathics/builtin/datentime.py index 82f0532ef..29c41a5d3 100644 --- a/mathics/builtin/datentime.py +++ b/mathics/builtin/datentime.py @@ -1126,7 +1126,7 @@ class TimeConstrained(Builtin): : Number of seconds a is not a positive machine-sized number or Infinity. = TimeConstrained[Integrate[Sin[x] ^ 3, x], a] - >> a=.5; s + >> a=2.; s = Cos[x] (-3 + Cos[x] ^ 2) / 3 """ diff --git a/test/builtin/test_datentime.py b/test/builtin/test_datentime.py index 44cdeef11..6a07e874f 100644 --- a/test/builtin/test_datentime.py +++ b/test/builtin/test_datentime.py @@ -47,9 +47,9 @@ def test_timeconstrained_sympy(): def test_timeremaining(): - str_expr = "TimeConstrained[1+2; TimeRemaining[], 0.9]" + str_expr = "TimeConstrained[1+2; TimeRemaining[], 10]" result = evaluate(str_expr) - assert result is None or 0 < result.to_python() < 0.9 + assert result is None or 0 < result.to_python() < 10.0 def test_datelist(): From 20ef971862573b0339af2cd53588134ab39fec92 Mon Sep 17 00:00:00 2001 From: mmatera Date: Thu, 9 Jun 2022 15:14:21 -0300 Subject: [PATCH 7/9] fix non-windows --- mathics/builtin/datentime.py | 2 +- test/builtin/test_datentime.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mathics/builtin/datentime.py b/mathics/builtin/datentime.py index 29c41a5d3..edf26cfdb 100644 --- a/mathics/builtin/datentime.py +++ b/mathics/builtin/datentime.py @@ -1152,7 +1152,7 @@ def apply_3(self, expr, t, failexpr, evaluation): evaluation.timeout_queue.append((t, datetime.now().timestamp())) def request(): - expr.evaluate(evaluation) + return expr.evaluate(evaluation) res = run_with_timeout_and_stack(request, t, evaluation) except TimeoutInterrupt: diff --git a/test/builtin/test_datentime.py b/test/builtin/test_datentime.py index 6a07e874f..7c59fbb6c 100644 --- a/test/builtin/test_datentime.py +++ b/test/builtin/test_datentime.py @@ -19,7 +19,8 @@ def test_timeconstrained_assignment_1(): # if all the operations where instantaneous, then the # value of ``a`` should be 10. However, in macOS, ``a`` # just reach 3... - assert evaluate("a").to_python() <= 10 + result = evaluate("a").to_python() + assert result <= 10 def test_timeconstrained_assignment_2(): @@ -42,7 +43,6 @@ def test_timeconstrained_sympy(): # exception. str_expr = "TimeConstrained[Integrate[Sin[x]^1000000, x], 0.9]" result = evaluate(str_expr) - assert result is None or result == Symbol("$Aborted") From 54e71d3373376673b4bda3072cc0434d79afdc1b Mon Sep 17 00:00:00 2001 From: mmatera Date: Thu, 9 Jun 2022 22:22:04 -0300 Subject: [PATCH 8/9] fork --- mathics/core/evaluation.py | 4 +++- test/builtin/test_datentime.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index 80abb5f75..ca0cfddf0 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -8,7 +8,9 @@ # maybe better use process from threading import stack_size as set_thread_stack_size -from multiprocessing import Process, Queue +from multiprocessing import Process, Queue, set_start_method as mp_set_start_method + +mp_set_start_method("fork") from typing import Tuple diff --git a/test/builtin/test_datentime.py b/test/builtin/test_datentime.py index 7c59fbb6c..d301ef4e8 100644 --- a/test/builtin/test_datentime.py +++ b/test/builtin/test_datentime.py @@ -35,6 +35,20 @@ def test_timeconstrained_assignment_2(): assert evaluate("a").to_python() == 1.0 +def test_timeconstrained_assignment_3(): + # This test checks if the assignment is really aborted + # if the RHS exceeds the wall time. + str_expr1 = ( + "a=1.;TimeConstrained[TimeConstrained[a=(Pause[.2];2.), .1, a=-2], .1,a=-3]" + ) + result = evaluate(str_expr1) + str_expected = "-3" + expected = evaluate(str_expected) + assert result == expected + time.sleep(0.2) + assert evaluate("a").to_python() == -3 + + def test_timeconstrained_sympy(): # This test tries to run a large and onerous calculus that runs # in sympy (outside the control of Mathics). From 732e576c1533b490019f2586ea6657dfb9864fe2 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 10 Jun 2022 09:33:19 -0300 Subject: [PATCH 9/9] adjusting times --- test/builtin/test_datentime.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/builtin/test_datentime.py b/test/builtin/test_datentime.py index d301ef4e8..39fe81aa8 100644 --- a/test/builtin/test_datentime.py +++ b/test/builtin/test_datentime.py @@ -39,13 +39,13 @@ def test_timeconstrained_assignment_3(): # This test checks if the assignment is really aborted # if the RHS exceeds the wall time. str_expr1 = ( - "a=1.;TimeConstrained[TimeConstrained[a=(Pause[.2];2.), .1, a=-2], .1,a=-3]" + "a=1.;TimeConstrained[TimeConstrained[a=(Pause[.1];2.), .3, a=-2], .1,a=-3]" ) result = evaluate(str_expr1) str_expected = "-3" expected = evaluate(str_expected) assert result == expected - time.sleep(0.2) + time.sleep(0.3) assert evaluate("a").to_python() == -3