API refactor
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-07 16:25:52 +09:00
parent 76d0d86211
commit 91c7e04474
1171 changed files with 81940 additions and 44117 deletions

View File

@@ -0,0 +1,49 @@
"""Code related to handling annotations."""
import sys
import types
import typing
from inspect import isclass
def is_none_type(value: typing.Any) -> bool:
"""Check if the given value is a NoneType."""
if sys.version_info < (3, 10):
# raise Exception('below 3.10', value, type(None))
return value is type(None)
return value == types.NoneType # type: ignore[no-any-return]
def get_optional_arg(annotation: typing.Any) -> typing.Any:
"""Get the argument from an Optional[...] annotation, or None if it is no such annotation."""
origin = typing.get_origin(annotation)
if origin != typing.Union and (sys.version_info >= (3, 10) and origin != types.UnionType):
return None
union_args = typing.get_args(annotation)
if len(union_args) != 2: # Union does _not_ have two members, so it's not an Optional
return None
has_none_arg = any(is_none_type(arg) for arg in union_args)
# There will always be at least one type arg, as we have already established that this is a Union with exactly
# two members, and both cannot be None (`Union[None, None]` does not work).
type_arg = next(arg for arg in union_args if not is_none_type(arg)) # pragma: no branch
if has_none_arg:
return type_arg
return None
def annotation_is_class(annotation: typing.Any) -> bool:
"""Test if a given annotation is a class that can be used in isinstance()/issubclass()."""
# isclass() returns True for generic type hints (e.g. `list[str]`) until Python 3.10.
# NOTE: The guard for Python 3.9 is because types.GenericAlias is only added in Python 3.9. This is not a problem
# as the syntax is added in the same version in the first place.
if (3, 9) <= sys.version_info < (3, 11) and isinstance(annotation, types.GenericAlias):
return False
return isclass(annotation)
def annotation_issubclass(annotation: typing.Any, cls: type) -> bool:
"""Test if a given annotation is of the given subclass."""
return annotation_is_class(annotation) and issubclass(annotation, cls)

View File

@@ -595,8 +595,7 @@ class LimitedSet:
break # oldest item hasn't expired yet
self.pop()
def pop(self, default=None) -> Any:
# type: (Any) -> Any
def pop(self, default: Any = None) -> Any:
"""Remove and return the oldest item, or :const:`None` when empty."""
while self._heap:
_, item = heappop(self._heap)

View File

@@ -54,6 +54,9 @@ def _boundmethod_safe_weakref(obj):
def _make_lookup_key(receiver, sender, dispatch_uid):
if dispatch_uid:
return (dispatch_uid, _make_id(sender))
# Issue #9119 - retry-wrapped functions use the underlying function for dispatch_uid
elif hasattr(receiver, '_dispatch_uid'):
return (receiver._dispatch_uid, _make_id(sender))
else:
return (_make_id(receiver), _make_id(sender))
@@ -170,6 +173,7 @@ class Signal: # pragma: no cover
# it up later with the original func id
options['dispatch_uid'] = _make_id(fun)
fun = _retry_receiver(fun)
fun._dispatch_uid = options['dispatch_uid']
self._connect_signal(fun, sender, options['weak'],
options['dispatch_uid'])

View File

@@ -51,8 +51,13 @@ def instantiate(name, *args, **kwargs):
@contextmanager
def cwd_in_path():
"""Context adding the current working directory to sys.path."""
cwd = os.getcwd()
if cwd in sys.path:
try:
cwd = os.getcwd()
except FileNotFoundError:
cwd = None
if not cwd:
yield
elif cwd in sys.path:
yield
else:
sys.path.insert(0, cwd)

View File

@@ -50,9 +50,9 @@ TIMEZONE_REGEX = re.compile(
)
def parse_iso8601(datestring):
def parse_iso8601(datestring: str) -> datetime:
"""Parse and convert ISO-8601 string to datetime."""
warn("parse_iso8601", "v5.3", "v6", "datetime.datetime.fromisoformat")
warn("parse_iso8601", "v5.3", "v6", "datetime.datetime.fromisoformat or dateutil.parser.isoparse")
m = ISO8601_REGEX.match(datestring)
if not m:
raise ValueError('unable to parse date string %r' % datestring)

View File

@@ -37,7 +37,7 @@ base_logger = logger = _get_logger('celery')
def set_in_sighandler(value):
"""Set flag signifiying that we're inside a signal handler."""
"""Set flag signifying that we're inside a signal handler."""
global _in_sighandler
_in_sighandler = value

