fsspec 2025.5.0__tar.gz → 2025.7.0__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.7.0}/.github/workflows/main.yaml +2 -18
  2. {fsspec-2025.5.0 → fsspec-2025.7.0}/.pre-commit-config.yaml +2 -3
  3. {fsspec-2025.5.0 → fsspec-2025.7.0}/PKG-INFO +11 -3
  4. {fsspec-2025.5.0 → fsspec-2025.7.0}/README.md +8 -0
  5. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/api.rst +9 -0
  6. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/changelog.rst +40 -0
  7. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/_version.py +2 -2
  8. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/asyn.py +3 -16
  9. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/caching.py +2 -3
  10. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/compression.py +17 -10
  11. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/generic.py +4 -5
  12. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/gui.py +2 -1
  13. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/asyn_wrapper.py +1 -1
  14. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/cache_metadata.py +3 -2
  15. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/cached.py +47 -3
  16. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/ftp.py +1 -9
  17. fsspec-2025.7.0/fsspec/implementations/gist.py +232 -0
  18. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/git.py +1 -2
  19. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/github.py +0 -2
  20. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/http.py +24 -14
  21. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/local.py +5 -1
  22. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/memory.py +1 -2
  23. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/reference.py +1 -1
  24. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/json.py +6 -10
  25. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/registry.py +10 -2
  26. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/spec.py +4 -4
  27. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/utils.py +1 -3
  28. {fsspec-2025.5.0 → fsspec-2025.7.0}/pyproject.toml +4 -3
  29. fsspec-2025.5.0/ci/environment-typecheck.yml +0 -15
  30. {fsspec-2025.5.0 → fsspec-2025.7.0}/.codespellrc +0 -0
  31. {fsspec-2025.5.0 → fsspec-2025.7.0}/.coveragerc +0 -0
  32. {fsspec-2025.5.0 → fsspec-2025.7.0}/.gitattributes +0 -0
  33. {fsspec-2025.5.0 → fsspec-2025.7.0}/.github/workflows/pypipublish.yaml +0 -0
  34. {fsspec-2025.5.0 → fsspec-2025.7.0}/.gitignore +0 -0
  35. {fsspec-2025.5.0 → fsspec-2025.7.0}/LICENSE +0 -0
  36. {fsspec-2025.5.0 → fsspec-2025.7.0}/ci/environment-downstream.yml +0 -0
  37. {fsspec-2025.5.0 → fsspec-2025.7.0}/ci/environment-friends.yml +0 -0
  38. /fsspec-2025.5.0/ci/environment-py38.yml → /fsspec-2025.7.0/ci/environment-linux.yml +0 -0
  39. {fsspec-2025.5.0 → fsspec-2025.7.0}/ci/environment-win.yml +0 -0
  40. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/Makefile +0 -0
  41. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/README.md +0 -0
  42. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/environment.yml +0 -0
  43. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/make.bat +0 -0
  44. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/_static/custom.css +0 -0
  45. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/async.rst +0 -0
  46. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/conf.py +0 -0
  47. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/copying.rst +0 -0
  48. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/developer.rst +0 -0
  49. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/features.rst +0 -0
  50. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/img/gui.png +0 -0
  51. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/index.rst +0 -0
  52. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/intro.rst +0 -0
  53. {fsspec-2025.5.0 → fsspec-2025.7.0}/docs/source/usage.rst +0 -0
  54. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/__init__.py +0 -0
  55. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/archive.py +0 -0
  56. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/callbacks.py +0 -0
  57. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/config.py +0 -0
  58. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/conftest.py +0 -0
  59. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/core.py +0 -0
  60. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/dircache.py +0 -0
  61. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/exceptions.py +0 -0
  62. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/fuse.py +0 -0
  63. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/__init__.py +0 -0
  64. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/arrow.py +0 -0
  65. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/cache_mapper.py +0 -0
  66. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/dask.py +0 -0
  67. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/data.py +0 -0
  68. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/dbfs.py +0 -0
  69. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/dirfs.py +0 -0
  70. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/http_sync.py +0 -0
  71. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/jupyter.py +0 -0
  72. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/libarchive.py +0 -0
  73. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/sftp.py +0 -0
  74. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/smb.py +0 -0
  75. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/tar.py +0 -0
  76. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/webhdfs.py +0 -0
  77. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/implementations/zip.py +0 -0
  78. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/mapping.py +0 -0
  79. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/parquet.py +0 -0
  80. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/__init__.py +0 -0
  81. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/common.py +0 -0
  82. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/copy.py +0 -0
  83. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/get.py +0 -0
  84. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/mv.py +0 -0
  85. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/open.py +0 -0
  86. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/pipe.py +0 -0
  87. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/tests/abstract/put.py +0 -0
  88. {fsspec-2025.5.0 → fsspec-2025.7.0}/fsspec/transaction.py +0 -0
  89. {fsspec-2025.5.0 → fsspec-2025.7.0}/install_s3fs.sh +0 -0
  90. {fsspec-2025.5.0 → fsspec-2025.7.0}/readthedocs.yml +0 -0
  91. {fsspec-2025.5.0 → fsspec-2025.7.0}/setup.cfg +0 -0
