Repository: fsspec/filesystem_spec

Issue:
glob("*") not returning any matches with ZipFileSystem
Using `glob("*")` does not return any matches with ZipFileSystem.

Example zipfile: [testdata.zip](https://github.com/fsspec/filesystem_spec/files/13559185/testdata.zip)
```
➜ unzip -l testdata.zip
Archive:  testdata.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  12-05-2023 11:50   001/
        0  12-05-2023 11:50   001/test1.txt
        0  12-05-2023 11:50   002/
        0  12-05-2023 11:50   002/test2.txt
        0  12-05-2023 11:50   003/
        0  12-05-2023 11:50   003/test3.txt
---------                     -------
        0                     6 files
```

Testscript:
```python
import fsspec
from fsspec.implementations.zip import ZipFileSystem

fs = ZipFileSystem("testdata.zip")
print(fs.glob("*"))

print(fsspec._version.get_versions())
```

Output with current fsspec (and also 2023.12.0):
```
[]
{'date': '2023-12-02T20:51:30-0500', 'dirty': False, 'error': None, 'full-revisionid': '5cf9cd952c5d276835d3caef9c32fcf69d55b10c', 'version': '2023.12.0'}
```

Output with fsspec 2023.10.0:
```
['001/', '002/', '003/']
{'date': '2023-10-21T13:35:51-0400', 'dirty': False, 'error': None, 'full-revisionid': 'e20f626b87b5bb87d223495a56aefd768272a7ca', 'version': '2023.10.0'}
```

Expected behaviour: Current version should give the same result as 2023.10.0.

Note: Using glob * with a LocalFileSystem works as expected in both versions.

Discussion:

Patch:
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index a2e0d5c..45aa171 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -1,6 +1,13 @@
 Changelog
 =========
 
+2023.12.1
+---------
+
+Fixes
+
+- Remove trailing "/" from directory names in zipFS (#1445)
+
 2023.12.0
 ---------
 
diff --git a/fsspec/archive.py b/fsspec/archive.py
index 9bdd8ef..cba8ad5 100644
--- a/fsspec/archive.py
+++ b/fsspec/archive.py
@@ -38,7 +38,7 @@ class AbstractArchiveFileSystem(AbstractFileSystem):
         self._get_dirs()
         path = self._strip_protocol(path)
         if path in {"", "/"} and self.dir_cache:
-            return {"name": "/", "type": "directory", "size": 0}
+            return {"name": "", "type": "directory", "size": 0}
         if path in self.dir_cache:
             return self.dir_cache[path]
         elif path + "/" in self.dir_cache:
@@ -64,7 +64,7 @@ class AbstractArchiveFileSystem(AbstractFileSystem):
                 # root directory entry
                 ppath = p.rstrip("/").split("/", 1)[0]
                 if ppath not in paths:
-                    out = {"name": ppath + "/", "size": 0, "type": "directory"}
+                    out = {"name": ppath, "size": 0, "type": "directory"}
                     paths[ppath] = out
         out = sorted(paths.values(), key=lambda _: _["name"])
         if detail:
diff --git a/fsspec/implementations/libarchive.py b/fsspec/implementations/libarchive.py
index 592e897..eb6f145 100644
--- a/fsspec/implementations/libarchive.py
+++ b/fsspec/implementations/libarchive.py
@@ -164,8 +164,7 @@ class LibArchiveFileSystem(AbstractArchiveFileSystem):
                     continue
                 self.dir_cache.update(
                     {
-                        dirname
-                        + "/": {"name": dirname + "/", "size": 0, "type": "directory"}
+                        dirname: {"name": dirname, "size": 0, "type": "directory"}
                         for dirname in self._all_dirnames(set(entry.name))
                     }
                 )
@@ -178,7 +177,7 @@ class LibArchiveFileSystem(AbstractArchiveFileSystem):
         # not in all formats), so get the directories names from the files names
         self.dir_cache.update(
             {
-                dirname + "/": {"name": dirname + "/", "size": 0, "type": "directory"}
+                dirname: {"name": dirname, "size": 0, "type": "directory"}
                 for dirname in self._all_dirnames(list_names)
             }
         )
diff --git a/fsspec/implementations/tar.py b/fsspec/implementations/tar.py
index 62bb58f..412e5ba 100644
--- a/fsspec/implementations/tar.py
+++ b/fsspec/implementations/tar.py
@@ -106,11 +106,12 @@ class TarFileSystem(AbstractArchiveFileSystem):
 
         # This enables ls to get directories as children as well as files
         self.dir_cache = {
-            dirname + "/": {"name": dirname + "/", "size": 0, "type": "directory"}
+            dirname: {"name": dirname, "size": 0, "type": "directory"}
             for dirname in self._all_dirnames(self.tar.getnames())
         }
         for member in self.tar.getmembers():
             info = member.get_info()
+            info["name"] = info["name"].rstrip("/")
             info["type"] = typemap.get(info["type"], "file")
             self.dir_cache[info["name"]] = info
 
diff --git a/fsspec/implementations/zip.py b/fsspec/implementations/zip.py
index f828a68..dae927b 100644
--- a/fsspec/implementations/zip.py
+++ b/fsspec/implementations/zip.py
@@ -83,7 +83,7 @@ class ZipFileSystem(AbstractArchiveFileSystem):
             # not read from the file.
             files = self.zip.infolist()
             self.dir_cache = {
-                dirname + "/": {"name": dirname + "/", "size": 0, "type": "directory"}
+                dirname: {"name": dirname, "size": 0, "type": "directory"}
                 for dirname in self._all_dirnames(self.zip.namelist())
             }
             for z in files:


Test patch:
diff --git a/fsspec/implementations/tests/test_archive.py b/fsspec/implementations/tests/test_archive.py
index bde42df..6caf790 100644
--- a/fsspec/implementations/tests/test_archive.py
+++ b/fsspec/implementations/tests/test_archive.py
@@ -276,10 +276,10 @@ class TestAnyArchive:
         with scenario.provider(archive_data) as archive:
             fs = fsspec.filesystem(scenario.protocol, fo=archive)
 
-            assert fs.ls("", detail=False) == ["a", "b", "deeply/"]
+            assert fs.ls("", detail=False) == ["a", "b", "deeply"]
             assert fs.ls("/") == fs.ls("")
 
-            assert fs.ls("deeply", detail=False) == ["deeply/nested/"]
+            assert fs.ls("deeply", detail=False) == ["deeply/nested"]
             assert fs.ls("deeply/") == fs.ls("deeply")
 
             assert fs.ls("deeply/nested", detail=False) == ["deeply/nested/path"]
@@ -293,8 +293,8 @@ class TestAnyArchive:
             assert fs.find("", withdirs=True) == [
                 "a",
                 "b",
-                "deeply/",
-                "deeply/nested/",
+                "deeply",
+                "deeply/nested",
                 "deeply/nested/path",
             ]
 
@@ -347,7 +347,7 @@ class TestAnyArchive:
             # Iterate over all directories.
             for d in fs._all_dirnames(archive_data.keys()):
                 lhs = project(fs.info(d), ["name", "size", "type"])
-                expected = {"name": f"{d}/", "size": 0, "type": "directory"}
+                expected = {"name": f"{d}", "size": 0, "type": "directory"}
                 assert lhs == expected
 
             # Iterate over all files.
diff --git a/fsspec/implementations/tests/test_tar.py b/fsspec/implementations/tests/test_tar.py
index 6a7aca4..23a4c2e 100644
--- a/fsspec/implementations/tests/test_tar.py
+++ b/fsspec/implementations/tests/test_tar.py
@@ -26,7 +26,7 @@ def test_info():
             lhs = fs.info(d)
             del lhs["chksum"]
             expected = {
-                "name": f"{d}/",
+                "name": f"{d}",
                 "size": 0,
                 "type": "directory",
                 "devmajor": 0,
@@ -234,10 +234,10 @@ def test_ls_with_folders(compression: str, tmp_path: Path):
         fs = TarFileSystem(fd)
         assert fs.find("/", withdirs=True) == [
             "a.pdf",
-            "b/",
+            "b",
             "b/c.pdf",
-            "d/",
-            "d/e/",
+            "d",
+            "d/e",
             "d/e/f.pdf",
             "d/g.pdf",
         ]
diff --git a/fsspec/implementations/tests/test_zip.py b/fsspec/implementations/tests/test_zip.py
index cd88d9d..3c6c50b 100644
--- a/fsspec/implementations/tests/test_zip.py
+++ b/fsspec/implementations/tests/test_zip.py
@@ -46,8 +46,8 @@ def test_not_cached():
 def test_root_info():
     with tempzip(archive_data) as z:
         fs = fsspec.filesystem("zip", fo=z)
-        assert fs.info("/") == {"name": "/", "type": "directory", "size": 0}
-        assert fs.info("") == {"name": "/", "type": "directory", "size": 0}
+        assert fs.info("/") == {"name": "", "type": "directory", "size": 0}
+        assert fs.info("") == {"name": "", "type": "directory", "size": 0}
 
 
 def test_write_seek(m):
@@ -83,3 +83,14 @@ def test_mapper(m):
         # fails because this is write mode and we cannot also read
         mapper["a"]
     assert "a" in mapper  # but be can list
+
+
+def test_zip_glob_star(m):
+    with fsspec.open(
+        "zip://adir/afile::memory://out.zip", mode="wb", zip={"mode": "w"}
+    ) as f:
+        f.write(b"data")
+
+    fs, _ = fsspec.core.url_to_fs("zip::memory://out.zip")
+    outfiles = fs.glob("*")
+    assert len(outfiles) == 1