View File

@@ -1,4 +1,6 @@
"""Worker name utilities."""
from __future__ import annotations
import os
import socket
from functools import partial
@@ -22,13 +24,18 @@ NODENAME_DEFAULT = 'celery'
gethostname = memoize(1, Cache=dict)(socket.gethostname)
__all__ = (
'worker_direct', 'gethostname', 'nodename',
'anon_nodename', 'nodesplit', 'default_nodename',
'node_format', 'host_format',
'worker_direct',
'gethostname',
'nodename',
'anon_nodename',
'nodesplit',
'default_nodename',
'node_format',
'host_format',
)
def worker_direct(hostname):
def worker_direct(hostname: str | Queue) -> Queue:
"""Return the :class:`kombu.Queue` being a direct route to a worker.
Arguments:
@@ -46,21 +53,20 @@ def worker_direct(hostname):
)
def nodename(name, hostname):
def nodename(name: str, hostname: str) -> str:
"""Create node name from name/hostname pair."""
return NODENAME_SEP.join((name, hostname))
def anon_nodename(hostname=None, prefix='gen'):
def anon_nodename(hostname: str | None = None, prefix: str = 'gen') -> str:
"""Return the nodename for this process (not a worker).
This is used for e.g. the origin task message field.
"""
return nodename(''.join([prefix, str(os.getpid())]),
hostname or gethostname())
return nodename(''.join([prefix, str(os.getpid())]), hostname or gethostname())
def nodesplit(name):
def nodesplit(name: str) -> tuple[None, str] | list[str]:
"""Split node name into tuple of name/hostname."""
parts = name.split(NODENAME_SEP, 1)
if len(parts) == 1:
@@ -68,21 +74,21 @@ def nodesplit(name):
return parts
def default_nodename(hostname):
def default_nodename(hostname: str) -> str:
"""Return the default nodename for this process."""
name, host = nodesplit(hostname or '')
return nodename(name or NODENAME_DEFAULT, host or gethostname())
def node_format(s, name, **extra):
def node_format(s: str, name: str, **extra: dict) -> str:
"""Format worker node name (name@host.com)."""
shortname, host = nodesplit(name)
return host_format(
s, host, shortname or NODENAME_DEFAULT, p=name, **extra)
return host_format(s, host, shortname or NODENAME_DEFAULT, p=name, **extra)
def _fmt_process_index(prefix='', default='0'):
def _fmt_process_index(prefix: str = '', default: str = '0') -> str:
from .log import current_process_index
index = current_process_index()
return f'{prefix}{index}' if index else default
@@ -90,13 +96,19 @@ def _fmt_process_index(prefix='', default='0'):
_fmt_process_index_with_prefix = partial(_fmt_process_index, '-', '')
def host_format(s, host=None, name=None, **extra):
def host_format(s: str, host: str | None = None, name: str | None = None, **extra: dict) -> str:
"""Format host %x abbreviations."""
host = host or gethostname()
hname, _, domain = host.partition('.')
name = name or hname
keys = dict({
'h': host, 'n': name, 'd': domain,
'i': _fmt_process_index, 'I': _fmt_process_index_with_prefix,
}, **extra)
keys = dict(
{
'h': host,
'n': name,
'd': domain,
'i': _fmt_process_index,
'I': _fmt_process_index_with_prefix,
},
**extra,
)
return simple_format(s, keys)

View File

@@ -0,0 +1,20 @@
from __future__ import annotations
def detect_quorum_queues(app, driver_type: str) -> tuple[bool, str]:
"""Detect if any of the queues are quorum queues.
Returns:
tuple[bool, str]: A tuple containing a boolean indicating if any of the queues are quorum queues
and the name of the first quorum queue found or an empty string if no quorum queues were found.
"""
is_rabbitmq_broker = driver_type == 'amqp'
if is_rabbitmq_broker:
queues = app.amqp.queues
for qname in queues:
qarguments = queues[qname].queue_arguments or {}
if qarguments.get("x-queue-type") == "quorum":
return True, qname
return False, ""

View File

@@ -15,7 +15,7 @@ from decimal import Decimal
from itertools import chain
from numbers import Number
from pprint import _recursion
from typing import Any, AnyStr, Callable, Dict, Iterator, List, Sequence, Set, Tuple # noqa
from typing import Any, AnyStr, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple # noqa
from .text import truncate
@@ -41,7 +41,7 @@ _quoted = namedtuple('_quoted', ('value',))
#: Recursion protection.
_dirty = namedtuple('_dirty', ('objid',))
#: Types that are repsented as chars.
#: Types that are represented as chars.
chars_t = (bytes, str)
#: Types that are regarded as safe to call repr on.
@@ -194,9 +194,12 @@ def _reprseq(val, lit_start, lit_end, builtin_type, chainer):
)
def reprstream(stack, seen=None, maxlevels=3, level=0, isinstance=isinstance):
def reprstream(stack: deque,
seen: Optional[Set] = None,
maxlevels: int = 3,
level: int = 0,
isinstance: Callable = isinstance) -> Iterator[Any]:
"""Streaming repr, yielding tokens."""
# type: (deque, Set, int, int, Callable) -> Iterator[Any]
seen = seen or set()
append = stack.append
popleft = stack.popleft

View File

@@ -1,4 +1,6 @@
"""System information utilities."""
from __future__ import annotations
import os
from math import ceil
@@ -9,16 +11,16 @@ __all__ = ('load_average', 'df')
if hasattr(os, 'getloadavg'):
def _load_average():
def _load_average() -> tuple[float, ...]:
return tuple(ceil(l * 1e2) / 1e2 for l in os.getloadavg())
else: # pragma: no cover
# Windows doesn't have getloadavg
def _load_average():
return (0.0, 0.0, 0.0)
def _load_average() -> tuple[float, ...]:
return 0.0, 0.0, 0.0,
def load_average():
def load_average() -> tuple[float, ...]:
"""Return system load average as a triple."""
return _load_average()
@@ -26,23 +28,23 @@ def load_average():
class df:
"""Disk information."""
def __init__(self, path):
def __init__(self, path: str | bytes | os.PathLike) -> None:
self.path = path
@property
def total_blocks(self):
def total_blocks(self) -> float:
return self.stat.f_blocks * self.stat.f_frsize / 1024
@property
def available(self):
def available(self) -> float:
return self.stat.f_bavail * self.stat.f_frsize / 1024
@property
def capacity(self):
def capacity(self) -> int:
avail = self.stat.f_bavail
used = self.stat.f_blocks - self.stat.f_bfree
return int(ceil(used * 100.0 / (used + avail) + 0.5))
@cached_property
def stat(self):
def stat(self) -> os.statvfs_result:
return os.statvfs(os.path.abspath(self.path))

View File

@@ -1,6 +1,7 @@
"""Terminals and colors."""
from __future__ import annotations
import base64
import codecs
import os
import platform
import sys
@@ -8,6 +9,8 @@ from functools import reduce
__all__ = ('colored',)
from typing import Any
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
OP_SEQ = '\033[%dm'
RESET_SEQ = '\033[0m'
@@ -26,7 +29,7 @@ _IMG_PRE = '\033Ptmux;\033\033]' if TERM_IS_SCREEN else '\033]'
_IMG_POST = '\a\033\\' if TERM_IS_SCREEN else '\a'
def fg(s):
def fg(s: int) -> str:
return COLOR_SEQ % s
@@ -41,11 +44,11 @@ class colored:
... c.green('dog ')))
"""
def __init__(self, *s, **kwargs):
self.s = s
self.enabled = not IS_WINDOWS and kwargs.get('enabled', True)
self.op = kwargs.get('op', '')
self.names = {
def __init__(self, *s: object, **kwargs: Any) -> None:
self.s: tuple[object, ...] = s
self.enabled: bool = not IS_WINDOWS and kwargs.get('enabled', True)
self.op: str = kwargs.get('op', '')
self.names: dict[str, Any] = {
'black': self.black,
'red': self.red,
'green': self.green,
@@ -56,10 +59,10 @@ class colored:
'white': self.white,
}
def _add(self, a, b):
return str(a) + str(b)
def _add(self, a: object, b: object) -> str:
return f"{a}{b}"
def _fold_no_color(self, a, b):
def _fold_no_color(self, a: Any, b: Any) -> str:
try:
A = a.no_color()
except AttributeError:
@@ -69,109 +72,113 @@ class colored:
except AttributeError:
B = str(b)
return ''.join((str(A), str(B)))
return f"{A}{B}"
def no_color(self):
def no_color(self) -> str:
if self.s:
return str(reduce(self._fold_no_color, self.s))
return ''
def embed(self):
def embed(self) -> str:
prefix = ''
if self.enabled:
prefix = self.op
return ''.join((str(prefix), str(reduce(self._add, self.s))))
return f"{prefix}{reduce(self._add, self.s)}"
def __str__(self):
def __str__(self) -> str:
suffix = ''
if self.enabled:
suffix = RESET_SEQ
return str(''.join((self.embed(), str(suffix))))
return f"{self.embed()}{suffix}"
def node(self, s, op):
def node(self, s: tuple[object, ...], op: str) -> colored:
return self.__class__(enabled=self.enabled, op=op, *s)
def black(self, *s):
def black(self, *s: object) -> colored:
return self.node(s, fg(30 + BLACK))
def red(self, *s):
def red(self, *s: object) -> colored:
return self.node(s, fg(30 + RED))
def green(self, *s):
def green(self, *s: object) -> colored:
return self.node(s, fg(30 + GREEN))
def yellow(self, *s):
def yellow(self, *s: object) -> colored:
return self.node(s, fg(30 + YELLOW))
def blue(self, *s):
def blue(self, *s: object) -> colored:
return self.node(s, fg(30 + BLUE))
def magenta(self, *s):
def magenta(self, *s: object) -> colored:
return self.node(s, fg(30 + MAGENTA))
def cyan(self, *s):
def cyan(self, *s: object) -> colored:
return self.node(s, fg(30 + CYAN))
def white(self, *s):
def white(self, *s: object) -> colored:
return self.node(s, fg(30 + WHITE))
def __repr__(self):
def __repr__(self) -> str:
return repr(self.no_color())
def bold(self, *s):
def bold(self, *s: object) -> colored:
return self.node(s, OP_SEQ % 1)
def underline(self, *s):
def underline(self, *s: object) -> colored:
return self.node(s, OP_SEQ % 4)
def blink(self, *s):
def blink(self, *s: object) -> colored:
return self.node(s, OP_SEQ % 5)
def reverse(self, *s):
def reverse(self, *s: object) -> colored:
return self.node(s, OP_SEQ % 7)
def bright(self, *s):
def bright(self, *s: object) -> colored:
return self.node(s, OP_SEQ % 8)
def ired(self, *s):
def ired(self, *s: object) -> colored:
return self.node(s, fg(40 + RED))
def igreen(self, *s):
def igreen(self, *s: object) -> colored:
return self.node(s, fg(40 + GREEN))
def iyellow(self, *s):
def iyellow(self, *s: object) -> colored:
return self.node(s, fg(40 + YELLOW))
def iblue(self, *s):
def iblue(self, *s: colored) -> colored:
return self.node(s, fg(40 + BLUE))
def imagenta(self, *s):
def imagenta(self, *s: object) -> colored:
return self.node(s, fg(40 + MAGENTA))
def icyan(self, *s):
def icyan(self, *s: object) -> colored:
return self.node(s, fg(40 + CYAN))
def iwhite(self, *s):
def iwhite(self, *s: object) -> colored:
return self.node(s, fg(40 + WHITE))
def reset(self, *s):
return self.node(s or [''], RESET_SEQ)
def reset(self, *s: object) -> colored:
return self.node(s or ('',), RESET_SEQ)
def __add__(self, other):
return str(self) + str(other)
def __add__(self, other: object) -> str:
return f"{self}{other}"
def supports_images():
return sys.stdin.isatty() and ITERM_PROFILE
def supports_images() -> bool:
try:
return sys.stdin.isatty() and bool(os.environ.get('ITERM_PROFILE'))
except AttributeError:
return False
def _read_as_base64(path):
with codecs.open(path, mode='rb') as fh:
def _read_as_base64(path: str) -> str:
with open(path, mode='rb') as fh:
encoded = base64.b64encode(fh.read())
return encoded if isinstance(encoded, str) else encoded.decode('ascii')
return encoded.decode('ascii')
def imgcat(path, inline=1, preserve_aspect_ratio=0, **kwargs):
def imgcat(path: str, inline: int = 1, preserve_aspect_ratio: int = 0, **kwargs: Any) -> str:
return '\n%s1337;File=inline=%d;preserveAspectRatio=%d:%s%s' % (
_IMG_PRE, inline, preserve_aspect_ratio,
_read_as_base64(path), _IMG_POST)

View File

@@ -14,6 +14,7 @@ from types import ModuleType
from typing import Any, Callable
from dateutil import tz as dateutil_tz
from dateutil.parser import isoparse
from kombu.utils.functional import reprcall
from kombu.utils.objects import cached_property
@@ -40,6 +41,9 @@ C_REMDEBUG = os.environ.get('C_REMDEBUG', False)
DAYNAMES = 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
WEEKDAYS = dict(zip(DAYNAMES, range(7)))
MONTHNAMES = 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'
YEARMONTHS = dict(zip(MONTHNAMES, range(1, 13)))
RATE_MODIFIER_MAP = {
's': lambda n: n,
'm': lambda n: n / 60.0,
@@ -200,7 +204,7 @@ def delta_resolution(dt: datetime, delta: timedelta) -> datetime:
def remaining(
start: datetime, ends_in: timedelta, now: Callable | None = None,
relative: bool = False) -> timedelta:
"""Calculate the remaining time for a start date and a timedelta.
"""Calculate the real remaining time for a start date and a timedelta.
For example, "how many seconds left for 30 seconds after start?"
@@ -211,24 +215,28 @@ def remaining(
using :func:`delta_resolution` (i.e., rounded to the
resolution of `ends_in`).
now (Callable): Function returning the current time and date.
Defaults to :func:`datetime.utcnow`.
Defaults to :func:`datetime.now(timezone.utc)`.
Returns:
~datetime.timedelta: Remaining time.
"""
now = now or datetime.utcnow()
if str(
start.tzinfo) == str(
now.tzinfo) and now.utcoffset() != start.utcoffset():
# DST started/ended
start = start.replace(tzinfo=now.tzinfo)
now = now or datetime.now(datetime_timezone.utc)
end_date = start + ends_in
if relative:
end_date = delta_resolution(end_date, ends_in).replace(microsecond=0)
ret = end_date - now
# Using UTC to calculate real time difference.
# Python by default uses wall time in arithmetic between datetimes with
# equal non-UTC timezones.
now_utc = now.astimezone(timezone.utc)
end_date_utc = end_date.astimezone(timezone.utc)
ret = end_date_utc - now_utc
if C_REMDEBUG: # pragma: no cover
print('rem: NOW:{!r} START:{!r} ENDS_IN:{!r} END_DATE:{} REM:{}'.format(
now, start, ends_in, end_date, ret))
print(
'rem: NOW:{!r} NOW_UTC:{!r} START:{!r} ENDS_IN:{!r} '
'END_DATE:{} END_DATE_UTC:{!r} REM:{}'.format(
now, now_utc, start, ends_in, end_date, end_date_utc, ret)
)
return ret
@@ -257,6 +265,21 @@ def weekday(name: str) -> int:
raise KeyError(name)
def yearmonth(name: str) -> int:
"""Return the position of a month: 1 - 12, where 1 is January.
Example:
>>> yearmonth('january'), yearmonth('jan'), yearmonth('may')
(1, 1, 5)
"""
abbreviation = name[0:3].lower()
try:
return YEARMONTHS[abbreviation]
except KeyError:
# Show original day name in exception, instead of abbr.
raise KeyError(name)
def humanize_seconds(
secs: int, prefix: str = '', sep: str = '', now: str = 'now',
microseconds: bool = False) -> str:
@@ -288,7 +311,7 @@ def maybe_iso8601(dt: datetime | str | None) -> None | datetime:
return
if isinstance(dt, datetime):
return dt
return datetime.fromisoformat(dt)
return isoparse(dt)
def is_naive(dt: datetime) -> bool:
@@ -302,7 +325,7 @@ def _can_detect_ambiguous(tz: tzinfo) -> bool:
return isinstance(tz, ZoneInfo) or hasattr(tz, "is_ambiguous")
def _is_ambigious(dt: datetime, tz: tzinfo) -> bool:
def _is_ambiguous(dt: datetime, tz: tzinfo) -> bool:
"""Helper function to determine if a timezone is ambiguous using python's dateutil module.
Returns False if the timezone cannot detect ambiguity, or if there is no ambiguity, otherwise True.
@@ -319,7 +342,7 @@ def make_aware(dt: datetime, tz: tzinfo) -> datetime:
"""Set timezone for a :class:`~datetime.datetime` object."""
dt = dt.replace(tzinfo=tz)
if _is_ambigious(dt, tz):
if _is_ambiguous(dt, tz):
dt = min(dt.replace(fold=0), dt.replace(fold=1))
return dt

View File

@@ -10,6 +10,7 @@ import threading
from itertools import count
from threading import TIMEOUT_MAX as THREAD_TIMEOUT_MAX
from time import sleep
from typing import Any, Callable, Iterator, Optional, Tuple
from kombu.asynchronous.timer import Entry
from kombu.asynchronous.timer import Timer as Schedule
@@ -30,20 +31,23 @@ class Timer(threading.Thread):
Entry = Entry
Schedule = Schedule
running = False
on_tick = None
running: bool = False
on_tick: Optional[Callable[[float], None]] = None
_timer_count = count(1)
_timer_count: count = count(1)
if TIMER_DEBUG: # pragma: no cover
def start(self, *args, **kwargs):
def start(self, *args: Any, **kwargs: Any) -> None:
import traceback
print('- Timer starting')
traceback.print_stack()
super().start(*args, **kwargs)
def __init__(self, schedule=None, on_error=None, on_tick=None,
on_start=None, max_interval=None, **kwargs):
def __init__(self, schedule: Optional[Schedule] = None,
on_error: Optional[Callable[[Exception], None]] = None,
on_tick: Optional[Callable[[float], None]] = None,
on_start: Optional[Callable[['Timer'], None]] = None,
max_interval: Optional[float] = None, **kwargs: Any) -> None:
self.schedule = schedule or self.Schedule(on_error=on_error,
max_interval=max_interval)
self.on_start = on_start
@@ -60,8 +64,10 @@ class Timer(threading.Thread):
self.daemon = True
self.name = f'Timer-{next(self._timer_count)}'
def _next_entry(self):
def _next_entry(self) -> Optional[float]:
with self.not_empty:
delay: Optional[float]
entry: Optional[Entry]
delay, entry = next(self.scheduler)
if entry is None:
if delay is None:
@@ -70,10 +76,10 @@ class Timer(threading.Thread):
return self.schedule.apply_entry(entry)
__next__ = next = _next_entry # for 2to3
def run(self):
def run(self) -> None:
try:
self.running = True
self.scheduler = iter(self.schedule)
self.scheduler: Iterator[Tuple[Optional[float], Optional[Entry]]] = iter(self.schedule)
while not self.__is_shutdown.is_set():
delay = self._next_entry()
@@ -94,61 +100,61 @@ class Timer(threading.Thread):
sys.stderr.flush()
os._exit(1)
def stop(self):
def stop(self) -> None:
self.__is_shutdown.set()
if self.running:
self.__is_stopped.wait()
self.join(THREAD_TIMEOUT_MAX)
self.running = False
def ensure_started(self):
def ensure_started(self) -> None:
if not self.running and not self.is_alive():
if self.on_start:
self.on_start(self)
self.start()
def _do_enter(self, meth, *args, **kwargs):
def _do_enter(self, meth: str, *args: Any, **kwargs: Any) -> Entry:
self.ensure_started()
with self.mutex:
entry = getattr(self.schedule, meth)(*args, **kwargs)
self.not_empty.notify()
return entry
def enter(self, entry, eta, priority=None):
def enter(self, entry: Entry, eta: float, priority: Optional[int] = None) -> Entry:
return self._do_enter('enter_at', entry, eta, priority=priority)
def call_at(self, *args, **kwargs):
def call_at(self, *args: Any, **kwargs: Any) -> Entry:
return self._do_enter('call_at', *args, **kwargs)
def enter_after(self, *args, **kwargs):
def enter_after(self, *args: Any, **kwargs: Any) -> Entry:
return self._do_enter('enter_after', *args, **kwargs)
def call_after(self, *args, **kwargs):
def call_after(self, *args: Any, **kwargs: Any) -> Entry:
return self._do_enter('call_after', *args, **kwargs)
def call_repeatedly(self, *args, **kwargs):
def call_repeatedly(self, *args: Any, **kwargs: Any) -> Entry:
return self._do_enter('call_repeatedly', *args, **kwargs)
def exit_after(self, secs, priority=10):
def exit_after(self, secs: float, priority: int = 10) -> None:
self.call_after(secs, sys.exit, priority)
def cancel(self, tref):
def cancel(self, tref: Entry) -> None:
tref.cancel()
def clear(self):
def clear(self) -> None:
self.schedule.clear()
def empty(self):
def empty(self) -> bool:
return not len(self)
def __len__(self):
def __len__(self) -> int:
return len(self.schedule)
def __bool__(self):
def __bool__(self) -> bool:
"""``bool(timer)``."""
return True
__nonzero__ = __bool__
@property
def queue(self):
def queue(self) -> list:
return self.schedule.queue