2021-07-14 22:53:15 +08:00
|
|
|
|
import functools
|
|
|
|
|
import inspect
|
|
|
|
|
import threading
|
2021-09-22 06:21:48 +08:00
|
|
|
|
from Difficult_Rocket import crash
|
2021-10-31 23:26:32 +08:00
|
|
|
|
from typing import Optional, Callable
|
|
|
|
|
|
2021-07-14 22:53:15 +08:00
|
|
|
|
|
|
|
|
|
"""
|
2021-09-24 14:50:42 +08:00
|
|
|
|
This part of code come from MCDReforged(https://github.com/Fallen-Breath/MCDReforged)
|
2021-07-14 22:53:15 +08:00
|
|
|
|
Very thanks to Fallen_Breath and other coder who helped MCDR worked better
|
2021-09-24 14:50:42 +08:00
|
|
|
|
GNU Lesser General Public License v3.0(GNU LGPL v3)
|
2021-09-24 14:53:12 +08:00
|
|
|
|
(have some changes)
|
2021-07-14 22:53:15 +08:00
|
|
|
|
"""
|
|
|
|
|
|
2021-10-10 16:51:53 +08:00
|
|
|
|
__all__ = [
|
|
|
|
|
'new_thread',
|
|
|
|
|
'FunctionThread'
|
|
|
|
|
]
|
2021-07-14 22:53:15 +08:00
|
|
|
|
|
2021-10-10 16:51:53 +08:00
|
|
|
|
|
|
|
|
|
def copy_signature(target: Callable, origin: Callable) -> Callable:
|
|
|
|
|
"""
|
|
|
|
|
Copy the function signature of origin into target
|
|
|
|
|
"""
|
|
|
|
|
# https://stackoverflow.com/questions/39926567/python-create-decorator-preserving-function-arguments
|
|
|
|
|
target.__signature__ = inspect.signature(origin)
|
|
|
|
|
return target
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FunctionThread(threading.Thread):
|
|
|
|
|
__NONE = object()
|
|
|
|
|
|
|
|
|
|
def __init__(self, target, name, args, kwargs):
|
2021-10-23 17:01:59 +08:00
|
|
|
|
super().__init__(target=target, args=args, kwargs=kwargs, name=name)
|
2021-10-10 16:51:53 +08:00
|
|
|
|
self.__return_value = self.__NONE
|
|
|
|
|
self.__error = None
|
|
|
|
|
|
|
|
|
|
def wrapped_target(*args_, **kwargs_):
|
|
|
|
|
try:
|
|
|
|
|
self.__return_value = target(*args_, **kwargs_)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.__error = e
|
2021-10-23 17:01:59 +08:00
|
|
|
|
print(e)
|
2021-10-10 16:51:53 +08:00
|
|
|
|
raise e from None
|
|
|
|
|
|
|
|
|
|
self._target = wrapped_target
|
|
|
|
|
|
|
|
|
|
def get_return_value(self, block: bool = False, timeout: Optional[float] = None):
|
|
|
|
|
if block:
|
|
|
|
|
self.join(timeout)
|
|
|
|
|
if self.__return_value is self.__NONE:
|
|
|
|
|
if self.is_alive():
|
|
|
|
|
raise RuntimeError('The thread is still running')
|
|
|
|
|
raise self.__error
|
|
|
|
|
return self.__return_value
|
2021-10-23 17:01:59 +08:00
|
|
|
|
|
|
|
|
|
def join(self, timeout: Optional[float] = None) -> None:
|
|
|
|
|
super().join(timeout)
|
2021-10-10 16:51:53 +08:00
|
|
|
|
|
2021-10-23 17:01:59 +08:00
|
|
|
|
def start(self) -> None:
|
2021-10-31 23:26:32 +08:00
|
|
|
|
super().start()
|
2021-10-10 16:51:53 +08:00
|
|
|
|
|
2021-10-23 17:01:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def new_thread(thread_name: Optional[str or Callable] = None,
|
|
|
|
|
daemon: bool = False,
|
|
|
|
|
log_thread: bool = True):
|
2021-07-14 22:53:15 +08:00
|
|
|
|
"""
|
|
|
|
|
Use a new thread to execute the decorated function
|
|
|
|
|
The function return value will be set to the thread instance that executes this function
|
|
|
|
|
The name of the thread can be specified in parameter
|
|
|
|
|
"""
|
|
|
|
|
def wrapper(func):
|
|
|
|
|
@functools.wraps(func) # to preserve the origin function information
|
|
|
|
|
def wrap(*args, **kwargs):
|
2021-10-10 16:51:53 +08:00
|
|
|
|
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name)
|
2021-10-23 17:01:59 +08:00
|
|
|
|
thread.daemon = daemon
|
2021-10-10 16:51:53 +08:00
|
|
|
|
thread.start()
|
2021-10-23 17:01:59 +08:00
|
|
|
|
if log_thread:
|
|
|
|
|
crash.all_thread.append(thread)
|
2021-10-10 16:51:53 +08:00
|
|
|
|
return thread
|
2021-07-14 22:53:15 +08:00
|
|
|
|
# bring the signature of the func to the wrap function
|
|
|
|
|
# so inspect.getfullargspec(func) works correctly
|
2021-10-10 16:51:53 +08:00
|
|
|
|
copy_signature(wrap, func)
|
|
|
|
|
wrap.original = func # access this field to get the original function
|
2021-07-14 22:53:15 +08:00
|
|
|
|
return wrap
|
|
|
|
|
# Directly use @on_new_thread without ending brackets case
|
|
|
|
|
if isinstance(thread_name, Callable):
|
|
|
|
|
this_is_a_function = thread_name
|
|
|
|
|
thread_name = None
|
|
|
|
|
return wrapper(this_is_a_function)
|
|
|
|
|
# Use @on_new_thread with ending brackets case
|
|
|
|
|
return wrapper
|
2021-10-10 16:51:53 +08:00
|
|
|
|
|