Merge pull request #1759 from Textualize/typing-fix-9-feb-2023

typing fixes
This commit is contained in:
Will McGugan
2023-02-09 17:41:59 +00:00
committed by GitHub
15 changed files with 1333 additions and 1285 deletions

2315
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -69,6 +69,7 @@ syrupy = "^3.0.0"
mkdocs-rss-plugin = "^1.5.0" mkdocs-rss-plugin = "^1.5.0"
httpx = "^0.23.1" httpx = "^0.23.1"
msgpack-types = "^0.2.0" msgpack-types = "^0.2.0"
types-setuptools = "^67.2.0.1"
[tool.black] [tool.black]
includes = "src" includes = "src"

View File

@@ -191,7 +191,7 @@ BORDER_LOCATIONS: dict[
INVISIBLE_EDGE_TYPES = cast("frozenset[EdgeType]", frozenset(("", "none", "hidden"))) INVISIBLE_EDGE_TYPES = cast("frozenset[EdgeType]", frozenset(("", "none", "hidden")))
BorderValue: TypeAlias = Tuple[EdgeType, Union[str, Color, Style]] BorderValue: TypeAlias = Tuple[EdgeType, Color]
BoxSegments: TypeAlias = Tuple[ BoxSegments: TypeAlias = Tuple[
Tuple[Segment, Segment, Segment], Tuple[Segment, Segment, Segment],

View File

@@ -10,11 +10,12 @@ when setting and getting.
from __future__ import annotations from __future__ import annotations
from operator import attrgetter from operator import attrgetter
from typing import TYPE_CHECKING, Generic, Iterable, NamedTuple, TypeVar, cast from typing import TYPE_CHECKING, Generic, Iterable, NamedTuple, Sequence, TypeVar, cast
import rich.errors import rich.errors
import rich.repr import rich.repr
from rich.style import Style from rich.style import Style
from typing_extensions import TypeAlias
from .._border import normalize_border_value from .._border import normalize_border_value
from ..color import Color, ColorParseError from ..color import Color, ColorParseError
@@ -51,7 +52,7 @@ if TYPE_CHECKING:
from .types import AlignHorizontal, AlignVertical, DockEdge, EdgeType from .types import AlignHorizontal, AlignVertical, DockEdge, EdgeType
BorderDefinition = ( BorderDefinition: TypeAlias = (
"Sequence[tuple[EdgeType, str | Color] | None] | tuple[EdgeType, str | Color]" "Sequence[tuple[EdgeType, str | Color] | None] | tuple[EdgeType, str | Color]"
) )
@@ -153,7 +154,7 @@ class ScalarProperty:
Returns: Returns:
The Scalar object or ``None`` if it's not set. The Scalar object or ``None`` if it's not set.
""" """
return obj.get_rule(self.name) return cast("Scalar | None", obj.get_rule(self.name))
def __set__( def __set__(
self, obj: StylesBase, value: float | int | Scalar | str | None self, obj: StylesBase, value: float | int | Scalar | str | None
@@ -233,7 +234,7 @@ class ScalarListProperty:
def __get__( def __get__(
self, obj: StylesBase, objtype: type[StylesBase] | None = None self, obj: StylesBase, objtype: type[StylesBase] | None = None
) -> tuple[Scalar, ...] | None: ) -> tuple[Scalar, ...] | None:
return obj.get_rule(self.name) return cast("tuple[Scalar, ...]", obj.get_rule(self.name))
def __set__( def __set__(
self, obj: StylesBase, value: str | Iterable[str | float] | None self, obj: StylesBase, value: str | Iterable[str | float] | None
@@ -289,7 +290,10 @@ class BoxProperty:
A ``tuple[EdgeType, Style]`` containing the string type of the box and A ``tuple[EdgeType, Style]`` containing the string type of the box and
it's style. Example types are "rounded", "solid", and "dashed". it's style. Example types are "rounded", "solid", and "dashed".
""" """
return obj.get_rule(self.name) or ("", self._default_color) return cast(
"tuple[EdgeType, Color]",
obj.get_rule(self.name) or ("", self._default_color),
)
def __set__(self, obj: Styles, border: tuple[EdgeType, str | Color] | None): def __set__(self, obj: Styles, border: tuple[EdgeType, str | Color] | None):
"""Set the box property. """Set the box property.
@@ -452,7 +456,7 @@ class BorderProperty:
check_refresh() check_refresh()
return return
if isinstance(border, tuple) and len(border) == 2: if isinstance(border, tuple) and len(border) == 2:
_border = normalize_border_value(border) _border = normalize_border_value(border) # type: ignore
setattr(obj, top, _border) setattr(obj, top, _border)
setattr(obj, right, _border) setattr(obj, right, _border)
setattr(obj, bottom, _border) setattr(obj, bottom, _border)
@@ -462,15 +466,15 @@ class BorderProperty:
count = len(border) count = len(border)
if count == 1: if count == 1:
_border = normalize_border_value(border[0]) _border = normalize_border_value(border[0]) # type: ignore
setattr(obj, top, _border) setattr(obj, top, _border)
setattr(obj, right, _border) setattr(obj, right, _border)
setattr(obj, bottom, _border) setattr(obj, bottom, _border)
setattr(obj, left, _border) setattr(obj, left, _border)
elif count == 2: elif count == 2:
_border1, _border2 = ( _border1, _border2 = (
normalize_border_value(border[0]), normalize_border_value(border[0]), # type: ignore
normalize_border_value(border[1]), normalize_border_value(border[1]), # type: ignore
) )
setattr(obj, top, _border1) setattr(obj, top, _border1)
setattr(obj, bottom, _border1) setattr(obj, bottom, _border1)
@@ -478,10 +482,10 @@ class BorderProperty:
setattr(obj, left, _border2) setattr(obj, left, _border2)
elif count == 4: elif count == 4:
_border1, _border2, _border3, _border4 = ( _border1, _border2, _border3, _border4 = (
normalize_border_value(border[0]), normalize_border_value(border[0]), # type: ignore
normalize_border_value(border[1]), normalize_border_value(border[1]), # type: ignore
normalize_border_value(border[2]), normalize_border_value(border[2]), # type: ignore
normalize_border_value(border[3]), normalize_border_value(border[3]), # type: ignore
) )
setattr(obj, top, _border1) setattr(obj, top, _border1)
setattr(obj, right, _border2) setattr(obj, right, _border2)
@@ -513,7 +517,7 @@ class SpacingProperty:
Returns: Returns:
The Spacing. If unset, returns the null spacing ``(0, 0, 0, 0)``. The Spacing. If unset, returns the null spacing ``(0, 0, 0, 0)``.
""" """
return obj.get_rule(self.name, NULL_SPACING) return cast(Spacing, obj.get_rule(self.name, NULL_SPACING))
def __set__(self, obj: StylesBase, spacing: SpacingDimensions | None): def __set__(self, obj: StylesBase, spacing: SpacingDimensions | None):
"""Set the Spacing. """Set the Spacing.
@@ -594,7 +598,7 @@ class LayoutProperty:
Returns: Returns:
The ``Layout`` object. The ``Layout`` object.
""" """
return obj.get_rule(self.name) return cast("Layout | None", obj.get_rule(self.name))
def __set__(self, obj: StylesBase, layout: str | Layout | None): def __set__(self, obj: StylesBase, layout: str | Layout | None):
""" """
@@ -648,7 +652,7 @@ class OffsetProperty:
The ``ScalarOffset`` indicating the adjustment that The ``ScalarOffset`` indicating the adjustment that
will be made to widget position prior to it being rendered. will be made to widget position prior to it being rendered.
""" """
return obj.get_rule(self.name, NULL_SCALAR) return cast("ScalarOffset", obj.get_rule(self.name, NULL_SCALAR))
def __set__( def __set__(
self, obj: StylesBase, offset: tuple[int | str, int | str] | ScalarOffset | None self, obj: StylesBase, offset: tuple[int | str, int | str] | ScalarOffset | None
@@ -734,7 +738,7 @@ class StringEnumProperty:
Returns: Returns:
The string property value. The string property value.
""" """
return obj.get_rule(self.name, self._default) return cast(str, obj.get_rule(self.name, self._default))
def _before_refresh(self, obj: StylesBase, value: str | None) -> None: def _before_refresh(self, obj: StylesBase, value: str | None) -> None:
"""Do any housekeeping before asking for a layout refresh after a value change.""" """Do any housekeeping before asking for a layout refresh after a value change."""
@@ -795,7 +799,7 @@ class NameProperty:
Returns: Returns:
The name. The name.
""" """
return obj.get_rule(self.name, "") return cast(str, obj.get_rule(self.name, ""))
def __set__(self, obj: StylesBase, name: str | None): def __set__(self, obj: StylesBase, name: str | None):
"""Set the name property. """Set the name property.
@@ -929,7 +933,7 @@ class StyleFlagsProperty:
Returns: Returns:
The ``Style`` object. The ``Style`` object.
""" """
return obj.get_rule(self.name, Style.null()) return cast(Style, obj.get_rule(self.name, Style.null()))
def __set__(self, obj: StylesBase, style_flags: Style | str | None): def __set__(self, obj: StylesBase, style_flags: Style | str | None):
"""Set the style using a style flag string. """Set the style using a style flag string.
@@ -992,7 +996,7 @@ class TransitionsProperty:
e.g. ``{"offset": Transition(...), ...}``. If no transitions have been set, an empty ``dict`` e.g. ``{"offset": Transition(...), ...}``. If no transitions have been set, an empty ``dict``
is returned. is returned.
""" """
return obj.get_rule("transitions", {}) return cast("dict[str, Transition]", obj.get_rule("transitions", {}))
def __set__(self, obj: Styles, transitions: dict[str, Transition] | None) -> None: def __set__(self, obj: Styles, transitions: dict[str, Transition] | None) -> None:
_rich_traceback_omit = True _rich_traceback_omit = True

View File

@@ -113,7 +113,6 @@ class StylesBuilder:
suggested_property_name=suggested_property_name, suggested_property_name=suggested_property_name,
), ),
) )
return
tokens = declaration.tokens tokens = declaration.tokens
@@ -182,7 +181,13 @@ class StylesBuilder:
""" """
if len(tokens) != 1: if len(tokens) != 1:
string_enum_help_text(name, valid_values=list(valid_values), context="css"), self.error(
name,
tokens[0],
string_enum_help_text(
name, valid_values=list(valid_values), context="css"
),
)
token = tokens[0] token = tokens[0]
token_name, value, _, _, location, _ = token token_name, value, _, _, location, _ = token
@@ -239,7 +244,7 @@ class StylesBuilder:
return return
if len(tokens) == 1: if len(tokens) == 1:
try: try:
self.styles._rules[name.replace("-", "_")] = Scalar.parse( self.styles._rules[name.replace("-", "_")] = Scalar.parse( # type: ignore
tokens[0].value tokens[0].value
) )
except ScalarParseError: except ScalarParseError:
@@ -390,7 +395,7 @@ class StylesBuilder:
name, num_values_supplied=len(space), context="css" name, num_values_supplied=len(space), context="css"
), ),
) )
self.styles._rules[name] = Spacing.unpack(cast(SpacingDimensions, tuple(space))) self.styles._rules[name] = Spacing.unpack(cast(SpacingDimensions, tuple(space))) # type: ignore
def _process_space_partial(self, name: str, tokens: list[Token]) -> None: def _process_space_partial(self, name: str, tokens: list[Token]) -> None:
"""Process granular margin / padding declarations.""" """Process granular margin / padding declarations."""
@@ -418,7 +423,7 @@ class StylesBuilder:
spacing_list = list(current_spacing) spacing_list = list(current_spacing)
spacing_list[_EDGE_SPACING_MAP[edge]] = space spacing_list[_EDGE_SPACING_MAP[edge]] = space
self.styles._rules[style_name] = Spacing(*spacing_list) self.styles._rules[style_name] = Spacing(*spacing_list) # type: ignore
process_padding = _process_space process_padding = _process_space
process_margin = _process_space process_margin = _process_space
@@ -444,7 +449,7 @@ class StylesBuilder:
token_name, value, _, _, _, _ = token token_name, value, _, _, _, _ = token
if token_name == "token": if token_name == "token":
if value in VALID_BORDER: if value in VALID_BORDER:
border_type = value border_type = value # type: ignore
else: else:
try: try:
border_color = Color.parse(value) border_color = Color.parse(value)
@@ -464,7 +469,7 @@ class StylesBuilder:
def _process_border_edge(self, edge: str, name: str, tokens: list[Token]) -> None: def _process_border_edge(self, edge: str, name: str, tokens: list[Token]) -> None:
border = self._parse_border(name, tokens) border = self._parse_border(name, tokens)
self.styles._rules[f"border_{edge}"] = border self.styles._rules[f"border_{edge}"] = border # type: ignore
def process_border(self, name: str, tokens: list[Token]) -> None: def process_border(self, name: str, tokens: list[Token]) -> None:
border = self._parse_border(name, tokens) border = self._parse_border(name, tokens)
@@ -486,7 +491,7 @@ class StylesBuilder:
def _process_outline(self, edge: str, name: str, tokens: list[Token]) -> None: def _process_outline(self, edge: str, name: str, tokens: list[Token]) -> None:
border = self._parse_border(name, tokens) border = self._parse_border(name, tokens)
self.styles._rules[f"outline_{edge}"] = border self.styles._rules[f"outline_{edge}"] = border # type: ignore
def process_outline(self, name: str, tokens: list[Token]) -> None: def process_outline(self, name: str, tokens: list[Token]) -> None:
border = self._parse_border(name, tokens) border = self._parse_border(name, tokens)
@@ -579,14 +584,14 @@ class StylesBuilder:
color: Color | None = None color: Color | None = None
alpha: float | None = None alpha: float | None = None
self.styles._rules[f"auto_{name}"] = False self.styles._rules[f"auto_{name}"] = False # type: ignore
for token in tokens: for token in tokens:
if ( if (
"background" not in name "background" not in name
and token.name == "token" and token.name == "token"
and token.value == "auto" and token.value == "auto"
): ):
self.styles._rules[f"auto_{name}"] = True self.styles._rules[f"auto_{name}"] = True # type: ignore
elif token.name == "scalar": elif token.name == "scalar":
alpha_scalar = Scalar.parse(token.value) alpha_scalar = Scalar.parse(token.value)
if alpha_scalar.unit != Unit.PERCENT: if alpha_scalar.unit != Unit.PERCENT:
@@ -608,7 +613,7 @@ class StylesBuilder:
if color is not None or alpha is not None: if color is not None or alpha is not None:
if alpha is not None: if alpha is not None:
color = (color or Color(255, 255, 255)).with_alpha(alpha) color = (color or Color(255, 255, 255)).with_alpha(alpha)
self.styles._rules[name] = color self.styles._rules[name] = color # type: ignore
process_tint = process_color process_tint = process_color
process_background = process_color process_background = process_color
@@ -636,7 +641,7 @@ class StylesBuilder:
) )
style_definition = " ".join(token.value for token in tokens) style_definition = " ".join(token.value for token in tokens)
self.styles._rules[name.replace("-", "_")] = style_definition self.styles._rules[name.replace("-", "_")] = style_definition # type: ignore
process_link_style = process_text_style process_link_style = process_text_style
process_link_hover_style = process_text_style process_link_hover_style = process_text_style
@@ -653,7 +658,7 @@ class StylesBuilder:
text_align_help_text(), text_align_help_text(),
) )
self.styles._rules["text_align"] = tokens[0].value self.styles._rules["text_align"] = tokens[0].value # type: ignore
def process_dock(self, name: str, tokens: list[Token]) -> None: def process_dock(self, name: str, tokens: list[Token]) -> None:
if not tokens: if not tokens:
@@ -766,8 +771,8 @@ class StylesBuilder:
align_error(name, token_horizontal) align_error(name, token_horizontal)
name = name.replace("-", "_") name = name.replace("-", "_")
self.styles._rules[f"{name}_horizontal"] = token_horizontal.value self.styles._rules[f"{name}_horizontal"] = token_horizontal.value # type: ignore
self.styles._rules[f"{name}_vertical"] = token_vertical.value self.styles._rules[f"{name}_vertical"] = token_vertical.value # type: ignore
def process_align_horizontal(self, name: str, tokens: list[Token]) -> None: def process_align_horizontal(self, name: str, tokens: list[Token]) -> None:
try: try:
@@ -779,7 +784,7 @@ class StylesBuilder:
string_enum_help_text(name, VALID_ALIGN_HORIZONTAL, context="css"), string_enum_help_text(name, VALID_ALIGN_HORIZONTAL, context="css"),
) )
else: else:
self.styles._rules[name.replace("-", "_")] = value self.styles._rules[name.replace("-", "_")] = value # type: ignore
def process_align_vertical(self, name: str, tokens: list[Token]) -> None: def process_align_vertical(self, name: str, tokens: list[Token]) -> None:
try: try:
@@ -791,7 +796,7 @@ class StylesBuilder:
string_enum_help_text(name, VALID_ALIGN_VERTICAL, context="css"), string_enum_help_text(name, VALID_ALIGN_VERTICAL, context="css"),
) )
else: else:
self.styles._rules[name.replace("-", "_")] = value self.styles._rules[name.replace("-", "_")] = value # type: ignore
process_content_align = process_align process_content_align = process_align
process_content_align_horizontal = process_align_horizontal process_content_align_horizontal = process_align_horizontal
@@ -807,7 +812,7 @@ class StylesBuilder:
string_enum_help_text(name, VALID_SCROLLBAR_GUTTER, context="css"), string_enum_help_text(name, VALID_SCROLLBAR_GUTTER, context="css"),
) )
else: else:
self.styles._rules[name.replace("-", "_")] = value self.styles._rules[name.replace("-", "_")] = value # type: ignore
def process_scrollbar_size(self, name: str, tokens: list[Token]) -> None: def process_scrollbar_size(self, name: str, tokens: list[Token]) -> None:
def scrollbar_size_error(name: str, token: Token) -> None: def scrollbar_size_error(name: str, token: Token) -> None:
@@ -876,7 +881,7 @@ class StylesBuilder:
token, token,
table_rows_or_columns_help_text(name, token.value, context="css"), table_rows_or_columns_help_text(name, token.value, context="css"),
) )
self.styles._rules[name.replace("-", "_")] = scalars self.styles._rules[name.replace("-", "_")] = scalars # type: ignore
process_grid_rows = _process_grid_rows_or_columns process_grid_rows = _process_grid_rows_or_columns
process_grid_columns = _process_grid_rows_or_columns process_grid_columns = _process_grid_rows_or_columns
@@ -893,7 +898,7 @@ class StylesBuilder:
value = int(token.value) value = int(token.value)
if value == 0: if value == 0:
self.error(name, token, integer_help_text(name)) self.error(name, token, integer_help_text(name))
self.styles._rules[name.replace("-", "_")] = value self.styles._rules[name.replace("-", "_")] = value # type: ignore
process_grid_gutter_horizontal = _process_integer process_grid_gutter_horizontal = _process_integer
process_grid_gutter_vertical = _process_integer process_grid_gutter_vertical = _process_integer

