2121
2222import pytest
2323
24+ from beets import util
2425from beets .dbcore import types
2526from beets .dbcore .query import (
2627 AndQuery ,
@@ -278,6 +279,23 @@ class TestPathQuery:
278279 and path separator detection across different platforms.
279280 """
280281
282+ @staticmethod
283+ def abs_query_path (path : str , trailing_sep : bool = False ) -> str :
284+ """Build a platform-correct absolute query path without normalizing it.
285+
286+ On Windows, leading-slash paths are drive-rooted but Python 3.13 no
287+ longer treats them as absolute. Prefix the current drive so explicit
288+ path queries stay absolute while preserving raw segments such as ``..``.
289+ """
290+ if os .path .__name__ == "ntpath" and path .startswith ("/" ):
291+ drive , _ = os .path .splitdrive (os .fsdecode (util .normpath (os .sep )))
292+ path = drive + path
293+
294+ path = path .replace ("/" , os .sep )
295+ if trailing_sep :
296+ path = os .path .join (path , "" )
297+ return path .replace ("\\ " , "\\ \\ " )
298+
281299 @pytest .fixture (scope = "class" )
282300 def lib (self , helper ):
283301 helper .add_item (path = b"/aaa/bb/c.mp3" , title = "path item" )
@@ -290,24 +308,32 @@ def lib(self, helper):
290308 return helper .lib
291309
292310 @pytest .mark .parametrize (
293- "q , expected_titles" ,
311+ "path , expected_titles, trailing_sep " ,
294312 [
295- _p ("path: /aaa/bb/c.mp3" , ["path item" ], id = "exact-match" ),
296- _p ("path: /aaa" , ["path item" ], id = "parent-dir-no-slash" ),
297- _p ("path: /aaa/ " , ["path item" ], id = "parent-dir-with-slash" ),
298- _p ("path: /aa" , [], id = "no-match-does-not-match-parent-dir" ),
299- _p ("path: /xyzzy/ " , [], id = "no-match" ),
300- _p ("path:/b/ " , [], id = "fragment-no-match" ),
301- _p ("path: /x/../aaa/bb" , ["path item" ], id = "non-normalized" ),
302- _p ("path::c \\ .mp3$" , ["path item" ], id = "regex" ),
303- _p ("path: /c/_" , ["with underscore" ], id = "underscore-escaped" ),
304- _p ("path: /c/%" , ["with percent" ], id = "percent-escaped" ),
305- _p ("path: /c/\\ \\ x" , ["with backslash" ], id = "backslash-escaped" ),
313+ _p ("/aaa/bb/c.mp3" , ["path item" ], False , id = "exact-match" ),
314+ _p ("/aaa" , ["path item" ], False , id = "parent-dir-no-slash" ),
315+ _p ("/aaa" , ["path item" ], True , id = "parent-dir-with-slash" ),
316+ _p ("/aa" , [], False , id = "no-match-does-not-match-parent-dir" ),
317+ _p ("/xyzzy" , [], True , id = "no-match" ),
318+ _p ("/b " , [], True , id = "fragment-no-match" ),
319+ _p ("/x/../aaa/bb" , ["path item" ], False , id = "non-normalized" ),
320+ _p (r"c\ .mp3$" , ["path item" ], False , id = "regex" ),
321+ _p ("/c/_" , ["with underscore" ], False , id = "underscore-escaped" ),
322+ _p ("/c/%" , ["with percent" ], False , id = "percent-escaped" ),
323+ _p (r" /c/\x" , ["with backslash" ], False , id = "backslash-escaped" ),
306324 ],
307325 )
308- def test_explicit (self , monkeypatch , lib , q , expected_titles ):
326+ def test_explicit (
327+ self , monkeypatch , lib , path , expected_titles , trailing_sep
328+ ):
309329 """Test explicit path queries with different path specifications."""
310330 monkeypatch .setattr ("beets.util.case_sensitive" , lambda * _ : True )
331+ if path == r"c\.mp3$" :
332+ q = f"path::{ path } "
333+ elif path == r"/c/\x" and os .path .__name__ != "ntpath" :
334+ q = r"path:/c/\\x"
335+ else :
336+ q = f"path:{ self .abs_query_path (path , trailing_sep = trailing_sep )} "
311337
312338 assert {i .title for i in lib .items (q )} == set (expected_titles )
313339
@@ -318,7 +344,6 @@ def test_absolute(self, lib, helper, query):
318344 item_path = helper .lib_path / "item.mp3"
319345 bytes_path = os .fsencode (item_path )
320346 helper .add_item (path = bytes_path , title = "absolute item" )
321- # Escape backslashes for Windows paths
322347 q = f"{ query } { item_path } " .replace ("\\ " , "\\ \\ " )
323348
324349 assert {i .title for i in lib .items (q )} == {"absolute item" }
@@ -360,7 +385,7 @@ def test_case_sensitivity(
360385 self , lib , monkeypatch , case_sensitive , expected_titles
361386 ):
362387 """Test path matching with different case sensitivity settings."""
363- q = "path:/a/b/c2.mp3"
388+ q = f "path:{ self . abs_query_path ( ' /a/b/c2.mp3' ) } "
364389 monkeypatch .setattr (
365390 "beets.util.case_sensitive" , lambda * _ : case_sensitive
366391 )
0 commit comments