fsspec 2025.5.0__tar.gz → 2025.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. {fsspec-2025.5.0 → fsspec-2025.5.1}/PKG-INFO +1 -1
  2. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/api.rst +4 -0
  3. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/changelog.rst +11 -0
  4. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/_version.py +2 -2
  5. fsspec-2025.5.1/fsspec/implementations/gist.py +232 -0
  6. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/github.py +0 -2
  7. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/registry.py +4 -0
  8. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/spec.py +4 -4
  9. {fsspec-2025.5.0 → fsspec-2025.5.1}/.codespellrc +0 -0
  10. {fsspec-2025.5.0 → fsspec-2025.5.1}/.coveragerc +0 -0
  11. {fsspec-2025.5.0 → fsspec-2025.5.1}/.gitattributes +0 -0
  12. {fsspec-2025.5.0 → fsspec-2025.5.1}/.github/workflows/main.yaml +0 -0
  13. {fsspec-2025.5.0 → fsspec-2025.5.1}/.github/workflows/pypipublish.yaml +0 -0
  14. {fsspec-2025.5.0 → fsspec-2025.5.1}/.gitignore +0 -0
  15. {fsspec-2025.5.0 → fsspec-2025.5.1}/.pre-commit-config.yaml +0 -0
  16. {fsspec-2025.5.0 → fsspec-2025.5.1}/LICENSE +0 -0
  17. {fsspec-2025.5.0 → fsspec-2025.5.1}/README.md +0 -0
  18. {fsspec-2025.5.0 → fsspec-2025.5.1}/ci/environment-downstream.yml +0 -0
  19. {fsspec-2025.5.0 → fsspec-2025.5.1}/ci/environment-friends.yml +0 -0
  20. {fsspec-2025.5.0 → fsspec-2025.5.1}/ci/environment-py38.yml +0 -0
  21. {fsspec-2025.5.0 → fsspec-2025.5.1}/ci/environment-typecheck.yml +0 -0
  22. {fsspec-2025.5.0 → fsspec-2025.5.1}/ci/environment-win.yml +0 -0
  23. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/Makefile +0 -0
  24. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/README.md +0 -0
  25. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/environment.yml +0 -0
  26. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/make.bat +0 -0
  27. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/_static/custom.css +0 -0
  28. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/async.rst +0 -0
  29. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/conf.py +0 -0
  30. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/copying.rst +0 -0
  31. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/developer.rst +0 -0
  32. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/features.rst +0 -0
  33. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/img/gui.png +0 -0
  34. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/index.rst +0 -0
  35. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/intro.rst +0 -0
  36. {fsspec-2025.5.0 → fsspec-2025.5.1}/docs/source/usage.rst +0 -0
  37. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/__init__.py +0 -0
  38. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/archive.py +0 -0
  39. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/asyn.py +0 -0
  40. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/caching.py +0 -0
  41. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/callbacks.py +0 -0
  42. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/compression.py +0 -0
  43. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/config.py +0 -0
  44. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/conftest.py +0 -0
  45. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/core.py +0 -0
  46. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/dircache.py +0 -0
  47. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/exceptions.py +0 -0
  48. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/fuse.py +0 -0
  49. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/generic.py +0 -0
  50. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/gui.py +0 -0
  51. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/__init__.py +0 -0
  52. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/arrow.py +0 -0
  53. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/asyn_wrapper.py +0 -0
  54. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/cache_mapper.py +0 -0
  55. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/cache_metadata.py +0 -0
  56. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/cached.py +0 -0
  57. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/dask.py +0 -0
  58. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/data.py +0 -0
  59. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/dbfs.py +0 -0
  60. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/dirfs.py +0 -0
  61. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/ftp.py +0 -0
  62. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/git.py +0 -0
  63. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/http.py +0 -0
  64. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/http_sync.py +0 -0
  65. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/jupyter.py +0 -0
  66. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/libarchive.py +0 -0
  67. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/local.py +0 -0
  68. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/memory.py +0 -0
  69. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/reference.py +0 -0
  70. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/sftp.py +0 -0
  71. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/smb.py +0 -0
  72. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/tar.py +0 -0
  73. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/webhdfs.py +0 -0
  74. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/implementations/zip.py +0 -0
  75. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/json.py +0 -0
  76. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/mapping.py +0 -0
  77. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/parquet.py +0 -0
  78. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/__init__.py +0 -0
  79. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/common.py +0 -0
  80. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/copy.py +0 -0
  81. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/get.py +0 -0
  82. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/mv.py +0 -0
  83. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/open.py +0 -0
  84. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/pipe.py +0 -0
  85. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/tests/abstract/put.py +0 -0
  86. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/transaction.py +0 -0
  87. {fsspec-2025.5.0 → fsspec-2025.5.1}/fsspec/utils.py +0 -0
  88. {fsspec-2025.5.0 → fsspec-2025.5.1}/install_s3fs.sh +0 -0
  89. {fsspec-2025.5.0 → fsspec-2025.5.1}/pyproject.toml +0 -0
  90. {fsspec-2025.5.0 → fsspec-2025.5.1}/readthedocs.yml +0 -0
  91. {fsspec-2025.5.0 → fsspec-2025.5.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fsspec
3
- Version: 2025.5.0
3
+ Version: 2025.5.1
4
4
  Summary: File-system specification
5
5
  Project-URL: Changelog, https://filesystem-spec.readthedocs.io/en/latest/changelog.html
6
6
  Project-URL: Documentation, https://filesystem-spec.readthedocs.io/en/latest/
@@ -117,6 +117,7 @@ Built-in Implementations
117
117
  fsspec.implementations.dbfs.DatabricksFileSystem
118
118
  fsspec.implementations.dirfs.DirFileSystem
119
119
  fsspec.implementations.ftp.FTPFileSystem
120
+ fsspec.implementations.gist.GistFileSystem
120
121
  fsspec.implementations.git.GitFileSystem
121
122
  fsspec.implementations.github.GithubFileSystem
122
123
  fsspec.implementations.http.HTTPFileSystem
@@ -162,6 +163,9 @@ Built-in Implementations
162
163
  .. autoclass:: fsspec.implementations.ftp.FTPFileSystem
163
164
  :members: __init__
164
165
 
166
+ .. autoclass:: fsspec.implementations.gist.GistFileSystem
167
+ :members: __init__
168
+
165
169
  .. autoclass:: fsspec.implementations.git.GitFileSystem
166
170
  :members: __init__
167
171
 
@@ -1,6 +1,17 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2025.5.1
5
+ --------
6
+
7
+ Enhancements
8
+
9
+ - file system for GitHub gists (#1791)
10
+
11
+ Other
12
+
13
+ - doc fixes (#1847, 1848)
14
+
4
15
  2025.5.0
5
16
  --------
6
17
 
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2025.5.0'
21
- __version_tuple__ = version_tuple = (2025, 5, 0)
20
+ __version__ = version = '2025.5.1'
21
+ __version_tuple__ = version_tuple = (2025, 5, 1)
@@ -0,0 +1,232 @@
1
+ import requests
2
+
3
+ from ..spec import AbstractFileSystem
4
+ from ..utils import infer_storage_options
5
+ from .memory import MemoryFile
6
+
7
+
8
+ class GistFileSystem(AbstractFileSystem):
9
+ """
10
+ Interface to files in a single GitHub Gist.
11
+
12
+ Provides read-only access to a gist's files. Gists do not contain
13
+ subdirectories, so file listing is straightforward.
14
+
15
+ Parameters
16
+ ----------
17
+ gist_id : str
18
+ The ID of the gist you want to access (the long hex value from the URL).
19
+ filenames : list[str] (optional)
20
+ If provided, only make a file system representing these files, and do not fetch
21
+ the list of all files for this gist.
22
+ sha : str (optional)
23
+ If provided, fetch a particular revision of the gist. If omitted,
24
+ the latest revision is used.
25
+ username : str (optional)
26
+ GitHub username for authentication (required if token is given).
27
+ token : str (optional)
28
+ GitHub personal access token (required if username is given).
29
+ timeout : (float, float) or float, optional
30
+ Connect and read timeouts for requests (default 60s each).
31
+ kwargs : dict
32
+ Stored on `self.request_kw` and passed to `requests.get` when fetching Gist
33
+ metadata or reading ("opening") a file.
34
+ """
35
+
36
+ protocol = "gist"
37
+ gist_url = "https://api.github.com/gists/{gist_id}"
38
+ gist_rev_url = "https://api.github.com/gists/{gist_id}/{sha}"
39
+
40
+ def __init__(
41
+ self,
42
+ gist_id,
43
+ filenames=None,
44
+ sha=None,
45
+ username=None,
46
+ token=None,
47
+ timeout=None,
48
+ **kwargs,
49
+ ):
50
+ super().__init__()
51
+ self.gist_id = gist_id
52
+ self.filenames = filenames
53
+ self.sha = sha # revision of the gist (optional)
54
+ if (username is None) ^ (token is None):
55
+ # Both or neither must be set
56
+ if username or token:
57
+ raise ValueError("Auth requires both username and token, or neither.")
58
+ self.username = username
59
+ self.token = token
60
+ self.request_kw = kwargs
61
+ # Default timeouts to 60s connect/read if none provided
62
+ self.timeout = timeout if timeout is not None else (60, 60)
63
+
64
+ # We use a single-level "directory" cache, because a gist is essentially flat
65
+ self.dircache[""] = self._fetch_file_list()
66
+
67
+ @property
68
+ def kw(self):
69
+ """Auth parameters passed to 'requests' if we have username/token."""
70
+ if self.username is not None and self.token is not None:
71
+ return {"auth": (self.username, self.token), **self.request_kw}
72
+ return self.request_kw
73
+
74
+ def _fetch_gist_metadata(self):
75
+ """
76
+ Fetch the JSON metadata for this gist (possibly for a specific revision).
77
+ """
78
+ if self.sha:
79
+ url = self.gist_rev_url.format(gist_id=self.gist_id, sha=self.sha)
80
+ else:
81
+ url = self.gist_url.format(gist_id=self.gist_id)
82
+
83
+ r = requests.get(url, timeout=self.timeout, **self.kw)
84
+ if r.status_code == 404:
85
+ raise FileNotFoundError(
86
+ f"Gist not found: {self.gist_id}@{self.sha or 'latest'}"
87
+ )
88
+ r.raise_for_status()
89
+ return r.json()
90
+
91
+ def _fetch_file_list(self):
92
+ """
93
+ Returns a list of dicts describing each file in the gist. These get stored
94
+ in self.dircache[""].
95
+ """
96
+ meta = self._fetch_gist_metadata()
97
+ if self.filenames:
98
+ available_files = meta.get("files", {})
99
+ files = {}
100
+ for fn in self.filenames:
101
+ if fn not in available_files:
102
+ raise FileNotFoundError(fn)
103
+ files[fn] = available_files[fn]
104
+ else:
105
+ files = meta.get("files", {})
106
+
107
+ out = []
108
+ for fname, finfo in files.items():
109
+ if finfo is None:
110
+ # Occasionally GitHub returns a file entry with null if it was deleted
111
+ continue
112
+ # Build a directory entry
113
+ out.append(
114
+ {
115
+ "name": fname, # file's name
116
+ "type": "file", # gists have no subdirectories
117
+ "size": finfo.get("size", 0), # file size in bytes
118
+ "raw_url": finfo.get("raw_url"),
119
+ }
120
+ )
121
+ return out
122
+
123
+ @classmethod
124
+ def _strip_protocol(cls, path):
125
+ """
126
+ Remove 'gist://' from the path, if present.
127
+ """
128
+ # The default infer_storage_options can handle gist://username:token@id/file
129
+ # or gist://id/file, but let's ensure we handle a normal usage too.
130
+ # We'll just strip the protocol prefix if it exists.
131
+ path = infer_storage_options(path).get("path", path)
132
+ return path.lstrip("/")
133
+
134
+ @staticmethod
135
+ def _get_kwargs_from_urls(path):
136
+ """
137
+ Parse 'gist://' style URLs into GistFileSystem constructor kwargs.
138
+ For example:
139
+ gist://:TOKEN@<gist_id>/file.txt
140
+ gist://username:TOKEN@<gist_id>/file.txt
141
+ """
142
+ so = infer_storage_options(path)
143
+ out = {}
144
+ if "username" in so and so["username"]:
145
+ out["username"] = so["username"]
146
+ if "password" in so and so["password"]:
147
+ out["token"] = so["password"]
148
+ if "host" in so and so["host"]:
149
+ # We interpret 'host' as the gist ID
150
+ out["gist_id"] = so["host"]
151
+
152
+ # Extract SHA and filename from path
153
+ if "path" in so and so["path"]:
154
+ path_parts = so["path"].rsplit("/", 2)[-2:]
155
+ if len(path_parts) == 2:
156
+ if path_parts[0]: # SHA present
157
+ out["sha"] = path_parts[0]
158
+ if path_parts[1]: # filename also present
159
+ out["filenames"] = [path_parts[1]]
160
+
161
+ return out
162
+
163
+ def ls(self, path="", detail=False, **kwargs):
164
+ """
165
+ List files in the gist. Gists are single-level, so any 'path' is basically
166
+ the filename, or empty for all files.
167
+
168
+ Parameters
169
+ ----------
170
+ path : str, optional
171
+ The filename to list. If empty, returns all files in the gist.
172
+ detail : bool, default False
173
+ If True, return a list of dicts; if False, return a list of filenames.
174
+ """
175
+ path = self._strip_protocol(path or "")
176
+ # If path is empty, return all
177
+ if path == "":
178
+ results = self.dircache[""]
179
+ else:
180
+ # We want just the single file with this name
181
+ all_files = self.dircache[""]
182
+ results = [f for f in all_files if f["name"] == path]
183
+ if not results:
184
+ raise FileNotFoundError(path)
185
+ if detail:
186
+ return results
187
+ else:
188
+ return sorted(f["name"] for f in results)
189
+
190
+ def _open(self, path, mode="rb", block_size=None, **kwargs):
191
+ """
192
+ Read a single file from the gist.
193
+ """
194
+ if mode != "rb":
195
+ raise NotImplementedError("GitHub Gist FS is read-only (no write).")
196
+
197
+ path = self._strip_protocol(path)
198
+ # Find the file entry in our dircache
199
+ matches = [f for f in self.dircache[""] if f["name"] == path]
200
+ if not matches:
201
+ raise FileNotFoundError(path)
202
+ finfo = matches[0]
203
+
204
+ raw_url = finfo.get("raw_url")
205
+ if not raw_url:
206
+ raise FileNotFoundError(f"No raw_url for file: {path}")
207
+
208
+ r = requests.get(raw_url, timeout=self.timeout, **self.kw)
209
+ if r.status_code == 404:
210
+ raise FileNotFoundError(path)
211
+ r.raise_for_status()
212
+ return MemoryFile(path, None, r.content)
213
+
214
+ def cat(self, path, recursive=False, on_error="raise", **kwargs):
215
+ """
216
+ Return {path: contents} for the given file or files. If 'recursive' is True,
217
+ and path is empty, returns all files in the gist.
218
+ """
219
+ paths = self.expand_path(path, recursive=recursive)
220
+ out = {}
221
+ for p in paths:
222
+ try:
223
+ with self.open(p, "rb") as f:
224
+ out[p] = f.read()
225
+ except FileNotFoundError as e:
226
+ if on_error == "raise":
227
+ raise e
228
+ elif on_error == "omit":
229
+ pass # skip
230
+ else:
231
+ out[p] = e
232
+ return out
@@ -7,8 +7,6 @@ from ..spec import AbstractFileSystem
7
7
  from ..utils import infer_storage_options
8
8
  from .memory import MemoryFile
9
9
 
10
- # TODO: add GIST backend, would be very similar
11
-
12
10
 
13
11
  class GithubFileSystem(AbstractFileSystem):
14
12
  """Interface to files in github
@@ -122,6 +122,10 @@ known_implementations = {
122
122
  "err": "Please install gdrivefs for access to Google Drive",
123
123
  },
124
124
  "generic": {"class": "fsspec.generic.GenericFileSystem"},
125
+ "gist": {
126
+ "class": "fsspec.implementations.gist.GistFileSystem",
127
+ "err": "Install the requests package to use the gist FS",
128
+ },
125
129
  "git": {
126
130
  "class": "fsspec.implementations.git.GitFileSystem",
127
131
  "err": "Install pygit2 to browse local git repos",
@@ -557,9 +557,9 @@ class AbstractFileSystem(metaclass=_Cached):
557
557
  path: str
558
558
  The glob pattern to match against
559
559
  maxdepth: int or None
560
- Maximum depth for '**' patterns. Applied on the first '**' found.
560
+ Maximum depth for ``'**'`` patterns. Applied on the first ``'**'`` found.
561
561
  Must be at least 1 if provided.
562
- **kwargs:
562
+ kwargs:
563
563
  Additional arguments passed to ``find`` (e.g., detail=True)
564
564
 
565
565
  Returns
@@ -570,7 +570,7 @@ class AbstractFileSystem(metaclass=_Cached):
570
570
  -----
571
571
  Supported patterns:
572
572
  - '*': Matches any sequence of characters within a single directory level
573
- - '**': Matches any number of directory levels (must be an entire path component)
573
+ - ``'**'``: Matches any number of directory levels (must be an entire path component)
574
574
  - '?': Matches exactly one character
575
575
  - '[abc]': Matches any character in the set
576
576
  - '[a-z]': Matches any character in the range
@@ -584,7 +584,7 @@ class AbstractFileSystem(metaclass=_Cached):
584
584
  - Special characters in character classes are escaped properly
585
585
 
586
586
  Limitations:
587
- - '**' must be a complete path component (e.g., 'a/**/b', not 'a**b')
587
+ - ``'**'`` must be a complete path component (e.g., ``'a/**/b'``, not ``'a**b'``)
588
588
  - No brace expansion ('{a,b}.txt')
589
589
  - No extended glob patterns ('+(pattern)', '!(pattern)')
590
590
  """
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes