mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #6121 from TomJGooding/build-update-supported-python-versions
build!: update supported Python versions
This commit is contained in:
12
.github/workflows/pythonpackage.yml
vendored
12
.github/workflows/pythonpackage.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -35,18 +35,18 @@ jobs:
|
||||
cache: "poetry"
|
||||
- name: Install dependencies
|
||||
run: poetry install --no-interaction --extras syntax
|
||||
if: ${{ matrix.python-version != '3.8' && matrix.python-version != '3.9' }}
|
||||
- name: Install dependencies for 3.8 and 3.9
|
||||
if: ${{ matrix.python-version != '3.9' }}
|
||||
- name: Install dependencies for 3.9
|
||||
run: poetry install --no-interaction
|
||||
if: ${{ matrix.python-version == '3.8' || matrix.python-version == '3.9' }}
|
||||
if: ${{ matrix.python-version == '3.9' }}
|
||||
- name: Test with pytest (Py310+ - with syntax highlighting)
|
||||
run: |
|
||||
poetry run pytest tests -v --cov=./src/textual --cov-report=xml:./coverage.xml --cov-report term-missing
|
||||
if: ${{ matrix.python-version != '3.8' && matrix.python-version != '3.9' }}
|
||||
if: ${{ matrix.python-version != '3.9' }}
|
||||
- name: Test with pytest (Py39 - without syntax highlighting)
|
||||
run: |
|
||||
poetry run pytest tests -v --cov=./src/textual --cov-report=xml:./coverage.xml --cov-report term-missing -m 'not syntax'
|
||||
if: ${{ matrix.python-version == '3.8' || matrix.python-version == '3.9' }}
|
||||
if: ${{ matrix.python-version == '3.9' }}
|
||||
- name: Upload snapshot report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Unreleased
|
||||
## [6.3.0] - 2025-10-11
|
||||
|
||||
### Added
|
||||
|
||||
@@ -15,6 +15,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
- Fixed highlight not auto-detecting lexer https://github.com/Textualize/textual/pull/6167
|
||||
|
||||
### Changed
|
||||
|
||||
- Dropped support for Python3.8 https://github.com/Textualize/textual/pull/6121/
|
||||
- Added support for Python3.14 https://github.com/Textualize/textual/pull/6121/
|
||||
|
||||
## [6.2.1] - 2025-10-01
|
||||
|
||||
- Fix inability to copy text outside of an input/textarea when it was focused https://github.com/Textualize/textual/pull/6148
|
||||
@@ -3144,6 +3149,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
|
||||
- New handler system for messages that doesn't require inheritance
|
||||
- Improved traceback handling
|
||||
|
||||
[6.3.0]: https://github.com/Textualize/textual/compare/v6.2.1...v6.3.0
|
||||
[6.2.1]: https://github.com/Textualize/textual/compare/v6.2.0...v6.2.1
|
||||
[6.2.0]: https://github.com/Textualize/textual/compare/v6.1.0...v6.2.0
|
||||
[6.1.0]: https://github.com/Textualize/textual/compare/v6.0.0...v6.1.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
[](https://discord.gg/Enf6Z3qhVr)
|
||||
[](https://pypi.org/project/textual/)
|
||||
[](https://pypi.org/project/textual/)
|
||||
[](https://badge.fury.io/py/textual)
|
||||

|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ All you need to get started building Textual apps.
|
||||
|
||||
## Requirements
|
||||
|
||||
Textual requires Python 3.8 or later (if you have a choice, pick the most recent Python). Textual runs on Linux, macOS, Windows and probably any OS where Python also runs.
|
||||
Textual requires Python 3.9 or later (if you have a choice, pick the most recent Python). Textual runs on Linux, macOS, Windows and probably any OS where Python also runs.
|
||||
|
||||
!!! info "Your platform"
|
||||
|
||||
|
||||
2863
poetry.lock
generated
2863
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "textual"
|
||||
version = "6.2.1"
|
||||
version = "6.3.0"
|
||||
homepage = "https://github.com/Textualize/textual"
|
||||
repository = "https://github.com/Textualize/textual"
|
||||
documentation = "https://textual.textualize.io/"
|
||||
@@ -17,12 +17,12 @@ classifiers = [
|
||||
"Operating System :: Microsoft :: Windows :: Windows 11",
|
||||
"Operating System :: MacOS",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
include = [
|
||||
@@ -41,12 +41,12 @@ include = [
|
||||
"Bug Tracker" = "https://github.com/Textualize/textual/issues"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
target-version = "py39"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8.1"
|
||||
python = "^3.9"
|
||||
markdown-it-py = { extras = ["plugins", "linkify"], version = ">=2.1.0" }
|
||||
rich = ">=13.3.3"
|
||||
rich = ">=14.2.0"
|
||||
#rich = {path="../rich", develop=true}
|
||||
typing-extensions = "^4.4.0"
|
||||
platformdirs = ">=3.6.0,<5"
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 25 KiB |
@@ -7,7 +7,7 @@ from textual.layout import WidgetPlacement
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
def test_arrange_empty():
|
||||
async def test_arrange_empty():
|
||||
container = Widget(id="container")
|
||||
|
||||
result = arrange(container, [], Size(80, 24), Size(80, 24))
|
||||
@@ -15,7 +15,7 @@ def test_arrange_empty():
|
||||
assert result.widgets == set()
|
||||
|
||||
|
||||
def test_arrange_dock_top():
|
||||
async def test_arrange_dock_top():
|
||||
container = Widget(id="container")
|
||||
container._parent = App()
|
||||
child = Widget(id="child")
|
||||
@@ -36,7 +36,7 @@ def test_arrange_dock_top():
|
||||
assert result.widgets == {child, header}
|
||||
|
||||
|
||||
def test_arrange_dock_left():
|
||||
async def test_arrange_dock_left():
|
||||
container = Widget(id="container")
|
||||
container._parent = App()
|
||||
child = Widget(id="child")
|
||||
@@ -61,7 +61,7 @@ def test_arrange_dock_left():
|
||||
assert result.widgets == {child, header}
|
||||
|
||||
|
||||
def test_arrange_dock_right():
|
||||
async def test_arrange_dock_right():
|
||||
container = Widget(id="container")
|
||||
container._parent = App()
|
||||
child = Widget(id="child")
|
||||
@@ -86,7 +86,7 @@ def test_arrange_dock_right():
|
||||
assert result.widgets == {child, header}
|
||||
|
||||
|
||||
def test_arrange_dock_bottom():
|
||||
async def test_arrange_dock_bottom():
|
||||
container = Widget(id="container")
|
||||
container._parent = App()
|
||||
child = Widget(id="child")
|
||||
@@ -111,7 +111,7 @@ def test_arrange_dock_bottom():
|
||||
assert result.widgets == {child, header}
|
||||
|
||||
|
||||
def test_arrange_dock_badly():
|
||||
async def test_arrange_dock_badly():
|
||||
child = Widget(id="child")
|
||||
child.styles.dock = "nowhere"
|
||||
with pytest.raises(AssertionError):
|
||||
|
||||
@@ -31,7 +31,7 @@ def test_border_render_row():
|
||||
]
|
||||
|
||||
|
||||
def test_border_title_single_line():
|
||||
async def test_border_title_single_line():
|
||||
"""The border_title gets set to a single line even when multiple lines are provided."""
|
||||
widget = Widget()
|
||||
|
||||
@@ -59,7 +59,7 @@ def test_border_title_single_line():
|
||||
assert widget.border_title == "[bold]Hello World[/bold]"
|
||||
|
||||
|
||||
def test_border_subtitle_single_line():
|
||||
async def test_border_subtitle_single_line():
|
||||
"""The border_subtitle gets set to a single line even when multiple lines are provided."""
|
||||
widget = Widget()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from textual.geometry import Size, Spacing
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
def test_content_box():
|
||||
async def test_content_box():
|
||||
one = Fraction(1)
|
||||
|
||||
class TestWidget(Widget):
|
||||
@@ -44,7 +44,7 @@ def test_content_box():
|
||||
assert box_model == BoxModel(Fraction(14), Fraction(12), Spacing(0, 0, 0, 0))
|
||||
|
||||
|
||||
def test_width():
|
||||
async def test_width():
|
||||
"""Test width settings."""
|
||||
|
||||
one = Fraction(1)
|
||||
@@ -93,7 +93,7 @@ def test_width():
|
||||
assert box_model == BoxModel(Fraction(27), Fraction(16), Spacing(1, 2, 3, 4))
|
||||
|
||||
|
||||
def test_height():
|
||||
async def test_height():
|
||||
"""Test height settings."""
|
||||
|
||||
one = Fraction(1)
|
||||
@@ -143,7 +143,7 @@ def test_height():
|
||||
assert box_model == BoxModel(Fraction(54), Fraction(8), Spacing(1, 2, 3, 4))
|
||||
|
||||
|
||||
def test_max():
|
||||
async def test_max():
|
||||
"""Check that max_width and max_height are respected."""
|
||||
one = Fraction(1)
|
||||
|
||||
@@ -166,7 +166,7 @@ def test_max():
|
||||
assert box_model == BoxModel(Fraction(40), Fraction(30), Spacing(0, 0, 0, 0))
|
||||
|
||||
|
||||
def test_min():
|
||||
async def test_min():
|
||||
"""Check that min_width and min_height are respected."""
|
||||
|
||||
one = Fraction(1)
|
||||
|
||||
@@ -286,7 +286,7 @@ def test_first_line():
|
||||
assert first_line.spans == [Span(0, 3, "red")]
|
||||
|
||||
|
||||
def test_split_and_tabs():
|
||||
async def test_split_and_tabs():
|
||||
spans = [
|
||||
Span(0, 49, style="$text"),
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from textual.app import App
|
||||
|
||||
|
||||
def test_textual_env_var(monkeypatch):
|
||||
async def test_textual_env_var(monkeypatch):
|
||||
monkeypatch.setenv("TEXTUAL", "")
|
||||
app = App()
|
||||
assert app.features == set()
|
||||
|
||||
@@ -2,7 +2,7 @@ from textual.app import App, ComposeResult
|
||||
from textual.widgets import Log
|
||||
|
||||
|
||||
def test_process_line():
|
||||
async def test_process_line():
|
||||
log = Log()
|
||||
assert log._process_line("foo") == "foo"
|
||||
assert log._process_line("foo\t") == "foo "
|
||||
|
||||
@@ -9,14 +9,14 @@ def test_empty_list():
|
||||
assert len(NodeList()) == 0
|
||||
|
||||
|
||||
def test_add_one():
|
||||
async def test_add_one():
|
||||
"""Does adding a node to the node list report as having one item?"""
|
||||
nodes = NodeList()
|
||||
nodes._append(Widget())
|
||||
assert len(nodes) == 1
|
||||
|
||||
|
||||
def test_length_hint():
|
||||
async def test_length_hint():
|
||||
"""Check length hint dunder method."""
|
||||
nodes = NodeList()
|
||||
assert nodes.__length_hint__() == 0
|
||||
@@ -26,7 +26,7 @@ def test_length_hint():
|
||||
assert nodes.__length_hint__() == 3
|
||||
|
||||
|
||||
def test_repeat_add_one():
|
||||
async def test_repeat_add_one():
|
||||
"""Does adding the same item to the node list ignore the additional adds?"""
|
||||
nodes = NodeList()
|
||||
widget = Widget()
|
||||
@@ -35,7 +35,7 @@ def test_repeat_add_one():
|
||||
assert len(nodes) == 1
|
||||
|
||||
|
||||
def test_insert():
|
||||
async def test_insert():
|
||||
nodes = NodeList()
|
||||
widget1 = Widget()
|
||||
widget2 = Widget()
|
||||
@@ -46,7 +46,7 @@ def test_insert():
|
||||
assert list(nodes) == [widget1, widget2, widget3]
|
||||
|
||||
|
||||
def test_truthy():
|
||||
async def test_truthy():
|
||||
"""Does a node list act as a truthy object?"""
|
||||
nodes = NodeList()
|
||||
assert not bool(nodes)
|
||||
@@ -54,7 +54,7 @@ def test_truthy():
|
||||
assert bool(nodes)
|
||||
|
||||
|
||||
def test_contains():
|
||||
async def test_contains():
|
||||
"""Can we check if a widget is (not) within the list?"""
|
||||
widget = Widget()
|
||||
nodes = NodeList()
|
||||
@@ -64,7 +64,7 @@ def test_contains():
|
||||
assert Widget() not in nodes
|
||||
|
||||
|
||||
def test_index():
|
||||
async def test_index():
|
||||
"""Can we get the index of a widget in the list?"""
|
||||
widget = Widget()
|
||||
nodes = NodeList()
|
||||
@@ -74,7 +74,7 @@ def test_index():
|
||||
assert nodes.index(widget) == 0
|
||||
|
||||
|
||||
def test_remove():
|
||||
async def test_remove():
|
||||
"""Can we remove a widget we've added?"""
|
||||
widget = Widget()
|
||||
nodes = NodeList()
|
||||
@@ -84,7 +84,7 @@ def test_remove():
|
||||
assert widget not in nodes
|
||||
|
||||
|
||||
def test_clear():
|
||||
async def test_clear():
|
||||
"""Can we clear the list?"""
|
||||
nodes = NodeList()
|
||||
assert len(nodes) == 0
|
||||
@@ -100,7 +100,7 @@ def test_clear():
|
||||
assert widget not in nodes
|
||||
|
||||
|
||||
def test_listy():
|
||||
async def test_listy():
|
||||
nodes = NodeList()
|
||||
widget1 = Widget()
|
||||
widget2 = Widget()
|
||||
|
||||
@@ -4,12 +4,12 @@ from textual.widgets import Placeholder
|
||||
from textual.widgets._placeholder import InvalidPlaceholderVariant
|
||||
|
||||
|
||||
def test_invalid_placeholder_variant():
|
||||
async def test_invalid_placeholder_variant():
|
||||
with pytest.raises(InvalidPlaceholderVariant):
|
||||
Placeholder(variant="this is clearly not a valid variant!")
|
||||
|
||||
|
||||
def test_invalid_reactive_variant_change():
|
||||
async def test_invalid_reactive_variant_change():
|
||||
p = Placeholder()
|
||||
with pytest.raises(InvalidPlaceholderVariant):
|
||||
p.variant = "this is clearly not a valid variant!"
|
||||
|
||||
@@ -11,7 +11,7 @@ from textual.widget import Widget
|
||||
from textual.widgets import ProgressBar
|
||||
|
||||
|
||||
def test_initial_status():
|
||||
async def test_initial_status():
|
||||
pb = ProgressBar()
|
||||
assert pb.total is None
|
||||
assert pb.progress == 0
|
||||
@@ -23,7 +23,7 @@ def test_initial_status():
|
||||
assert pb.percentage == 0
|
||||
|
||||
|
||||
def test_advance():
|
||||
async def test_advance():
|
||||
pb = ProgressBar(total=100)
|
||||
|
||||
pb.advance(10)
|
||||
@@ -39,7 +39,7 @@ def test_advance():
|
||||
assert pb.percentage == approx(0.520625)
|
||||
|
||||
|
||||
def test_advance_backwards():
|
||||
async def test_advance_backwards():
|
||||
pb = ProgressBar(total=100)
|
||||
|
||||
pb.progress = 50
|
||||
@@ -48,7 +48,7 @@ def test_advance_backwards():
|
||||
assert pb.progress == 40
|
||||
|
||||
|
||||
def test_progress_overflow():
|
||||
async def test_progress_overflow():
|
||||
pb = ProgressBar(total=100)
|
||||
|
||||
pb.advance(999_999)
|
||||
@@ -58,19 +58,19 @@ def test_progress_overflow():
|
||||
assert pb.percentage == 1
|
||||
|
||||
|
||||
def test_progress_underflow():
|
||||
async def test_progress_underflow():
|
||||
pb = ProgressBar(total=100)
|
||||
|
||||
pb.advance(-999_999)
|
||||
assert pb.percentage == 0
|
||||
|
||||
|
||||
def test_non_negative_total():
|
||||
async def test_non_negative_total():
|
||||
pb = ProgressBar(total=-100)
|
||||
assert pb.total == 0
|
||||
|
||||
|
||||
def test_update_total():
|
||||
async def test_update_total():
|
||||
pb = ProgressBar()
|
||||
|
||||
pb.update(total=100)
|
||||
@@ -86,7 +86,7 @@ def test_update_total():
|
||||
assert pb.total == 100
|
||||
|
||||
|
||||
def test_update_progress():
|
||||
async def test_update_progress():
|
||||
pb = ProgressBar(total=100)
|
||||
|
||||
pb.update(progress=10)
|
||||
@@ -99,7 +99,7 @@ def test_update_progress():
|
||||
assert pb.progress == 40
|
||||
|
||||
|
||||
def test_update_advance():
|
||||
async def test_update_advance():
|
||||
pb = ProgressBar(total=100)
|
||||
|
||||
pb.update(advance=10)
|
||||
@@ -112,7 +112,7 @@ def test_update_advance():
|
||||
assert pb.progress == 30
|
||||
|
||||
|
||||
def test_update():
|
||||
async def test_update():
|
||||
pb = ProgressBar()
|
||||
|
||||
pb.update(total=100, progress=30, advance=20)
|
||||
@@ -120,7 +120,7 @@ def test_update():
|
||||
assert pb.progress == 50
|
||||
|
||||
|
||||
def test_go_back_to_indeterminate():
|
||||
async def test_go_back_to_indeterminate():
|
||||
pb = ProgressBar()
|
||||
|
||||
pb.total = 100
|
||||
|
||||
@@ -30,7 +30,7 @@ async def test_query_errors():
|
||||
app.query_one("1")
|
||||
|
||||
|
||||
def test_query():
|
||||
async def test_query():
|
||||
class View(Widget):
|
||||
pass
|
||||
|
||||
@@ -159,7 +159,7 @@ def test_query():
|
||||
_ = app.query(".float").last(View)
|
||||
|
||||
|
||||
def test_query_classes():
|
||||
async def test_query_classes():
|
||||
class App(Widget):
|
||||
pass
|
||||
|
||||
@@ -225,7 +225,7 @@ def test_query_classes():
|
||||
assert len(app.query(".test")) == 0
|
||||
|
||||
|
||||
def test_invalid_query():
|
||||
async def test_invalid_query():
|
||||
class App(Widget):
|
||||
pass
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ def test_resolve(scalars, total, gutter, result):
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_fraction_unit():
|
||||
async def test_resolve_fraction_unit():
|
||||
"""Test resolving fraction units in combination with minimum widths."""
|
||||
widget1 = Widget()
|
||||
widget2 = Widget()
|
||||
@@ -124,7 +124,7 @@ def test_resolve_fraction_unit():
|
||||
) == Fraction(2)
|
||||
|
||||
|
||||
def test_resolve_fraction_unit_stress_test():
|
||||
async def test_resolve_fraction_unit_stress_test():
|
||||
"""Check for zero division errors."""
|
||||
# https://github.com/Textualize/textual/issues/2673
|
||||
widget = Widget()
|
||||
@@ -149,7 +149,7 @@ def test_resolve_fraction_unit_stress_test():
|
||||
assert resolved_unit <= remaining_space
|
||||
|
||||
|
||||
def test_resolve_issue_2502():
|
||||
async def test_resolve_issue_2502():
|
||||
"""Test https://github.com/Textualize/textual/issues/2502"""
|
||||
|
||||
widget = Widget()
|
||||
|
||||
@@ -4,23 +4,23 @@ from textual.widgets import Rule
|
||||
from textual.widgets.rule import InvalidLineStyle, InvalidRuleOrientation
|
||||
|
||||
|
||||
def test_invalid_rule_orientation():
|
||||
async def test_invalid_rule_orientation():
|
||||
with pytest.raises(InvalidRuleOrientation):
|
||||
Rule(orientation="invalid orientation!")
|
||||
|
||||
|
||||
def test_invalid_rule_line_style():
|
||||
async def test_invalid_rule_line_style():
|
||||
with pytest.raises(InvalidLineStyle):
|
||||
Rule(line_style="invalid line style!")
|
||||
|
||||
|
||||
def test_invalid_reactive_rule_orientation_change():
|
||||
async def test_invalid_reactive_rule_orientation_change():
|
||||
rule = Rule()
|
||||
with pytest.raises(InvalidRuleOrientation):
|
||||
rule.orientation = "invalid orientation!"
|
||||
|
||||
|
||||
def test_invalid_reactive_rule_line_style_change():
|
||||
async def test_invalid_reactive_rule_line_style_change():
|
||||
rule = Rule()
|
||||
with pytest.raises(InvalidLineStyle):
|
||||
rule.line_style = "invalid line style!"
|
||||
|
||||
@@ -58,7 +58,7 @@ async def test_signal():
|
||||
assert called == 3
|
||||
|
||||
|
||||
def test_signal_errors():
|
||||
async def test_signal_errors():
|
||||
"""Check exceptions raised by Signal class."""
|
||||
app = App()
|
||||
test_signal = Signal(app, "test")
|
||||
|
||||
@@ -2,7 +2,7 @@ from textual.content import Content
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
def test_content_property():
|
||||
async def test_content_property():
|
||||
static = Static()
|
||||
assert static.content == ""
|
||||
static.content = "Foo"
|
||||
|
||||
@@ -3,7 +3,7 @@ from rich.text import Text
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
def test_make_renderable_expand_tabs():
|
||||
async def test_make_renderable_expand_tabs():
|
||||
# Regression test for https://github.com/Textualize/textual/issues/3007
|
||||
text_log = RichLog()
|
||||
renderable = text_log._make_renderable("\tfoo")
|
||||
|
||||
@@ -63,7 +63,7 @@ async def test_widget_construct():
|
||||
["visible", True, "visible"],
|
||||
],
|
||||
)
|
||||
def test_widget_set_visible_true(set_val, get_val, style_str):
|
||||
async def test_widget_set_visible_true(set_val, get_val, style_str):
|
||||
widget = Widget()
|
||||
widget.visible = set_val
|
||||
|
||||
@@ -71,7 +71,7 @@ def test_widget_set_visible_true(set_val, get_val, style_str):
|
||||
assert widget.styles.visibility == style_str
|
||||
|
||||
|
||||
def test_widget_set_visible_invalid_string():
|
||||
async def test_widget_set_visible_invalid_string():
|
||||
widget = Widget()
|
||||
|
||||
with pytest.raises(StyleValueError):
|
||||
@@ -80,7 +80,7 @@ def test_widget_set_visible_invalid_string():
|
||||
assert widget.visible
|
||||
|
||||
|
||||
def test_widget_content_width():
|
||||
async def test_widget_content_width():
|
||||
class TextWidget(Widget):
|
||||
def __init__(self, text: str, id: str) -> None:
|
||||
self.text = text
|
||||
@@ -238,19 +238,19 @@ async def test_widget_mount_ids_must_be_unique_mounting_multiple_calls(hierarchy
|
||||
parent.mount(widget2)
|
||||
|
||||
|
||||
def test_get_pseudo_class_state():
|
||||
async def test_get_pseudo_class_state():
|
||||
widget = Widget()
|
||||
pseudo_classes = widget.get_pseudo_class_state()
|
||||
assert pseudo_classes == PseudoClasses(enabled=True, focus=False, hover=False)
|
||||
|
||||
|
||||
def test_get_pseudo_class_state_disabled():
|
||||
async def test_get_pseudo_class_state_disabled():
|
||||
widget = Widget(disabled=True)
|
||||
pseudo_classes = widget.get_pseudo_class_state()
|
||||
assert pseudo_classes == PseudoClasses(enabled=False, focus=False, hover=False)
|
||||
|
||||
|
||||
def test_get_pseudo_class_state_parent_disabled():
|
||||
async def test_get_pseudo_class_state_parent_disabled():
|
||||
child = Widget()
|
||||
_parent = Widget(disabled=True)
|
||||
child._attach(_parent)
|
||||
@@ -258,14 +258,14 @@ def test_get_pseudo_class_state_parent_disabled():
|
||||
assert pseudo_classes == PseudoClasses(enabled=False, focus=False, hover=False)
|
||||
|
||||
|
||||
def test_get_pseudo_class_state_hover():
|
||||
async def test_get_pseudo_class_state_hover():
|
||||
widget = Widget()
|
||||
widget.mouse_hover = True
|
||||
pseudo_classes = widget.get_pseudo_class_state()
|
||||
assert pseudo_classes == PseudoClasses(enabled=True, focus=False, hover=True)
|
||||
|
||||
|
||||
def test_get_pseudo_class_state_focus():
|
||||
async def test_get_pseudo_class_state_focus():
|
||||
widget = Widget()
|
||||
widget.has_focus = True
|
||||
pseudo_classes = widget.get_pseudo_class_state()
|
||||
@@ -314,7 +314,7 @@ async def test_remove_unmounted():
|
||||
assert mounted
|
||||
|
||||
|
||||
def test_render_str() -> None:
|
||||
async def test_render_str() -> None:
|
||||
widget = Label()
|
||||
assert widget.render_str("foo") == Content("foo")
|
||||
assert widget.render_str("[b]foo") == Content.from_markup("[b]foo")
|
||||
@@ -371,11 +371,11 @@ def test_children_must_be_widgets():
|
||||
Widget(1, 2, 3)
|
||||
|
||||
|
||||
def test_orphan_widget_has_no_siblings():
|
||||
async def test_orphan_widget_has_no_siblings():
|
||||
assert Widget().siblings == []
|
||||
|
||||
|
||||
def test__allow_scroll_default():
|
||||
async def test__allow_scroll_default():
|
||||
assert not Widget()._allow_scroll
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ async def test_offset_getter_setter():
|
||||
assert label.offset == Offset(7, 3)
|
||||
|
||||
|
||||
def test_get_set_tooltip():
|
||||
async def test_get_set_tooltip():
|
||||
widget = Widget()
|
||||
assert widget.tooltip is None
|
||||
widget.tooltip = "This is a tooltip."
|
||||
|
||||
@@ -11,7 +11,7 @@ class Body(Widget):
|
||||
pass
|
||||
|
||||
|
||||
def test_find_dom_spot():
|
||||
async def test_find_dom_spot():
|
||||
# Build up a "fake" DOM for an application.
|
||||
screen = Widget(name="Screen")
|
||||
header = Widget(name="Header", id="header")
|
||||
|
||||
@@ -6,7 +6,7 @@ from textual.widgets import Tree
|
||||
from textual.widgets.tree import NodeID, UnknownNodeID
|
||||
|
||||
|
||||
def test_get_tree_node_by_id() -> None:
|
||||
async def test_get_tree_node_by_id() -> None:
|
||||
"""It should be possible to get a TreeNode by its ID."""
|
||||
tree = Tree[None]("Anakin")
|
||||
child = tree.root.add("Leia")
|
||||
|
||||
@@ -4,13 +4,13 @@ from textual.widgets import Tree
|
||||
from textual.widgets.tree import AddNodeError
|
||||
|
||||
|
||||
def test_tree_node_add_before_and_after_raises_exception():
|
||||
async def test_tree_node_add_before_and_after_raises_exception():
|
||||
tree = Tree[None]("root")
|
||||
with pytest.raises(AddNodeError):
|
||||
tree.root.add("error", before=99, after=0)
|
||||
|
||||
|
||||
def test_tree_node_add_before_or_after_with_invalid_type_raises_exception():
|
||||
async def test_tree_node_add_before_or_after_with_invalid_type_raises_exception():
|
||||
tree = Tree[None]("root")
|
||||
tree.root.add("node")
|
||||
with pytest.raises(TypeError):
|
||||
@@ -19,7 +19,7 @@ def test_tree_node_add_before_or_after_with_invalid_type_raises_exception():
|
||||
tree.root.add("after node", after="node")
|
||||
|
||||
|
||||
def test_tree_node_add_before_index():
|
||||
async def test_tree_node_add_before_index():
|
||||
tree = Tree[None]("root")
|
||||
tree.root.add("node")
|
||||
tree.root.add("before node", before=0)
|
||||
@@ -38,7 +38,7 @@ def test_tree_node_add_before_index():
|
||||
assert str(tree.root.children[6].label) == "last"
|
||||
|
||||
|
||||
def test_tree_node_add_after_index():
|
||||
async def test_tree_node_add_after_index():
|
||||
tree = Tree[None]("root")
|
||||
tree.root.add("node")
|
||||
tree.root.add("after node", after=0)
|
||||
@@ -57,7 +57,7 @@ def test_tree_node_add_after_index():
|
||||
assert str(tree.root.children[6].label) == "last"
|
||||
|
||||
|
||||
def test_tree_node_add_relative_to_unknown_node_raises_exception():
|
||||
async def test_tree_node_add_relative_to_unknown_node_raises_exception():
|
||||
tree = Tree[None]("root")
|
||||
removed_node = tree.root.add("removed node")
|
||||
removed_node.remove()
|
||||
@@ -67,7 +67,7 @@ def test_tree_node_add_relative_to_unknown_node_raises_exception():
|
||||
tree.root.add("node", after=removed_node)
|
||||
|
||||
|
||||
def test_tree_node_add_before_node():
|
||||
async def test_tree_node_add_before_node():
|
||||
tree = Tree[None]("root")
|
||||
node = tree.root.add("node")
|
||||
before_node = tree.root.add("before node", before=node)
|
||||
@@ -86,7 +86,7 @@ def test_tree_node_add_before_node():
|
||||
assert str(tree.root.children[6].label) == "last"
|
||||
|
||||
|
||||
def test_tree_node_add_after_node():
|
||||
async def test_tree_node_add_after_node():
|
||||
tree = Tree[None]("root")
|
||||
node = tree.root.add("node")
|
||||
after_node = tree.root.add("after node", after=node)
|
||||
@@ -105,7 +105,7 @@ def test_tree_node_add_after_node():
|
||||
assert str(tree.root.children[6].label) == "last"
|
||||
|
||||
|
||||
def test_tree_node_add_leaf_before_or_after():
|
||||
async def test_tree_node_add_leaf_before_or_after():
|
||||
tree = Tree[None]("root")
|
||||
leaf = tree.root.add_leaf("leaf")
|
||||
tree.root.add_leaf("before leaf", before=leaf)
|
||||
|
||||
@@ -9,7 +9,7 @@ def label_of(node: TreeNode[None]):
|
||||
return str(node.label)
|
||||
|
||||
|
||||
def test_tree_node_children() -> None:
|
||||
async def test_tree_node_children() -> None:
|
||||
"""A node's children property should act like an immutable list."""
|
||||
CHILDREN = 23
|
||||
tree = Tree[None]("Root")
|
||||
|
||||
@@ -4,7 +4,7 @@ from textual.widgets import Tree
|
||||
from textual.widgets.tree import TreeNode
|
||||
|
||||
|
||||
def test_tree_node_label() -> None:
|
||||
async def test_tree_node_label() -> None:
|
||||
"""It should be possible to modify a TreeNode's label."""
|
||||
node = TreeNode(Tree[None]("Xenomorph Lifecycle"), None, 0, "Facehugger")
|
||||
assert node.label == Text("Facehugger")
|
||||
@@ -12,7 +12,7 @@ def test_tree_node_label() -> None:
|
||||
assert node.label == Text("Chestbuster")
|
||||
|
||||
|
||||
def test_tree_node_label_via_tree() -> None:
|
||||
async def test_tree_node_label_via_tree() -> None:
|
||||
"""It should be possible to modify a TreeNode's label when created via a Tree."""
|
||||
tree = Tree[None]("Xenomorph Lifecycle")
|
||||
node = tree.root.add("Facehugger")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from textual.widgets import Tree
|
||||
|
||||
|
||||
def test_tree_node_parent() -> None:
|
||||
async def test_tree_node_parent() -> None:
|
||||
"""It should be possible to access a TreeNode's parent."""
|
||||
tree = Tree[None]("Anakin")
|
||||
child = tree.root.add("Leia")
|
||||
|
||||
Reference in New Issue
Block a user