mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
@@ -43,28 +43,28 @@ class Logger:
|
||||
self._group = group
|
||||
self._verbosity = verbosity
|
||||
|
||||
@property
|
||||
def log(self) -> LogCallable:
|
||||
if self._log is None:
|
||||
try:
|
||||
app = active_app.get()
|
||||
except LookupError:
|
||||
raise LoggerError("Unable to log without an active app.") from None
|
||||
return app._log
|
||||
return self._log
|
||||
|
||||
def __rich_repr__(self) -> rich.repr.Result:
|
||||
yield self._group, LogGroup.INFO
|
||||
yield self._verbosity, LogVerbosity.NORMAL
|
||||
|
||||
def __call__(self, *args: object, **kwargs) -> None:
|
||||
caller = inspect.stack()[1]
|
||||
try:
|
||||
self.log(
|
||||
app = active_app.get()
|
||||
except LookupError:
|
||||
raise LoggerError("Unable to log without an active app.") from None
|
||||
if not app.devtools.is_connected:
|
||||
return
|
||||
|
||||
previous_frame = inspect.currentframe().f_back
|
||||
caller = inspect.getframeinfo(previous_frame)
|
||||
|
||||
_log = self._log or app._log
|
||||
try:
|
||||
_log(
|
||||
self._group,
|
||||
self._verbosity,
|
||||
caller,
|
||||
*args,
|
||||
_textual_calling_frame=caller,
|
||||
**kwargs,
|
||||
)
|
||||
except LoggerError:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import asyncio
|
||||
from time import monotonic
|
||||
|
||||
from ._time import time
|
||||
|
||||
|
||||
"""
|
||||
@@ -14,16 +15,15 @@ by mocking the few functions exposed by this module.
|
||||
# (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 self.get_time_no_wait()
|
||||
return time()
|
||||
|
||||
def get_time_no_wait(self) -> float:
|
||||
return monotonic()
|
||||
return time()
|
||||
|
||||
async def sleep(self, seconds: float) -> None:
|
||||
await asyncio.sleep(seconds)
|
||||
|
||||
|
||||
# That's our target for mocking time! :-)
|
||||
_clock = _Clock()
|
||||
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@ Timer context manager, only used in debug.
|
||||
|
||||
"""
|
||||
|
||||
from time import time
|
||||
|
||||
import contextlib
|
||||
from typing import Generator
|
||||
from time import perf_counter
|
||||
|
||||
from . import log
|
||||
|
||||
@@ -14,8 +13,8 @@ from . import log
|
||||
@contextlib.contextmanager
|
||||
def timer(subject: str = "time") -> Generator[None, None, None]:
|
||||
"""print the elapsed time. (only used in debugging)"""
|
||||
start = time()
|
||||
start = perf_counter()
|
||||
yield
|
||||
elapsed = time() - start
|
||||
elapsed = perf_counter() - start
|
||||
elapsed_ms = elapsed * 1000
|
||||
log(f"{subject} elapsed {elapsed_ms:.2f}ms")
|
||||
|
||||
12
src/textual/_time.py
Normal file
12
src/textual/_time.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import platform
|
||||
|
||||
from time import monotonic, perf_counter
|
||||
|
||||
PLATFORM = platform.system()
|
||||
WINDOWS = PLATFORM == "Windows"
|
||||
|
||||
|
||||
if WINDOWS:
|
||||
time = perf_counter
|
||||
else:
|
||||
time = monotonic
|
||||
@@ -513,8 +513,8 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self,
|
||||
group: LogGroup,
|
||||
verbosity: LogVerbosity,
|
||||
_textual_calling_frame: inspect.FrameInfo,
|
||||
*objects: Any,
|
||||
_textual_calling_frame: inspect.FrameInfo | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""Write to logs or devtools.
|
||||
@@ -539,9 +539,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
if verbosity.value > LogVerbosity.NORMAL.value and not self.devtools.verbose:
|
||||
return
|
||||
|
||||
if not _textual_calling_frame:
|
||||
_textual_calling_frame = inspect.stack()[1]
|
||||
|
||||
try:
|
||||
if len(objects) == 1 and not kwargs:
|
||||
self.devtools.log(
|
||||
|
||||
@@ -3,12 +3,11 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import inspect
|
||||
import json
|
||||
|
||||
import pickle
|
||||
from time import time
|
||||
from asyncio import Queue, Task, QueueFull
|
||||
from asyncio import Queue, QueueFull, Task
|
||||
from io import StringIO
|
||||
from typing import Type, Any, NamedTuple
|
||||
from time import time
|
||||
from typing import Any, NamedTuple, Type
|
||||
|
||||
from rich.console import Console
|
||||
from rich.segment import Segment
|
||||
@@ -22,12 +21,12 @@ class DevtoolsDependenciesMissingError(Exception):
|
||||
|
||||
try:
|
||||
import aiohttp
|
||||
import msgpack
|
||||
from aiohttp import (
|
||||
ClientResponseError,
|
||||
ClientConnectorError,
|
||||
ClientResponseError,
|
||||
ClientWebSocketResponse,
|
||||
)
|
||||
import msgpack
|
||||
except ImportError:
|
||||
# TODO: Add link to documentation on how to install devtools
|
||||
raise DevtoolsDependenciesMissingError(
|
||||
|
||||
@@ -39,7 +39,9 @@ class StdoutRedirector:
|
||||
if not self.devtools.is_connected:
|
||||
return
|
||||
|
||||
caller = inspect.stack()[1]
|
||||
previous_frame = inspect.currentframe().f_back
|
||||
caller = inspect.getframeinfo(previous_frame)
|
||||
|
||||
self._buffer.append(DevtoolsLog(string, caller=caller))
|
||||
|
||||
# By default, `print` adds a "\n" suffix which results in a buffer
|
||||
|
||||
@@ -6,7 +6,6 @@ import base64
|
||||
import json
|
||||
import pickle
|
||||
from json import JSONDecodeError
|
||||
from time import time
|
||||
from typing import cast
|
||||
|
||||
from aiohttp import WSMessage, WSMsgType
|
||||
@@ -17,6 +16,7 @@ from rich.markup import escape
|
||||
import msgpack
|
||||
|
||||
from textual._log import LogGroup
|
||||
from textual._time import time
|
||||
from textual.devtools.renderables import (
|
||||
DevConsoleLog,
|
||||
DevConsoleNotice,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from time import time
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from . import events
|
||||
from . import _clock, events
|
||||
from ._types import MessageTarget
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -20,7 +19,7 @@ class Driver(ABC):
|
||||
self._target = target
|
||||
self._debug = debug
|
||||
self._loop = asyncio.get_running_loop()
|
||||
self._mouse_down_time = time()
|
||||
self._mouse_down_time = _clock.get_time_no_wait()
|
||||
|
||||
def send_event(self, event: events.Event) -> None:
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
|
||||
@@ -263,6 +263,11 @@ class EventMonitor(threading.Thread):
|
||||
key_event = input_record.Event.KeyEvent
|
||||
key = key_event.uChar.UnicodeChar
|
||||
if key_event.bKeyDown or key == "\x1b":
|
||||
if (
|
||||
key_event.dwControlKeyState
|
||||
and key_event.wVirtualKeyCode == 0
|
||||
):
|
||||
continue
|
||||
append_key(key)
|
||||
elif event_type == WINDOW_BUFFER_SIZE_EVENT:
|
||||
# Window size changed, store size
|
||||
|
||||
@@ -10,21 +10,20 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import inspect
|
||||
from asyncio import CancelledError, Queue, QueueEmpty, Task
|
||||
from time import time
|
||||
from functools import partial
|
||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Iterable
|
||||
from weakref import WeakSet
|
||||
|
||||
from . import events, log, messages, Logger
|
||||
from . import Logger, events, log, messages
|
||||
from ._callback import invoke
|
||||
from ._context import NoActiveAppError, active_app
|
||||
from .errors import DuplicateKeyHandlers
|
||||
from .keys import _get_key_aliases
|
||||
from .timer import Timer, TimerCallback
|
||||
from ._time import time
|
||||
from .case import camel_to_snake
|
||||
from .errors import DuplicateKeyHandlers
|
||||
from .events import Event
|
||||
from .message import Message
|
||||
from .reactive import Reactive
|
||||
from .timer import Timer, TimerCallback
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .app import App
|
||||
|
||||
@@ -461,14 +461,16 @@ class Widget(DOMNode):
|
||||
self.highlight_link_id = hover_style.link_id
|
||||
|
||||
def watch_scroll_x(self, new_value: float) -> None:
|
||||
self.horizontal_scrollbar.position = int(new_value)
|
||||
self.refresh(layout=True)
|
||||
self.horizontal_scrollbar.refresh()
|
||||
if self.show_horizontal_scrollbar:
|
||||
self.horizontal_scrollbar.position = int(new_value)
|
||||
self.horizontal_scrollbar.refresh()
|
||||
self.refresh(layout=True)
|
||||
|
||||
def watch_scroll_y(self, new_value: float) -> None:
|
||||
self.vertical_scrollbar.position = int(new_value)
|
||||
self.refresh(layout=True)
|
||||
self.vertical_scrollbar.refresh()
|
||||
if self.show_vertical_scrollbar:
|
||||
self.vertical_scrollbar.position = int(new_value)
|
||||
self.vertical_scrollbar.refresh()
|
||||
self.refresh(layout=True)
|
||||
|
||||
def validate_scroll_x(self, value: float) -> float:
|
||||
return clamp(value, 0, self.max_scroll_x)
|
||||
|
||||
Reference in New Issue
Block a user