internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: Textualize/textual

Issue:
Link the documentation for `Header` up with the title and subtitle information on `App`
When faced with finding out how to set the text in a `Header` a reader may look for `Header` in our docs; it doesn't make the connection between it and the title and subtitle on `App` obvious. The docs for `Header` should [link to those docs](https://textual.textualize.io/guide/app/#title-and-subtitle).


Discussion:

Resolving patch:
diff --git a/docs/examples/widgets/header_app_title.py b/docs/examples/widgets/header_app_title.py
new file mode 100644
--- /dev/null
+++ b/docs/examples/widgets/header_app_title.py
@@ -0,0 +1,16 @@
+from textual.app import App, ComposeResult
+from textual.widgets import Header
+
+
+class HeaderApp(App):
+    def compose(self) -> ComposeResult:
+        yield Header()
+
+    def on_mount(self) -> None:
+        self.title = "Header Application"
+        self.sub_title = "With title and sub-title"
+
+
+if __name__ == "__main__":
+    app = HeaderApp()
+    app.run()


Test patch:
Repository: Textualize/textual

Issue:
Removing an option from an `OptionList` when the mouse is hovering over the last item causes a crash
Given this code:

```python
from textual import on
from textual.app import App, ComposeResult
from textual.widgets import OptionList

class OptionListRemoveOnDeleteApp(App[None]):

    def compose(self) -> ComposeResult:
        yield OptionList(*[f"This is option {n}" for n in range(20)])

    @on(OptionList.OptionSelected)
    def remove_option(self, event: OptionList.OptionSelected) -> None:
        self.query_one(OptionList).remove_option_at_index(event.option_index)

if __name__ == "__main__":
    OptionListRemoveOnDeleteApp().run()
```

hover the mouse over the last item in the list, then either click on the last item, or hit <kbd>Enter</kbd>.  It will result in a `IndexError: list index out of range`.

