fsspec 2024.5.0__tar.gz → 2024.6.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 (145) hide show
  1. {fsspec-2024.5.0 → fsspec-2024.6.0}/.pre-commit-config.yaml +1 -1
  2. {fsspec-2024.5.0 → fsspec-2024.6.0}/PKG-INFO +11 -5
  3. {fsspec-2024.5.0 → fsspec-2024.6.0}/README.md +4 -4
  4. fsspec-2024.6.0/docs/environment.yml +5 -0
  5. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/changelog.rst +20 -0
  6. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/developer.rst +2 -2
  7. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/index.rst +1 -1
  8. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/_version.py +2 -2
  9. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/caching.py +3 -2
  10. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/compression.py +1 -1
  11. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/cached.py +1 -13
  12. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/github.py +12 -0
  13. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/reference.py +6 -0
  14. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/smb.py +10 -0
  15. fsspec-2024.6.0/fsspec/json.py +81 -0
  16. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/registry.py +24 -18
  17. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/spec.py +76 -34
  18. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/utils.py +1 -1
  19. {fsspec-2024.5.0 → fsspec-2024.6.0}/pyproject.toml +33 -1
  20. {fsspec-2024.5.0 → fsspec-2024.6.0}/readthedocs.yml +2 -0
  21. fsspec-2024.5.0/docs/environment.yml +0 -11
  22. fsspec-2024.5.0/fsspec/implementations/tests/__init__.py +0 -0
  23. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_file_listing.yaml +0 -112
  24. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_mkdir.yaml +0 -582
  25. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_read_pyarrow_non_partitioned.yaml +0 -873
  26. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_read_range.yaml +0 -458
  27. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_read_range_chunked.yaml +0 -1355
  28. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_write_and_read.yaml +0 -795
  29. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_write_pyarrow_non_partitioned.yaml +0 -613
  30. fsspec-2024.5.0/fsspec/implementations/tests/conftest.py +0 -39
  31. fsspec-2024.5.0/fsspec/implementations/tests/local/__init__.py +0 -0
  32. fsspec-2024.5.0/fsspec/implementations/tests/local/local_fixtures.py +0 -18
  33. fsspec-2024.5.0/fsspec/implementations/tests/local/local_test.py +0 -14
  34. fsspec-2024.5.0/fsspec/implementations/tests/memory/__init__.py +0 -0
  35. fsspec-2024.5.0/fsspec/implementations/tests/memory/memory_fixtures.py +0 -27
  36. fsspec-2024.5.0/fsspec/implementations/tests/memory/memory_test.py +0 -14
  37. fsspec-2024.5.0/fsspec/implementations/tests/out.zip +0 -0
  38. fsspec-2024.5.0/fsspec/implementations/tests/test_archive.py +0 -382
  39. fsspec-2024.5.0/fsspec/implementations/tests/test_arrow.py +0 -259
  40. fsspec-2024.5.0/fsspec/implementations/tests/test_cached.py +0 -1306
  41. fsspec-2024.5.0/fsspec/implementations/tests/test_common.py +0 -35
  42. fsspec-2024.5.0/fsspec/implementations/tests/test_dask.py +0 -29
  43. fsspec-2024.5.0/fsspec/implementations/tests/test_data.py +0 -20
  44. fsspec-2024.5.0/fsspec/implementations/tests/test_dbfs.py +0 -268
  45. fsspec-2024.5.0/fsspec/implementations/tests/test_dirfs.py +0 -588
  46. fsspec-2024.5.0/fsspec/implementations/tests/test_ftp.py +0 -178
  47. fsspec-2024.5.0/fsspec/implementations/tests/test_git.py +0 -76
  48. fsspec-2024.5.0/fsspec/implementations/tests/test_http.py +0 -577
  49. fsspec-2024.5.0/fsspec/implementations/tests/test_jupyter.py +0 -57
  50. fsspec-2024.5.0/fsspec/implementations/tests/test_libarchive.py +0 -33
  51. fsspec-2024.5.0/fsspec/implementations/tests/test_local.py +0 -1285
  52. fsspec-2024.5.0/fsspec/implementations/tests/test_memory.py +0 -382
  53. fsspec-2024.5.0/fsspec/implementations/tests/test_reference.py +0 -720
  54. fsspec-2024.5.0/fsspec/implementations/tests/test_sftp.py +0 -233
  55. fsspec-2024.5.0/fsspec/implementations/tests/test_smb.py +0 -139
  56. fsspec-2024.5.0/fsspec/implementations/tests/test_tar.py +0 -243
  57. fsspec-2024.5.0/fsspec/implementations/tests/test_webhdfs.py +0 -197
  58. fsspec-2024.5.0/fsspec/implementations/tests/test_zip.py +0 -134
  59. fsspec-2024.5.0/fsspec/tests/__init__.py +0 -0
  60. fsspec-2024.5.0/fsspec/tests/conftest.py +0 -188
  61. fsspec-2024.5.0/fsspec/tests/data/listing.html +0 -1
  62. fsspec-2024.5.0/fsspec/tests/test_api.py +0 -498
  63. fsspec-2024.5.0/fsspec/tests/test_async.py +0 -230
  64. fsspec-2024.5.0/fsspec/tests/test_caches.py +0 -255
  65. fsspec-2024.5.0/fsspec/tests/test_callbacks.py +0 -89
  66. fsspec-2024.5.0/fsspec/tests/test_compression.py +0 -164
  67. fsspec-2024.5.0/fsspec/tests/test_config.py +0 -129
  68. fsspec-2024.5.0/fsspec/tests/test_core.py +0 -466
  69. fsspec-2024.5.0/fsspec/tests/test_downstream.py +0 -40
  70. fsspec-2024.5.0/fsspec/tests/test_file.py +0 -200
  71. fsspec-2024.5.0/fsspec/tests/test_fuse.py +0 -147
  72. fsspec-2024.5.0/fsspec/tests/test_generic.py +0 -90
  73. fsspec-2024.5.0/fsspec/tests/test_gui.py +0 -23
  74. fsspec-2024.5.0/fsspec/tests/test_mapping.py +0 -228
  75. fsspec-2024.5.0/fsspec/tests/test_parquet.py +0 -140
  76. fsspec-2024.5.0/fsspec/tests/test_registry.py +0 -134
  77. fsspec-2024.5.0/fsspec/tests/test_spec.py +0 -1167
  78. fsspec-2024.5.0/fsspec/tests/test_utils.py +0 -478
  79. {fsspec-2024.5.0 → fsspec-2024.6.0}/.codespellrc +0 -0
  80. {fsspec-2024.5.0 → fsspec-2024.6.0}/.coveragerc +0 -0
  81. {fsspec-2024.5.0 → fsspec-2024.6.0}/.gitattributes +0 -0
  82. {fsspec-2024.5.0 → fsspec-2024.6.0}/.github/workflows/codespell.yml +0 -0
  83. {fsspec-2024.5.0 → fsspec-2024.6.0}/.github/workflows/main.yaml +0 -0
  84. {fsspec-2024.5.0 → fsspec-2024.6.0}/.github/workflows/pypipublish.yaml +0 -0
  85. {fsspec-2024.5.0 → fsspec-2024.6.0}/.gitignore +0 -0
  86. {fsspec-2024.5.0 → fsspec-2024.6.0}/LICENSE +0 -0
  87. {fsspec-2024.5.0 → fsspec-2024.6.0}/ci/environment-downstream.yml +0 -0
  88. {fsspec-2024.5.0 → fsspec-2024.6.0}/ci/environment-friends.yml +0 -0
  89. {fsspec-2024.5.0 → fsspec-2024.6.0}/ci/environment-py38.yml +0 -0
  90. {fsspec-2024.5.0 → fsspec-2024.6.0}/ci/environment-typecheck.yml +0 -0
  91. {fsspec-2024.5.0 → fsspec-2024.6.0}/ci/environment-win.yml +0 -0
  92. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/Makefile +0 -0
  93. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/README.md +0 -0
  94. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/make.bat +0 -0
  95. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/_static/custom.css +0 -0
  96. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/api.rst +0 -0
  97. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/async.rst +0 -0
  98. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/conf.py +0 -0
  99. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/copying.rst +0 -0
  100. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/features.rst +0 -0
  101. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/img/gui.png +0 -0
  102. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/intro.rst +0 -0
  103. {fsspec-2024.5.0 → fsspec-2024.6.0}/docs/source/usage.rst +0 -0
  104. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/__init__.py +0 -0
  105. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/archive.py +0 -0
  106. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/asyn.py +0 -0
  107. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/callbacks.py +0 -0
  108. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/config.py +0 -0
  109. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/conftest.py +0 -0
  110. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/core.py +0 -0
  111. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/dircache.py +0 -0
  112. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/exceptions.py +0 -0
  113. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/fuse.py +0 -0
  114. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/generic.py +0 -0
  115. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/gui.py +0 -0
  116. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/__init__.py +0 -0
  117. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/arrow.py +0 -0
  118. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/cache_mapper.py +0 -0
  119. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/cache_metadata.py +0 -0
  120. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/dask.py +0 -0
  121. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/data.py +0 -0
  122. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/dbfs.py +0 -0
  123. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/dirfs.py +0 -0
  124. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/ftp.py +0 -0
  125. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/git.py +0 -0
  126. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/http.py +0 -0
  127. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/jupyter.py +0 -0
  128. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/libarchive.py +0 -0
  129. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/local.py +0 -0
  130. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/memory.py +0 -0
  131. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/sftp.py +0 -0
  132. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/tar.py +0 -0
  133. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/webhdfs.py +0 -0
  134. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/implementations/zip.py +0 -0
  135. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/mapping.py +0 -0
  136. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/parquet.py +0 -0
  137. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/tests/abstract/__init__.py +0 -0
  138. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/tests/abstract/common.py +0 -0
  139. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/tests/abstract/copy.py +0 -0
  140. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/tests/abstract/get.py +0 -0
  141. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/tests/abstract/mv.py +0 -0
  142. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/tests/abstract/put.py +0 -0
  143. {fsspec-2024.5.0 → fsspec-2024.6.0}/fsspec/transaction.py +0 -0
  144. {fsspec-2024.5.0 → fsspec-2024.6.0}/install_s3fs.sh +0 -0
  145. {fsspec-2024.5.0 → fsspec-2024.6.0}/setup.cfg +0 -0
@@ -14,7 +14,7 @@ repos:
14
14
  - id: check-yaml
15
15
  - repo: https://github.com/astral-sh/ruff-pre-commit
16
16
  # Ruff version.
17
- rev: v0.3.4
17
+ rev: v0.4.4
18
18
  hooks:
19
19
  # Run the linter.
20
20
  - id: ruff
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fsspec
3
- Version: 2024.5.0
3
+ Version: 2024.6.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/
@@ -59,6 +59,12 @@ Requires-Dist: distributed; extra == 'dask'
59
59
  Provides-Extra: dev
60
60
  Requires-Dist: pre-commit; extra == 'dev'
61
61
  Requires-Dist: ruff; extra == 'dev'
62
+ Provides-Extra: doc
63
+ Requires-Dist: numpydoc; extra == 'doc'
64
+ Requires-Dist: sphinx; extra == 'doc'
65
+ Requires-Dist: sphinx-design; extra == 'doc'
66
+ Requires-Dist: sphinx-rtd-theme; extra == 'doc'
67
+ Requires-Dist: yarl; extra == 'doc'
62
68
  Provides-Extra: dropbox
63
69
  Requires-Dist: dropbox; extra == 'dropbox'
64
70
  Requires-Dist: dropboxdrivefs; extra == 'dropbox'