View File

@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Iterable
import rich.repr import rich.repr
from ._help_renderables import HelpText
from .styles import Styles from .styles import Styles
from .tokenize import Token from .tokenize import Token
from .types import Specificity3 from .types import Specificity3
@@ -155,7 +156,7 @@ class SelectorSet:
class RuleSet: class RuleSet:
selector_set: list[SelectorSet] = field(default_factory=list) selector_set: list[SelectorSet] = field(default_factory=list)
styles: Styles = field(default_factory=Styles) styles: Styles = field(default_factory=Styles)
errors: list[tuple[Token, str]] = field(default_factory=list) errors: list[tuple[Token, str | HelpText]] = field(default_factory=list)
is_default_rules: bool = False is_default_rules: bool = False
tie_breaker: int = 0 tie_breaker: int = 0

View File

@@ -5,6 +5,7 @@ from pathlib import PurePath
from typing import Iterable, Iterator, NoReturn from typing import Iterable, Iterator, NoReturn
from ..suggestions import get_suggestion from ..suggestions import get_suggestion
from ._help_renderables import HelpText
from ._styles_builder import DeclarationError, StylesBuilder from ._styles_builder import DeclarationError, StylesBuilder
from .errors import UnresolvedVariableError from .errors import UnresolvedVariableError
from .model import ( from .model import (
@@ -130,7 +131,7 @@ def parse_rule_set(
declaration = Declaration(token, "") declaration = Declaration(token, "")
errors: list[tuple[Token, str]] = [] errors: list[tuple[Token, str | HelpText]] = []
while True: while True:
token = next(tokens) token = next(tokens)

View File

@@ -367,8 +367,8 @@ class StylesBase(ABC):
def auto_dimensions(self) -> bool: def auto_dimensions(self) -> bool:
"""Check if width or height are set to 'auto'.""" """Check if width or height are set to 'auto'."""
has_rule = self.has_rule has_rule = self.has_rule
return (has_rule("width") and self.width.is_auto) or ( return (has_rule("width") and self.width.is_auto) or ( # type: ignore
has_rule("height") and self.height.is_auto has_rule("height") and self.height.is_auto # type: ignore
) )
@abstractmethod @abstractmethod
@@ -603,7 +603,7 @@ class Styles(StylesBase):
Returns: Returns:
``True`` if a rule was cleared, or ``False`` if it was already not set. ``True`` if a rule was cleared, or ``False`` if it was already not set.
""" """
changed = self._rules.pop(rule, None) is not None changed = self._rules.pop(rule, None) is not None # type: ignore
if changed: if changed:
self._updates += 1 self._updates += 1
return changed return changed
@@ -622,12 +622,12 @@ class Styles(StylesBase):
``True`` if the rule changed, otherwise ``False``. ``True`` if the rule changed, otherwise ``False``.
""" """
if value is None: if value is None:
changed = self._rules.pop(rule, None) is not None changed = self._rules.pop(rule, None) is not None # type: ignore
if changed: if changed:
self._updates += 1 self._updates += 1
return changed return changed
current = self._rules.get(rule) current = self._rules.get(rule)
self._rules[rule] = value self._rules[rule] = value # type: ignore
changed = current != value changed = current != value
if changed: if changed:
self._updates += 1 self._updates += 1
@@ -646,7 +646,7 @@ class Styles(StylesBase):
def reset(self) -> None: def reset(self) -> None:
"""Reset the rules to initial state.""" """Reset the rules to initial state."""
self._updates += 1 self._updates += 1
self._rules.clear() self._rules.clear() # type: ignore
def merge(self, other: Styles) -> None: def merge(self, other: Styles) -> None:
"""Merge values from another Styles. """Merge values from another Styles.
@@ -736,25 +736,25 @@ class Styles(StylesBase):
left = get_rule(f"{name}_left") left = get_rule(f"{name}_left")
if top == right and right == bottom and bottom == left: if top == right and right == bottom and bottom == left:
border_type, border_color = rules[f"{name}_top"] border_type, border_color = rules[f"{name}_top"] # type: ignore
yield name, f"{border_type} {border_color.hex}" yield name, f"{border_type} {border_color.hex}"
return return
# Check for edges # Check for edges
if has_top: if has_top:
border_type, border_color = rules[f"{name}_top"] border_type, border_color = rules[f"{name}_top"] # type: ignore
yield f"{name}-top", f"{border_type} {border_color.hex}" yield f"{name}-top", f"{border_type} {border_color.hex}"
if has_right: if has_right:
border_type, border_color = rules[f"{name}_right"] border_type, border_color = rules[f"{name}_right"] # type: ignore
yield f"{name}-right", f"{border_type} {border_color.hex}" yield f"{name}-right", f"{border_type} {border_color.hex}"
if has_bottom: if has_bottom:
border_type, border_color = rules[f"{name}_bottom"] border_type, border_color = rules[f"{name}_bottom"] # type: ignore
yield f"{name}-bottom", f"{border_type} {border_color.hex}" yield f"{name}-bottom", f"{border_type} {border_color.hex}"
if has_left: if has_left:
border_type, border_color = rules[f"{name}_left"] border_type, border_color = rules[f"{name}_left"] # type: ignore
yield f"{name}-left", f"{border_type} {border_color.hex}" yield f"{name}-left", f"{border_type} {border_color.hex}"
@property @property
@@ -770,15 +770,14 @@ class Styles(StylesBase):
rules = self.get_rules() rules = self.get_rules()
get_rule = rules.get get_rule = rules.get
has_rule = rules.__contains__
if has_rule("display"): if "display" in rules:
append_declaration("display", rules["display"]) append_declaration("display", rules["display"])
if has_rule("visibility"): if "visibility" in rules:
append_declaration("visibility", rules["visibility"]) append_declaration("visibility", rules["visibility"])
if has_rule("padding"): if "padding" in rules:
append_declaration("padding", rules["padding"].css) append_declaration("padding", rules["padding"].css)
if has_rule("margin"): if "margin" in rules:
append_declaration("margin", rules["margin"].css) append_declaration("margin", rules["margin"].css)
for name, rule in self._get_border_css_lines(rules, "border"): for name, rule in self._get_border_css_lines(rules, "border"):
@@ -787,90 +786,90 @@ class Styles(StylesBase):
for name, rule in self._get_border_css_lines(rules, "outline"): for name, rule in self._get_border_css_lines(rules, "outline"):
append_declaration(name, rule) append_declaration(name, rule)
if has_rule("offset"): if "offset" in rules:
x, y = self.offset x, y = self.offset
append_declaration("offset", f"{x} {y}") append_declaration("offset", f"{x} {y}")
if has_rule("dock"): if "dock" in rules:
append_declaration("dock", rules["dock"]) append_declaration("dock", rules["dock"])
if has_rule("layers"): if "layers" in rules:
append_declaration("layers", " ".join(self.layers)) append_declaration("layers", " ".join(self.layers))
if has_rule("layer"): if "layer" in rules:
append_declaration("layer", self.layer) append_declaration("layer", self.layer)
if has_rule("layout"): if "layout" in rules:
assert self.layout is not None assert self.layout is not None
append_declaration("layout", self.layout.name) append_declaration("layout", self.layout.name)
if has_rule("color"): if "color" in rules:
append_declaration("color", self.color.hex) append_declaration("color", self.color.hex)
if has_rule("background"): if "background" in rules:
append_declaration("background", self.background.hex) append_declaration("background", self.background.hex)
if has_rule("text_style"): if "text_style" in rules:
append_declaration("text-style", str(get_rule("text_style"))) append_declaration("text-style", str(get_rule("text_style")))
if has_rule("tint"): if "tint" in rules:
append_declaration("tint", self.tint.css) append_declaration("tint", self.tint.css)
if has_rule("overflow_x"): if "overflow_x" in rules:
append_declaration("overflow-x", self.overflow_x) append_declaration("overflow-x", self.overflow_x)
if has_rule("overflow_y"): if "overflow_y" in rules:
append_declaration("overflow-y", self.overflow_y) append_declaration("overflow-y", self.overflow_y)
if has_rule("scrollbar_color"): if "scrollbar_color" in rules:
append_declaration("scrollbar-color", self.scrollbar_color.css) append_declaration("scrollbar-color", self.scrollbar_color.css)
if has_rule("scrollbar_color_hover"): if "scrollbar_color_hover" in rules:
append_declaration("scrollbar-color-hover", self.scrollbar_color_hover.css) append_declaration("scrollbar-color-hover", self.scrollbar_color_hover.css)
if has_rule("scrollbar_color_active"): if "scrollbar_color_active" in rules:
append_declaration( append_declaration(
"scrollbar-color-active", self.scrollbar_color_active.css "scrollbar-color-active", self.scrollbar_color_active.css
) )
if has_rule("scrollbar_corner_color"): if "scrollbar_corner_color" in rules:
append_declaration( append_declaration(
"scrollbar-corner-color", self.scrollbar_corner_color.css "scrollbar-corner-color", self.scrollbar_corner_color.css
) )
if has_rule("scrollbar_background"): if "scrollbar_background" in rules:
append_declaration("scrollbar-background", self.scrollbar_background.css) append_declaration("scrollbar-background", self.scrollbar_background.css)
if has_rule("scrollbar_background_hover"): if "scrollbar_background_hover" in rules:
append_declaration( append_declaration(
"scrollbar-background-hover", self.scrollbar_background_hover.css "scrollbar-background-hover", self.scrollbar_background_hover.css
) )
if has_rule("scrollbar_background_active"): if "scrollbar_background_active" in rules:
append_declaration( append_declaration(
"scrollbar-background-active", self.scrollbar_background_active.css "scrollbar-background-active", self.scrollbar_background_active.css
) )
if has_rule("scrollbar_gutter"): if "scrollbar_gutter" in rules:
append_declaration("scrollbar-gutter", self.scrollbar_gutter) append_declaration("scrollbar-gutter", self.scrollbar_gutter)
if has_rule("scrollbar_size"): if "scrollbar_size" in rules:
append_declaration( append_declaration(
"scrollbar-size", "scrollbar-size",
f"{self.scrollbar_size_horizontal} {self.scrollbar_size_vertical}", f"{self.scrollbar_size_horizontal} {self.scrollbar_size_vertical}",
) )
else: else:
if has_rule("scrollbar_size_horizontal"): if "scrollbar_size_horizontal" in rules:
append_declaration( append_declaration(
"scrollbar-size-horizontal", str(self.scrollbar_size_horizontal) "scrollbar-size-horizontal", str(self.scrollbar_size_horizontal)
) )
if has_rule("scrollbar_size_vertical"): if "scrollbar_size_vertical" in rules:
append_declaration( append_declaration(
"scrollbar-size-vertical", str(self.scrollbar_size_vertical) "scrollbar-size-vertical", str(self.scrollbar_size_vertical)
) )
if has_rule("box_sizing"): if "box_sizing" in rules:
append_declaration("box-sizing", self.box_sizing) append_declaration("box-sizing", self.box_sizing)
if has_rule("width"): if "width" in rules:
append_declaration("width", str(self.width)) append_declaration("width", str(self.width))
if has_rule("height"): if "height" in rules:
append_declaration("height", str(self.height)) append_declaration("height", str(self.height))
if has_rule("min_width"): if "min_width" in rules:
append_declaration("min-width", str(self.min_width)) append_declaration("min-width", str(self.min_width))
if has_rule("min_height"): if "min_height" in rules:
append_declaration("min-height", str(self.min_height)) append_declaration("min-height", str(self.min_height))
if has_rule("max_width"): if "max_width" in rules:
append_declaration("max-width", str(self.min_width)) append_declaration("max-width", str(self.min_width))
if has_rule("max_height"): if "max_height" in rules:
append_declaration("max-height", str(self.min_height)) append_declaration("max-height", str(self.min_height))
if has_rule("transitions"): if "transitions" in rules:
append_declaration( append_declaration(
"transition", "transition",
", ".join( ", ".join(
@@ -879,74 +878,74 @@ class Styles(StylesBase):
), ),
) )
if has_rule("align_horizontal") and has_rule("align_vertical"): if "align_horizontal" in rules and "align_vertical" in rules:
append_declaration( append_declaration(
"align", f"{self.align_horizontal} {self.align_vertical}" "align", f"{self.align_horizontal} {self.align_vertical}"
) )
elif has_rule("align_horizontal"): elif "align_horizontal" in rules:
append_declaration("align-horizontal", self.align_horizontal) append_declaration("align-horizontal", self.align_horizontal)
elif has_rule("align_vertical"): elif "align_vertical" in rules:
append_declaration("align-vertical", self.align_vertical) append_declaration("align-vertical", self.align_vertical)
if has_rule("content_align_horizontal") and has_rule("content_align_vertical"): if "content_align_horizontal" in rules and "content_align_vertical" in rules:
append_declaration( append_declaration(
"content-align", "content-align",
f"{self.content_align_horizontal} {self.content_align_vertical}", f"{self.content_align_horizontal} {self.content_align_vertical}",
) )
elif has_rule("content_align_horizontal"): elif "content_align_horizontal" in rules:
append_declaration( append_declaration(
"content-align-horizontal", self.content_align_horizontal "content-align-horizontal", self.content_align_horizontal
) )
elif has_rule("content_align_vertical"): elif "content_align_vertical" in rules:
append_declaration("content-align-vertical", self.content_align_vertical) append_declaration("content-align-vertical", self.content_align_vertical)
if has_rule("text_align"): if "text_align" in rules:
append_declaration("text-align", self.text_align) append_declaration("text-align", self.text_align)
if has_rule("opacity"): if "opacity" in rules:
append_declaration("opacity", str(self.opacity)) append_declaration("opacity", str(self.opacity))
if has_rule("text_opacity"): if "text_opacity" in rules:
append_declaration("text-opacity", str(self.text_opacity)) append_declaration("text-opacity", str(self.text_opacity))
if has_rule("grid_columns"): if "grid_columns" in rules:
append_declaration( append_declaration(
"grid-columns", "grid-columns",
" ".join(str(scalar) for scalar in self.grid_columns or ()), " ".join(str(scalar) for scalar in self.grid_columns or ()),
) )
if has_rule("grid_rows"): if "grid_rows" in rules:
append_declaration( append_declaration(
"grid-rows", "grid-rows",
" ".join(str(scalar) for scalar in self.grid_rows or ()), " ".join(str(scalar) for scalar in self.grid_rows or ()),
) )
if has_rule("grid_size_columns"): if "grid_size_columns" in rules:
append_declaration("grid-size-columns", str(self.grid_size_columns)) append_declaration("grid-size-columns", str(self.grid_size_columns))
if has_rule("grid_size_rows"): if "grid_size_rows" in rules:
append_declaration("grid-size-rows", str(self.grid_size_rows)) append_declaration("grid-size-rows", str(self.grid_size_rows))
if has_rule("grid_gutter_horizontal"): if "grid_gutter_horizontal" in rules:
append_declaration( append_declaration(
"grid-gutter-horizontal", str(self.grid_gutter_horizontal) "grid-gutter-horizontal", str(self.grid_gutter_horizontal)
) )
if has_rule("grid_gutter_vertical"): if "grid_gutter_vertical" in rules:
append_declaration("grid-gutter-vertical", str(self.grid_gutter_vertical)) append_declaration("grid-gutter-vertical", str(self.grid_gutter_vertical))
if has_rule("row_span"): if "row_span" in rules:
append_declaration("row-span", str(self.row_span)) append_declaration("row-span", str(self.row_span))
if has_rule("column_span"): if "column_span" in rules:
append_declaration("column-span", str(self.column_span)) append_declaration("column-span", str(self.column_span))
if has_rule("link_color"): if "link_color" in rules:
append_declaration("link-color", self.link_color.css) append_declaration("link-color", self.link_color.css)
if has_rule("link_background"): if "link_background" in rules:
append_declaration("link-background", self.link_background.css) append_declaration("link-background", self.link_background.css)
if has_rule("link_style"): if "link_style" in rules:
append_declaration("link-style", str(self.link_style)) append_declaration("link-style", str(self.link_style))
if has_rule("link_hover_color"): if "link_hover_color" in rules:
append_declaration("link-hover-color", self.link_hover_color.css) append_declaration("link-hover-color", self.link_hover_color.css)
if has_rule("link_hover_background"): if "link_hover_background" in rules:
append_declaration("link-hover-background", self.link_hover_background.css) append_declaration("link-hover-background", self.link_hover_background.css)
if has_rule("link_hover_style"): if "link_hover_style" in rules:
append_declaration("link-hover-style", str(self.link_hover_style)) append_declaration("link-hover-style", str(self.link_hover_style))
lines.sort() lines.sort()

View File

@@ -4,7 +4,7 @@ import os
from collections import defaultdict from collections import defaultdict
from operator import itemgetter from operator import itemgetter
from pathlib import Path, PurePath from pathlib import Path, PurePath
from typing import Iterable, NamedTuple, cast from typing import Iterable, NamedTuple, Sequence, cast
import rich.repr import rich.repr
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
@@ -248,7 +248,7 @@ class Stylesheet:
self.source[str(path)] = CssSource(css, False, 0) self.source[str(path)] = CssSource(css, False, 0)
self._require_parse = True self._require_parse = True
def read_all(self, paths: list[PurePath]) -> None: def read_all(self, paths: Sequence[PurePath]) -> None:
"""Read multiple CSS files, in order. """Read multiple CSS files, in order.
Args: Args:

View File

@@ -132,10 +132,12 @@ class DOMNode(MessagePump):
check_identifiers("class name", *_classes) check_identifiers("class name", *_classes)
self._classes.update(_classes) self._classes.update(_classes)
self.children = NodeList() self.children: NodeList = NodeList()
self._css_styles: Styles = Styles(self) self._css_styles: Styles = Styles(self)
self._inline_styles: Styles = Styles(self) self._inline_styles: Styles = Styles(self)
self.styles = RenderStyles(self, self._css_styles, self._inline_styles) self.styles: RenderStyles = RenderStyles(
self, self._css_styles, self._inline_styles
)
# A mapping of class names to Styles set in COMPONENT_CLASSES # A mapping of class names to Styles set in COMPONENT_CLASSES
self._component_styles: dict[str, RenderStyles] = {} self._component_styles: dict[str, RenderStyles] = {}

View File

@@ -7,12 +7,12 @@ from ctypes import Structure, Union, byref, wintypes
from ctypes.wintypes import BOOL, CHAR, DWORD, HANDLE, SHORT, UINT, WCHAR, WORD from ctypes.wintypes import BOOL, CHAR, DWORD, HANDLE, SHORT, UINT, WCHAR, WORD
from typing import IO, Callable, List, Optional from typing import IO, Callable, List, Optional
from .._types import EventTarget from .._types import MessageTarget
from .._xterm_parser import XTermParser from .._xterm_parser import XTermParser
from ..events import Event, Resize from ..events import Event, Resize
from ..geometry import Size from ..geometry import Size
KERNEL32 = ctypes.WinDLL("kernel32", use_last_error=True) KERNEL32 = ctypes.WinDLL("kernel32", use_last_error=True) # type: ignore
# Console input modes # Console input modes
ENABLE_ECHO_INPUT = 0x0004 ENABLE_ECHO_INPUT = 0x0004
@@ -130,7 +130,7 @@ def _set_console_mode(file: IO, mode: int) -> bool:
Returns: Returns:
True on success, otherwise False. True on success, otherwise False.
""" """
windows_filehandle = msvcrt.get_osfhandle(file.fileno()) windows_filehandle = msvcrt.get_osfhandle(file.fileno()) # type: ignore
success = KERNEL32.SetConsoleMode(windows_filehandle, mode) success = KERNEL32.SetConsoleMode(windows_filehandle, mode)
return success return success
@@ -144,7 +144,7 @@ def _get_console_mode(file: IO) -> int:
Returns: Returns:
The current console mode. The current console mode.
""" """
windows_filehandle = msvcrt.get_osfhandle(file.fileno()) windows_filehandle = msvcrt.get_osfhandle(file.fileno()) # type: ignore
mode = wintypes.DWORD() mode = wintypes.DWORD()
KERNEL32.GetConsoleMode(windows_filehandle, ctypes.byref(mode)) KERNEL32.GetConsoleMode(windows_filehandle, ctypes.byref(mode))
return mode.value return mode.value
@@ -211,7 +211,7 @@ class EventMonitor(threading.Thread):
self, self,
loop: AbstractEventLoop, loop: AbstractEventLoop,
app, app,
target: EventTarget, target: MessageTarget,
exit_event: threading.Event, exit_event: threading.Event,
process_event: Callable[[Event], None], process_event: Callable[[Event], None],
) -> None: ) -> None:

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
import os import os
from pathlib import PurePath from pathlib import PurePath
from typing import Callable from typing import Callable, Sequence
import rich.repr import rich.repr
@@ -13,7 +13,7 @@ from ._callback import invoke
class FileMonitor: class FileMonitor:
"""Monitors files for changes and invokes a callback when it does.""" """Monitors files for changes and invokes a callback when it does."""
def __init__(self, paths: list[PurePath], callback: Callable) -> None: def __init__(self, paths: Sequence[PurePath], callback: Callable) -> None:
self.paths = paths self.paths = paths
self.callback = callback self.callback = callback
self._modified = self._get_last_modified_time() self._modified = self._get_last_modified_time()

View File

@@ -30,14 +30,14 @@ class ScrollView(Widget):
"""Not transparent, i.e. renders something.""" """Not transparent, i.e. renders something."""
return False return False
def watch_scroll_x(self, new_value: float) -> None: def watch_scroll_x(self, old_value: float, new_value: float) -> None:
if self.show_horizontal_scrollbar: if self.show_horizontal_scrollbar and round(old_value) != round(new_value):
self.horizontal_scrollbar.position = int(new_value) self.horizontal_scrollbar.position = round(new_value)
self.refresh() self.refresh()
def watch_scroll_y(self, new_value: float) -> None: def watch_scroll_y(self, old_value: float, new_value: float) -> None:
if self.show_vertical_scrollbar: if self.show_vertical_scrollbar and round(old_value) != round(new_value):
self.vertical_scrollbar.position = int(new_value) self.vertical_scrollbar.position = round(new_value)
self.refresh() self.refresh()
def on_mount(self): def on_mount(self):

View File

@@ -117,7 +117,7 @@ class _Styled:
""" """
def __init__( def __init__(
self, renderable: "RenderableType", style: Style, link_style: Style | None self, renderable: "ConsoleRenderable", style: Style, link_style: Style | None
) -> None: ) -> None:
self.renderable = renderable self.renderable = renderable
self.style = style self.style = style
@@ -133,7 +133,7 @@ class _Styled:
if style: if style:
apply = style.__add__ apply = style.__add__
result_segments = ( result_segments = (
_Segment(text, apply(_style), control) _Segment(text, apply(_style), None)
for text, _style, control in result_segments for text, _style, control in result_segments
) )
link_style = self.link_style link_style = self.link_style
@@ -141,19 +141,22 @@ class _Styled:
result_segments = ( result_segments = (
_Segment( _Segment(
text, text,
(
style style
if style._meta is None if style._meta is None
else (style + link_style if "@click" in style.meta else style), else (style + link_style if "@click" in style.meta else style)
),
control, control,
) )
for text, style, control in result_segments for text, style, control in result_segments
if style is not None
) )
return result_segments return result_segments
def __rich_measure__( def __rich_measure__(
self, console: "Console", options: "ConsoleOptions" self, console: "Console", options: "ConsoleOptions"
) -> Measurement: ) -> Measurement:
return self.renderable.__rich_measure__(console, options) return Measurement.get(console, options, self.renderable)
class RenderCache(NamedTuple): class RenderCache(NamedTuple):
@@ -1414,6 +1417,7 @@ class Widget(DOMNode):
easing = DEFAULT_SCROLL_EASING easing = DEFAULT_SCROLL_EASING
if maybe_scroll_x: if maybe_scroll_x:
assert x is not None
self.scroll_target_x = x self.scroll_target_x = x
if x != self.scroll_x: if x != self.scroll_x:
self.animate( self.animate(
@@ -1425,6 +1429,7 @@ class Widget(DOMNode):
) )
scrolled_x = True scrolled_x = True
if maybe_scroll_y: if maybe_scroll_y:
assert y is not None
self.scroll_target_y = y self.scroll_target_y = y
if y != self.scroll_y: if y != self.scroll_y:
self.animate( self.animate(
@@ -1438,10 +1443,12 @@ class Widget(DOMNode):
else: else:
if maybe_scroll_x: if maybe_scroll_x:
assert x is not None
scroll_x = self.scroll_x scroll_x = self.scroll_x
self.scroll_target_x = self.scroll_x = x self.scroll_target_x = self.scroll_x = x
scrolled_x = scroll_x != self.scroll_x scrolled_x = scroll_x != self.scroll_x
if maybe_scroll_y: if maybe_scroll_y:
assert y is not None
scroll_y = self.scroll_y scroll_y = self.scroll_y
self.scroll_target_y = self.scroll_y = y self.scroll_target_y = self.scroll_y = y
scrolled_y = scroll_y != self.scroll_y scrolled_y = scroll_y != self.scroll_y

View File

@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING, ClassVar, Generic, NewType, TypeVar from typing import TYPE_CHECKING, ClassVar, Generic, Iterable, NewType, TypeVar, cast
import rich.repr import rich.repr
from rich.style import NULL_STYLE, Style from rich.style import NULL_STYLE, Style
@@ -783,7 +783,7 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
assert self._tree_lines_cached is not None assert self._tree_lines_cached is not None
return self._tree_lines_cached return self._tree_lines_cached
def _on_idle(self) -> None: async def _on_idle(self, event: events.Idle) -> None:
"""Check tree needs a rebuild on idle.""" """Check tree needs a rebuild on idle."""
# Property calls build if required # Property calls build if required
self._tree_lines self._tree_lines
@@ -891,6 +891,7 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
Returns: Returns:
Strings for space, vertical, terminator and cross. Strings for space, vertical, terminator and cross.
""" """
lines: tuple[Iterable[str], Iterable[str], Iterable[str], Iterable[str]]
if self.show_guides: if self.show_guides:
lines = self.LINES["default"] lines = self.LINES["default"]
if style.bold: if style.bold:
@@ -901,11 +902,11 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
lines = (" ", " ", " ", " ") lines = (" ", " ", " ", " ")
guide_depth = max(0, self.guide_depth - 2) guide_depth = max(0, self.guide_depth - 2)
lines = tuple( guide_lines = tuple(
f"{vertical}{horizontal * guide_depth} " f"{characters[0]}{characters[1] * guide_depth} "
for vertical, horizontal in lines for characters in lines
) )
return lines return cast("tuple[str, str, str, str]", guide_lines)
if is_hover: if is_hover:
line_style = self.get_component_rich_style("tree--highlight-line") line_style = self.get_component_rich_style("tree--highlight-line")