diff --git a/src/textual/_animator.py b/src/textual/_animator.py index 564f87da8..060da0c98 100644 --- a/src/textual/_animator.py +++ b/src/textual/_animator.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any, Callable, TypeVar from typing_extensions import Protocol, runtime_checkable -from . import _clock +from . import _time from ._callback import invoke from ._easing import DEFAULT_EASING, EASING from ._types import CallbackType @@ -385,7 +385,7 @@ class Animator: """Get the current wall clock time, via the internal Timer.""" # N.B. We could remove this method and always call `self._timer.get_time()` internally, # but it's handy to have in mocking situations - return _clock.get_time_no_wait() + return _time.get_time() async def wait_for_idle(self) -> None: """Wait for any animations to complete.""" diff --git a/src/textual/_clock.py b/src/textual/_clock.py deleted file mode 100644 index 408b64f87..000000000 --- a/src/textual/_clock.py +++ /dev/null @@ -1,57 +0,0 @@ -import asyncio - -from ._time import time - -""" -A module that serves as the single source of truth for everything time-related in a Textual app. -Having this logic centralised makes it easier to simulate time in integration tests, -by mocking the few functions exposed by this module. -""" - - -# N.B. This class and its singleton instance have to be hidden APIs because we want to be able to mock time, -# even for Python modules that imported functions such as `get_time` *before* we mocked this internal _Clock. -# (so mocking public APIs such as `get_time` wouldn't affect direct references to then that were done during imports) -class _Clock: - async def get_time(self) -> float: - return time() - - def get_time_no_wait(self) -> float: - return time() - - async def sleep(self, seconds: float) -> None: - await asyncio.sleep(seconds) - - -_clock = _Clock() - - -def get_time_no_wait() -> float: - """ - Get the current wall clock time. - - Returns: - The value (in fractional seconds) of a monotonic clock, i.e. a clock that cannot go backwards. - """ - return _clock.get_time_no_wait() - - -async def get_time() -> float: - """ - Asynchronous version of `get_time`. Useful in situations where we want asyncio to be - able to "do things" elsewhere right before we fetch the time. - - Returns: - The value (in fractional seconds) of a monotonic clock, i.e. a clock that cannot go backwards. - """ - return await _clock.get_time() - - -async def sleep(seconds: float) -> None: - """ - Coroutine that completes after a given time (in seconds). - - Args: - seconds: The duration we should wait for before unblocking the awaiter - """ - return await _clock.sleep(seconds) diff --git a/src/textual/_time.py b/src/textual/_time.py index 8e71d5ea8..7e24cef10 100644 --- a/src/textual/_time.py +++ b/src/textual/_time.py @@ -42,3 +42,12 @@ else: sleep_for = secs - 0.0005 if sleep_for > 0: await asyncio_sleep(sleep_for) + + +get_time = time +"""Get the current wall clock (monotonic) time. + +Returns: + The value (in fractional seconds) of a monotonic clock, + i.e. a clock that cannot go backwards. +""" diff --git a/src/textual/driver.py b/src/textual/driver.py index 3aee951ef..652b79f66 100644 --- a/src/textual/driver.py +++ b/src/textual/driver.py @@ -4,7 +4,7 @@ import asyncio from abc import ABC, abstractmethod from typing import TYPE_CHECKING -from . import _clock, events +from . import _time, events from ._types import MessageTarget from .events import MouseUp @@ -26,7 +26,7 @@ class Driver(ABC): self._debug = debug self._size = size self._loop = asyncio.get_running_loop() - self._mouse_down_time = _clock.get_time_no_wait() + self._mouse_down_time = _time.get_time() self._down_buttons: list[int] = [] self._last_move_event: events.MouseMove | None = None diff --git a/src/textual/message.py b/src/textual/message.py index 7b8b3befe..5d2ddb793 100644 --- a/src/textual/message.py +++ b/src/textual/message.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, ClassVar import rich.repr -from . import _clock +from . import _time from ._context import active_message_pump from ._types import MessageTarget as MessageTarget from .case import camel_to_snake @@ -34,7 +34,7 @@ class Message: def __init__(self) -> None: self._sender: MessageTarget | None = active_message_pump.get(None) - self.time: float = _clock.get_time_no_wait() + self.time: float = _time.get_time() self._forwarded = False self._no_default_action = False self._stop_propagation = False diff --git a/src/textual/timer.py b/src/textual/timer.py index f2e8ba25e..85867c5d1 100644 --- a/src/textual/timer.py +++ b/src/textual/timer.py @@ -13,7 +13,7 @@ from typing import Awaitable, Callable, Union from rich.repr import Result, rich_repr -from . import _clock, events +from . import _time, events from ._asyncio import create_task from ._callback import invoke from ._context import active_app @@ -126,15 +126,15 @@ class Timer: _repeat = self._repeat _interval = self._interval await self._active.wait() - start = _clock.get_time_no_wait() + start = _time.get_time() while _repeat is None or count <= _repeat: next_timer = start + ((count + 1) * _interval) - now = await _clock.get_time() + now = _time.get_time() if self._skip and next_timer < now: count += 1 continue - now = await _clock.get_time() + now = _time.get_time() wait_time = max(0, next_timer - now) if wait_time > 1 / 1000: await sleep(wait_time) @@ -142,7 +142,7 @@ class Timer: count += 1 await self._active.wait() if self._reset: - start = _clock.get_time_no_wait() + start = _time.get_time() count = 0 self._reset = False continue