2025-03-27 11:14:12 +03:00

179 lines
5.4 KiB
Python

# Urwid main loop code
# Copyright (C) 2004-2012 Ian Ward
# Copyright (C) 2008 Walter Mundt
# Copyright (C) 2009 Andrew Psaltis
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Urwid web site: https://urwid.org/
"""Abstract shared code for urwid EventLoop implementation."""
from __future__ import annotations
import abc
import logging
import signal
import typing
if typing.TYPE_CHECKING:
import asyncio
from collections.abc import Callable
from concurrent.futures import Executor, Future
from types import FrameType
from typing_extensions import ParamSpec
_T = typing.TypeVar("_T")
_Spec = ParamSpec("_Spec")
__all__ = ("EventLoop", "ExitMainLoop")
class ExitMainLoop(Exception):
"""
When this exception is raised within a main loop the main loop
will exit cleanly.
"""
class EventLoop(abc.ABC):
"""
Abstract class representing an event loop to be used by :class:`MainLoop`.
"""
__slots__ = ("logger",)
def __init__(self) -> None:
self.logger = logging.getLogger(__name__).getChild(self.__class__.__name__)
def run_in_executor(
self,
executor: Executor,
func: Callable[_Spec, _T],
*args: _Spec.args,
**kwargs: _Spec.kwargs,
) -> Future[_T] | asyncio.Future[_T]:
"""Run callable in executor if supported.
:param executor: Executor to use for running the function
:type executor: concurrent.futures.Executor
:param func: function to call
:type func: Callable
:param args: arguments to function (positional only)
:type args: object
:param kwargs: keyword arguments to function (keyword only)
:type kwargs: object
:return: future object for the function call outcome.
(exact future type depends on the event loop type)
:rtype: concurrent.futures.Future | asyncio.Future
"""
raise NotImplementedError
@abc.abstractmethod
def alarm(self, seconds: float, callback: Callable[[], typing.Any]) -> typing.Any:
"""
Call callback() a given time from now. No parameters are
passed to callback.
This method has no default implementation.
Returns a handle that may be passed to remove_alarm()
seconds -- floating point time to wait before calling callback
callback -- function to call from event loop
"""
@abc.abstractmethod
def enter_idle(self, callback):
"""
Add a callback for entering idle.
This method has no default implementation.
Returns a handle that may be passed to remove_idle()
"""
@abc.abstractmethod
def remove_alarm(self, handle) -> bool:
"""
Remove an alarm.
This method has no default implementation.
Returns True if the alarm exists, False otherwise
"""
@abc.abstractmethod
def remove_enter_idle(self, handle) -> bool:
"""
Remove an idle callback.
This method has no default implementation.
Returns True if the handle was removed.
"""
@abc.abstractmethod
def remove_watch_file(self, handle) -> bool:
"""
Remove an input file.
This method has no default implementation.
Returns True if the input file exists, False otherwise
"""
@abc.abstractmethod
def run(self) -> None:
"""
Start the event loop. Exit the loop when any callback raises
an exception. If ExitMainLoop is raised, exit cleanly.
This method has no default implementation.
"""
@abc.abstractmethod
def watch_file(self, fd: int, callback: Callable[[], typing.Any]):
"""
Call callback() when fd has some data to read. No parameters
are passed to callback.
This method has no default implementation.
Returns a handle that may be passed to remove_watch_file()
fd -- file descriptor to watch for input
callback -- function to call when input is available
"""
def set_signal_handler(
self,
signum: int,
handler: Callable[[int, FrameType | None], typing.Any] | int | signal.Handlers,
) -> Callable[[int, FrameType | None], typing.Any] | int | signal.Handlers | None:
"""
Sets the signal handler for signal signum.
The default implementation of :meth:`set_signal_handler`
is simply a proxy function that calls :func:`signal.signal()`
and returns the resulting value.
signum -- signal number
handler -- function (taking signum as its single argument),
or `signal.SIG_IGN`, or `signal.SIG_DFL`
"""
return signal.signal(signum, handler)