diff --git a/sshfs/spec.py b/sshfs/spec.py index 5c1fc73..2a2ef6b 100644 --- a/sshfs/spec.py +++ b/sshfs/spec.py @@ -84,7 +84,14 @@ def __init__( def _strip_protocol(cls, path): # Remove components such as host and username from path. inferred_path = infer_storage_options(path)["path"] - return super()._strip_protocol(inferred_path) + stripped = super()._strip_protocol(inferred_path) + # super() collapses a bare "/" to the (empty) root_marker. Restore it + # only when the caller explicitly supplied the absolute root, so that + # walk("/") lists from the root while relative paths (and "") still + # resolve against the home directory. + if not stripped and inferred_path.startswith("/"): + return "/" + return stripped @staticmethod def _get_kwargs_from_urls(urlpath): diff --git a/tests/test_sshfs.py b/tests/test_sshfs.py index e55639c..d9bc45e 100644 --- a/tests/test_sshfs.py +++ b/tests/test_sshfs.py @@ -203,6 +203,48 @@ def test_ls(fs, remote_dir): assert dirs == expected +def test_walk(fs, remote_dir): + fs.mkdir(remote_dir + "/a") + fs.mkdir(remote_dir + "/a/b") + fs.touch(remote_dir + "/a/f1") + fs.touch(remote_dir + "/a/b/f2") + + result = { + root: (sorted(dirs), sorted(files)) + for root, dirs, files in fs.walk(remote_dir + "/a") + } + assert result == { + remote_dir + "/a": (["b"], ["f1"]), + remote_dir + "/a/b": ([], ["f2"]), + } + + +def test_walk_root(fs): + # Regression: SSHFileSystem._strip_protocol("/") used to collapse the + # root to "", so walk("/") listed the home directory and yielded + # relative paths that were not considered to exist. + root, dirs, files = next(iter(fs.walk("/", maxdepth=1, detail=True))) + assert root == "/" + for info in dirs.values(): + assert info["name"].startswith("/") + assert fs.exists(info["name"]) + + +def test_strip_protocol(): + strip = SSHFileSystem._strip_protocol + # An explicit absolute root is preserved so walk("/") lists from the root. + assert strip("/") == "/" + assert strip("ssh://host/") == "/" + # Empty and relative paths still resolve against the home directory + # instead of being redirected to the root. + assert strip("") == "" + assert strip("ssh://host") == "" + assert strip("foo/bar") == "foo/bar" + # Regular absolute paths are unaffected. + assert strip("/foo/bar") == "/foo/bar" + assert strip("ssh://host/foo/bar") == "/foo/bar" + + def test_mkdir(fs, remote_dir): fs.mkdir(remote_dir + "dir/") assert fs.isdir(remote_dir + "dir/")