At first glance it seems to be to do with the value of `_mouse_hovering_over`, which should possibly be reset to `None` when an option is removed (especially if the option that is hovered over is the option being removed *and* it's the last option); or [the test for drawing of a `mouse_over` line in `render_line` should do a double check that the hover option is still a known option](https://github.com/Textualize/textual/blob/74aa90f5ac3e9cbd5e002cf504241a9b2d257930/src/textual/widgets/_option_list.py#L927-L930).


Discussion:

Resolving patch:
diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py
--- a/src/textual/widgets/_option_list.py
+++ b/src/textual/widgets/_option_list.py
@@ -613,6 +613,7 @@ def _remove_option(self, index: int) -> None:
         self._refresh_content_tracking(force=True)
         # Force a re-validation of the highlight.
         self.highlighted = self.highlighted
+        self._mouse_hovering_over = None
         self.refresh()
 
     def remove_option(self, option_id: str) -> Self:


Test patch:
diff --git a/tests/option_list/test_option_removal.py b/tests/option_list/test_option_removal.py
--- a/tests/option_list/test_option_removal.py
+++ b/tests/option_list/test_option_removal.py
@@ -5,6 +5,7 @@
 import pytest
 
 from textual.app import App, ComposeResult
+from textual.geometry import Offset
 from textual.widgets import OptionList
 from textual.widgets.option_list import Option, OptionDoesNotExist
 
@@ -99,3 +100,13 @@ async def test_remove_invalid_index() -> None:
     async with OptionListApp().run_test() as pilot:
         with pytest.raises(OptionDoesNotExist):
             pilot.app.query_one(OptionList).remove_option_at_index(23)
+
+
+async def test_remove_with_hover_on_last_option():
+    """https://github.com/Textualize/textual/issues/3270"""
+    async with OptionListApp().run_test() as pilot:
+        await pilot.hover(OptionList, Offset(1, 1) + Offset(2, 1))
+        option_list = pilot.app.query_one(OptionList)
+        assert option_list._mouse_hovering_over == 1
+        option_list.remove_option_at_index(0)
+        assert option_list._mouse_hovering_over == None
Repository: Textualize/textual

Issue:
`DataTable` supplementary types should be included in the documentation
It seems that the following `DataTable`-related types aren't included anywhere in the documentation:

- `CellDoesNotExist`
- `CellKey`
- `CellType`
- `Column`
- `ColumnDoesNotExist`
- `ColumnKey`
- `CursorType`
- `DuplicateKey`
- `Row`
- `RowDoesNotExist`
- `RowKey`

As such it would seem that their location, should the dev wish to import one or more of them, isn't obvious. It also means that when they're shown in the documentation as parameter and return types, etc, they're not linked to.


Discussion:

Resolving patch:
diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py
--- a/src/textual/widgets/_data_table.py
+++ b/src/textual/widgets/_data_table.py
@@ -38,7 +38,7 @@
 LineCacheKey: TypeAlias = "tuple[int, int, int, int, Coordinate, Coordinate, Style, CursorType, bool, int, PseudoClasses]"
 RowCacheKey: TypeAlias = "tuple[RowKey, int, Style, Coordinate, Coordinate, CursorType, bool, bool, int, PseudoClasses]"
 CursorType = Literal["cell", "row", "column", "none"]
-"""The legal types of cursors for [`DataTable.cursor_type`][textual.widgets.DataTable.cursor_type]."""
+"""The valid types of cursors for [`DataTable.cursor_type`][textual.widgets.DataTable.cursor_type]."""
 CellType = TypeVar("CellType")
 
 CELL_X_PADDING = 2
@@ -47,18 +47,18 @@
 class CellDoesNotExist(Exception):
     """The cell key/index was invalid.
 
-    Raised when the user supplies coordinates or cell keys which
-    do not exist in the DataTable."""
+    Raised when the coordinates or cell key provided does not exist
+    in the DataTable (e.g. out of bounds index, invalid key)"""
 
 
 class RowDoesNotExist(Exception):
-    """Raised when the user supplies a row index or row key which does
-    not exist in the DataTable (e.g. out of bounds index, invalid key)"""
+    """Raised when the row index or row key provided does not exist
+    in the DataTable (e.g. out of bounds index, invalid key)"""
 
 
 class ColumnDoesNotExist(Exception):
-    """Raised when the user supplies a column index or column key which does
-    not exist in the DataTable (e.g. out of bounds index, invalid key)"""
+    """Raised when the column index or column key provided does not exist
+    in the DataTable (e.g. out of bounds index, invalid key)"""
 
 
 class DuplicateKey(Exception):
diff --git a/src/textual/widgets/data_table.py b/src/textual/widgets/data_table.py
--- a/src/textual/widgets/data_table.py
+++ b/src/textual/widgets/data_table.py
@@ -1,5 +1,3 @@
-"""Make non-widget DataTable support classes available."""
-
 from ._data_table import (
     CellDoesNotExist,
     CellKey,


Test patch:
Repository: Textualize/textual

Issue:
`TimeoutError` when finding nothing in the Command Palette (Python <3.11)
To test this, make a fresh installation of Textual, then run:

```sh
$ python -m textual
```

To run up the demo. Press <kbd>Ctrl</kbd>+<kbd>\\</kbd> to pull up the command palette, then type something that won't be found, such as "hello". Within a moment or so you should get a `TimeoutError`.

Note that this seems to *only* be an issue with Pythons less than 3.11.


Discussion:
I was able to reproduce the issue on python 3.10
As best as I can tell, this comes down to this commit: eaa749665f9b8271eff45be8e5e1e72ac8729b9e

@willmcgugan Was there a reason for switching from `asyncio.TimeoutError` to `TimeoutError` here? I also gather that there is a change from 3.11 onward that we need to take into account?

Resolving patch:
diff --git a/src/textual/command.py b/src/textual/command.py
--- a/src/textual/command.py
+++ b/src/textual/command.py
@@ -7,7 +7,7 @@
 from __future__ import annotations
 
 from abc import ABC, abstractmethod
-from asyncio import CancelledError, Queue, Task, wait, wait_for
+from asyncio import CancelledError, Queue, Task, TimeoutError, wait, wait_for
 from dataclasses import dataclass
 from functools import total_ordering
 from time import monotonic


Test patch:
Repository: Textualize/textual

Issue:
Pilot.click can't use `Screen` as selector.
If you try something like `pilot.click(Screen)`, you get a `NoMatches` exception from the query.
Pilot.click can't use `Screen` as selector.
If you try something like `pilot.click(Screen)`, you get a `NoMatches` exception from the query.


Discussion:
Ah, it looks like it is doing `screen.query_one`. It should probably be `app.query_one`.
Ah, it looks like it is doing `screen.query_one`. It should probably be `app.query_one`.

Resolving patch:
diff --git a/src/textual/pilot.py b/src/textual/pilot.py
--- a/src/textual/pilot.py
+++ b/src/textual/pilot.py
@@ -100,7 +100,7 @@ async def click(
         app = self.app
         screen = app.screen
         if selector is not None:
-            target_widget = screen.query_one(selector)
+            target_widget = app.query_one(selector)
         else:
             target_widget = screen
 
@@ -132,7 +132,7 @@ async def hover(
         app = self.app
         screen = app.screen
         if selector is not None:
-            target_widget = screen.query_one(selector)
+            target_widget = app.query_one(selector)
         else:
             target_widget = screen
 


Test patch:
diff --git a/tests/test_pilot.py b/tests/test_pilot.py
--- a/tests/test_pilot.py
+++ b/tests/test_pilot.py
@@ -52,3 +52,21 @@ def action_beep(self) -> None:
     with pytest.raises(ZeroDivisionError):
         async with FailingApp().run_test() as pilot:
             await pilot.press("b")
+
+
+async def test_pilot_click_screen():
+    """Regression test for https://github.com/Textualize/textual/issues/3395.
+
+    Check we can use `Screen` as a selector for a click."""
+
+    async with App().run_test() as pilot:
+        await pilot.click("Screen")
+
+
+async def test_pilot_hover_screen():
+    """Regression test for https://github.com/Textualize/textual/issues/3395.
+
+    Check we can use `Screen` as a selector for a hover."""
+
+    async with App().run_test() as pilot:
+        await pilot.hover("Screen")
Repository: Textualize/textual

Issue:
Command Palette shows intermediate frames
The command palette show an intermediate frame before showing the results.

It may be hard to see in this video, but the first character produces a frame were the list is present, but there is no content. I think we should be able to suppress showing the list when no results have been retrieved.

https://github.com/Textualize/textual/assets/554369/8ae13d37-5c90-48a7-abba-1faa877241d8




Discussion:
> I think we should be able to suppress showing the list when no results have been retrieved.

Should be, although we also need to take into account that we *do* want to show the list before results are received at some point, so that it can be shown that we're busy (there's pretty much already logic for that so hopefully it'll just be an extra step/check).

Resolving patch:
diff --git a/src/textual/command.py b/src/textual/command.py
--- a/src/textual/command.py
+++ b/src/textual/command.py
@@ -586,6 +586,7 @@ def _show_no_matches() -> None:
                     id=self._NO_MATCHES,
                 )
             )
+            self._list_visible = True
 
         self._no_matches_timer = self.set_timer(
             self._NO_MATCHES_COUNTDOWN,
@@ -765,6 +766,7 @@ def _refresh_command_list(
         command_list.clear_options().add_options(sorted(commands, reverse=True))
         if highlighted is not None:
             command_list.highlighted = command_list.get_option_index(highlighted.id)
+        self._list_visible = bool(command_list.option_count)
 
     _RESULT_BATCH_TIME: Final[float] = 0.25
     """How long to wait before adding commands to the command list."""
@@ -813,10 +815,7 @@ async def _gather_commands(self, search_value: str) -> None:
         # grab a reference to that.
         worker = get_current_worker()
 
-        # We're ready to show results, ensure the list is visible.
-        self._list_visible = True
-
-        # Go into a busy mode.
+        # Reset busy mode.
         self._show_busy = False
 
         # A flag to keep track of if the current content of the command hit


Test patch:
Repository: Textualize/textual

Issue:
[FEAT] Add `Input.clear` method
Following discussions in #3427, it would make sense to add a `Input.clear` method.

I'm happy to a submit a PR, if it helps.


Discussion:
PR would be great :)

Resolving patch:
diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py
--- a/src/textual/widgets/_input.py
+++ b/src/textual/widgets/_input.py
@@ -481,6 +481,10 @@ def insert_text_at_cursor(self, text: str) -> None:
             self.value = f"{before}{text}{after}"
             self.cursor_position += len(text)
 
+    def clear(self) -> None:
+        """Clear the input."""
+        self.value = ""
+
     def action_cursor_left(self) -> None:
         """Move the cursor one position to the left."""
         self.cursor_position -= 1


Test patch:
diff --git a/tests/input/test_input_clear.py b/tests/input/test_input_clear.py
new file mode 100644
--- /dev/null
+++ b/tests/input/test_input_clear.py
@@ -0,0 +1,16 @@
+from textual.app import App, ComposeResult
+from textual.widgets import Input
+
+
+class InputApp(App):
+    def compose(self) -> ComposeResult:
+        yield Input("Hello, World!")
+
+
+async def test_input_clear():
+    async with InputApp().run_test() as pilot:
+        input_widget = pilot.app.query_one(Input)
+        assert input_widget.value == "Hello, World!"
+        input_widget.clear()
+        await pilot.pause()
+        assert input_widget.value == ""
Repository: Textualize/textual

Issue:
Fractional styles raise exception when set to integers
Although technically correct (the best kind of correct), it's quite awkward that you can't do something like

```py
some_widget.styles.text_opacity = 0
```

This raises an exception because `0` is not a float.
Text opacity – and the other related styles – should be made to work with integers, too.


Discussion:

Resolving patch:
diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py
--- a/src/textual/css/_style_properties.py
+++ b/src/textual/css/_style_properties.py
@@ -1063,8 +1063,8 @@ def __set__(self, obj: StylesBase, value: float | str | None) -> None:
                 obj.refresh(children=self.children)
             return
 
-        if isinstance(value, float):
-            float_value = value
+        if isinstance(value, (int, float)):
+            float_value = float(value)
         elif isinstance(value, str) and value.endswith("%"):
             float_value = float(Scalar.parse(value).value) / 100
         else:


Test patch:
diff --git a/tests/css/test_styles.py b/tests/css/test_styles.py
--- a/tests/css/test_styles.py
+++ b/tests/css/test_styles.py
@@ -148,6 +148,13 @@ def test_opacity_set_invalid_type_error():
         styles.text_opacity = "invalid value"
 
 
+def test_opacity_set_allows_integer_value():
+    """Regression test for https://github.com/Textualize/textual/issues/3414"""
+    styles = RenderStyles(DOMNode(), Styles(), Styles())
+    styles.text_opacity = 0
+    assert styles.text_opacity == 0.0
+
+
 @pytest.mark.parametrize(
     "size_dimension_input,size_dimension_expected_output",
     [
Repository: Textualize/textual

Issue:
linter complaint about using a worker as a timer callback
Have you checked closed issues? https://github.com/Textualize/textual/issues?q=is%3Aissue+is%3Aclosed

yes

Please give a brief but clear explanation of the issue. If you can, include a complete working example that demonstrates the bug. **Check it can run without modifications.**

```
from textual import work
from textual.app import App
class testy(App):
    @work
    async def worky(self) -> None:
        pass
    def compose(self):
        self.set_timer(1, self.worky)
```

```
$ pyright test.py
/home/guy/gits/ttuna/test.py
  /home/guy/gits/ttuna/test.py:8:27 - error: Argument of type "() -> Worker[None]" cannot be assigned to parameter "callback" of type "TimerCallback | None" in function "set_timer"
    Type "() -> Worker[None]" cannot be assigned to type "TimerCallback | None"
      Type "() -> Worker[None]" cannot be assigned to type "() -> Awaitable[None]"
        Function return type "Worker[None]" is incompatible with type "Awaitable[None]"
          "Worker[None]" is incompatible with protocol "Awaitable[None]"
            "__await__" is not present
      Type "() -> Worker[None]" cannot be assigned to type "() -> None"
        Function return type "Worker[None]" is incompatible with type "None"
          Type cannot be assigned to type "None"
    ... (reportGeneralTypeIssues)
1 error, 0 warnings, 0 informations 
```

The same error is reported for `set_interval` as well


Discussion:
The callback in `set_timer` is typed as returning `None`, but your `worky` method returns a Worker instance.

That might be too strict. We could maybe have it typed as returning Any.

In the meantime, `set_timer(1, lambda: self.worky())` may quell the typing error. 
Thanks, the linter is apparently too smart to be fooled by `lambda`. Passing it through an intermediate function works, though:
```
    @work
    async def worky(self) -> None:
        pass
    def do_worky(self) -> None:
        self.worky()
    def compose(self):
        self.set_timer(1, self.do_worky())
```
produces no complaint.

Resolving patch:
diff --git a/src/textual/_work_decorator.py b/src/textual/_work_decorator.py
--- a/src/textual/_work_decorator.py
+++ b/src/textual/_work_decorator.py
@@ -1,5 +1,4 @@
 """
-
 A decorator used to create [workers](/guide/workers).
 """
 
diff --git a/src/textual/timer.py b/src/textual/timer.py
--- a/src/textual/timer.py
+++ b/src/textual/timer.py
@@ -8,7 +8,7 @@
 
 import weakref
 from asyncio import CancelledError, Event, Task
-from typing import Awaitable, Callable, Union
+from typing import Any, Awaitable, Callable, Union
 
 from rich.repr import Result, rich_repr
 
@@ -19,7 +19,7 @@
 from ._time import sleep
 from ._types import MessageTarget
 
-TimerCallback = Union[Callable[[], Awaitable[None]], Callable[[], None]]
+TimerCallback = Union[Callable[[], Awaitable[Any]], Callable[[], Any]]
 """Type of valid callbacks to be used with timers."""
 
 


Test patch:
Repository: Textualize/textual

Issue:
Duplicate CSS errors from screens
CSS errors in screens are still duplicate (see #3566, and https://github.com/Textualize/textual/pull/3566/files#r1367884524 in particular).

CSS parsing errors show up as duplicate when parsing the `CSS` class variable or css from an external file, but not from `DEFAULT_CSS`.

App to reproduce:

```py
from textual.app import App
from textual.screen import Screen


class MyScreen(Screen[None]):
    # CSS_PATH = "myscreen.tcss"
    CSS = "Screen { screen-baz: spam; }"


class MyApp(App[None]):
    def on_mount(self):
        self.push_screen(MyScreen())


if __name__ == "__main__":
    MyApp().run()
```


Discussion:

Resolving patch:
diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py
--- a/src/textual/css/stylesheet.py
+++ b/src/textual/css/stylesheet.py
@@ -389,10 +389,19 @@ def reparse(self) -> None:
                 tie_breaker=tie_breaker,
                 scope=scope,
             )
-        stylesheet.parse()
-        self._rules = stylesheet.rules
-        self._rules_map = None
-        self.source = stylesheet.source
+        try:
+            stylesheet.parse()
+        except Exception:
+            # If we don't update self's invalid CSS, we might end up reparsing this CSS
+            # before Textual quits application mode.
+            # See https://github.com/Textualize/textual/issues/3581.
+            self._invalid_css.update(stylesheet._invalid_css)
+            raise
+        else:
+            self._rules = stylesheet.rules
+            self._rules_map = None
+            self.source = stylesheet.source
+            self._require_parse = False
 
     @classmethod
     def _check_rule(


Test patch:
Repository: Textualize/textual

Issue:
Tweak the documentation for `Widget.get_child_by_type`
The docstring for `Widget.get_child_by_type` could do with a small tweak as there's two things that aren't clear:

- It's really `get_first_child_by_type` (as in, if there are multiple children of that type it won't blow up like `query_one` would)
- It's also an exact type match, it makes a point of *not* looking for child types (so looking for `Static` *won't* return a `Label`)

The docstring should probably be clear about this.


Discussion:

Resolving patch:
diff --git a/src/textual/widget.py b/src/textual/widget.py
--- a/src/textual/widget.py
+++ b/src/textual/widget.py
@@ -603,16 +603,18 @@ def get_widget_by_id(
         raise NoMatches(f"No descendant found with id={id!r}")
 
     def get_child_by_type(self, expect_type: type[ExpectType]) -> ExpectType:
-        """Get a child of a give type.
+        """Get the first immediate child of a given type.
+
+        Only returns exact matches, and so will not match subclasses of the given type.
 
         Args:
-            expect_type: The type of the expected child.
+            expect_type: The type of the child to search for.
 
         Raises:
-            NoMatches: If no valid child is found.
+            NoMatches: If no matching child is found.
 
         Returns:
-            A widget.
+            The first immediate child widget with the expected type.
         """
         for child in self._nodes:
             # We want the child with the exact type (not subclasses)


Test patch:
Repository: Textualize/textual

Issue:
`Button.update` method?
I'm often caught out by `Button.update` not updating the label (or doing anything!)

I thought about overriding the method to simply assign `Button.label`, but the argument type for the `Static` base class is `RenderableType` whereas the button label is `TextType` (I'm afraid I don't fully understand the difference!)

Am I the only one who gets caught out by this?


Discussion:
Thank you for your issue. Give us a little time to review it.

PS. You might want to check the [FAQ](https://textual.textualize.io/FAQ/) if you haven't done so already.

This is an automated reply, generated by [FAQtory](https://github.com/willmcgugan/faqtory)
At the present time, it's nonsensical for `Button` to inherit from `Static`, so I'm guessing this inheritance is remnant of a previous `Button` implementation.

The point of `Static` is to cache the rendering and use the cache in its method `render`.
The implementation of `Button` overrides `Static.render`, so we're not making use of what the only thing we get by inheriting from `Static`...

So, we'll drop inheritance of `Static` and instead you can make use of the reactive attribute `label`.

Resolving patch:
diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py
--- a/src/textual/widgets/_button.py
+++ b/src/textual/widgets/_button.py
@@ -14,7 +14,7 @@
 from ..message import Message
 from ..pad import HorizontalPad
 from ..reactive import reactive
-from ..widgets import Static
+from ..widget import Widget
 
 ButtonVariant = Literal["default", "primary", "success", "warning", "error"]
 """The names of the valid button variants.
@@ -29,7 +29,7 @@ class InvalidButtonVariant(Exception):
     """Exception raised if an invalid button variant is used."""
 
 
-class Button(Static, can_focus=True):
+class Button(Widget, can_focus=True):
     """A simple clickable button."""
 
     DEFAULT_CSS = """


Test patch: