auto-coder 0.1.251__py3-none-any.whl → 0.1.252__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

@@ -0,0 +1,201 @@
1
+ from concurrent.futures import ThreadPoolExecutor, TimeoutError, CancelledError
2
+ from threading import Event
3
+ from inspect import signature
4
+ from functools import wraps
5
+ from typing import Any, Optional
6
+ import threading
7
+ import logging
8
+ import time
9
+ from autocoder.common.global_cancel import global_cancel
10
+
11
+ class CancellationRequested(Exception):
12
+ """Raised when a task is requested to be cancelled."""
13
+ pass
14
+
15
+
16
+ def run_in_thread(timeout: Optional[float] = None):
17
+ """Decorator that runs a function in a thread with signal handling.
18
+
19
+ Args:
20
+ timeout (float, optional): Maximum time to wait for thread completion in seconds.
21
+ If None, will wait indefinitely.
22
+
23
+ The decorated function will run in a separate thread and can be interrupted by
24
+ signals like Ctrl+C (KeyboardInterrupt). When interrupted, it will log the event
25
+ and clean up gracefully.
26
+ """
27
+ def decorator(func):
28
+ @wraps(func)
29
+ def wrapper(*args, **kwargs):
30
+ with ThreadPoolExecutor(max_workers=1) as executor:
31
+ future = executor.submit(func, *args, **kwargs)
32
+ start_time = time.time()
33
+
34
+ while True:
35
+ try:
36
+ # 使用较短的超时时间进行轮询,确保能够响应中断信号
37
+ poll_timeout = 0.1
38
+ if timeout is not None:
39
+ remaining = timeout - (time.time() - start_time)
40
+ if remaining <= 0:
41
+ future.cancel()
42
+ raise TimeoutError(f"Timeout after {timeout}s in {func.__name__}")
43
+ poll_timeout = min(poll_timeout, remaining)
44
+
45
+ try:
46
+ return future.result(timeout=poll_timeout)
47
+ except TimeoutError:
48
+ continue # 继续轮询
49
+
50
+ except KeyboardInterrupt:
51
+ logging.warning("KeyboardInterrupt received, attempting to cancel task...")
52
+ future.cancel()
53
+ raise
54
+ except Exception as e:
55
+ logging.error(f"Error occurred in thread: {str(e)}")
56
+ raise
57
+ return wrapper
58
+ return decorator
59
+
60
+ def run_in_thread_with_cancel(timeout: Optional[float] = None):
61
+ """Decorator that runs a function in a thread with explicit cancellation support.
62
+
63
+ Args:
64
+ timeout (float, optional): Maximum time to wait for thread completion in seconds.
65
+ If None, will wait indefinitely.
66
+
67
+ The decorated function MUST accept 'cancel_event' as its first parameter.
68
+ This cancel_event is a threading.Event object that can be used to check if
69
+ cancellation has been requested.
70
+
71
+ The decorated function can be called with an external cancel_event passed as a keyword argument.
72
+ If not provided, a new Event will be created.
73
+
74
+ Example:
75
+ @run_in_thread_with_cancel(timeout=10)
76
+ def long_task(cancel_event, arg1, arg2):
77
+ while not cancel_event.is_set():
78
+ # do work
79
+ if cancel_event.is_set():
80
+ raise CancellationRequested()
81
+
82
+ # 使用外部传入的cancel_event
83
+ external_cancel = Event()
84
+ try:
85
+ result = long_task(arg1, arg2, cancel_event=external_cancel)
86
+ except CancelledError:
87
+ print("Task was cancelled")
88
+
89
+ # 在其他地方取消任务
90
+ external_cancel.set()
91
+ """
92
+ def decorator(func):
93
+ # 检查函数签名
94
+ sig = signature(func)
95
+ params = list(sig.parameters.keys())
96
+ if not params or params[0] != 'cancel_event':
97
+ raise ValueError(
98
+ f"Function {func.__name__} must have 'cancel_event' as its first parameter. "
99
+ f"Current parameters: {params}"
100
+ )
101
+
102
+ @wraps(func)
103
+ def wrapper(*args, **kwargs):
104
+ # 从kwargs中提取或创建cancel_event
105
+ cancel_event = kwargs.pop('cancel_event', None) or Event()
106
+
107
+ def cancellable_task():
108
+ try:
109
+ return func(cancel_event, *args, **kwargs)
110
+ except CancellationRequested:
111
+ logging.info(f"Task {func.__name__} was cancelled")
112
+ raise
113
+ except Exception as e:
114
+ logging.error(f"Error in {func.__name__}: {str(e)}")
115
+ raise
116
+
117
+ with ThreadPoolExecutor(max_workers=1) as executor:
118
+ future = executor.submit(cancellable_task)
119
+ start_time = time.time()
120
+
121
+ while True:
122
+ try:
123
+ # 使用较短的超时时间进行轮询,确保能够响应中断信号
124
+ poll_timeout = 0.1
125
+ if timeout is not None:
126
+ remaining = timeout - (time.time() - start_time)
127
+ if remaining <= 0:
128
+ cancel_event.set()
129
+ future.cancel()
130
+ raise TimeoutError(f"Timeout after {timeout}s in {func.__name__}")
131
+ poll_timeout = min(poll_timeout, remaining)
132
+
133
+ try:
134
+ return future.result(timeout=poll_timeout)
135
+ except TimeoutError:
136
+ continue # 继续轮询
137
+
138
+ except KeyboardInterrupt:
139
+ logging.warning(f"KeyboardInterrupt received, cancelling {func.__name__}...")
140
+ cancel_event.set()
141
+ future.cancel()
142
+ raise CancelledError("Task cancelled by user")
143
+ except CancellationRequested:
144
+ logging.info(f"Task {func.__name__} was cancelled")
145
+ raise CancelledError("Task cancelled by request")
146
+ except Exception as e:
147
+ logging.error(f"Error occurred in thread: {str(e)}")
148
+ raise
149
+
150
+ return wrapper
151
+ return decorator
152
+
153
+
154
+ def run_in_raw_thread():
155
+ """A decorator that runs a function in a separate thread and handles exceptions.
156
+
157
+ Args:
158
+ func: The function to run in a thread
159
+
160
+ Returns:
161
+ A wrapper function that executes the decorated function in a thread
162
+
163
+ The decorator will:
164
+ 1. Run the function in a separate thread
165
+ 2. Handle KeyboardInterrupt properly
166
+ 3. Propagate exceptions from the thread
167
+ 4. Support function arguments
168
+ 5. Preserve function metadata
169
+ """
170
+ def decorator(func):
171
+
172
+ @wraps(func)
173
+ def wrapper(*args, **kwargs):
174
+ # Store thread results
175
+ result = []
176
+ exception = []
177
+ def worker():
178
+ try:
179
+ ret = func(*args, **kwargs)
180
+ result.append(ret)
181
+ global_cancel.reset()
182
+ except Exception as e:
183
+ global_cancel.reset()
184
+ raise
185
+
186
+ # Create and start thread with a meaningful name
187
+ thread = threading.Thread(target=worker, name=f"{func.__name__}_thread")
188
+ thread.daemon = True # Make thread daemon so it doesn't prevent program exit
189
+
190
+ try:
191
+ thread.start()
192
+ while thread.is_alive():
193
+ thread.join(0.1)
194
+
195
+ return result[0] if result else None
196
+ except KeyboardInterrupt:
197
+ global_cancel.set()
198
+ raise KeyboardInterrupt("Task was cancelled by user")
199
+
200
+ return wrapper
201
+ return decorator
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.251"
1
+ __version__ = "0.1.252"