diff --git a/popper/util.py b/popper/util.py index 5b2f0a48..a4098cc1 100644 --- a/popper/util.py +++ b/popper/util.py @@ -10,11 +10,15 @@ from typing import NamedTuple from time import perf_counter from contextlib import contextmanager +from math import comb +import threading + class Literal(NamedTuple): predicate: str arguments: tuple + clingo.script.enable_python() TIMEOUT=1200 @@ -73,6 +77,9 @@ class TimeoutError(Exception): def handler(signum, frame): raise TimeoutError() + + if not (hasattr(signal, 'SIGALRM') and hasattr(signal, 'alarm')): + return _windows_timeout(settings, func, args, kwargs, timeout_duration) # set the timeout handler signal.signal(signal.SIGALRM, handler) @@ -92,6 +99,36 @@ def handler(signum, frame): return result +def _windows_timeout(settings, func, args=(), kwargs={}, timeout_duration=1): + """ + A replacement for the `timeout` function that works on Windows, since the `signal` module is not fully supported on Windows. + """ + result = None + + def target(): + nonlocal result + try: + result = func(*args, **kwargs) + except Exception as e: + result = e + + thread = threading.Thread(target=target) + thread.start() + thread.join(timeout_duration) + + if thread.is_alive(): + # If the thread is still alive after the timeout_duration, it means it timed out + thread.join() # Ensure the thread terminates properly + settings.logger.warn(f'TIMEOUT OF {int(settings.timeout)} SECONDS EXCEEDED') + # raise TimeoutError(f"Execution of {func.__name__} timed out after {timeout_duration} seconds") + return result + + if isinstance(result, Exception): + # If the function raised an exception, re-raise it here + raise result + + return result + def load_kbpath(kbpath): def fix_path(filename): full_filename = os.path.join(kbpath, filename)