Merge pull request #384 from Textualize/margin-padding-granularity

tweak to log
This commit is contained in:
Will McGugan
2022-04-13 15:12:36 +01:00
committed by GitHub
8 changed files with 115 additions and 40 deletions

View File

@@ -58,7 +58,7 @@ App > Screen {
#header {
color: $text-primary-darken-1;
background: $primary-darken-1;
height: 3;
height: 3
}
@@ -76,7 +76,7 @@ Tweet {
max-width: 80;
margin: 1 3;
background: $panel;
color: $text-panel
color: $text-panel;
layout: vertical;
/* border: outer $primary; */
padding: 1;

View File

@@ -4,6 +4,7 @@ import asyncio
import inspect
import os
import platform
from time import perf_counter
import warnings
from asyncio import AbstractEventLoop
from pathlib import Path
@@ -29,7 +30,7 @@ from ._callback import invoke
from ._context import active_app
from ._event_broker import extract_handler_actions, NoHandler
from .binding import Bindings, NoBinding
from .css.stylesheet import Stylesheet, StylesheetParseError, StylesheetError
from .css.stylesheet import Stylesheet
from .devtools.client import DevtoolsClient, DevtoolsConnectionError
from .design import ColorSystem
from .dom import DOMNode
@@ -100,8 +101,9 @@ class App(DOMNode):
driver_class (Type[Driver], optional): Driver class, or None to use default. Defaults to None.
title (str, optional): Title of the application. Defaults to "Textual Application".
"""
self.console = Console(markup=False, highlight=False)
self.console = Console(markup=False, highlight=False, emoji=False)
self.error_console = Console(markup=False, stderr=True)
self._screen = screen
self.driver_class = driver_class or self.get_driver_class()
self._title = title
@@ -121,7 +123,20 @@ class App(DOMNode):
self.bindings = Bindings()
self._title = title
self.log_file = open(log, "wt") if log else None
self._log_console: Console | None = None
if log:
self.log_file = open(log, "wt")
self._log_console = Console(
file=self.log_file,
markup=False,
emoji=False,
highlight=False,
width=100,
)
else:
self._log_console = None
self._log_file = None
self.log_verbosity = log_verbosity
self.bindings.bind("ctrl+c", "quit", show=False, allow_forward=False)
@@ -219,30 +234,36 @@ class App(DOMNode):
_textual_calling_frame (inspect.FrameInfo | None): The frame info to include in
the log message sent to the devtools server.
"""
output = ""
if verbosity > self.log_verbosity:
return
try:
output = f" ".join(str(arg) for arg in objects)
if kwargs:
key_values = " ".join(f"{key}={value}" for key, value in kwargs.items())
output = " ".join((output, key_values))
if not _textual_calling_frame:
_textual_calling_frame = inspect.stack()[1]
calling_path = _textual_calling_frame.filename
calling_lineno = _textual_calling_frame.lineno
if self.devtools.is_connected and verbosity <= self.log_verbosity:
if len(objects) > 1 or len(kwargs) >= 1 and output:
self.devtools.log(output, path=calling_path, lineno=calling_lineno)
else:
if len(objects) == 1 and not kwargs:
if self._log_console is not None:
self._log_console.print(objects[0])
if self.devtools.is_connected:
if not _textual_calling_frame:
_textual_calling_frame = inspect.stack()[1]
calling_path = _textual_calling_frame.filename
calling_lineno = _textual_calling_frame.lineno
self.devtools.log(
*objects, path=calling_path, lineno=calling_lineno
objects[0], path=calling_path, lineno=calling_lineno
)
if self.log_file and verbosity <= self.log_verbosity:
self.log_file.write(output + "\n")
self.log_file.flush()
else:
output = " ".join(str(arg) for arg in objects)
if kwargs:
key_values = " ".join(
f"{key}={value}" for key, value in kwargs.items()
)
output = " ".join((output, key_values))
if self._log_console is not None:
self._log_console.print(output, soft_wrap=True)
if self.devtools.is_connected:
if not _textual_calling_frame:
_textual_calling_frame = inspect.stack()[1]
calling_path = _textual_calling_frame.filename
calling_lineno = _textual_calling_frame.lineno
self.devtools.log(output, path=calling_path, lineno=calling_lineno)
except Exception:
pass
@@ -306,15 +327,18 @@ class App(DOMNode):
event_loop.close()
async def _on_css_change(self) -> None:
"""Called when the CSS changes (if watch_css is True)."""
if self.css_file is not None:
stylesheet = Stylesheet(variables=self.get_css_variables())
try:
self.log("loading", self.css_file)
time = perf_counter()
stylesheet.read(self.css_file)
except StylesheetError as error:
self.log(error)
elapsed = (perf_counter() - time) * 1000
self.log(f"loaded {self.css_file} in {elapsed:.0f}ms")
except Exception as error:
# TODO: catch specific exceptions
self.console.bell()
self.log(error)
else:
self.reset_styles()
self.stylesheet = stylesheet

View File

@@ -275,8 +275,8 @@ class StylesBuilder:
space: list[int] = []
append = space.append
for token in tokens:
token_name, value, _, _, location, _ = token
if token_name in ("number", "scalar"):
token_name, value, _, _, _, _ = token
if token_name == "number":
try:
append(int(value))
except ValueError:
@@ -289,11 +289,42 @@ class StylesBuilder:
)
self.styles._rules[name] = Spacing.unpack(cast(SpacingDimensions, tuple(space)))
def process_padding(self, name: str, tokens: list[Token]) -> None:
self._process_space(name, tokens)
def _process_space_partial(self, name: str, tokens: list[Token]) -> None:
"""Process granular margin / padding declarations."""
if len(tokens) != 1:
self.error(name, tokens[0], "expected a single token here")
def process_margin(self, name: str, tokens: list[Token]) -> None:
self._process_space(name, tokens)
_EDGE_SPACING_MAP = {"top": 0, "right": 1, "bottom": 2, "left": 3}
token = tokens[0]
token_name, value, _, _, _, _ = token
if token_name == "number":
space = int(value)
else:
self.error(name, token, f"expected a number here; found {value!r}")
style_name, _, edge = name.replace("-", "_").partition("_")
current_spacing = cast(
"tuple[int, int, int, int]",
self.styles._rules.get(style_name, (0, 0, 0, 0)),
)
spacing_list = list(current_spacing)
spacing_list[_EDGE_SPACING_MAP[edge]] = space
self.styles._rules[style_name] = Spacing(*spacing_list)
process_padding = _process_space
process_margin = _process_space
process_margin_top = _process_space_partial
process_margin_right = _process_space_partial
process_margin_bottom = _process_space_partial
process_margin_left = _process_space_partial
process_padding_top = _process_space_partial
process_padding_right = _process_space_partial
process_padding_bottom = _process_space_partial
process_padding_left = _process_space_partial
def _parse_border(self, name: str, tokens: list[Token]) -> tuple[str, Color]:
border_type = "solid"