@@ -178,7 +184,7 @@ Description-Content-Type: text/markdown
178
184
  [![Anaconda-Server Badge](https://anaconda.org/conda-forge/fsspec/badges/version.svg)](https://anaconda.org/conda-forge/fsspec)
179
185
  ![Build](https://github.com/fsspec/filesystem_spec/workflows/CI/badge.svg)
180
186
  [![Docs](https://readthedocs.org/projects/filesystem-spec/badge/?version=latest)](https://filesystem-spec.readthedocs.io/en/latest/?badge=latest)
181
- [![PyPi downloads](https://img.shields.io/pypi/dm/fsspec?label=pypi%20downloads&style=flat)](https://pepy.tech/project/fsspec)
187
+ [![fsspec Downloads Last Month](https://assets.piptrends.com/get-last-month-downloads-badge/fsspec.svg 'fsspec Downloads Last Month by pip Trends')](https://piptrends.com/package/fsspec)
182
188
 
183
189
  A specification for pythonic filesystems.
184
190
 
@@ -225,13 +231,13 @@ CI runtime. For local use, pick a version suitable for you.
225
231
  mamba create -n fsspec -c conda-forge python=3.9 -y
226
232
  conda activate fsspec
227
233
 
228
- # Standard dev test install.
229
- pip install -e ".[dev,test]"
234
+ # Standard dev install with docs and tests.
235
+ pip install -e ".[dev,doc,test]"
230
236
 
231
237
  # Full tests except for downstream
232
238
  pip install s3fs
233
239
  pip uninstall s3fs
234
- pip install -e .[dev,test_full]
240
+ pip install -e .[dev,doc,test_full]
235
241
  pip install s3fs --no-deps
236
242
  pytest -v
237
243
 
@@ -4,7 +4,7 @@
4
4
  [![Anaconda-Server Badge](https://anaconda.org/conda-forge/fsspec/badges/version.svg)](https://anaconda.org/conda-forge/fsspec)
5
5
  ![Build](https://github.com/fsspec/filesystem_spec/workflows/CI/badge.svg)
6
6
  [![Docs](https://readthedocs.org/projects/filesystem-spec/badge/?version=latest)](https://filesystem-spec.readthedocs.io/en/latest/?badge=latest)
7
- [![PyPi downloads](https://img.shields.io/pypi/dm/fsspec?label=pypi%20downloads&style=flat)](https://pepy.tech/project/fsspec)
7
+ [![fsspec Downloads Last Month](https://assets.piptrends.com/get-last-month-downloads-badge/fsspec.svg 'fsspec Downloads Last Month by pip Trends')](https://piptrends.com/package/fsspec)
8
8
 
9
9
  A specification for pythonic filesystems.
10
10
 
@@ -51,13 +51,13 @@ CI runtime. For local use, pick a version suitable for you.
51
51
  mamba create -n fsspec -c conda-forge python=3.9 -y
52
52
  conda activate fsspec
53
53
 
54
- # Standard dev test install.
55
- pip install -e ".[dev,test]"
54
+ # Standard dev install with docs and tests.
55
+ pip install -e ".[dev,doc,test]"
56
56
 
57
57
  # Full tests except for downstream
58
58
  pip install s3fs
59
59
  pip uninstall s3fs
60
- pip install -e .[dev,test_full]
60
+ pip install -e .[dev,doc,test_full]
61
61
  pip install s3fs --no-deps
62
62
  pytest -v
63
63
 
@@ -0,0 +1,5 @@
1
+ name: fsspec
2
+ channels:
3
+ - defaults
4
+ dependencies:
5
+ - python=3.9
@@ -1,6 +1,26 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2024.6.0
5
+ --------
6
+
7
+ Enhancements
8
+
9
+ - allow dicts (not just bytes) for referenceFS (#1616
10
+ - make filesystems JSON serializeable (#1612)
11
+ - implement multifile cat() for github (#1620)
12
+
13
+ Fixes
14
+
15
+ - implement auto_mkdir for SMB (#1604)
16
+
17
+ Other
18
+
19
+ - add doc deps to pyproject (#1613)
20
+ - re-remove test from package (#1611)
21
+ - formatting (#1610, 1608, 1602)
22
+ - change monthly download badge (#1607)
23
+
4
24
  2024.5.0
5
25
  --------
6
26
 
@@ -107,9 +107,9 @@ The following can be used to install ``fsspec`` in development mode
107
107
 
108
108
  git clone https://github.com/fsspec/filesystem_spec
109
109
  cd filesystem_spec
110
- pip install -e .
110
+ pip install -e .[dev,doc,test]
111
111
 
112
- A number of additional dependencies are required to run tests, see "ci/environment*.yml", as
112
+ A number of additional dependencies are required to run tests in full, see "ci/environment*.yml", as
113
113
  well as Docker. Most implementation-specific tests should skip if their requirements are
114
114
  not met.
115
115
 
@@ -69,7 +69,7 @@ The following libraries use ``fsspec`` internally for path and file handling:
69
69
  .. _xarray: https://docs.xarray.dev/
70
70
  .. _zarr: https://zarr.readthedocs.io/
71
71
  .. _DVC: https://dvc.org/
72
- .. _kedro: https://kedro.readthedocs.io/en/stable/01_introduction/01_introduction.html
72
+ .. _kedro: https://docs.kedro.org/en/stable/tutorial/set_up_data.html#supported-data-locations
73
73
  .. _pyxet: https://github.com/xetdata/pyxet
74
74
  .. _Huggingface🤗 Datasets: https://github.com/huggingface/datasets
75
75
  .. _pyarrow: https://arrow.apache.org/docs/python/
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2024.5.0'
16
- __version_tuple__ = version_tuple = (2024, 5, 0)
15
+ __version__ = version = '2024.6.0'
16
+ __version_tuple__ = version_tuple = (2024, 6, 0)
@@ -15,6 +15,7 @@ from typing import (
15
15
  ClassVar,
16
16
  Generic,
17
17
  NamedTuple,
18
+ Optional,
18
19
  OrderedDict,
19
20
  TypeVar,
20
21
  )
@@ -574,7 +575,7 @@ class KnownPartsOfAFile(BaseCache):
574
575
  blocksize: int,
575
576
  fetcher: Fetcher,
576
577
  size: int,
577
- data: dict[tuple[int, int], bytes] = {},
578
+ data: Optional[dict[tuple[int, int], bytes]] = None,
578
579
  strict: bool = True,
579
580
  **_: Any,
580
581
  ):
@@ -597,7 +598,7 @@ class KnownPartsOfAFile(BaseCache):
597
598
 
598
599
  self.data = dict(zip(offsets, blocks))
599
600
  else:
600
- self.data = data
601
+ self.data = {}
601
602
 
602
603
  def _fetch(self, start: int | None, stop: int | None) -> bytes:
603
604
  if start is None:
@@ -139,7 +139,7 @@ class SnappyFile(AbstractBufferedFile):
139
139
  try:
140
140
  import snappy
141
141
 
142
- snappy.compress
142
+ snappy.compress(b"")
143
143
  # Snappy may use the .sz file extension, but this is not part of the
144
144
  # standard implementation.
145
145
  register_compression("snappy", SnappyFile, [])
@@ -425,7 +425,6 @@ class CachingFileSystem(AbstractFileSystem):
425
425
  "clear_cache",
426
426
  "clear_expired_cache",
427
427
  "pop_from_cache",
428
- "_mkcache",
429
428
  "local_file",
430
429
  "_paths_from_path",
431
430
  "get_mapper",
@@ -435,12 +434,10 @@ class CachingFileSystem(AbstractFileSystem):
435
434
  "__hash__",
436
435
  "__eq__",
437
436
  "to_json",
437
+ "to_dict",
438
438
  "cache_size",
439
439
  "pipe_file",
440
440
  "pipe",
441
- "isdir",
442
- "isfile",
443
- "exists",
444
441
  "start_transaction",
445
442
  "end_transaction",
446
443
  }:
@@ -510,15 +507,6 @@ class CachingFileSystem(AbstractFileSystem):
510
507
  ^ hash(self.target_protocol)
511
508
  )
512
509
 
513
- def to_json(self):
514
- """Calculate JSON representation.
515
-
516
- Not implemented yet for CachingFileSystem.
517
- """
518
- raise NotImplementedError(
519
- "CachingFileSystem JSON representation not implemented"
520
- )
521
-
522
510
 
523
511
  class WholeFileCacheFileSystem(CachingFileSystem):
524
512
  """Caches whole remote files on first access
@@ -1,5 +1,7 @@
1
1
  import requests
2
2
 
3
+ import fsspec
4
+
3
5
  from ..spec import AbstractFileSystem
4
6
  from ..utils import infer_storage_options
5
7
  from .memory import MemoryFile
@@ -225,3 +227,13 @@ class GithubFileSystem(AbstractFileSystem):
225
227
  raise FileNotFoundError(path)
226
228
  r.raise_for_status()
227
229
  return MemoryFile(None, None, r.content)
230
+
231
+ def cat(self, path, recursive=False, on_error="raise", **kwargs):
232
+ paths = self.expand_path(path, recursive=recursive)
233
+ urls = [
234
+ self.rurl.format(org=self.org, repo=self.repo, path=u, sha=self.root)
235
+ for u, sh in paths
236
+ ]
237
+ fs = fsspec.filesystem("http")
238
+ data = fs.cat(urls, on_error="return")
239
+ return {u: v for ((k, v), u) in zip(data.items(), urls)}
@@ -935,6 +935,10 @@ class ReferenceFileSystem(AsyncFileSystem):
935
935
 
936
936
  def _process_references0(self, references):
937
937
  """Make reference dict for Spec Version 0"""
938
+ references = {
939
+ key: json.dumps(val) if isinstance(val, dict) else val
940
+ for key, val in references.items()
941
+ }
938
942
  self.references = references
939
943
 
940
944
  def _process_references1(self, references, template_overrides=None):
@@ -952,6 +956,8 @@ class ReferenceFileSystem(AsyncFileSystem):
952
956
  if v.startswith("base64:"):
953
957
  self.references[k] = base64.b64decode(v[7:])
954
958
  self.references[k] = v
959
+ elif isinstance(v, dict):
960
+ self.references[k] = json.dumps(v)
955
961
  elif self.templates:
956
962
  u = v[0]
957
963
  if "{{" in u:
@@ -68,6 +68,7 @@ class SMBFileSystem(AbstractFileSystem):
68
68
  encrypt=None,
69
69
  share_access=None,
70
70
  register_session_retries=5,
71
+ auto_mkdir=False,
71
72
  **kwargs,
72
73
  ):
73
74
  """
@@ -102,6 +103,10 @@ class SMBFileSystem(AbstractFileSystem):
102
103
  - 'r': Allow other handles to be opened with read access.
103
104
  - 'w': Allow other handles to be opened with write access.
104
105
  - 'd': Allow other handles to be opened with delete access.
106
+ auto_mkdir: bool
107
+ Whether, when opening a file, the directory containing it should
108
+ be created (if it doesn't already exist). This is assumed by pyarrow
109
+ and zarr-python code.
105
110
  """
106
111
  super().__init__(**kwargs)
107
112
  self.host = host
@@ -113,6 +118,7 @@ class SMBFileSystem(AbstractFileSystem):
113
118
  self.temppath = kwargs.pop("temppath", "")
114
119
  self.share_access = share_access
115
120
  self.register_session_retries = register_session_retries
121
+ self.auto_mkdir = auto_mkdir
116
122
  self._connect()
117
123
 
118
124
  @property
@@ -224,6 +230,8 @@ class SMBFileSystem(AbstractFileSystem):
224
230
  By specifying 'share_access' in 'kwargs' it is possible to override the
225
231
  default shared access setting applied in the constructor of this object.
226
232
  """
233
+ if self.auto_mkdir and "w" in mode:
234
+ self.makedirs(self._parent(path), exist_ok=True)
227
235
  bls = block_size if block_size is not None and block_size >= 0 else -1
228
236
  wpath = _as_unc_path(self.host, path)
229
237
  share_access = kwargs.pop("share_access", self.share_access)
@@ -245,6 +253,8 @@ class SMBFileSystem(AbstractFileSystem):
245
253
  """Copy within two locations in the same filesystem"""
246
254
  wpath1 = _as_unc_path(self.host, path1)
247
255
  wpath2 = _as_unc_path(self.host, path2)
256
+ if self.auto_mkdir:
257
+ self.makedirs(self._parent(path2), exist_ok=True)
248
258
  smbclient.copyfile(wpath1, wpath2, port=self._port, **kwargs)
249
259
 
250
260
  def _rm(self, path):
@@ -0,0 +1,81 @@
1
+ import json
2
+ from contextlib import suppress
3
+ from pathlib import PurePath
4
+ from typing import Any, Callable, Dict, List, Optional, Tuple
5
+
6
+ from .registry import _import_class, get_filesystem_class
7
+ from .spec import AbstractFileSystem
8
+
9
+
10
+ class FilesystemJSONEncoder(json.JSONEncoder):
11
+ def default(self, o: Any) -> Any:
12
+ if isinstance(o, AbstractFileSystem):
13
+ return o.to_dict()
14
+ if isinstance(o, PurePath):
15
+ cls = type(o)
16
+ return {"cls": f"{cls.__module__}.{cls.__name__}", "str": str(o)}
17
+
18
+ return super().default(o)
19
+
20
+
21
+ class FilesystemJSONDecoder(json.JSONDecoder):
22
+ def __init__(
23
+ self,
24
+ *,
25
+ object_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
26
+ parse_float: Optional[Callable[[str], Any]] = None,
27
+ parse_int: Optional[Callable[[str], Any]] = None,
28
+ parse_constant: Optional[Callable[[str], Any]] = None,
29
+ strict: bool = True,
30
+ object_pairs_hook: Optional[Callable[[List[Tuple[str, Any]]], Any]] = None,
31
+ ) -> None:
32
+ self.original_object_hook = object_hook
33
+
34
+ super().__init__(
35
+ object_hook=self.custom_object_hook,
36
+ parse_float=parse_float,
37
+ parse_int=parse_int,
38
+ parse_constant=parse_constant,
39
+ strict=strict,
40
+ object_pairs_hook=object_pairs_hook,
41
+ )
42
+
43
+ @classmethod
44
+ def try_resolve_path_cls(cls, dct: Dict[str, Any]):
45
+ with suppress(Exception):
46
+ fqp = dct["cls"]
47
+
48
+ path_cls = _import_class(fqp)
49
+
50
+ if issubclass(path_cls, PurePath):
51
+ return path_cls
52
+
53
+ return None
54
+
55
+ @classmethod
56
+ def try_resolve_fs_cls(cls, dct: Dict[str, Any]):
57
+ with suppress(Exception):
58
+ if "cls" in dct:
59
+ try:
60
+ fs_cls = _import_class(dct["cls"])
61
+ if issubclass(fs_cls, AbstractFileSystem):
62
+ return fs_cls
63
+ except Exception:
64
+ if "protocol" in dct: # Fallback if cls cannot be imported
65
+ return get_filesystem_class(dct["protocol"])
66
+
67
+ raise
68
+
69
+ return None
70
+
71
+ def custom_object_hook(self, dct: Dict[str, Any]):
72
+ if "cls" in dct:
73
+ if (obj_cls := self.try_resolve_fs_cls(dct)) is not None:
74
+ return AbstractFileSystem.from_dict(dct)
75
+ if (obj_cls := self.try_resolve_path_cls(dct)) is not None:
76
+ return obj_cls(dct["str"])
77
+
78
+ if self.original_object_hook is not None:
79
+ return self.original_object_hook(dct)
80
+
81
+ return dct
@@ -257,27 +257,33 @@ update the current installation.
257
257
  """
258
258
 
259
259
 
260
- def _import_class(cls, minv=None):
261
- """Take a string FQP and return the imported class or identifier
260
+ def _import_class(fqp: str):
261
+ """Take a fully-qualified path and return the imported class or identifier.
262
262
 
263
- cls is of the form "package.module.klass" or "package.module:subobject.klass"
263
+ ``fqp`` is of the form "package.module.klass" or
264
+ "package.module:subobject.klass".
265
+
266
+ Warnings
267
+ --------
268
+ This can import arbitrary modules. Make sure you haven't installed any modules
269
+ that may execute malicious code at import time.
264
270
  """
265
- if ":" in cls:
266
- mod, name = cls.rsplit(":", 1)
267
- s3 = mod == "s3fs"
268
- mod = importlib.import_module(mod)
269
- if s3 and mod.__version__.split(".") < ["0", "5"]:
270
- warnings.warn(s3_msg)
271
- for part in name.split("."):
272
- mod = getattr(mod, part)
273
- return mod
271
+ if ":" in fqp:
272
+ mod, name = fqp.rsplit(":", 1)
274
273
  else:
275
- mod, name = cls.rsplit(".", 1)
276
- s3 = mod == "s3fs"
277
- mod = importlib.import_module(mod)
278
- if s3 and mod.__version__.split(".") < ["0", "5"]:
279
- warnings.warn(s3_msg)
280
- return getattr(mod, name)
274
+ mod, name = fqp.rsplit(".", 1)
275
+
276
+ is_s3 = mod == "s3fs"
277
+ mod = importlib.import_module(mod)
278
+ if is_s3 and mod.__version__.split(".") < ["0", "5"]:
279
+ warnings.warn(s3_msg)
280
+ for part in name.split("."):
281
+ mod = getattr(mod, part)
282
+
283
+ if not isinstance(mod, type):
284
+ raise TypeError(f"{fqp} is not a class")
285
+
286
+ return mod
281
287
 
282
288
 
283
289
  def filesystem(protocol, **storage_options):
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
+ import json
4
5
  import logging
5
6
  import os
6
7
  import threading
@@ -9,7 +10,7 @@ import weakref
9
10
  from errno import ESPIPE
10
11
  from glob import has_magic
11
12
  from hashlib import sha256
12
- from typing import ClassVar
13
+ from typing import Any, ClassVar, Dict, Tuple
13
14
 
14
15
  from .callbacks import DEFAULT_CALLBACK
15
16
  from .config import apply_config, conf
@@ -115,6 +116,10 @@ class AbstractFileSystem(metaclass=_Cached):
115
116
  #: Extra *class attributes* that should be considered when hashing.
116
117
  _extra_tokenize_attributes = ()
117
118
 
119
+ # Set by _Cached metaclass
120
+ storage_args: Tuple[Any, ...]
121
+ storage_options: Dict[str, Any]
122
+
118
123
  def __init__(self, *args, **storage_options):
119
124
  """Create and configure file-system instance
120
125
 
@@ -1381,61 +1386,98 @@ class AbstractFileSystem(metaclass=_Cached):
1381
1386
  length = size - offset
1382
1387
  return read_block(f, offset, length, delimiter)
1383
1388
 
1384
- def to_json(self):
1389
+ def to_json(self) -> str:
1385
1390
  """
1386
- JSON representation of this filesystem instance
1391
+ JSON representation of this filesystem instance.
1387
1392
 
1388
1393
  Returns
1389
1394
  -------
1390
- str: JSON structure with keys cls (the python location of this class),
1391
- protocol (text name of this class's protocol, first one in case of
1392
- multiple), args (positional args, usually empty), and all other
1393
- kwargs as their own keys.
1395
+ JSON string with keys ``cls`` (the python location of this class),
1396
+ protocol (text name of this class's protocol, first one in case of
1397
+ multiple), ``args`` (positional args, usually empty), and all other
1398
+ keyword arguments as their own keys.
1399
+ """
1400
+ from .json import FilesystemJSONEncoder
1401
+
1402
+ return json.dumps(self, cls=FilesystemJSONEncoder)
1403
+
1404
+ @staticmethod
1405
+ def from_json(blob: str) -> AbstractFileSystem:
1394
1406
  """
1395
- import json
1407
+ Recreate a filesystem instance from JSON representation.
1408
+
1409
+ See ``.to_json()`` for the expected structure of the input.
1410
+
1411
+ Parameters
1412
+ ----------
1413
+ blob: str
1414
+
1415
+ Returns
1416
+ -------
1417
+ file system instance, not necessarily of this particular class.
1396
1418
 
1419
+ Warnings
1420
+ --------
1421
+ This can import arbitrary modules (as determined by the ``cls`` key).
1422
+ Make sure you haven't installed any modules that may execute malicious code
1423
+ at import time.
1424
+ """
1425
+ from .json import FilesystemJSONDecoder
1426
+
1427
+ return json.loads(blob, cls=FilesystemJSONDecoder)
1428
+
1429
+ def to_dict(self) -> Dict[str, Any]:
1430
+ """
1431
+ JSON-serializable dictionary representation of this filesystem instance.
1432
+
1433
+ Returns
1434
+ -------
1435
+ Dictionary with keys ``cls`` (the python location of this class),
1436
+ protocol (text name of this class's protocol, first one in case of
1437
+ multiple), ``args`` (positional args, usually empty), and all other
1438
+ keyword arguments as their own keys.
1439
+ """
1397
1440
  cls = type(self)
1398
- cls = ".".join((cls.__module__, cls.__name__))
1399
- proto = (
1400
- self.protocol[0]
1401
- if isinstance(self.protocol, (tuple, list))
1402
- else self.protocol
1403
- )
1404
- return json.dumps(
1405
- dict(
1406
- cls=cls,
1407
- protocol=proto,
1408
- args=self.storage_args,
1409
- **self.storage_options,
1410
- )
1441
+ proto = self.protocol
1442
+
1443
+ return dict(
1444
+ cls=f"{cls.__module__}:{cls.__name__}",
1445
+ protocol=proto[0] if isinstance(proto, (tuple, list)) else proto,
1446
+ args=self.storage_args,
1447
+ **self.storage_options,
1411
1448
  )
1412
1449
 
1413
1450
  @staticmethod
1414
- def from_json(blob):
1451
+ def from_dict(dct: Dict[str, Any]) -> AbstractFileSystem:
1415
1452
  """
1416
- Recreate a filesystem instance from JSON representation
1453
+ Recreate a filesystem instance from dictionary representation.
1417
1454
 
1418
- See ``.to_json()`` for the expected structure of the input
1455
+ See ``.to_dict()`` for the expected structure of the input.
1419
1456
 
1420
1457
  Parameters
1421
1458
  ----------
1422
- blob: str
1459
+ dct: Dict[str, Any]
1423
1460
 
1424
1461
  Returns
1425
1462
  -------
1426
1463
  file system instance, not necessarily of this particular class.
1464
+
1465
+ Warnings
1466
+ --------
1467
+ This can import arbitrary modules (as determined by the ``cls`` key).
1468
+ Make sure you haven't installed any modules that may execute malicious code
1469
+ at import time.
1427
1470
  """
1428
- import json
1471
+ from .json import FilesystemJSONDecoder
1429
1472
 
1430
- from .registry import _import_class, get_filesystem_class
1473
+ cls = FilesystemJSONDecoder.try_resolve_fs_cls(dct)
1474
+ if cls is None:
1475
+ raise ValueError("Not a serialized AbstractFileSystem")
1431
1476
 
1432
- dic = json.loads(blob)
1433
- protocol = dic.pop("protocol")
1434
- try:
1435
- cls = _import_class(dic.pop("cls"))
1436
- except (ImportError, ValueError, RuntimeError, KeyError):
1437
- cls = get_filesystem_class(protocol)
1438
- return cls(*dic.pop("args", ()), **dic)
1477
+ dct.pop("cls", None)
1478
+ dct.pop("protocol", None)
1479
+
1480
+ return cls(*dct.pop("args", ()), **dct)
1439
1481
 
1440
1482
  def _get_pyarrow_filesystem(self):
1441
1483
  """
@@ -435,7 +435,7 @@ def isfilelike(f: Any) -> TypeGuard[IO[bytes]]:
435
435
 
436
436
  def get_protocol(url: str) -> str:
437
437
  url = stringify_path(url)
438
- parts = re.split(r"(\:\:|\://)", url, 1)
438
+ parts = re.split(r"(\:\:|\://)", url, maxsplit=1)
439
439
  if len(parts) > 1:
440
440
  return parts[0]
441
441
  return "file"