webshell/.env/lib/python3.12/site-packages/uvloop/cbhandles.pyx

435 lines
12 KiB
Cython

@cython.no_gc_clear
@cython.freelist(DEFAULT_FREELIST_SIZE)
cdef class Handle:
def __cinit__(self):
self._cancelled = 0
self.cb_type = 0
self._source_traceback = None
cdef inline _set_loop(self, Loop loop):
self.loop = loop
if UVLOOP_DEBUG:
loop._debug_cb_handles_total += 1
loop._debug_cb_handles_count += 1
if loop._debug:
self._source_traceback = extract_stack()
cdef inline _set_context(self, object context):
if context is None:
context = Context_CopyCurrent()
self.context = context
def __dealloc__(self):
if UVLOOP_DEBUG and self.loop is not None:
self.loop._debug_cb_handles_count -= 1
if self.loop is None:
raise RuntimeError('Handle.loop is None in Handle.__dealloc__')
def __init__(self):
raise TypeError(
'{} is not supposed to be instantiated from Python'.format(
self.__class__.__name__))
cdef inline _run(self):
cdef:
int cb_type
object callback
if self._cancelled:
return
cb_type = self.cb_type
# Since _run is a cdef and there's no BoundMethod,
# we guard 'self' manually (since the callback
# might cause GC of the handle.)
Py_INCREF(self)
try:
assert self.context is not None
Context_Enter(self.context)
if cb_type == 1:
callback = self.arg1
if callback is None:
raise RuntimeError(
'cannot run Handle; callback is not set')
args = self.arg2
if args is None:
callback()
else:
callback(*args)
elif cb_type == 2:
(<method_t>self.callback)(self.arg1)
elif cb_type == 3:
(<method1_t>self.callback)(self.arg1, self.arg2)
elif cb_type == 4:
(<method2_t>self.callback)(self.arg1, self.arg2, self.arg3)
elif cb_type == 5:
(<method3_t>self.callback)(
self.arg1, self.arg2, self.arg3, self.arg4)
else:
raise RuntimeError('invalid Handle.cb_type: {}'.format(
cb_type))
except (KeyboardInterrupt, SystemExit):
raise
except BaseException as ex:
if cb_type == 1:
msg = 'Exception in callback {}'.format(callback)
else:
msg = 'Exception in callback {}'.format(self.meth_name)
context = {
'message': msg,
'exception': ex,
'handle': self,
}
if self._source_traceback is not None:
context['source_traceback'] = self._source_traceback
self.loop.call_exception_handler(context)
finally:
context = self.context
Py_DECREF(self)
Context_Exit(context)
cdef _cancel(self):
self._cancelled = 1
self.callback = NULL
self.arg1 = self.arg2 = self.arg3 = self.arg4 = None
cdef _format_handle(self):
# Mirrors `asyncio.base_events._format_handle`.
if self.cb_type == 1 and self.arg1 is not None:
cb = self.arg1
if isinstance(getattr(cb, '__self__', None), aio_Task):
try:
return repr(cb.__self__)
except (AttributeError, TypeError, ValueError) as ex:
# Cython generates empty __code__ objects for coroutines
# that can crash asyncio.Task.__repr__ with an
# AttributeError etc. Guard against that.
self.loop.call_exception_handler({
'message': 'exception in Task.__repr__',
'task': cb.__self__,
'exception': ex,
'handle': self,
})
return repr(self)
# Public API
def __repr__(self):
info = [self.__class__.__name__]
if self._cancelled:
info.append('cancelled')
if self.cb_type == 1 and self.arg1 is not None:
func = self.arg1
# Cython can unset func.__qualname__/__name__, hence the checks.
if hasattr(func, '__qualname__') and func.__qualname__:
cb_name = func.__qualname__
elif hasattr(func, '__name__') and func.__name__:
cb_name = func.__name__
else:
cb_name = repr(func)
info.append(cb_name)
elif self.meth_name is not None:
info.append(self.meth_name)
if self._source_traceback is not None:
frame = self._source_traceback[-1]
info.append('created at {}:{}'.format(frame[0], frame[1]))
return '<' + ' '.join(info) + '>'
def cancel(self):
self._cancel()
def cancelled(self):
return self._cancelled
@cython.no_gc_clear
@cython.freelist(DEFAULT_FREELIST_SIZE)
cdef class TimerHandle:
def __cinit__(self, Loop loop, object callback, object args,
uint64_t delay, object context):
self.loop = loop
self.callback = callback
self.args = args
self._cancelled = 0
if UVLOOP_DEBUG:
self.loop._debug_cb_timer_handles_total += 1
self.loop._debug_cb_timer_handles_count += 1
if context is None:
context = Context_CopyCurrent()
self.context = context
if loop._debug:
self._debug_info = (
format_callback_name(callback),
extract_stack()
)
else:
self._debug_info = None
self.timer = UVTimer.new(
loop, <method_t>self._run, self, delay)
self.timer.start()
self._when = self.timer.get_when() * 1e-3
# Only add to loop._timers when `self.timer` is successfully created
loop._timers.add(self)
property _source_traceback:
def __get__(self):
if self._debug_info is not None:
return self._debug_info[1]
def __dealloc__(self):
if UVLOOP_DEBUG:
self.loop._debug_cb_timer_handles_count -= 1
if self.timer is not None:
raise RuntimeError('active TimerHandle is deallacating')
cdef _cancel(self):
if self._cancelled == 1:
return
self._cancelled = 1
self._clear()
cdef inline _clear(self):
if self.timer is None:
return
self.callback = None
self.args = None
try:
self.loop._timers.remove(self)
finally:
self.timer._close()
self.timer = None # let the UVTimer handle GC
cdef _run(self):
if self._cancelled == 1:
return
if self.callback is None:
raise RuntimeError('cannot run TimerHandle; callback is not set')
callback = self.callback
args = self.args
# Since _run is a cdef and there's no BoundMethod,
# we guard 'self' manually.
Py_INCREF(self)
if self.loop._debug:
started = time_monotonic()
try:
assert self.context is not None
Context_Enter(self.context)
if args is not None:
callback(*args)
else:
callback()
except (KeyboardInterrupt, SystemExit):
raise
except BaseException as ex:
context = {
'message': 'Exception in callback {}'.format(callback),
'exception': ex,
'handle': self,
}
if self._debug_info is not None:
context['source_traceback'] = self._debug_info[1]
self.loop.call_exception_handler(context)
else:
if self.loop._debug:
delta = time_monotonic() - started
if delta > self.loop.slow_callback_duration:
aio_logger.warning(
'Executing %r took %.3f seconds',
self, delta)
finally:
context = self.context
Py_DECREF(self)
Context_Exit(context)
self._clear()
# Public API
def __repr__(self):
info = [self.__class__.__name__]
if self._cancelled:
info.append('cancelled')
if self._debug_info is not None:
callback_name = self._debug_info[0]
source_traceback = self._debug_info[1]
else:
callback_name = None
source_traceback = None
if callback_name is not None:
info.append(callback_name)
elif self.callback is not None:
info.append(format_callback_name(self.callback))
if source_traceback is not None:
frame = source_traceback[-1]
info.append('created at {}:{}'.format(frame[0], frame[1]))
return '<' + ' '.join(info) + '>'
def cancelled(self):
return self._cancelled
def cancel(self):
self._cancel()
def when(self):
return self._when
cdef format_callback_name(func):
if hasattr(func, '__qualname__'):
cb_name = getattr(func, '__qualname__')
elif hasattr(func, '__name__'):
cb_name = getattr(func, '__name__')
else:
cb_name = repr(func)
return cb_name
cdef new_Handle(Loop loop, object callback, object args, object context):
cdef Handle handle
handle = Handle.__new__(Handle)
handle._set_loop(loop)
handle._set_context(context)
handle.cb_type = 1
handle.arg1 = callback
handle.arg2 = args
return handle
cdef new_MethodHandle(Loop loop, str name, method_t callback, object context,
object bound_to):
cdef Handle handle
handle = Handle.__new__(Handle)
handle._set_loop(loop)
handle._set_context(context)
handle.cb_type = 2
handle.meth_name = name
handle.callback = <void*> callback
handle.arg1 = bound_to
return handle
cdef new_MethodHandle1(Loop loop, str name, method1_t callback, object context,
object bound_to, object arg):
cdef Handle handle
handle = Handle.__new__(Handle)
handle._set_loop(loop)
handle._set_context(context)
handle.cb_type = 3
handle.meth_name = name
handle.callback = <void*> callback
handle.arg1 = bound_to
handle.arg2 = arg
return handle
cdef new_MethodHandle2(Loop loop, str name, method2_t callback, object context,
object bound_to, object arg1, object arg2):
cdef Handle handle
handle = Handle.__new__(Handle)
handle._set_loop(loop)
handle._set_context(context)
handle.cb_type = 4
handle.meth_name = name
handle.callback = <void*> callback
handle.arg1 = bound_to
handle.arg2 = arg1
handle.arg3 = arg2
return handle
cdef new_MethodHandle3(Loop loop, str name, method3_t callback, object context,
object bound_to, object arg1, object arg2, object arg3):
cdef Handle handle
handle = Handle.__new__(Handle)
handle._set_loop(loop)
handle._set_context(context)
handle.cb_type = 5
handle.meth_name = name
handle.callback = <void*> callback
handle.arg1 = bound_to
handle.arg2 = arg1
handle.arg3 = arg2
handle.arg4 = arg3
return handle
cdef extract_stack():
"""Replacement for traceback.extract_stack() that only does the
necessary work for asyncio debug mode.
"""
try:
f = sys_getframe()
# sys._getframe() might raise ValueError if being called without a frame, e.g.
# from Cython or similar C extensions.
except ValueError:
return None
if f is None:
return
try:
stack = tb_StackSummary.extract(tb_walk_stack(f),
limit=DEBUG_STACK_DEPTH,
lookup_lines=False)
finally:
f = None
stack.reverse()
return stack