[App] Add _path suffixes to PathLike constructor args

This commit is contained in:
Olivier Philippon
2022-05-04 12:18:04 +01:00
parent 3f09af3768
commit 73db7ae3ad
26 changed files with 46 additions and 51 deletions

View File

@@ -87,7 +87,7 @@ class ColorChanger(App):
self.background = f"on color({event.key})" self.background = f"on color({event.key})"
ColorChanger.run(log="textual.log") ColorChanger.run(log_path="textual.log")
``` ```
You'll notice that the `on_key` method above contains an additional `event` parameter which wasn't present on the beeper example. If the `event` argument is present, Textual will call the handler with an event object. Every event has an associated handler object, in this case it is a KeyEvent which contains additional information regarding which key was pressed. You'll notice that the `on_key` method above contains an additional `event` parameter which wasn't present on the beeper example. If the `event` argument is present, Textual will call the handler with an event object. Every event has an associated handler object, in this case it is a KeyEvent which contains additional information regarding which key was pressed.
@@ -114,7 +114,7 @@ class SimpleApp(App):
await self.view.dock(Placeholder(), Placeholder(), edge="top") await self.view.dock(Placeholder(), Placeholder(), edge="top")
SimpleApp.run(log="textual.log") SimpleApp.run(log_path="textual.log")
``` ```
This app contains a single event handler `on_mount`. The mount event is sent when the app or widget is ready to start processing events, and is typically used for initialization. You may have noticed that `on_mount` is an `async` function. Since Textual is an asynchronous framework we will need this if we need to call most other methods. This app contains a single event handler `on_mount`. The mount event is sent when the app or widget is ready to start processing events, and is typically used for initialization. You may have noticed that `on_mount` is an `async` function. Since Textual is an asynchronous framework we will need this if we need to call most other methods.
@@ -137,7 +137,7 @@ await self.view.dock(Placeholder(), Placeholder(), edge="top")
You will notice that this time we are docking _two_ Placeholder objects onto the `"top"` edge. We haven't set an explicit size this time so Textual will divide the remaining size amongst the two new widgets. You will notice that this time we are docking _two_ Placeholder objects onto the `"top"` edge. We haven't set an explicit size this time so Textual will divide the remaining size amongst the two new widgets.
The last line calls the `run` class method in the usual way, but with an argument we haven't seen before: `log="textual.log"` tells Textual to write log information to the given file. You can tail textual.log to see events being processed and other debug information. The last line calls the `run` class method in the usual way, but with an argument we haven't seen before: `log_path="textual.log"` tells Textual to write log information to the given file. You can tail textual.log to see events being processed and other debug information.
If you run the above example, you will see something like the following: If you run the above example, you will see something like the following:
@@ -183,7 +183,7 @@ class HoverApp(App):
await self.view.dock(*hovers, edge="top") await self.view.dock(*hovers, edge="top")
HoverApp.run(log="textual.log") HoverApp.run(log_path="textual.log")
``` ```
The `Hover` class is a custom widget which displays a panel containing the classic text "Hello World". The first line in the Hover class may seem a little mysterious at this point: The `Hover` class is a custom widget which displays a panel containing the classic text "Hello World". The first line in the Hover class may seem a little mysterious at this point:

View File

@@ -7,4 +7,4 @@ class ColorChanger(App):
self.background = f"on color({event.key})" self.background = f"on color({event.key})"
ColorChanger.run(log="textual.log") ColorChanger.run(log_path="textual.log")

View File

@@ -29,4 +29,4 @@ class HoverApp(App):
await self.screen.dock(*hovers, edge="top") await self.screen.dock(*hovers, edge="top")
HoverApp.run(log="textual.log") HoverApp.run(log_path="textual.log")

View File

@@ -12,4 +12,4 @@ class SimpleApp(App):
await self.screen.dock(Placeholder(), Placeholder(), edge="top") await self.screen.dock(Placeholder(), Placeholder(), edge="top")
SimpleApp.run(log="textual.log") SimpleApp.run(log_path="textual.log")

View File

