Difficult-Rocket/Difficult_Rocket/utils/new_thread.py

97 lines
3.2 KiB
Python
Raw Normal View History

2021-12-26 23:06:03 +08:00
# -------------------------------
# Difficult Rocket
# Copyright © 2021-2022 by shenjackyuanjie
# All rights reserved
# -------------------------------
2022-02-08 17:15:09 +08:00
# 本文件以 GNU Lesser General Public License v3.0GNU LGPL v3) 开源协议进行授权 (谢谢狐狸写出这么好的MCDR)
2021-12-26 23:06:03 +08:00
2021-07-14 22:53:15 +08:00
import functools
import inspect
import threading
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.0GNU LGPL v3)
2021-09-24 14:53:12 +08:00
(have some changes)
2021-07-14 22:53:15 +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)
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)
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-23 17:01:59 +08:00
def start(self) -> None:
2021-10-31 23:26:32 +08:00
super().start()
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):
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name)
2021-10-23 17:01:59 +08:00
thread.daemon = daemon
thread.start()
2021-10-23 17:01:59 +08:00
if log_thread:
crash.all_thread.append(thread)
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
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