@@ -32,7 +32,7 @@ jobs:
32
32
  - name: Setup conda
33
33
  uses: conda-incubator/setup-miniconda@v3
34
34
  with:
35
- environment-file: ci/environment-py38.yml
35
+ environment-file: ci/environment-linux.yml
36
36
  python-version: ${{ matrix.PY }}
37
37
 
38
38
  - name: Run Tests
@@ -43,7 +43,7 @@ jobs:
43
43
 
44
44
  win:
45
45
  name: pytest-win
46
- runs-on: windows-2019
46
+ runs-on: windows-2022
47
47
 
48
48
  env:
49
49
  CIRUN: true
@@ -75,22 +75,6 @@ jobs:
75
75
  python-version: "3.11"
76
76
  - uses: pre-commit/action@main
77
77
 
78
- # typecheck:
79
- # runs-on: ubuntu-latest
80
- # steps:
81
- # - name: Checkout
82
- # uses: actions/checkout@v4
83
- #
84
- # - name: Setup conda
85
- # uses: conda-incubator/setup-miniconda@v3
86
- # with:
87
- # environment-file: ci/environment-typecheck.yml
88
- #
89
- # - name: mypy
90
- # shell: bash -l {0}
91
- # run: |
92
- # mypy fsspec
93
- #
94
78
  downstream:
95
79
  name: downstream
96
80
  runs-on: ubuntu-24.04
@@ -13,11 +13,10 @@ repos:
13
13
  - id: check-json
14
14
  - id: check-yaml
15
15
  - repo: https://github.com/astral-sh/ruff-pre-commit
16
- # Ruff version.
17
- rev: v0.9.2
16
+ rev: v0.12.2
18
17
  hooks:
19
18
  # Run the linter.
20
- - id: ruff
19
+ - id: ruff-check
21
20
  args: [ --fix, "--show-fixes"]
22
21
  - id: ruff-format
23
22
  types_or: [python]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fsspec
3
- Version: 2025.5.0
3
+ Version: 2025.7.0
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/
@@ -58,7 +58,7 @@ Requires-Dist: dask; extra == 'dask'
58
58
  Requires-Dist: distributed; extra == 'dask'
59
59
  Provides-Extra: dev
60
60
  Requires-Dist: pre-commit; extra == 'dev'
61
- Requires-Dist: ruff; extra == 'dev'
61
+ Requires-Dist: ruff>=0.5; extra == 'dev'
62
62
  Provides-Extra: doc
63
63
  Requires-Dist: numpydoc; extra == 'doc'
64
64
  Requires-Dist: sphinx; extra == 'doc'
@@ -172,7 +172,7 @@ Requires-Dist: smbprotocol; extra == 'test-full'
172
172
  Requires-Dist: tqdm; extra == 'test-full'
173
173
  Requires-Dist: urllib3; extra == 'test-full'
174
174
  Requires-Dist: zarr; extra == 'test-full'
175
- Requires-Dist: zstandard; extra == 'test-full'
175
+ Requires-Dist: zstandard; (python_version < '3.14') and extra == 'test-full'
176
176
  Provides-Extra: tqdm
177
177
  Requires-Dist: tqdm; extra == 'tqdm'
178
178
  Description-Content-Type: text/markdown
@@ -275,3 +275,11 @@ filesystem_spec repository to setup pre-commit hooks. ``black`` will now be run
275
275
  before you commit, reformatting any changed files. You can format without
276
276
  committing via ``pre-commit run`` or skip these checks with ``git commit
277
277
  --no-verify``.
278
+
279
+ ## Support
280
+
281
+ Work on this repository is supported in part by:
282
+
283
+ "Anaconda, Inc. - Advancing AI through open source."
284
+
285
+ <a href="https://anaconda.com/"><img src="https://camo.githubusercontent.com/b8555ef2222598ed37ce38ac86955febbd25de7619931bb7dd3c58432181d3b6/68747470733a2f2f626565776172652e6f72672f636f6d6d756e6974792f6d656d626572732f616e61636f6e64612f616e61636f6e64612d6c617267652e706e67" alt="anaconda logo" width="40%"/></a>
@@ -96,3 +96,11 @@ filesystem_spec repository to setup pre-commit hooks. ``black`` will now be run
96
96
  before you commit, reformatting any changed files. You can format without
97
97
  committing via ``pre-commit run`` or skip these checks with ``git commit
98
98
  --no-verify``.
99
+
100
+ ## Support
101
+
102
+ Work on this repository is supported in part by:
103
+
104
+ "Anaconda, Inc. - Advancing AI through open source."
105
+
106
+ <a href="https://anaconda.com/"><img src="https://camo.githubusercontent.com/b8555ef2222598ed37ce38ac86955febbd25de7619931bb7dd3c58432181d3b6/68747470733a2f2f626565776172652e6f72672f636f6d6d756e6974792f6d656d626572732f616e61636f6e64612f616e61636f6e64612d6c617267652e706e67" alt="anaconda logo" width="40%"/></a>
@@ -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
 
@@ -231,6 +235,7 @@ documentation carefully before using any particular package.
231
235
  - `irods`_ for access to iRODS servers, with protocol "irods://"
232
236
  - `lakefs`_ for lakeFS data lakes, with protocol "lakefs://"
233
237
  - `morefs`_ for `OverlayFileSystem`, `DictFileSystem`, and others
238
+ - `obstore`_: zero-dependency access to Amazon S3, Google Cloud Storage, and Azure Blob Storage using the underlying Rust `object_store`_ library, with protocols "s3://", "gs://", and "abfs://".
234
239
  - `ocifs`_ for access to Oracle Cloud Object Storage, with protocol "oci://"
235
240
  - `ocilake`_ for OCI Data Lake storage
236
241
  - `ossfs`_ for Alibaba Cloud (Aliyun) Object Storage System (OSS)
@@ -246,6 +251,7 @@ documentation carefully before using any particular package.
246
251
  - `wandbfsspec`_ to access Weights & Biases (experimental)
247
252
  - `webdav4`_ for WebDAV, with protocol "webdav://" or "dav://"
248
253
  - `xrootd`_ for xrootd, with protocol "root://"
254
+ - `msgraphfs`_ for Microsoft storage (ie Sharepoint) using the drive API through Microsoft Graph, with protocol "msgd://"
249
255
 
250
256
  .. _abfs: https://github.com/dask/adlfs
251
257
  .. _adl: https://github.com/dask/adlfs
@@ -266,6 +272,8 @@ documentation carefully before using any particular package.
266
272
  .. _irods: https://github.com/xwcl/irods_fsspec
267
273
  .. _lakefs: https://github.com/aai-institute/lakefs-spec
268
274
  .. _morefs: https://github.com/iterative/morefs
275
+ .. _object_store: https://docs.rs/object_store/latest/object_store/
276
+ .. _obstore: https://developmentseed.org/obstore/latest/
269
277
  .. _ocifs: https://ocifs.readthedocs.io/en/latest/
270
278
  .. _ocilake: https://github.com/oracle/ocifs
271
279
  .. _ossfs: https://github.com/fsspec/ossfs
@@ -280,6 +288,7 @@ documentation carefully before using any particular package.
280
288
  .. _wandbfsspec: https://github.com/alvarobartt/wandbfsspec
281
289
  .. _webdav4: https://github.com/skshetry/webdav4
282
290
  .. _xrootd: https://github.com/CoffeaTeam/fsspec-xrootd
291
+ .. _msgraphfs: https://github.com/acsone/msgraphfs
283
292
 
284
293
  .. _readbuffering:
285
294
 
@@ -1,6 +1,46 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2025.7.0
5
+ --------
6
+
7
+ Enhancements
8
+
9
+ - only download HTML mime for http listing (#1889)
10
+ - add tos:// to registry (#1878)
11
+
12
+ Fixes
13
+
14
+ - use st_birthtime in localFS, if available (#1883)
15
+ - allow cat_* in simplecache (#1881)
16
+ - remove deprecated asyncio use (#1862)
17
+ - create event loop if it doesn't exist (#1857)
18
+
19
+ Other
20
+
21
+ - remove references to py38 (#1888)
22
+ - ruff updates (#1887, 1864)
23
+ - github rate limits in CI (#1879, 1877)
24
+ - acknowledge Anaconda support (#1876)
25
+ - add obstore to known implementations (#1875)
26
+ - add Microsoft storage to known implementations (#1853)
27
+ - use builtins zstd for py3.14 (#1874)
28
+ - gdrivefs -> gdrive_fsspec (#1858)
29
+ - windows version in CI (#1855)
30
+ - error message typo (#1854)
31
+
32
+
33
+ 2025.5.1
34
+ --------
35
+
36
+ Enhancements
37
+
38
+ - file system for GitHub gists (#1791)
39
+
40
+ Other
41
+
42
+ - doc fixes (#1847, 1848)
43
+
4
44
  2025.5.0
5
45
  --------
6
46
 
@@ -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.7.0'
21
+ __version_tuple__ = version_tuple = (2025, 7, 0)
@@ -7,9 +7,9 @@ import numbers
7
7
  import os
8
8
  import re
9
9
  import threading
10
- from contextlib import contextmanager
10
+ from collections.abc import Iterable
11
11
  from glob import has_magic
12
- from typing import TYPE_CHECKING, Iterable
12
+ from typing import TYPE_CHECKING
13
13
 
14
14
  from .callbacks import DEFAULT_CALLBACK
15
15
  from .exceptions import FSTimeoutError
@@ -120,18 +120,6 @@ def sync_wrapper(func, obj=None):
120
120
  return wrapper
121
121
 
122
122
 
123
- @contextmanager
124
- def _selector_policy():
125
- original_policy = asyncio.get_event_loop_policy()
126
- try:
127
- if os.name == "nt" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
128
- asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
129
-
130
- yield
131
- finally:
132
- asyncio.set_event_loop_policy(original_policy)
133
-
134
-
135
123
  def get_loop():
136
124
  """Create or return the default fsspec IO loop
137
125
 
@@ -142,8 +130,7 @@ def get_loop():
142
130
  # repeat the check just in case the loop got filled between the
143
131
  # previous two calls from another thread
144
132
  if loop[0] is None:
145
- with _selector_policy():
146
- loop[0] = asyncio.new_event_loop()
133
+ loop[0] = asyncio.new_event_loop()
147
134
  th = threading.Thread(target=loop[0].run_forever, name="fsspecIO")
148
135
  th.daemon = True
149
136
  th.start()
@@ -7,6 +7,7 @@ import math
7
7
  import os
8
8
  import threading
9
9
  import warnings
10
+ from collections import OrderedDict
10
11
  from concurrent.futures import Future, ThreadPoolExecutor
11
12
  from itertools import groupby
12
13
  from operator import itemgetter
@@ -17,8 +18,6 @@ from typing import (
17
18
  ClassVar,
18
19
  Generic,
19
20
  NamedTuple,
20
- Optional,
21
- OrderedDict,
22
21
  TypeVar,
23
22
  )
24
23
 
@@ -629,7 +628,7 @@ class KnownPartsOfAFile(BaseCache):
629
628
  blocksize: int,
630
629
  fetcher: Fetcher,
631
630
  size: int,
632
- data: Optional[dict[tuple[int, int], bytes]] = None,
631
+ data: dict[tuple[int, int], bytes] | None = None,
633
632
  strict: bool = True,
634
633
  **_: Any,
635
634
  ):
@@ -155,19 +155,26 @@ except ImportError:
155
155
  pass
156
156
 
157
157
  try:
158
- import zstandard as zstd
158
+ # zstd in the standard library for python >= 3.14
159
+ from compression.zstd import ZstdFile
159
160
 
160
- def zstandard_file(infile, mode="rb"):
161
- if "r" in mode:
162
- cctx = zstd.ZstdDecompressor()
163
- return cctx.stream_reader(infile)
164
- else:
165
- cctx = zstd.ZstdCompressor(level=10)
166
- return cctx.stream_writer(infile)
161
+ register_compression("zstd", ZstdFile, "zst")
167
162
 
168
- register_compression("zstd", zstandard_file, "zst")
169
163
  except ImportError:
170
- pass
164
+ try:
165
+ import zstandard as zstd
166
+
167
+ def zstandard_file(infile, mode="rb"):
168
+ if "r" in mode:
169
+ cctx = zstd.ZstdDecompressor()
170
+ return cctx.stream_reader(infile)
171
+ else:
172
+ cctx = zstd.ZstdCompressor(level=10)
173
+ return cctx.stream_writer(infile)
174
+
175
+ register_compression("zstd", zstandard_file, "zst")
176
+ except ImportError:
177
+ pass
171
178
 
172
179
 
173
180
  def available_compressions():
@@ -5,7 +5,6 @@ import logging
5
5
  import os
6
6
  import shutil
7
7
  import uuid
8
- from typing import Optional
9
8
 
10
9
  from .asyn import AsyncFileSystem, _run_coros_in_chunks, sync_wrapper
11
10
  from .callbacks import DEFAULT_CALLBACK
@@ -289,7 +288,7 @@ class GenericFileSystem(AsyncFileSystem):
289
288
  url2,
290
289
  blocksize=2**20,
291
290
  callback=DEFAULT_CALLBACK,
292
- tempdir: Optional[str] = None,
291
+ tempdir: str | None = None,
293
292
  **kwargs,
294
293
  ):
295
294
  fs = _resolve_fs(url, self.method)
@@ -319,9 +318,9 @@ class GenericFileSystem(AsyncFileSystem):
319
318
  path2: list[str],
320
319
  recursive: bool = False,
321
320
  on_error: str = "ignore",
322
- maxdepth: Optional[int] = None,
323
- batch_size: Optional[int] = None,
324
- tempdir: Optional[str] = None,
321
+ maxdepth: int | None = None,
322
+ batch_size: int | None = None,
323
+ tempdir: str | None = None,
325
324
  **kwargs,
326
325
  ):
327
326
  # TODO: special case for one FS being local, which can use get/put
@@ -3,7 +3,8 @@ import contextlib
3
3
  import logging
4
4
  import os
5
5
  import re
6
- from typing import ClassVar, Sequence
6
+ from collections.abc import Sequence
7
+ from typing import ClassVar
7
8
 
8
9
  import panel as pn
9
10
 
@@ -82,7 +82,7 @@ class AsyncFileSystemWrapper(AsyncFileSystem):
82
82
  continue
83
83
 
84
84
  method = getattr(self.sync_fs, method_name)
85
- if callable(method) and not asyncio.iscoroutinefunction(method):
85
+ if callable(method) and not inspect.iscoroutinefunction(method):
86
86
  async_method = async_wrapper(method, obj=self)
87
87
  setattr(self, f"_{method_name}", async_method)
88
88
 
@@ -14,13 +14,14 @@ except ImportError:
14
14
  import json
15
15
 
16
16
  if TYPE_CHECKING:
17
- from typing import Any, Dict, Iterator, Literal
17
+ from collections.abc import Iterator
18
+ from typing import Any, Literal
18
19
 
19
20
  from typing_extensions import TypeAlias
20
21
 
21
22
  from .cached import CachingFileSystem
22
23
 
23
- Detail: TypeAlias = Dict[str, Any]
24
+ Detail: TypeAlias = dict[str, Any]
24
25
 
25
26
 
26
27
  class CacheMetadata:
@@ -16,6 +16,7 @@ from fsspec.core import BaseCache, MMapCache
16
16
  from fsspec.exceptions import BlocksizeMismatchError
17
17
  from fsspec.implementations.cache_mapper import create_cache_mapper
18
18
  from fsspec.implementations.cache_metadata import CacheMetadata
19
+ from fsspec.implementations.local import LocalFileSystem
19
20
  from fsspec.spec import AbstractBufferedFile
20
21
  from fsspec.transaction import Transaction
21
22
  from fsspec.utils import infer_compression
@@ -338,7 +339,7 @@ class CachingFileSystem(AbstractFileSystem):
338
339
  # explicitly submitting the size to the open call will avoid extra
339
340
  # operations when opening. This is particularly relevant
340
341
  # for any file that is read over a network, e.g. S3.
341
- size = detail.get("size", None)
342
+ size = detail.get("size")
342
343
 
343
344
  # call target filesystems open
344
345
  self._mkcache()
@@ -433,7 +434,9 @@ class CachingFileSystem(AbstractFileSystem):
433
434
  "open",
434
435
  "cat",
435
436
  "cat_file",
437
+ "_cat_file",
436
438
  "cat_ranges",
439
+ "_cat_ranges",
437
440
  "get",
438
441
  "read_block",
439
442
  "tail",
@@ -835,14 +838,55 @@ class SimpleCacheFileSystem(WholeFileCacheFileSystem):
835
838
  else:
836
839
  raise ValueError("path must be str or dict")
837
840
 
841
+ async def _cat_file(self, path, start=None, end=None, **kwargs):
842
+ logger.debug("async cat_file %s", path)
843
+ path = self._strip_protocol(path)
844
+ sha = self._mapper(path)
845
+ fn = self._check_file(path)
846
+
847
+ if not fn:
848
+ fn = os.path.join(self.storage[-1], sha)
849
+ await self.fs._get_file(path, fn, **kwargs)
850
+
851
+ with open(fn, "rb") as f: # noqa ASYNC230
852
+ if start:
853
+ f.seek(start)
854
+ size = -1 if end is None else end - f.tell()
855
+ return f.read(size)
856
+
857
+ async def _cat_ranges(
858
+ self, paths, starts, ends, max_gap=None, on_error="return", **kwargs
859
+ ):
860
+ logger.debug("async cat ranges %s", paths)
861
+ lpaths = []
862
+ rset = set()
863
+ download = []
864
+ rpaths = []
865
+ for p in paths:
866
+ fn = self._check_file(p)
867
+ if fn is None and p not in rset:
868
+ sha = self._mapper(p)
869
+ fn = os.path.join(self.storage[-1], sha)
870
+ download.append(fn)
871
+ rset.add(p)
872
+ rpaths.append(p)
873
+ lpaths.append(fn)
874
+ if download:
875
+ await self.fs._get(rpaths, download, on_error=on_error)
876
+
877
+ return LocalFileSystem().cat_ranges(
878
+ lpaths, starts, ends, max_gap=max_gap, on_error=on_error, **kwargs
879
+ )
880
+
838
881
  def cat_ranges(
839
882
  self, paths, starts, ends, max_gap=None, on_error="return", **kwargs
840
883
  ):
884
+ logger.debug("cat ranges %s", paths)
841
885
  lpaths = [self._check_file(p) for p in paths]
842
886
  rpaths = [p for l, p in zip(lpaths, paths) if l is False]
843
887
  lpaths = [l for l, p in zip(lpaths, paths) if l is False]
844
888
  self.fs.get(rpaths, lpaths)
845
- return super().cat_ranges(
889
+ return LocalFileSystem().cat_ranges(
846
890
  paths, starts, ends, max_gap=max_gap, on_error=on_error, **kwargs
847
891
  )
848
892
 
@@ -940,7 +984,7 @@ class LocalTempFile:
940
984
 
941
985
  def commit(self):
942
986
  self.fs.put(self.fn, self.path, **self.kwargs)
943
- # we do not delete local copy - it's still in the cache
987
+ # we do not delete the local copy, it's still in the cache.
944
988
 
945
989
  @property
946
990
  def name(self):
@@ -1,7 +1,5 @@
1
1
  import os
2
- import sys
3
2
  import uuid
4
- import warnings
5
3
  from ftplib import FTP, FTP_TLS, Error, error_perm
6
4
  from typing import Any
7
5
 
@@ -81,13 +79,7 @@ class FTPFileSystem(AbstractFileSystem):
81
79
  ftp_cls = FTP_TLS
82
80
  else:
83
81
  ftp_cls = FTP
84
- if sys.version_info >= (3, 9):
85
- self.ftp = ftp_cls(timeout=self.timeout, encoding=self.encoding)
86
- elif self.encoding:
87
- warnings.warn("`encoding` not supported for python<3.9, ignoring")
88
- self.ftp = ftp_cls(timeout=self.timeout)
89
- else:
90
- self.ftp = ftp_cls(timeout=self.timeout)
82
+ self.ftp = ftp_cls(timeout=self.timeout, encoding=self.encoding)
91
83
  self.ftp.connect(self.host, self.port)
92
84
  self.ftp.login(*self.cred)
93
85