nebius/SWE-bench-extra huggingface dataset:
Repository: TabViewer/tabview

Issue:
File URI scheme not supported
File URI scheme as filename is not supported:

`$ tabview file://home/asparagii/test.csv` leads to a FileNotFoundException

Comments:

Patch:
diff --git a/tabview/tabview.py b/tabview/tabview.py
index c98188a..b7f441c 100644
--- a/tabview/tabview.py
+++ b/tabview/tabview.py
@@ -25,6 +25,11 @@ from textwrap import wrap
 import unicodedata
 import shlex
 
+if sys.version_info.major < 3:
+    from urlparse import urlparse
+else:
+    from urllib.parse import urlparse
+
 
 if sys.version_info.major < 3:
     # Python 2.7 shim
@@ -1356,7 +1361,8 @@ def view(data, enc=None, start_pos=(0, 0), column_width=20, column_gap=2,
         while True:
             try:
                 if isinstance(data, basestring):
-                    with open(data, 'rb') as fd:
+                    parsed_path = parse_path(data)
+                    with open(parsed_path, 'rb') as fd:
                         new_data = fd.readlines()
                         if info == "":
                             info = data
@@ -1395,3 +1401,8 @@ def view(data, enc=None, start_pos=(0, 0), column_width=20, column_gap=2,
     finally:
         if lc_all is not None:
             locale.setlocale(locale.LC_ALL, lc_all)
+
+
+def parse_path(path):
+    parse_result = urlparse(path)
+    return parse_result.path


Test patch:
diff --git a/test/test_tabview.py b/test/test_tabview.py
index b82c38e..91f4409 100644
--- a/test/test_tabview.py
+++ b/test/test_tabview.py
@@ -107,6 +107,26 @@ class TestTabviewUnits(unittest.TestCase):
                 i = str(i)
             self.assertEqual(i, res[0][j])
 
+    def test_tabview_uri_parse(self):
+        # Strip 'file://' from uri (three slashes)
+        path = t.parse_path('file:///home/user/test.csv')
+        self.assertEqual(path, '/home/user/test.csv')
+
+        # Two slashes
+        path = t.parse_path('file://localhost/test.csv')
+        self.assertEqual(path, '/test.csv')
+
+        # Don't change if no 'file://' in string
+        path = t.parse_path('/home/user/test.csv')
+        self.assertEqual(path, '/home/user/test.csv')
+
+        # Don't change if relative path
+        path = t.parse_path('../test.csv')
+        self.assertEqual(path, '../test.csv')
+
+        path = t.parse_path('test.csv')
+        self.assertEqual(path, 'test.csv')
+
 
 class TestTabviewIntegration(unittest.TestCase):
     """Integration tests for tabview. Run through the curses routines and some
Repository: Textualize/textual

Issue:
A `Widget`-derived widget with `inherit_bindings=False`, but no `BINDINGS`, appears to still inherit bindings
This is *very* much related to #1343, but I think merits an issue of its own to consider first. The reasons *why* this happens seem clear enough, but I feel the setup may cause confusion. With Textual as it is of 0.6.0 (with the bindings for movement keys being on `Widget`), consider this widget:

```python
class Odradek( Widget, inherit_bindings=False ):
    pass
````

vs this:

```python
class Odradek( Widget, inherit_bindings=False ):
    BINDINGS = []
````

In the former case the bindings of `Widget` are still inherited. In the latter they're not.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1641b1a3..42111addd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Fixed issue with auto width/height and relative children https://github.com/Textualize/textual/issues/1319
 - Fixed issue with offset applied to containers https://github.com/Textualize/textual/issues/1256
 - Fixed default CSS retrieval for widgets with no `DEFAULT_CSS` that inherited from widgets with `DEFAULT_CSS` https://github.com/Textualize/textual/issues/1335
+- Fixed merging of `BINDINGS` when binding inheritance is set to `None` https://github.com/Textualize/textual/issues/1351
 
 ## [0.5.0] - 2022-11-20
 
diff --git a/src/textual/dom.py b/src/textual/dom.py
index 0bd45fcce..49057b67d 100644
--- a/src/textual/dom.py
+++ b/src/textual/dom.py
@@ -229,7 +229,7 @@ class DOMNode(MessagePump):
             if issubclass(base, DOMNode):
                 if not base._inherit_bindings:
                     bindings.clear()
-                bindings.append(Bindings(base.BINDINGS))
+                bindings.append(Bindings(base.__dict__.get("BINDINGS", [])))
         keys = {}
         for bindings_ in bindings:
             keys.update(bindings_.keys)


Test patch:
diff --git a/tests/test_dom.py b/tests/test_dom.py
index 620984361..c1b5221c0 100644
--- a/tests/test_dom.py
+++ b/tests/test_dom.py
@@ -45,6 +45,39 @@ def test_validate():
         node.toggle_class("1")
 
 
+def test_inherited_bindings():
+    """Test if binding merging is done correctly when (not) inheriting bindings."""
+    class A(DOMNode):
+        BINDINGS = [("a", "a", "a")]
+
+    class B(A):
+        BINDINGS = [("b", "b", "b")]
+
+    class C(B, inherit_bindings=False):
+        BINDINGS = [("c", "c", "c")]
+
+    class D(C, inherit_bindings=False):
+        pass
+
+    class E(D):
+        BINDINGS = [("e", "e", "e")]
+
+    a = A()
+    assert list(a._bindings.keys.keys()) == ["a"]
+
+    b = B()
+    assert list(b._bindings.keys.keys()) == ["a", "b"]
+
+    c = C()
+    assert list(c._bindings.keys.keys()) == ["c"]
+
+    d = D()
+    assert not list(d._bindings.keys.keys())
+
+    e = E()
+    assert list(e._bindings.keys.keys()) == ["e"]
+
+
 def test_get_default_css():
     class A(DOMNode):
         pass
Repository: Textualize/textual

Issue:
DataTable - When cursor_coordinate updates, we should scroll_cursor_visible
When the cursor moves out of the screen, we should scroll the cursor into visibility.

It seems like there is already code to do this, but because of #2458 the scrolling doesn't work.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4acf4a76..56b6aff24 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ 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
+
+### Changed
+
+- The DataTable cursor is now scrolled into view when the cursor coordinate is changed programmatically https://github.com/Textualize/textual/issues/2459
+
 ## [0.23.0] - 2023-05-03
 
 ### Fixed
diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py
index 0681c3993..5991c98db 100644
--- a/src/textual/widgets/_data_table.py
+++ b/src/textual/widgets/_data_table.py
@@ -950,6 +950,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
             elif self.cursor_type == "column":
                 self.refresh_column(old_coordinate.column)
                 self._highlight_column(new_coordinate.column)
+            self._scroll_cursor_into_view()
 
     def _highlight_coordinate(self, coordinate: Coordinate) -> None:
         """Apply highlighting to the cell at the coordinate, and post event."""


Test patch:
diff --git a/tests/test_data_table.py b/tests/test_data_table.py
index 56b148d56..0bba677cf 100644
--- a/tests/test_data_table.py
+++ b/tests/test_data_table.py
@@ -991,3 +991,28 @@ def test_key_string_lookup():
     assert dictionary[RowKey("foo")] == "bar"
     assert dictionary["hello"] == "world"
     assert dictionary[RowKey("hello")] == "world"
+
+
+async def test_scrolling_cursor_into_view():
+    """Regression test for https://github.com/Textualize/textual/issues/2459"""
+
+    class TableApp(App):
+        CSS = "DataTable { height: 100%; }"
+
+        def compose(self):
+            yield DataTable()
+
+        def on_mount(self) -> None:
+            table = self.query_one(DataTable)
+            table.add_column("n")
+            table.add_rows([(n,) for n in range(300)])
+
+        def key_c(self):
+            self.query_one(DataTable).cursor_coordinate = Coordinate(200, 0)
+
+    app = TableApp()
+
+    async with app.run_test() as pilot:
+        await pilot.press("c")
+        await pilot.pause()
+        assert app.query_one(DataTable).scroll_y > 100
Repository: Textualize/textual

Issue:
App title and sub_title should be converted to strings
I'm thinking that `App.title` and `App.sub_title` should be converted to strings.

Balancing convenience with hiding errors, this is probably the right thing to do 90% of the time.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e65e54e2..2992a923b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,12 @@ 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
+
+## Unrealeased
+
+### Changed
+
+- App `title` and `sub_title` attributes can be set to any type https://github.com/Textualize/textual/issues/2521
 
 ### Fixed
 
diff --git a/src/textual/app.py b/src/textual/app.py
index 113a7a220..12f66cf04 100644
--- a/src/textual/app.py
+++ b/src/textual/app.py
@@ -316,6 +316,7 @@ class App(Generic[ReturnType], DOMNode):
         the name of the app if it doesn't.
 
         Assign a new value to this attribute to change the title.
+        The new value is always converted to string.
         """
 
         self.sub_title = self.SUB_TITLE if self.SUB_TITLE is not None else ""
@@ -328,6 +329,7 @@ class App(Generic[ReturnType], DOMNode):
         the file being worker on.
 
         Assign a new value to this attribute to change the sub-title.
+        The new value is always converted to string.
         """
 
         self._logger = Logger(self._log)
@@ -406,6 +408,14 @@ class App(Generic[ReturnType], DOMNode):
         self.set_class(self.dark, "-dark-mode")
         self.set_class(not self.dark, "-light-mode")
 
+    def validate_title(self, title: Any) -> str:
+        """Make sure the title is set to a string."""
+        return str(title)
+
+    def validate_sub_title(self, sub_title: Any) -> str:
+        """Make sure the sub-title is set to a string."""
+        return str(sub_title)
+
     @property
     def workers(self) -> WorkerManager:
         """The [worker](guide/workers/) manager.


Test patch:
diff --git a/tests/test_app.py b/tests/test_app.py
index e529cfbd7..54bde8221 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -36,3 +36,33 @@ async def test_hover_update_styles():
         # We've hovered, so ensure the pseudoclass is present and background changed
         assert button.pseudo_classes == {"enabled", "hover"}
         assert button.styles.background != initial_background
+
+
+def test_setting_title():
+    app = MyApp()
+    app.title = None
+    assert app.title == "None"
+
+    app.title = ""
+    assert app.title == ""
+
+    app.title = 0.125
+    assert app.title == "0.125"
+
+    app.title = [True, False, 2]
+    assert app.title == "[True, False, 2]"
+
+
+def test_setting_sub_title():
+    app = MyApp()
+    app.sub_title = None
+    assert app.sub_title == "None"
+
+    app.sub_title = ""
+    assert app.sub_title == ""
+
+    app.sub_title = 0.125
+    assert app.sub_title == "0.125"
+
+    app.sub_title = [True, False, 2]
+    assert app.sub_title == "[True, False, 2]"
Repository: Textualize/textual

Issue:
`Switch` should stop the `Click` event from bubbling
At the moment `Switch` handles `Click` but then lets it bubble; there's no good reason to do that and it also stops the ability to write something like this:

```python
from textual.app        import App, ComposeResult
from textual.containers import Horizontal
from textual.widgets    import Header, Footer, Label, Switch

class LabeledSwitch( Horizontal ):

    def on_click( self ) -> None:
        self.query_one(Switch).toggle()

class ClickableLabelApp( App[ None ] ):

    def compose( self ) -> ComposeResult:
        yield Header()
        with LabeledSwitch():
            yield Label( "Click me!" )
            yield Switch()
        yield Footer()

if __name__ == "__main__":
    ClickableLabelApp().run()
```

where the idea is to make a compound widget that lets you click on the `Label` or the `Switch` and the `Switch` will toggle -- only it doesn't work if you click on the `Switch` because it ends up double-toggling.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d9b2b217..bf9a3b79b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 - Fixed a crash when a `SelectionList` had a prompt wider than itself https://github.com/Textualize/textual/issues/2900
+- Fixed a bug where `Click` events were bubbling up from `Switch` widgets https://github.com/Textualize/textual/issues/2366
 
 ## [0.30.0] - 2023-07-17
 
diff --git a/src/textual/widgets/_switch.py b/src/textual/widgets/_switch.py
index d19ee168e..eb0568c61 100644
--- a/src/textual/widgets/_switch.py
+++ b/src/textual/widgets/_switch.py
@@ -154,8 +154,9 @@ class Switch(Widget, can_focus=True):
     def get_content_height(self, container: Size, viewport: Size, width: int) -> int:
         return 1
 
-    async def _on_click(self, _: Click) -> None:
+    async def _on_click(self, event: Click) -> None:
         """Toggle the state of the switch."""
+        event.stop()
         self.toggle()
 
     def action_toggle(self) -> None:


Test patch:
diff --git a/tests/test_switch.py b/tests/test_switch.py
new file mode 100644
index 000000000..e0868118f
--- /dev/null
+++ b/tests/test_switch.py
@@ -0,0 +1,18 @@
+from textual.app import App, ComposeResult
+from textual.widgets import Switch
+
+
+async def test_switch_click_doesnt_bubble_up():
+    """Regression test for https://github.com/Textualize/textual/issues/2366"""
+
+    class SwitchApp(App[None]):
+        def compose(self) -> ComposeResult:
+            yield Switch()
+
+        async def on_click(self) -> None:
+            raise AssertionError(
+                "The app should never receive a click event when Switch is clicked."
+            )
+
+    async with SwitchApp().run_test() as pilot:
+        await pilot.click(Switch)
Repository: Textualize/textual

Issue:
Binding for `]` renders incorrectly in `Footer`
The binding `Binding("ctrl+right_square_bracket", "toggle_indent_width", "Cycle indent width")` renders like this:

<img width="431" alt="image" src="https://github.com/Textualize/textual/assets/5740731/2c2bd6fa-288b-4205-aba0-48eb1b6c41e0">

It should probably render as `Ctrl+]`.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 705eb5dd3..8019a5f1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Ensuring `TextArea.SelectionChanged` message only sends when the updated selection is different https://github.com/Textualize/textual/pull/3933
 - Fixed declaration after nested rule set causing a parse error https://github.com/Textualize/textual/pull/4012
 - ID and class validation was too lenient https://github.com/Textualize/textual/issues/3954
+- Fixed display of keys when used in conjunction with other keys https://github.com/Textualize/textual/pull/3050
 
 ## [0.47.1] - 2023-01-05
 
@@ -437,7 +438,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 ## [0.33.0] - 2023-08-15
 
-
 ### Fixed
 
 - Fixed unintuitive sizing behaviour of TabbedContent https://github.com/Textualize/textual/issues/2411
diff --git a/src/textual/keys.py b/src/textual/keys.py
index ef32404d1..36da438d2 100644
--- a/src/textual/keys.py
+++ b/src/textual/keys.py
@@ -283,6 +283,9 @@ def _get_key_display(key: str) -> str:
     """Given a key (i.e. the `key` string argument to Binding __init__),
     return the value that should be displayed in the app when referring
     to this key (e.g. in the Footer widget)."""
+    if "+" in key:
+        return "+".join([_get_key_display(key) for key in key.split("+")])
+
     display_alias = KEY_DISPLAY_ALIASES.get(key)
     if display_alias:
         return display_alias


Test patch:
diff --git a/tests/test_keys.py b/tests/test_keys.py
index 9f13e17d1..3aac179fc 100644
--- a/tests/test_keys.py
+++ b/tests/test_keys.py
@@ -52,3 +52,20 @@ async def test_character_bindings():
 
 def test_get_key_display():
     assert _get_key_display("minus") == "-"
+
+
+def test_get_key_display_when_used_in_conjunction():
+    """Test a key display is the same if used in conjunction with another key.
+    For example, "ctrl+right_square_bracket" should display the bracket as "]",
+    the same as it would without the ctrl modifier.
+
+    Regression test for #3035 https://github.com/Textualize/textual/issues/3035
+    """
+
+    right_square_bracket = _get_key_display("right_square_bracket")
+    ctrl_right_square_bracket = _get_key_display("ctrl+right_square_bracket")
+    assert ctrl_right_square_bracket == f"CTRL+{right_square_bracket}"
+
+    left = _get_key_display("left")
+    ctrl_left = _get_key_display("ctrl+left")
+    assert ctrl_left == f"CTRL+{left}"
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.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35a0624f8..c401104de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ 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
+
+### Fixed
+
+- `Pilot.click`/`Pilot.hover` can't use `Screen` as a selector https://github.com/Textualize/textual/issues/3395
+
 ## [0.38.1] - 2023-09-21
 
 ### Fixed
diff --git a/src/textual/pilot.py b/src/textual/pilot.py
index c3c64d2e9..c15441d0b 100644
--- a/src/textual/pilot.py
+++ b/src/textual/pilot.py
@@ -100,7 +100,7 @@ class Pilot(Generic[ReturnType]):
         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 @@ class Pilot(Generic[ReturnType]):
         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
index d631146c7..322146127 100644
--- a/tests/test_pilot.py
+++ b/tests/test_pilot.py
@@ -52,3 +52,21 @@ async def test_pilot_exception_catching_action():
     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:
It is possible to give a widget an ID that can't be queried back
It is possible to give a widget an ID that can't then be queried back; for example:

```python
from textual.app import App, ComposeResult
from textual.widgets import Label

class BadIDApp(App[None]):

    def compose(self) -> ComposeResult:
        yield Label("Hello, World!", id="foo&")

    def on_mount(self) -> None:
        self.notify(f"{self.query_one('#foo&')}")

if __name__ == "__main__":
    BadIDApp().run()
```

Perhaps we should validate widget IDs when they are assigned?

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44a95d744..bc2657ad1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - `SelectionList` option IDs are usable as soon as the widget is instantiated https://github.com/Textualize/textual/issues/3903
 - Fix issue with `Strip.crop` when crop window start aligned with strip end https://github.com/Textualize/textual/pull/3998
 - Fixed Strip.crop_extend https://github.com/Textualize/textual/pull/4011
+- ID and class validation was too lenient https://github.com/Textualize/textual/issues/3954
 - Fixed a crash if the `TextArea` language was set but tree-sitter lanuage binaries were not installed https://github.com/Textualize/textual/issues/4045
 
 
diff --git a/src/textual/dom.py b/src/textual/dom.py
index 461e9acea..2664881d9 100644
--- a/src/textual/dom.py
+++ b/src/textual/dom.py
@@ -80,7 +80,7 @@ def check_identifiers(description: str, *names: str) -> None:
         description: Description of where identifier is used for error message.
         *names: Identifiers to check.
     """
-    match = _re_identifier.match
+    match = _re_identifier.fullmatch
     for name in names:
         if match(name) is None:
             raise BadIdentifier(


Test patch:
diff --git a/tests/test_dom.py b/tests/test_dom.py
index 6f06fb809..ea14a25bf 100644
--- a/tests/test_dom.py
+++ b/tests/test_dom.py
@@ -259,3 +259,24 @@ def test_walk_children_with_self_breadth(search):
     ]
 
     assert children == ["f", "e", "d", "c", "b", "a"]
+
+
+@pytest.mark.parametrize(
+    "identifier",
+    [
+        " bad",
+        "  terrible  ",
+        "worse!  ",
+        "&ampersand",
+        "amper&sand",
+        "ampersand&",
+        "2_leading_digits",
+        "água",  # water
+        "cão",  # dog
+        "@'/.23",
+    ],
+)
+def test_id_validation(identifier: str):
+    """Regression tests for https://github.com/Textualize/textual/issues/3954."""
+    with pytest.raises(BadIdentifier):
+        DOMNode(id=identifier)