ai-station/.venv/lib/python3.12/site-packages/textual/__init__.py

208 lines
5.6 KiB
Python
Raw Normal View History

2025-12-25 14:54:33 +00:00
"""
The root Textual module.
Exposes some commonly used symbols.
"""
from __future__ import annotations
import inspect
import weakref
from typing import TYPE_CHECKING, Callable
import rich.repr
from textual import constants
from textual._context import active_app
from textual._log import LogGroup, LogVerbosity
from textual._on import on
from textual._work_decorator import work
if TYPE_CHECKING:
from typing_extensions import TypeAlias
__all__ = [
"__version__", # type: ignore
"log",
"on",
"work",
]
LogCallable: TypeAlias = "Callable"
if TYPE_CHECKING:
from importlib.metadata import version
from textual.app import App as _App
__version__ = version("textual")
"""The version of Textual."""
else:
def __getattr__(name: str) -> str:
"""Lazily get the version."""
if name == "__version__":
from importlib.metadata import version
return version("textual")
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
class LoggerError(Exception):
"""Raised when the logger failed."""
@rich.repr.auto
class Logger:
"""A [logger class](/guide/devtools/#logging-handler) that logs to the Textual [console](/guide/devtools#console)."""
def __init__(
self,
log_callable: LogCallable | None,
group: LogGroup = LogGroup.INFO,
verbosity: LogVerbosity = LogVerbosity.NORMAL,
app: _App | None = None,
) -> None:
self._log = log_callable
self._group = group
self._verbosity = verbosity
self._app = None if app is None else weakref.ref(app)
@property
def app(self) -> _App | None:
"""The associated application, or `None` if there isn't one."""
return None if self._app is None else self._app()
def __rich_repr__(self) -> rich.repr.Result:
yield self._group, LogGroup.INFO
yield self._verbosity, LogVerbosity.NORMAL
def __call__(self, *args: object, **kwargs) -> None:
if constants.LOG_FILE:
output = " ".join(str(arg) for arg in args)
if kwargs:
key_values = " ".join(
f"{key}={value!r}" for key, value in kwargs.items()
)
output = f"{output} {key_values}" if output else key_values
with open(constants.LOG_FILE, "a", encoding="utf-8") as log_file:
print(output, file=log_file)
app = self.app
if app is None:
try:
app = active_app.get()
except LookupError:
if constants.DEBUG:
print_args = (
*args,
*[f"{key}={value!r}" for key, value in kwargs.items()],
)
print(*print_args)
return
if not app._is_devtools_connected:
return
current_frame = inspect.currentframe()
assert current_frame is not None
previous_frame = current_frame.f_back
assert previous_frame is not None
caller = inspect.getframeinfo(previous_frame)
_log = self._log or app._log
try:
_log(
self._group,
self._verbosity,
caller,
*args,
**kwargs,
)
except LoggerError:
# If there is not active app, try printing
if constants.DEBUG:
print_args = (
*args,
*[f"{key}={value!r}" for key, value in kwargs.items()],
)
print(*print_args)
def verbosity(self, verbose: bool) -> Logger:
"""Get a new logger with selective verbosity.
Args:
verbose: True to use HIGH verbosity, otherwise NORMAL.
Returns:
New logger.
"""
verbosity = LogVerbosity.HIGH if verbose else LogVerbosity.NORMAL
return Logger(self._log, self._group, verbosity, app=self.app)
@property
def verbose(self) -> Logger:
"""A verbose logger."""
return Logger(self._log, self._group, LogVerbosity.HIGH, app=self.app)
@property
def event(self) -> Logger:
"""Logs events."""
return Logger(self._log, LogGroup.EVENT, app=self.app)
@property
def debug(self) -> Logger:
"""Logs debug messages."""
return Logger(self._log, LogGroup.DEBUG, app=self.app)
@property
def info(self) -> Logger:
"""Logs information."""
return Logger(self._log, LogGroup.INFO, app=self.app)
@property
def warning(self) -> Logger:
"""Logs warnings."""
return Logger(self._log, LogGroup.WARNING, app=self.app)
@property
def error(self) -> Logger:
"""Logs errors."""
return Logger(self._log, LogGroup.ERROR, app=self.app)
@property
def system(self) -> Logger:
"""Logs system information."""
return Logger(self._log, LogGroup.SYSTEM, app=self.app)
@property
def logging(self) -> Logger:
"""Logs from stdlib logging module."""
return Logger(self._log, LogGroup.LOGGING, app=self.app)
@property
def worker(self) -> Logger:
"""Logs worker information."""
return Logger(self._log, LogGroup.WORKER, app=self.app)
log = Logger(None)
"""Global logger that logs to the currently active app.
Example:
```python
from textual import log
log(locals())
```
!!! note
This logger will only work if there is an active app in the current thread.
Use `app.log` to write logs from a thread without an active app.
"""