@@ -143,9 +143,9 @@ class BasicApp(App):
sandbox_folder = Path(__file__).parent sandbox_folder = Path(__file__).parent
app = BasicApp( app = BasicApp(
css_file=sandbox_folder / "basic.css", css_path=sandbox_folder / "basic.css",
watch_css=True, watch_css=True,
log=sandbox_folder / "textual.log", log_path=sandbox_folder / "basic.log",
log_verbosity=0, log_verbosity=0,
) )

View File

@@ -35,4 +35,4 @@ class SmoothApp(App):
# self.set_timer(10, lambda: self.action("quit")) # self.set_timer(10, lambda: self.action("quit"))
SmoothApp.run(log="textual.log", log_verbosity=2) SmoothApp.run(log_path="textual.log", log_verbosity=2)

View File

@@ -30,4 +30,4 @@ class MyApp(App):
await self.call_later(add_content) await self.call_later(add_content)
MyApp.run(title="Simple App", log="textual.log") MyApp.run(title="Simple App", log_path="textual.log")

View File

@@ -55,4 +55,4 @@ class BordersApp(App):
self.mount(borders=borders_view) self.mount(borders=borders_view)
BordersApp.run(css_file="borders.css", log="textual.log") BordersApp.run(css_path="borders.css", log_path="textual.log")

View File

@@ -212,4 +212,4 @@ class CalculatorApp(App):
await self.screen.dock(Calculator()) await self.screen.dock(Calculator())
CalculatorApp.run(title="Calculator Test", log="textual.log") CalculatorApp.run(title="Calculator Test", log_path="textual.log")

View File

@@ -67,4 +67,4 @@ class MyApp(App):
# Run our app class # Run our app class
MyApp.run(title="Code Viewer", log="textual.log") MyApp.run(title="Code Viewer", log_path="textual.log")

View File

@@ -42,4 +42,4 @@ class EasingApp(App):
self.side = not self.side self.side = not self.side
EasingApp().run(log="textual.log") EasingApp().run(log_path="textual.log")

View File

@@ -31,4 +31,4 @@ class GridTest(App):
) )
GridTest.run(title="Grid Test", log="textual.log") GridTest.run(title="Grid Test", log_path="textual.log")

View File

@@ -19,4 +19,4 @@ class GridTest(App):
grid.place(*placeholders, center=Placeholder()) grid.place(*placeholders, center=Placeholder())
GridTest.run(title="Grid Test", log="textual.log") GridTest.run(title="Grid Test", log_path="textual.log")

View File

@@ -48,4 +48,4 @@ class MyApp(App):
await self.call_later(get_markdown, "richreadme.md") await self.call_later(get_markdown, "richreadme.md")
MyApp.run(title="Simple App", log="textual.log") MyApp.run(title="Simple App", log_path="textual.log")

View File

@@ -22,4 +22,4 @@ class AlignApp(App):
self.log(self.screen.tree) self.log(self.screen.tree)
AlignApp.run(css_file="align.css", log="textual.log", watch_css=True) AlignApp.run(css_path="align.css", log_path="textual.log", watch_css=True)

View File

