diff --git a/pylsp/_utils.py b/pylsp/_utils.py index c9eb6fb1..e9b5b4ff 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -96,7 +96,13 @@ def find_parents(root, path, names): # Split the relative by directory, generate all the parent directories, then check each of them. # This avoids running a loop that has different base-cases for unix/windows # e.g. /a/b and /a/b/c/d/e.py -> ['/a/b', 'c', 'd'] - dirs = [root] + os.path.relpath(os.path.dirname(path), root).split(os.path.sep) + try: + dirs = [root] + os.path.relpath(os.path.dirname(path), root).split(os.path.sep) + except ValueError: + # On Windows, relpath raises ValueError when path and root are on different mounts + # (e.g. a UNC share root vs a drive-letter path). Nothing to find in this case. + log.warning("Path %r not in %r", path, root) + return [] # Search each of /a/b/c, /a/b, /a while dirs: diff --git a/test/test_utils.py b/test/test_utils.py index 7ed6214f..e48449de 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -197,6 +197,19 @@ def test_find_parents(tmpdir) -> None: ] +def test_find_parents_cross_mount(tmpdir, monkeypatch) -> None: + """find_parents returns [] when root and path are on different mounts (Windows UNC).""" + import unittest.mock as mock + + subsubdir = tmpdir.ensure_dir("subdir", "subsubdir") + path = subsubdir.ensure("path.py") + + with mock.patch("os.path.relpath", side_effect=ValueError("path is on mount 'C:', start on mount '\\\\unc\\share'")): + result = _utils.find_parents(tmpdir.strpath, path.strpath, ["test.cfg"]) + + assert result == [] + + def test_merge_dicts() -> None: assert _utils.merge_dicts( {"a": True, "b": {"x": 123, "y": {"hello": "world"}}},