1111import threading
1212from enum import Enum
1313from typing import Callable , List , Tuple , Optional , Union
14+ import concurrent .futures
1415
1516
1617class TConnectionType (Enum ):
1718 """Connection type for signal-slot connections."""
19+
1820 DIRECT_CONNECTION = 1
1921 QUEUED_CONNECTION = 2
2022
2123
22- class _SignalConstants :
24+ class TSignalConstants :
2325 """Constants for signal-slot communication."""
26+
2427 FROM_EMIT = "_from_emit"
2528 THREAD = "_thread"
2629 LOOP = "_loop"
@@ -38,7 +41,7 @@ def _wrap_direct_function(func):
3841 def wrapper (* args , ** kwargs ):
3942 """Wrapper for directly connected functions"""
4043 # Remove FROM_EMIT
41- kwargs .pop (_SignalConstants .FROM_EMIT , False )
44+ kwargs .pop (TSignalConstants .FROM_EMIT , False )
4245
4346 # DIRECT_CONNECTION executes immediately regardless of thread
4447 if is_coroutine :
@@ -55,6 +58,7 @@ def wrapper(*args, **kwargs):
5558
5659class TSignal :
5760 """Signal class for tsignal."""
61+
5862 def __init__ (self ):
5963 self .connections : List [Tuple [Optional [object ], Callable , TConnectionType ]] = []
6064
@@ -74,8 +78,8 @@ def connect(
7478
7579 if hasattr (receiver_or_slot , "__self__" ):
7680 obj = receiver_or_slot .__self__
77- if hasattr (obj , _SignalConstants .THREAD ) and hasattr (
78- obj , _SignalConstants .LOOP
81+ if hasattr (obj , TSignalConstants .THREAD ) and hasattr (
82+ obj , TSignalConstants .LOOP
7983 ):
8084 receiver = obj
8185 slot = receiver_or_slot
@@ -161,17 +165,24 @@ def call_wrapper(s=slot):
161165 logger .error ("Error in signal emission: %s" , e , exc_info = True )
162166
163167
168+ class TSignalProperty (property ):
169+ def __init__ (self , fget , signal_name ):
170+ super ().__init__ (fget )
171+ self .signal_name = signal_name
172+
173+
164174def t_signal (func ):
165175 """Signal decorator"""
166176 sig_name = func .__name__
167177
168- @property
178+ # Using @property for lazy initialization of the signal.
179+ # The signal object is created only when first accessed, and a cached object is returned thereafter.
169180 def wrapper (self ):
170181 if not hasattr (self , f"_{ sig_name } " ):
171182 setattr (self , f"_{ sig_name } " , TSignal ())
172183 return getattr (self , f"_{ sig_name } " )
173184
174- return wrapper
185+ return TSignalProperty ( wrapper , sig_name )
175186
176187
177188def t_slot (func ):
@@ -183,12 +194,12 @@ def t_slot(func):
183194 @functools .wraps (func )
184195 async def wrapper (self , * args , ** kwargs ):
185196 """Wrapper for coroutine slots"""
186- from_emit = kwargs .pop (_SignalConstants .FROM_EMIT , False )
197+ from_emit = kwargs .pop (TSignalConstants .FROM_EMIT , False )
187198
188- if not hasattr (self , _SignalConstants .THREAD ):
199+ if not hasattr (self , TSignalConstants .THREAD ):
189200 self ._thread = threading .current_thread ()
190201
191- if not hasattr (self , _SignalConstants .LOOP ):
202+ if not hasattr (self , TSignalConstants .LOOP ):
192203 try :
193204 self ._loop = asyncio .get_running_loop ()
194205 except RuntimeError :
@@ -211,12 +222,12 @@ async def wrapper(self, *args, **kwargs):
211222 @functools .wraps (func )
212223 def wrapper (self , * args , ** kwargs ):
213224 """Wrapper for regular slots"""
214- from_emit = kwargs .pop (_SignalConstants .FROM_EMIT , False )
225+ from_emit = kwargs .pop (TSignalConstants .FROM_EMIT , False )
215226
216- if not hasattr (self , _SignalConstants .THREAD ):
227+ if not hasattr (self , TSignalConstants .THREAD ):
217228 self ._thread = threading .current_thread ()
218229
219- if not hasattr (self , _SignalConstants .LOOP ):
230+ if not hasattr (self , TSignalConstants .LOOP ):
220231 try :
221232 self ._loop = asyncio .get_running_loop ()
222233 except RuntimeError :
@@ -227,8 +238,18 @@ def wrapper(self, *args, **kwargs):
227238 current_thread = threading .current_thread ()
228239 if current_thread != self ._thread :
229240 logger .debug ("Executing regular slot from different thread" )
230- self ._loop .call_soon_threadsafe (lambda : func (self , * args , ** kwargs ))
231- return None
241+ # 동기 함수는 loop.call_soon_threadsafe와 Future를 사용
242+ future = concurrent .futures .Future ()
243+
244+ def callback ():
245+ try :
246+ result = func (self , * args , ** kwargs )
247+ future .set_result (result )
248+ except Exception as e :
249+ future .set_exception (e )
250+
251+ self ._loop .call_soon_threadsafe (callback )
252+ return future .result ()
232253
233254 return func (self , * args , ** kwargs )
234255
@@ -240,6 +261,7 @@ def t_with_signals(cls):
240261 original_init = cls .__init__
241262
242263 def __init__ (self , * args , ** kwargs ):
264+ logger .debug ("t_with_signals __init__" )
243265 # Set thread and event loop
244266 self ._thread = threading .current_thread ()
245267 try :
0 commit comments