diff --git a/.github/ISSUE_TEMPLATE/task.md b/.github/ISSUE_TEMPLATE/task.md index 9c4be4e926..f1c36916ed 100644 --- a/.github/ISSUE_TEMPLATE/task.md +++ b/.github/ISSUE_TEMPLATE/task.md @@ -6,5 +6,3 @@ labels: '' assignees: '' --- - - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 009c06616e..ce352e11b0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v5.0.0 hooks: - id: check-ast # simply checks whether the files parse as valid python - id: check-builtin-literals # requires literal syntax when initializing empty or zero python builtin types @@ -15,20 +15,21 @@ repos: - id: check-shebang-scripts-are-executable # ensures that (non-binary) files with a shebang are executable - id: check-vcs-permalinks # ensures that links to vcs websites are permalinks - id: end-of-file-fixer # ensures that a file is either empty, or ends with one newline + exclude: '^.*\.svg$' - id: mixed-line-ending # replaces or checks mixed line ending - repo: https://github.com/pycqa/isort - rev: '5.13.2' + rev: '6.0.1' hooks: - id: isort name: isort (python) language_version: '3.11' args: ['--profile', 'black', '--filter-files'] - repo: https://github.com/psf/black - rev: '24.1.1' + rev: '25.1.0' hooks: - id: black - repo: https://github.com/hadialqattan/pycln # removes unused imports - rev: v2.3.0 + rev: v2.5.0 hooks: - id: pycln language_version: '3.11' diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d1435248d5..e54b608481 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -125,4 +125,4 @@ enforcement ladder](https://github.com/mozilla/diversity). For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. \ No newline at end of file +https://www.contributor-covenant.org/translations. diff --git a/docs/blog/index.md b/docs/blog/index.md index 210ffd3f9b..8b01728346 100644 --- a/docs/blog/index.md +++ b/docs/blog/index.md @@ -1,2 +1 @@ # Textual Blog - diff --git a/docs/blog/posts/helo-world.md b/docs/blog/posts/helo-world.md index 90dab804b6..fa90e1f5a0 100644 --- a/docs/blog/posts/helo-world.md +++ b/docs/blog/posts/helo-world.md @@ -16,4 +16,3 @@ Welcome to the first post on the Textual blog. I plan on using this as a place to make announcements regarding new releases of Textual, and any other relevant news. The first piece of news is that we've reorganized this site a little. The Events, Styles, and Widgets references are now under "Reference", and what used to be under "Reference" is now "API" which contains API-level documentation. I hope that's a little clearer than it used to be! - diff --git a/docs/blog/posts/release0-24-0.md b/docs/blog/posts/release0-24-0.md index 95feb66fc9..8addc95216 100644 --- a/docs/blog/posts/release0-24-0.md +++ b/docs/blog/posts/release0-24-0.md @@ -50,7 +50,7 @@ This was also solved with a new rule called "constrain". Applying `constrain` to a widget will keep the widget within the bounds of the screen. In the case of `Select`, if you expand the options while at the bottom of the screen, then the overlay will be moved up so that you can see all the options. -These new rules are currently undocumented as they are still subject to change, but you can see them in the [Select](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_select.py#L179) source if you are interested. +These new rules are currently undocumented as they are still subject to change, but you can see them in the [Select](https://github.com/Textualize/textual/blob/v0.24.0/src/textual/widgets/_select.py#L179-L220) source if you are interested. In a future release these will be finalized and you can confidently use them in your own projects. diff --git a/docs/blog/posts/release0-6-0.md b/docs/blog/posts/release0-6-0.md index 715b6a3c39..958b89f74b 100644 --- a/docs/blog/posts/release0-6-0.md +++ b/docs/blog/posts/release0-6-0.md @@ -103,4 +103,3 @@ As always, there are a number of fixes in this release. Mostly related to layout ## What's next? The next release will focus on *pain points* we discovered while in a dog-fooding phase (see the [DevLog](https://textual.textualize.io/blog/category/devlog/) for details on what Textual devs have been building). - diff --git a/docs/examples/app/event01.py b/docs/examples/app/event01.py index 0f3bc3cdca..cbe6290e9b 100644 --- a/docs/examples/app/event01.py +++ b/docs/examples/app/event01.py @@ -1,5 +1,5 @@ -from textual.app import App from textual import events +from textual.app import App class EventApp(App): diff --git a/docs/examples/app/question01.py b/docs/examples/app/question01.py index 04c4e86898..c7be464892 100644 --- a/docs/examples/app/question01.py +++ b/docs/examples/app/question01.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.widgets import Label, Button +from textual.widgets import Button, Label class QuestionApp(App[str]): diff --git a/docs/examples/app/question03.py b/docs/examples/app/question03.py index 6fc372dedb..411c4ae1ae 100644 --- a/docs/examples/app/question03.py +++ b/docs/examples/app/question03.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.widgets import Label, Button +from textual.widgets import Button, Label class QuestionApp(App[str]): diff --git a/docs/examples/guide/actions/actions01.py b/docs/examples/guide/actions/actions01.py index 54b366f95e..7b599854dd 100644 --- a/docs/examples/guide/actions/actions01.py +++ b/docs/examples/guide/actions/actions01.py @@ -1,5 +1,5 @@ -from textual.app import App from textual import events +from textual.app import App class ActionsApp(App): diff --git a/docs/examples/guide/dom2.py b/docs/examples/guide/dom2.py index 35b6703278..84d9ebe963 100644 --- a/docs/examples/guide/dom2.py +++ b/docs/examples/guide/dom2.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.widgets import Header, Footer +from textual.widgets import Footer, Header class ExampleApp(App): diff --git a/docs/examples/guide/styles/box_sizing01.py b/docs/examples/guide/styles/box_sizing01.py index f2799b6e3e..7c276a4994 100644 --- a/docs/examples/guide/styles/box_sizing01.py +++ b/docs/examples/guide/styles/box_sizing01.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/dimensions01.py b/docs/examples/guide/styles/dimensions01.py index ed479f0f34..021e0d343a 100644 --- a/docs/examples/guide/styles/dimensions01.py +++ b/docs/examples/guide/styles/dimensions01.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/dimensions02.py b/docs/examples/guide/styles/dimensions02.py index 339aade995..d263c404e6 100644 --- a/docs/examples/guide/styles/dimensions02.py +++ b/docs/examples/guide/styles/dimensions02.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/dimensions03.py b/docs/examples/guide/styles/dimensions03.py index 4d361227e4..1dc49e891f 100644 --- a/docs/examples/guide/styles/dimensions03.py +++ b/docs/examples/guide/styles/dimensions03.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/dimensions04.py b/docs/examples/guide/styles/dimensions04.py index 405b5545e9..c1f52ee02b 100644 --- a/docs/examples/guide/styles/dimensions04.py +++ b/docs/examples/guide/styles/dimensions04.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/margin01.py b/docs/examples/guide/styles/margin01.py index 7036cb7257..7c8f7eac80 100644 --- a/docs/examples/guide/styles/margin01.py +++ b/docs/examples/guide/styles/margin01.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/outline01.py b/docs/examples/guide/styles/outline01.py index cd77d0b8c6..ef67f3cba2 100644 --- a/docs/examples/guide/styles/outline01.py +++ b/docs/examples/guide/styles/outline01.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/padding01.py b/docs/examples/guide/styles/padding01.py index 92c68948aa..916d6825b9 100644 --- a/docs/examples/guide/styles/padding01.py +++ b/docs/examples/guide/styles/padding01.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/styles/padding02.py b/docs/examples/guide/styles/padding02.py index 50bf0b940c..828f5cb54d 100644 --- a/docs/examples/guide/styles/padding02.py +++ b/docs/examples/guide/styles/padding02.py @@ -1,7 +1,6 @@ from textual.app import App, ComposeResult from textual.widgets import Static - TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. diff --git a/docs/examples/guide/widgets/checker03.py b/docs/examples/guide/widgets/checker03.py index 03ca19381c..7b4a3d5b97 100644 --- a/docs/examples/guide/widgets/checker03.py +++ b/docs/examples/guide/widgets/checker03.py @@ -1,11 +1,11 @@ from __future__ import annotations +from rich.segment import Segment + from textual.app import App, ComposeResult from textual.geometry import Size -from textual.strip import Strip from textual.scroll_view import ScrollView - -from rich.segment import Segment +from textual.strip import Strip class CheckerBoard(ScrollView): diff --git a/docs/examples/guide/widgets/checker04.py b/docs/examples/guide/widgets/checker04.py index 0445ffea5a..fc91798d74 100644 --- a/docs/examples/guide/widgets/checker04.py +++ b/docs/examples/guide/widgets/checker04.py @@ -1,14 +1,14 @@ from __future__ import annotations +from rich.segment import Segment +from rich.style import Style + from textual import events from textual.app import App, ComposeResult from textual.geometry import Offset, Region, Size from textual.reactive import var -from textual.strip import Strip from textual.scroll_view import ScrollView - -from rich.segment import Segment -from rich.style import Style +from textual.strip import Strip class CheckerBoard(ScrollView): diff --git a/docs/guide/styles.md b/docs/guide/styles.md index c251f9faf4..abb8d81f98 100644 --- a/docs/guide/styles.md +++ b/docs/guide/styles.md @@ -125,7 +125,7 @@ Together these styles compose the widget's *box model*. The following diagram sh Setting the width restricts the number of columns used by a widget, and setting the height restricts the number of rows. Let's look at an example which sets both dimensions. -```python title="dimensions01.py" hl_lines="21-22" +```python title="dimensions01.py" hl_lines="20-21" --8<-- "docs/examples/guide/styles/dimensions01.py" ``` @@ -142,7 +142,7 @@ In practice, we generally want the size of a widget to adapt to its content, whi Let's set the height to auto and see what happens. -```python title="dimensions02.py" hl_lines="22" +```python title="dimensions02.py" hl_lines="21" --8<-- "docs/examples/guide/styles/dimensions02.py" ``` @@ -163,7 +163,7 @@ Textual offers a few different *units* which allow you to specify dimensions rel The following example demonstrates applying percentage units: -```python title="dimensions03.py" hl_lines="21-22" +```python title="dimensions03.py" hl_lines="20-21" --8<-- "docs/examples/guide/styles/dimensions03.py" ``` @@ -193,7 +193,7 @@ When specifying `fr` units for a given dimension, Textual will divide the availa Let's look at an example. We will create two widgets, one with a height of `"2fr"` and one with a height of `"1fr"`. -```python title="dimensions04.py" hl_lines="24-25" +```python title="dimensions04.py" hl_lines="23-24" --8<-- "docs/examples/guide/styles/dimensions04.py" ``` @@ -219,7 +219,7 @@ The following styles set minimum and maximum sizes and can accept any of the val Padding adds space around your content which can aid readability. Setting [padding](../styles/padding.md) to an integer will add that number additional rows and columns around the content area. The following example sets padding to 2: -```python title="padding01.py" hl_lines="22" +```python title="padding01.py" hl_lines="21" --8<-- "docs/examples/guide/styles/padding01.py" ``` @@ -230,7 +230,7 @@ Notice the additional space around the text: You can also set padding to a tuple of *two* integers which will apply padding to the top/bottom and left/right edges. The following example sets padding to `(2, 4)` which adds two rows to the top and bottom of the widget, and 4 columns to the left and right of the widget. -```python title="padding02.py" hl_lines="22" +```python title="padding02.py" hl_lines="21" --8<-- "docs/examples/guide/styles/padding02.py" ``` @@ -289,7 +289,7 @@ Note the addition of the titles and their alignments: [Outline](../styles/outline.md) is similar to border and is set in the same way. The difference is that outline will not change the size of the widget, and may overlap the content area. The following example sets an outline on a widget: -```python title="outline01.py" hl_lines="22" +```python title="outline01.py" hl_lines="21" --8<-- "docs/examples/guide/styles/outline01.py" ``` @@ -325,7 +325,7 @@ The following example creates two widgets with a width of 30, a height of 6, and The first widget has the default `box_sizing` (`"border-box"`). The second widget sets `box_sizing` to `"content-box"`. -```python title="box_sizing01.py" hl_lines="32" +```python title="box_sizing01.py" hl_lines="31" --8<-- "docs/examples/guide/styles/box_sizing01.py" ``` @@ -340,7 +340,7 @@ Margin is similar to padding in that it adds space, but unlike padding, [margin] The following example creates two widgets, each with a margin of 2. -```python title="margin01.py" hl_lines="26-27" +```python title="margin01.py" hl_lines="25-26" --8<-- "docs/examples/guide/styles/margin01.py" ``` diff --git a/docs/guide/widgets.md b/docs/guide/widgets.md index 52710f797c..abd9256802 100644 --- a/docs/guide/widgets.md +++ b/docs/guide/widgets.md @@ -511,7 +511,7 @@ Let's add scrolling to our checkerboard example. A standard 8 x 8 board isn't su === "checker03.py" - ```python title="checker03.py" hl_lines="4 26-30 35-36 52-53" + ```python title="checker03.py" hl_lines="6 26-30 35-36 52-53" --8<-- "docs/examples/guide/widgets/checker03.py" ``` diff --git a/examples/json_tree.py b/examples/json_tree.py index a8bfd1bdbe..62033b971a 100644 --- a/examples/json_tree.py +++ b/examples/json_tree.py @@ -4,7 +4,7 @@ from rich.text import Text from textual.app import App, ComposeResult -from textual.widgets import Header, Footer, Tree +from textual.widgets import Footer, Header, Tree from textual.widgets.tree import TreeNode diff --git a/src/textual/_tree_sitter.py b/src/textual/_tree_sitter.py index 9d099c109b..c3dbee4f9c 100644 --- a/src/textual/_tree_sitter.py +++ b/src/textual/_tree_sitter.py @@ -1,9 +1,9 @@ from __future__ import annotations + from importlib import import_module from textual import log - try: from tree_sitter import Language diff --git a/src/textual/widgets/text_area.py b/src/textual/widgets/text_area.py index a879e2594c..857618564c 100644 --- a/src/textual/widgets/text_area.py +++ b/src/textual/widgets/text_area.py @@ -12,13 +12,13 @@ from textual.document._syntax_aware_document import SyntaxAwareDocument from textual.document._wrapped_document import WrappedDocument from textual.widgets._text_area import ( + BUILTIN_LANGUAGES, EndColumn, Highlight, HighlightName, LanguageDoesNotExist, StartColumn, ThemeDoesNotExist, - BUILTIN_LANGUAGES, ) __all__ = [ diff --git a/tests/input/test_input_restrict.py b/tests/input/test_input_restrict.py index 48d9d50ee7..d0465ec2e2 100644 --- a/tests/input/test_input_restrict.py +++ b/tests/input/test_input_restrict.py @@ -38,7 +38,6 @@ def test_input_number_type(): assert not re.fullmatch(number, "-inf") - def test_input_integer_type(): """Test input type regex""" integer = _RESTRICT_TYPES["integer"] diff --git a/tests/option_list/test_option_prompt_replacement.py b/tests/option_list/test_option_prompt_replacement.py index ef11b75383..4bcb2f7ca5 100644 --- a/tests/option_list/test_option_prompt_replacement.py +++ b/tests/option_list/test_option_prompt_replacement.py @@ -1,4 +1,5 @@ """Test replacing options prompt from an option list.""" + import pytest from textual.app import App, ComposeResult @@ -20,14 +21,18 @@ async def test_replace_option_prompt_with_invalid_id() -> None: """Attempting to replace the prompt of an option ID that doesn't exist should raise an exception.""" async with OptionListApp().run_test() as pilot: with pytest.raises(OptionDoesNotExist): - pilot.app.query_one(OptionList).replace_option_prompt("does-not-exist", "new-prompt") + pilot.app.query_one(OptionList).replace_option_prompt( + "does-not-exist", "new-prompt" + ) async def test_replace_option_prompt_with_invalid_index() -> None: """Attempting to replace the prompt of an option index that doesn't exist should raise an exception.""" async with OptionListApp().run_test() as pilot: with pytest.raises(OptionDoesNotExist): - pilot.app.query_one(OptionList).replace_option_prompt_at_index(23, "new-prompt") + pilot.app.query_one(OptionList).replace_option_prompt_at_index( + 23, "new-prompt" + ) async def test_replace_option_prompt_with_valid_id() -> None: @@ -41,12 +46,14 @@ async def test_replace_option_prompt_with_valid_id() -> None: async def test_replace_option_prompt_with_valid_index() -> None: """It should be possible to replace the prompt of an option index that does exist.""" async with OptionListApp().run_test() as pilot: - option_list = pilot.app.query_one(OptionList).replace_option_prompt_at_index(1, "new-prompt") + option_list = pilot.app.query_one(OptionList).replace_option_prompt_at_index( + 1, "new-prompt" + ) assert option_list.get_option_at_index(1).prompt == "new-prompt" async def test_replace_single_line_option_prompt_with_multiple() -> None: - """It should be possible to replace single line prompt with multiple lines """ + """It should be possible to replace single line prompt with multiple lines""" new_prompt = "new-prompt\nsecond line" async with OptionListApp().run_test() as pilot: option_list = pilot.app.query_one(OptionList) diff --git a/tests/select/test_blank_and_clear.py b/tests/select/test_blank_and_clear.py index 4d1921451f..3d4f7dca66 100644 --- a/tests/select/test_blank_and_clear.py +++ b/tests/select/test_blank_and_clear.py @@ -64,6 +64,7 @@ def compose(self): with pytest.raises(InvalidSelectValueError): select.clear() + async def test_selection_is_none_with_blank(): class SelectApp(App[None]): def compose(self): diff --git a/tests/selection_list/test_selection_click_checkbox.py b/tests/selection_list/test_selection_click_checkbox.py index e9c349e46b..8ca8b0fe0f 100644 --- a/tests/selection_list/test_selection_click_checkbox.py +++ b/tests/selection_list/test_selection_click_checkbox.py @@ -5,6 +5,7 @@ from textual.geometry import Offset from textual.widgets import SelectionList + class SelectionListApp(App[None]): """Test selection list application.""" @@ -25,7 +26,7 @@ async def test_click_on_prompt() -> None: """It should be possible to toggle a selection by clicking on the prompt.""" async with SelectionListApp().run_test() as pilot: assert isinstance(pilot.app, SelectionListApp) - await pilot.click(SelectionList, Offset(5,1)) + await pilot.click(SelectionList, Offset(5, 1)) await pilot.pause() assert pilot.app.clicks == [0] @@ -34,9 +35,10 @@ async def test_click_on_checkbox() -> None: """It should be possible to toggle a selection by clicking on the checkbox.""" async with SelectionListApp().run_test() as pilot: assert isinstance(pilot.app, SelectionListApp) - await pilot.click(SelectionList, Offset(3,1)) + await pilot.click(SelectionList, Offset(3, 1)) await pilot.pause() assert pilot.app.clicks == [0] + if __name__ == "__main__": SelectionListApp().run() diff --git a/tests/test_expand_tabs.py b/tests/test_expand_tabs.py index b120978da5..811803b7a5 100644 --- a/tests/test_expand_tabs.py +++ b/tests/test_expand_tabs.py @@ -29,4 +29,9 @@ def test_get_tab_widths(): assert get_tab_widths("\tbar") == [("", 4), ("bar", 0)] assert get_tab_widths("\tbar\t") == [("", 4), ("bar", 1)] assert get_tab_widths("\tfoo\t\t") == [("", 4), ("foo", 1), ("", 4)] - assert get_tab_widths("\t木foo\t木\t\t") == [("", 4), ("木foo", 3), ("木", 2), ("", 4)] + assert get_tab_widths("\t木foo\t木\t\t") == [ + ("", 4), + ("木foo", 3), + ("木", 2), + ("", 4), + ] diff --git a/tests/test_unmount.py b/tests/test_unmount.py index 3da75d1247..324a3812a3 100644 --- a/tests/test_unmount.py +++ b/tests/test_unmount.py @@ -50,5 +50,4 @@ async def on_mount(self) -> None: "MyScreen#main", ] - assert unmount_ids == expected diff --git a/tests/test_validation.py b/tests/test_validation.py index 38be8b38e9..6a6bee2f6a 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -160,7 +160,12 @@ def test_Regex_validate(regex, value, expected_result): ("123", 100, 200, True), # valid integer within range ("99", 100, 200, False), # valid integer but not in range ("201", 100, 200, False), # valid integer but not in range - ("1.23e4", None, None, False), # valid scientific notation, even resolving to an integer, is not valid + ( + "1.23e4", + None, + None, + False, + ), # valid scientific notation, even resolving to an integer, is not valid ("123.", None, None, False), # periods not valid in integers ("123_456", None, None, True), # underscores are valid python ("_123_456", None, None, False), # leading underscores are not valid python diff --git a/tests/test_win_sleep.py b/tests/test_win_sleep.py index 5ed9abbd83..fdbc9ae178 100644 --- a/tests/test_win_sleep.py +++ b/tests/test_win_sleep.py @@ -1,6 +1,6 @@ import asyncio -import time import sys +import time import pytest diff --git a/tests/text_area/test_edit_via_api.py b/tests/text_area/test_edit_via_api.py index e732680f0f..d4f8241ed3 100644 --- a/tests/text_area/test_edit_via_api.py +++ b/tests/text_area/test_edit_via_api.py @@ -115,9 +115,7 @@ async def test_insert_character_near_cursor_maintain_selection_offset( ], ) async def test_insert_newline_around_cursor_maintain_selection_offset( - cursor_location, - insert_location, - cursor_destination + cursor_location, insert_location, cursor_destination ): app = TextAreaApp() async with app.run_test(): diff --git a/tests/text_area/test_selection_bindings.py b/tests/text_area/test_selection_bindings.py index 4fd6947386..5985635cc6 100644 --- a/tests/text_area/test_selection_bindings.py +++ b/tests/text_area/test_selection_bindings.py @@ -254,9 +254,7 @@ async def test_cursor_page_down(app: TextAreaApp): text_area.selection = Selection.cursor((0, 1)) await pilot.press("pagedown") margin = 2 - assert text_area.selection == Selection.cursor( - (app.size.height - margin, 1) - ) + assert text_area.selection == Selection.cursor((app.size.height - margin, 1)) async def test_cursor_page_up(app: TextAreaApp): diff --git a/tests/tree/test_node_refresh.py b/tests/tree/test_node_refresh.py index 53e98b7387..8efaaec1ed 100644 --- a/tests/tree/test_node_refresh.py +++ b/tests/tree/test_node_refresh.py @@ -5,6 +5,7 @@ from textual.widgets import Tree from textual.widgets.tree import TreeNode + class HistoryTree(Tree): def __init__(self) -> None: @@ -33,7 +34,7 @@ async def test_initial_state() -> None: """Initially all the visible nodes should have had a render call.""" app = RefreshApp() async with app.run_test(): - assert app.query_one(HistoryTree).render_hits == {(0,0), (1,0), (2,0)} + assert app.query_one(HistoryTree).render_hits == {(0, 0), (1, 0), (2, 0)} async def test_root_refresh() -> None: @@ -45,6 +46,7 @@ async def test_root_refresh() -> None: await pilot.pause() assert (0, 1) in pilot.app.query_one(HistoryTree).render_hits + async def test_child_refresh() -> None: """A refresh of the child node should cause a subsequent render call.""" async with RefreshApp().run_test() as pilot: @@ -54,6 +56,7 @@ async def test_child_refresh() -> None: await pilot.pause() assert (1, 1) in pilot.app.query_one(HistoryTree).render_hits + async def test_grandchild_refresh() -> None: """A refresh of the grandchild node should cause a subsequent render call.""" async with RefreshApp().run_test() as pilot: diff --git a/tools/widget_documentation.py b/tools/widget_documentation.py index 04f3de86bf..7149e6d465 100644 --- a/tools/widget_documentation.py +++ b/tools/widget_documentation.py @@ -4,6 +4,7 @@ This goes through the widgets listed in textual.widgets and prints the scaffolding for the tables that are used to document the classvars BINDINGS and COMPONENT_CLASSES. """ + from __future__ import annotations from typing import TYPE_CHECKING