@@ -1,6 +1,3 @@
from pathlib import Path
from rich.align import Align
from rich.console import RenderableType from rich.console import RenderableType
from rich.syntax import Syntax from rich.syntax import Syntax
from rich.text import Text from rich.text import Text
@@ -141,11 +138,10 @@ class BasicApp(App):
self.panic(self.tree) self.panic(self.tree)
sandbox_folder = Path(__file__).parent
app = BasicApp( app = BasicApp(
css_file=sandbox_folder / "basic.css", css_path="basic.css",
watch_css=True, watch_css=True,
log=sandbox_folder / "textual.log", log_path="textual.log",
) )
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -5,7 +5,6 @@ from textual import layout
class ButtonsApp(App[str]): class ButtonsApp(App[str]):
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield layout.Vertical( yield layout.Vertical(
Button("foo", id="foo"), Button("foo", id="foo"),
@@ -19,7 +18,7 @@ class ButtonsApp(App[str]):
self.exit(event.button.id) self.exit(event.button.id)
app = ButtonsApp(log="textual.log", log_verbosity=2) app = ButtonsApp(log_path="textual.log", log_verbosity=2)
if __name__ == "__main__": if __name__ == "__main__":
result = app.run() result = app.run()

View File

@@ -34,4 +34,4 @@ class BasicApp(App):
self.panic(self.tree) self.panic(self.tree)
BasicApp.run(css_file="dev_sandbox.scss", watch_css=True, log="textual.log") BasicApp.run(css_path="dev_sandbox.scss", watch_css=True, log_path="textual.log")

View File

@@ -33,4 +33,4 @@ class BasicApp(App):
self.log(header.styles) self.log(header.styles)
BasicApp.run(css_file="local_styles.css", log="textual.log") BasicApp.run(css_path="local_styles.css", log_path="textual.log")

View File

@@ -144,4 +144,4 @@ class BasicApp(App):
self.mount(example.widget) self.mount(example.widget)
BasicApp.run(css_file="tabs.scss", watch_css=True, log="textual.log") BasicApp.run(css_path="tabs.scss", watch_css=True, log_path="textual.log")

View File

@@ -83,7 +83,7 @@ class BasicApp(App):
self.focused.styles.border_top = ("solid", "invalid-color") self.focused.styles.border_top = ("solid", "invalid-color")
app = BasicApp(css_file="uber.css", log="textual.log", log_verbosity=1) app = BasicApp(css_path="uber.css", log_path="textual.log", log_verbosity=1)
if __name__ == "__main__": if __name__ == "__main__":
app.run() app.run()

View File

@@ -106,20 +106,20 @@ class App(Generic[ReturnType], DOMNode):
def __init__( def __init__(
self, self,
driver_class: Type[Driver] | None = None, driver_class: Type[Driver] | None = None,
log: str | PurePath = "", log_path: str | PurePath = "",
log_verbosity: int = 1, log_verbosity: int = 1,
title: str = "Textual Application", title: str = "Textual Application",
css_file: str | PurePath | None = None, css_path: str | PurePath | None = None,
watch_css: bool = True, watch_css: bool = True,
): ):
"""Textual application base class """Textual application base class
Args: Args:
driver_class (Type[Driver] | None, optional): Driver class or ``None`` to auto-detect. Defaults to None. driver_class (Type[Driver] | None, optional): Driver class or ``None`` to auto-detect. Defaults to None.
log (str | PurePath, optional): Path to log file, or "" to disable. Defaults to "". log_path (str | PurePath, optional): Path to log file, or "" to disable. Defaults to "".
log_verbosity (int, optional): Log verbosity from 0-3. Defaults to 1. log_verbosity (int, optional): Log verbosity from 0-3. Defaults to 1.
title (str, optional): Default title of the application. Defaults to "Textual Application". title (str, optional): Default title of the application. Defaults to "Textual Application".
css_file (str | PurePath | None, optional): Path to CSS or ``None`` for no CSS file. Defaults to None. css_path (str | PurePath | None, optional): Path to CSS or ``None`` for no CSS file. Defaults to None.
watch_css (bool, optional): Watch CSS for changes. Defaults to True. watch_css (bool, optional): Watch CSS for changes. Defaults to True.
""" """
# N.B. This must be done *before* we call the parent constructor, because MessagePump's # N.B. This must be done *before* we call the parent constructor, because MessagePump's
@@ -151,8 +151,8 @@ class App(Generic[ReturnType], DOMNode):
self._log_console: Console | None = None self._log_console: Console | None = None
self._log_file: TextIO | None = None self._log_file: TextIO | None = None
if log: if log_path:
self._log_file = open(log, "wt") self._log_file = open(log_path, "wt")
self._log_console = Console( self._log_console = Console(
file=self._log_file, file=self._log_file,
markup=False, markup=False,
@@ -171,10 +171,10 @@ class App(Generic[ReturnType], DOMNode):
self.stylesheet = Stylesheet(variables=self.get_css_variables()) self.stylesheet = Stylesheet(variables=self.get_css_variables())
self._require_styles_update = False self._require_styles_update = False
self.css_file = css_file self.css_path = css_path
self.css_monitor = ( self.css_monitor = (
FileMonitor(css_file, self._on_css_change) FileMonitor(css_path, self._on_css_change)
if (watch_css and css_file) if (watch_css and css_path)
else None else None
) )
@@ -450,13 +450,13 @@ class App(Generic[ReturnType], DOMNode):
async def _on_css_change(self) -> None: async def _on_css_change(self) -> None:
"""Called when the CSS changes (if watch_css is True).""" """Called when the CSS changes (if watch_css is True)."""
if self.css_file is not None: if self.css_path is not None:
try: try:
time = perf_counter() time = perf_counter()
self.stylesheet.read(self.css_file) self.stylesheet.read(self.css_path)
elapsed = (perf_counter() - time) * 1000 elapsed = (perf_counter() - time) * 1000
self.log(f"loaded {self.css_file} in {elapsed:.0f}ms") self.log(f"loaded {self.css_path} in {elapsed:.0f}ms")
except Exception as error: except Exception as error:
# TODO: Catch specific exceptions # TODO: Catch specific exceptions
self.console.bell() self.console.bell()
@@ -647,8 +647,8 @@ class App(Generic[ReturnType], DOMNode):
except DevtoolsConnectionError: except DevtoolsConnectionError:
self.log(f"Couldn't connect to devtools ({self.devtools.url})") self.log(f"Couldn't connect to devtools ({self.devtools.url})")
try: try:
if self.css_file is not None: if self.css_path is not None:
self.stylesheet.read(self.css_file) self.stylesheet.read(self.css_path)
if self.CSS is not None: if self.CSS is not None:
self.stylesheet.add_source( self.stylesheet.add_source(
self.CSS, path=f"<{self.__class__.__name__}>" self.CSS, path=f"<{self.__class__.__name__}>"

View File

@@ -36,8 +36,8 @@ def _check_selectors(selectors: list[Selector], node: DOMNode) -> bool:
DESCENDENT = CombinatorType.DESCENDENT DESCENDENT = CombinatorType.DESCENDENT
css_path = node.css_path css_path_nodes = node.css_path_nodes
path_count = len(css_path) path_count = len(css_path_nodes)
selector_count = len(selectors) selector_count = len(selectors)
stack: list[tuple[int, int]] = [(0, 0)] stack: list[tuple[int, int]] = [(0, 0)]
@@ -51,7 +51,7 @@ def _check_selectors(selectors: list[Selector], node: DOMNode) -> bool:
if selector_index == selector_count or node_index == path_count: if selector_index == selector_count or node_index == path_count:
pop() pop()
else: else:
path_node = css_path[node_index] path_node = css_path_nodes[node_index]
selector = selectors[selector_index] selector = selectors[selector_index]
if selector.combinator == DESCENDENT: if selector.combinator == DESCENDENT:
# Find a matching descendent # Find a matching descendent

View File

@@ -167,7 +167,7 @@ class DOMNode(MessagePump):
return self.__class__.__name__.lower() return self.__class__.__name__.lower()
@property @property
def css_path(self) -> list[DOMNode]: def css_path_nodes(self) -> list[DOMNode]:
"""A list of nodes from the root to this node, forming a "path". """A list of nodes from the root to this node, forming a "path".
Returns: Returns:

View File

@@ -132,4 +132,4 @@ if __name__ == "__main__":
async def on_mount(self, event: events.Mount) -> None: async def on_mount(self, event: events.Mount) -> None:
await self.screen.dock(DirectoryTree("/Users/willmcgugan/projects")) await self.screen.dock(DirectoryTree("/Users/willmcgugan/projects"))
TreeApp.run(log="textual.log") TreeApp(log_path="textual.log").run()

View File

@@ -328,4 +328,4 @@ if __name__ == "__main__":
else: else:
await message.node.toggle() await message.node.toggle()
TreeApp.run(log="textual.log") TreeApp(log_path="textual.log").run()