Skip to content

Commit 14a2ade

Browse files
authored
Fix version check timeout, add tests (#2805)
1 parent 3145fea commit 14a2ade

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
lines changed

deeplabcut/gui/window.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
from pathlib import Path
1717
from typing import List
1818
from urllib.error import URLError
19-
from concurrent.futures import ThreadPoolExecutor, TimeoutError
2019
import qdarkstyle
20+
import multiprocessing
2121

2222
import deeplabcut
2323
from deeplabcut import auxiliaryfunctions, VERSION, compat
@@ -42,9 +42,30 @@
4242

4343

4444
def call_with_timeout(func, timeout, *args, **kwargs):
45-
with ThreadPoolExecutor(max_workers=1) as executor:
46-
future = executor.submit(func, *args, **kwargs)
47-
return future.result(timeout=timeout)
45+
def wrapper(queue, *args, **kwargs):
46+
try:
47+
result = func(*args, **kwargs)
48+
queue.put(result) # Pass the result back via the queue
49+
except Exception as e:
50+
queue.put(e) # Pass any exception back via the queue
51+
52+
queue = multiprocessing.Queue()
53+
process = multiprocessing.Process(target=wrapper, args=(queue, *args), kwargs=kwargs)
54+
process.start()
55+
process.join(timeout)
56+
57+
if process.is_alive():
58+
process.terminate() # Forcefully terminate the process
59+
process.join()
60+
raise TimeoutError(f"Function {func.__name__} did not complete within {timeout} seconds.")
61+
62+
if not queue.empty():
63+
result = queue.get()
64+
if isinstance(result, Exception):
65+
raise result # Re-raise the exception if it occurred in the function
66+
return result
67+
else:
68+
raise TimeoutError(f"Function {func.__name__} completed but did not return a result.")
4869

4970

5071
def _check_for_updates(silent=True):

tests/gui/test_gui_window.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#
2+
# DeepLabCut Toolbox (deeplabcut.org)
3+
# © A. & M.W. Mathis Labs
4+
# https://github.com/DeepLabCut/DeepLabCut
5+
#
6+
# Please see AUTHORS for contributors.
7+
# https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS
8+
#
9+
# Licensed under GNU Lesser General Public License v3.0
10+
#
11+
import pytest
12+
import time
13+
from deeplabcut.gui.window import call_with_timeout
14+
15+
def test_call_with_timeout():
16+
def succeeding_method(parameter):
17+
return parameter
18+
19+
parameter = (10, "Hello test")
20+
assert call_with_timeout(succeeding_method, 1, parameter) == parameter
21+
22+
def failing_method():
23+
raise ValueError("Raise value error on purpose")
24+
25+
with pytest.raises(ValueError):
26+
call_with_timeout(failing_method, timeout=1)
27+
28+
def hanging_method():
29+
while True:
30+
time.sleep(1)
31+
32+
with pytest.raises(TimeoutError):
33+
call_with_timeout(hanging_method, timeout=1)

0 commit comments

Comments
 (0)