View File

@@ -28,7 +28,7 @@ from ..dom import DOMNode
from .. import log
class StylesheetParseError(Exception):
class StylesheetParseError(StylesheetError):
def __init__(self, errors: StylesheetErrors) -> None:
self.errors = errors

View File

@@ -202,7 +202,7 @@ class Key(InputEvent):
@rich.repr.auto
class MouseEvent(InputEvent, bubble=True):
class MouseEvent(InputEvent, bubble=True, verbosity=2):
"""Sent in response to a mouse event"""
__slots__ = [
@@ -344,7 +344,7 @@ class MouseScrollDown(InputEvent, verbosity=3, bubble=True):
self.y = y
class MouseScrollUp(MouseScrollDown, bubble=True):
class MouseScrollUp(MouseScrollDown, verbosity=3, bubble=True):
pass

View File

@@ -91,7 +91,6 @@ class Screen(Widget):
def on_idle(self, event: events.Idle) -> None:
# Check for any widgets marked as 'dirty' (needs a repaint)
if self._dirty_widgets:
self.log(dirty=len(self._dirty_widgets))
for widget in self._dirty_widgets:
# Repaint widgets
# TODO: Combine these in to a single update.

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import pytest
@@ -9,6 +11,7 @@ from textual.css.stylesheet import Stylesheet, StylesheetParseError
from textual.css.tokenize import tokenize
from textual.css.tokenizer import Token, ReferencedBy
from textual.css.transition import Transition
from textual.geometry import Spacing
from textual.layouts.dock import DockLayout
@@ -1065,3 +1068,19 @@ class TestParseOpacity:
with pytest.raises(StylesheetParseError):
stylesheet.parse(css)
assert stylesheet.rules[0].errors
class TestParseMargin:
def test_margin_partial(self):
css = "#foo {margin: 1; margin-top: 2; margin-right: 3; margin-bottom: -1;}"
stylesheet = Stylesheet()
stylesheet.parse(css)
assert stylesheet.rules[0].styles.margin == Spacing(2, 3, -1, 1)
class TestParsePadding:
def test_padding_partial(self):
css = "#foo {padding: 1; padding-top: 2; padding-right: 3; padding-bottom: -1;}"
stylesheet = Stylesheet()
stylesheet.parse(css)
assert stylesheet.rules[0].styles.padding == Spacing(2, 3, -1, 1)

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import pytest
from textual.css.tokenize import tokenize