fsspec 2024.5.0__tar.gz → 2024.6.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 (145) hide show
  1. {fsspec-2024.5.0 → fsspec-2024.6.1}/.pre-commit-config.yaml +1 -1
  2. {fsspec-2024.5.0 → fsspec-2024.6.1}/PKG-INFO +10 -5
  3. {fsspec-2024.5.0 → fsspec-2024.6.1}/README.md +3 -4
  4. fsspec-2024.6.1/docs/environment.yml +5 -0
  5. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/changelog.rst +34 -0
  6. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/developer.rst +2 -2
  7. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/index.rst +1 -1
  8. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/_version.py +2 -2
  9. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/caching.py +3 -2
  10. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/compression.py +1 -1
  11. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/generic.py +3 -0
  12. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/cached.py +6 -16
  13. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/dirfs.py +2 -0
  14. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/github.py +12 -0
  15. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/http.py +2 -1
  16. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/reference.py +9 -0
  17. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/smb.py +10 -0
  18. fsspec-2024.6.1/fsspec/json.py +121 -0
  19. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/registry.py +24 -18
  20. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/spec.py +119 -33
  21. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/utils.py +1 -1
  22. {fsspec-2024.5.0 → fsspec-2024.6.1}/pyproject.toml +33 -1
  23. {fsspec-2024.5.0 → fsspec-2024.6.1}/readthedocs.yml +2 -0
  24. fsspec-2024.5.0/docs/environment.yml +0 -11
  25. fsspec-2024.5.0/fsspec/implementations/tests/__init__.py +0 -0
  26. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_file_listing.yaml +0 -112
  27. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_mkdir.yaml +0 -582
  28. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_read_pyarrow_non_partitioned.yaml +0 -873
  29. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_read_range.yaml +0 -458
  30. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_read_range_chunked.yaml +0 -1355
  31. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_write_and_read.yaml +0 -795
  32. fsspec-2024.5.0/fsspec/implementations/tests/cassettes/test_dbfs/test_dbfs_write_pyarrow_non_partitioned.yaml +0 -613
  33. fsspec-2024.5.0/fsspec/implementations/tests/conftest.py +0 -39
  34. fsspec-2024.5.0/fsspec/implementations/tests/local/__init__.py +0 -0
  35. fsspec-2024.5.0/fsspec/implementations/tests/local/local_fixtures.py +0 -18
  36. fsspec-2024.5.0/fsspec/implementations/tests/local/local_test.py +0 -14
  37. fsspec-2024.5.0/fsspec/implementations/tests/memory/__init__.py +0 -0
  38. fsspec-2024.5.0/fsspec/implementations/tests/memory/memory_fixtures.py +0 -27
  39. fsspec-2024.5.0/fsspec/implementations/tests/memory/memory_test.py +0 -14
  40. fsspec-2024.5.0/fsspec/implementations/tests/out.zip +0 -0
  41. fsspec-2024.5.0/fsspec/implementations/tests/test_archive.py +0 -382
  42. fsspec-2024.5.0/fsspec/implementations/tests/test_arrow.py +0 -259
  43. fsspec-2024.5.0/fsspec/implementations/tests/test_cached.py +0 -1306
  44. fsspec-2024.5.0/fsspec/implementations/tests/test_common.py +0 -35
  45. fsspec-2024.5.0/fsspec/implementations/tests/test_dask.py +0 -29
  46. fsspec-2024.5.0/fsspec/implementations/tests/test_data.py +0 -20
  47. fsspec-2024.5.0/fsspec/implementations/tests/test_dbfs.py +0 -268
  48. fsspec-2024.5.0/fsspec/implementations/tests/test_dirfs.py +0 -588
  49. fsspec-2024.5.0/fsspec/implementations/tests/test_ftp.py +0 -178
  50. fsspec-2024.5.0/fsspec/implementations/tests/test_git.py +0 -76
  51. fsspec-2024.5.0/fsspec/implementations/tests/test_http.py +0 -577
  52. fsspec-2024.5.0/fsspec/implementations/tests/test_jupyter.py +0 -57
  53. fsspec-2024.5.0/fsspec/implementations/tests/test_libarchive.py +0 -33
  54. fsspec-2024.5.0/fsspec/implementations/tests/test_local.py +0 -1285
  55. fsspec-2024.5.0/fsspec/implementations/tests/test_memory.py +0 -382
  56. fsspec-2024.5.0/fsspec/implementations/tests/test_reference.py +0 -720
  57. fsspec-2024.5.0/fsspec/implementations/tests/test_sftp.py +0 -233
  58. fsspec-2024.5.0/fsspec/implementations/tests/test_smb.py +0 -139
  59. fsspec-2024.5.0/fsspec/implementations/tests/test_tar.py +0 -243
  60. fsspec-2024.5.0/fsspec/implementations/tests/test_webhdfs.py +0 -197
  61. fsspec-2024.5.0/fsspec/implementations/tests/test_zip.py +0 -134
  62. fsspec-2024.5.0/fsspec/tests/__init__.py +0 -0
  63. fsspec-2024.5.0/fsspec/tests/conftest.py +0 -188
  64. fsspec-2024.5.0/fsspec/tests/data/listing.html +0 -1
  65. fsspec-2024.5.0/fsspec/tests/test_api.py +0 -498
  66. fsspec-2024.5.0/fsspec/tests/test_async.py +0 -230
  67. fsspec-2024.5.0/fsspec/tests/test_caches.py +0 -255
  68. fsspec-2024.5.0/fsspec/tests/test_callbacks.py +0 -89
  69. fsspec-2024.5.0/fsspec/tests/test_compression.py +0 -164
  70. fsspec-2024.5.0/fsspec/tests/test_config.py +0 -129
  71. fsspec-2024.5.0/fsspec/tests/test_core.py +0 -466
  72. fsspec-2024.5.0/fsspec/tests/test_downstream.py +0 -40
  73. fsspec-2024.5.0/fsspec/tests/test_file.py +0 -200
  74. fsspec-2024.5.0/fsspec/tests/test_fuse.py +0 -147
  75. fsspec-2024.5.0/fsspec/tests/test_generic.py +0 -90
  76. fsspec-2024.5.0/fsspec/tests/test_gui.py +0 -23
  77. fsspec-2024.5.0/fsspec/tests/test_mapping.py +0 -228
  78. fsspec-2024.5.0/fsspec/tests/test_parquet.py +0 -140
  79. fsspec-2024.5.0/fsspec/tests/test_registry.py +0 -134
  80. fsspec-2024.5.0/fsspec/tests/test_spec.py +0 -1167
  81. fsspec-2024.5.0/fsspec/tests/test_utils.py +0 -478
  82. {fsspec-2024.5.0 → fsspec-2024.6.1}/.codespellrc +0 -0
  83. {fsspec-2024.5.0 → fsspec-2024.6.1}/.coveragerc +0 -0
  84. {fsspec-2024.5.0 → fsspec-2024.6.1}/.gitattributes +0 -0
  85. {fsspec-2024.5.0 → fsspec-2024.6.1}/.github/workflows/codespell.yml +0 -0
  86. {fsspec-2024.5.0 → fsspec-2024.6.1}/.github/workflows/main.yaml +0 -0
  87. {fsspec-2024.5.0 → fsspec-2024.6.1}/.github/workflows/pypipublish.yaml +0 -0
  88. {fsspec-2024.5.0 → fsspec-2024.6.1}/.gitignore +0 -0
  89. {fsspec-2024.5.0 → fsspec-2024.6.1}/LICENSE +0 -0
  90. {fsspec-2024.5.0 → fsspec-2024.6.1}/ci/environment-downstream.yml +0 -0
  91. {fsspec-2024.5.0 → fsspec-2024.6.1}/ci/environment-friends.yml +0 -0
  92. {fsspec-2024.5.0 → fsspec-2024.6.1}/ci/environment-py38.yml +0 -0
  93. {fsspec-2024.5.0 → fsspec-2024.6.1}/ci/environment-typecheck.yml +0 -0
  94. {fsspec-2024.5.0 → fsspec-2024.6.1}/ci/environment-win.yml +0 -0
  95. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/Makefile +0 -0
  96. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/README.md +0 -0
  97. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/make.bat +0 -0
  98. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/_static/custom.css +0 -0
  99. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/api.rst +0 -0
  100. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/async.rst +0 -0
  101. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/conf.py +0 -0
  102. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/copying.rst +0 -0
  103. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/features.rst +0 -0
  104. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/img/gui.png +0 -0
  105. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/intro.rst +0 -0
  106. {fsspec-2024.5.0 → fsspec-2024.6.1}/docs/source/usage.rst +0 -0
  107. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/__init__.py +0 -0
  108. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/archive.py +0 -0
  109. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/asyn.py +0 -0
  110. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/callbacks.py +0 -0
  111. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/config.py +0 -0
  112. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/conftest.py +0 -0
  113. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/core.py +0 -0
  114. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/dircache.py +0 -0
  115. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/exceptions.py +0 -0
  116. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/fuse.py +0 -0
  117. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/gui.py +0 -0
  118. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/__init__.py +0 -0
  119. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/arrow.py +0 -0
  120. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/cache_mapper.py +0 -0
  121. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/cache_metadata.py +0 -0
  122. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/dask.py +0 -0
  123. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/data.py +0 -0
  124. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/dbfs.py +0 -0
  125. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/ftp.py +0 -0
  126. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/git.py +0 -0
  127. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/jupyter.py +0 -0
  128. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/libarchive.py +0 -0
  129. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/local.py +0 -0
  130. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/memory.py +0 -0
  131. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/sftp.py +0 -0
  132. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/tar.py +0 -0
  133. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/webhdfs.py +0 -0
  134. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/implementations/zip.py +0 -0
  135. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/mapping.py +0 -0
  136. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/parquet.py +0 -0
  137. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/tests/abstract/__init__.py +0 -0
  138. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/tests/abstract/common.py +0 -0
  139. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/tests/abstract/copy.py +0 -0
  140. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/tests/abstract/get.py +0 -0
  141. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/tests/abstract/mv.py +0 -0
  142. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/tests/abstract/put.py +0 -0
  143. {fsspec-2024.5.0 → fsspec-2024.6.1}/fsspec/transaction.py +0 -0
  144. {fsspec-2024.5.0 → fsspec-2024.6.1}/install_s3fs.sh +0 -0
  145. {fsspec-2024.5.0 → fsspec-2024.6.1}/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.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/
@@ -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,6 @@ 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)
182
187
 
183
188
  A specification for pythonic filesystems.
184
189
 
@@ -225,13 +230,13 @@ CI runtime. For local use, pick a version suitable for you.
225
230
  mamba create -n fsspec -c conda-forge python=3.9 -y
226
231
  conda activate fsspec
227
232
 
228
- # Standard dev test install.
229
- pip install -e ".[dev,test]"
233
+ # Standard dev install with docs and tests.
234
+ pip install -e ".[dev,doc,test]"
230
235
 
231
236
  # Full tests except for downstream
232
237
  pip install s3fs
233
238
  pip uninstall s3fs
234
- pip install -e .[dev,test_full]
239
+ pip install -e .[dev,doc,test_full]
235
240
  pip install s3fs --no-deps
236
241
  pytest -v
237
242
 
@@ -4,7 +4,6 @@
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)
8
7
 
9
8
  A specification for pythonic filesystems.
10
9
 
@@ -51,13 +50,13 @@ CI runtime. For local use, pick a version suitable for you.
51
50
  mamba create -n fsspec -c conda-forge python=3.9 -y
52
51
  conda activate fsspec
53
52
 
54
- # Standard dev test install.
55
- pip install -e ".[dev,test]"
53
+ # Standard dev install with docs and tests.
54
+ pip install -e ".[dev,doc,test]"
56
55
 
57
56
  # Full tests except for downstream
58
57
  pip install s3fs
59
58
  pip uninstall s3fs
60
- pip install -e .[dev,test_full]
59
+ pip install -e .[dev,doc,test_full]
61
60
  pip install s3fs --no-deps
62
61
  pytest -v
63
62
 
@@ -0,0 +1,5 @@
1
+ name: fsspec
2
+ channels:
3
+ - defaults
4
+ dependencies:
5
+ - python=3.9
@@ -1,6 +1,40 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2024.6.1
5
+ --------
6
+
7
+ Fixes
8
+
9
+ - fix appending to non-dict reference sets (#1634)
10
+ - don't let generic edit info dicts (#1633)
11
+ - set https's loop before calling super (#1633)
12
+ - cached write file doesn't need to update it's size on close (#1633)
13
+ - fix JSON serialize for FSs with interior FSs (#1628, 1627)
14
+ - option to strip "password" when pickling (#1625)
15
+ - fix filecache write (#1622)
16
+
17
+
18
+ 2024.6.0
19
+ --------
20
+
21
+ Enhancements
22
+
23
+ - allow dicts (not just bytes) for referenceFS (#1616
24
+ - make filesystems JSON serializeable (#1612)
25
+ - implement multifile cat() for github (#1620)
26
+
27
+ Fixes
28
+
29
+ - implement auto_mkdir for SMB (#1604)
30
+
31
+ Other
32
+
33
+ - add doc deps to pyproject (#1613)
34
+ - re-remove test from package (#1611)
35
+ - formatting (#1610, 1608, 1602)
36
+ - change monthly download badge (#1607)
37
+
4
38
  2024.5.0
5
39
  --------
6
40
 
@@ -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.1'
16
+ __version_tuple__ = version_tuple = (2024, 6, 1)
@@ -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, [])
@@ -197,6 +197,7 @@ class GenericFileSystem(AsyncFileSystem):
197
197
  )
198
198
  result = {}
199
199
  for k, v in out.items():
200
+ v = v.copy() # don't corrupt target FS dircache
200
201
  name = fs.unstrip_protocol(k)
201
202
  v["name"] = name
202
203
  result[name] = v
@@ -210,6 +211,7 @@ class GenericFileSystem(AsyncFileSystem):
210
211
  out = await fs._info(url, **kwargs)
211
212
  else:
212
213
  out = fs.info(url, **kwargs)
214
+ out = out.copy() # don't edit originals
213
215
  out["name"] = fs.unstrip_protocol(out["name"])
214
216
  return out
215
217
 
@@ -224,6 +226,7 @@ class GenericFileSystem(AsyncFileSystem):
224
226
  out = await fs._ls(url, detail=True, **kwargs)
225
227
  else:
226
228
  out = fs.ls(url, detail=True, **kwargs)
229
+ out = [o.copy() for o in out] # don't edit originals
227
230
  for o in out:
228
231
  o["name"] = fs.unstrip_protocol(o["name"])
229
232
  if detail:
@@ -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
@@ -663,7 +651,8 @@ class WholeFileCacheFileSystem(CachingFileSystem):
663
651
  def _open(self, path, mode="rb", **kwargs):
664
652
  path = self._strip_protocol(path)
665
653
  if "r" not in mode:
666
- fn = self._make_local_details(path)
654
+ hash = self._mapper(path)
655
+ fn = os.path.join(self.storage[-1], hash)
667
656
  user_specified_kwargs = {
668
657
  k: v
669
658
  for k, v in kwargs.items()
@@ -806,7 +795,8 @@ class SimpleCacheFileSystem(WholeFileCacheFileSystem):
806
795
  if self._intrans:
807
796
  f = [_ for _ in self.transaction.files if _.path == path]
808
797
  if f:
809
- return {"name": path, "size": f[0].size or f[0].tell(), "type": "file"}
798
+ size = os.path.getsize(f[0].fn) if f[0].closed else f[0].tell()
799
+ return {"name": path, "size": size, "type": "file"}
810
800
  f = any(_.path.startswith(path + "/") for _ in self.transaction.files)
811
801
  if f:
812
802
  return {"name": path, "size": 0, "type": "directory"}
@@ -912,7 +902,7 @@ class LocalTempFile:
912
902
  self.close()
913
903
 
914
904
  def close(self):
915
- self.size = self.fh.tell()
905
+ # self.size = self.fh.tell()
916
906
  if self.closed:
917
907
  return
918
908
  self.fh.close()
@@ -56,6 +56,8 @@ class DirFileSystem(AsyncFileSystem):
56
56
  if not path:
57
57
  return self.path
58
58
  return self.fs.sep.join((self.path, self._strip_protocol(path)))
59
+ if isinstance(path, dict):
60
+ return {self._join(_path): value for _path, value in path.items()}
59
61
  return [self._join(_path) for _path in path]
60
62
 
61
63
  def _relpath(self, path):
@@ -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)}
@@ -560,6 +560,7 @@ class HTTPFile(AbstractBufferedFile):
560
560
  if mode != "rb":
561
561
  raise NotImplementedError("File mode not supported")
562
562
  self.asynchronous = asynchronous
563
+ self.loop = loop
563
564
  self.url = url
564
565
  self.session = session
565
566
  self.details = {"name": url, "size": size, "type": "file"}
@@ -572,7 +573,6 @@ class HTTPFile(AbstractBufferedFile):
572
573
  cache_options=cache_options,
573
574
  **kwargs,
574
575
  )
575
- self.loop = loop
576
576
 
577
577
  def read(self, length=-1):
578
578
  """Read bytes from file
@@ -736,6 +736,7 @@ class HTTPStreamFile(AbstractBufferedFile):
736
736
  return r
737
737
 
738
738
  self.r = sync(self.loop, cor)
739
+ self.loop = fs.loop
739
740
 
740
741
  def seek(self, loc, whence=0):
741
742
  if loc == 0 and whence == 1:
@@ -935,6 +935,13 @@ class ReferenceFileSystem(AsyncFileSystem):
935
935
 
936
936
  def _process_references0(self, references):
937
937
  """Make reference dict for Spec Version 0"""
938
+ if isinstance(references, dict):
939
+ # do not do this for lazy/parquet backend, which will not make dicts,
940
+ # but must remain writable in the original object
941
+ references = {
942
+ key: json.dumps(val) if isinstance(val, dict) else val
943
+ for key, val in references.items()
944
+ }
938
945
  self.references = references
939
946
 
940
947
  def _process_references1(self, references, template_overrides=None):
@@ -952,6 +959,8 @@ class ReferenceFileSystem(AsyncFileSystem):
952
959
  if v.startswith("base64:"):
953
960
  self.references[k] = base64.b64decode(v[7:])
954
961
  self.references[k] = v
962
+ elif isinstance(v, dict):
963
+ self.references[k] = json.dumps(v)
955
964
  elif self.templates:
956
965
  u = v[0]
957
966
  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,121 @@
1
+ import json
2
+ from contextlib import suppress
3
+ from pathlib import PurePath
4
+ from typing import (
5
+ Any,
6
+ Callable,
7
+ ClassVar,
8
+ Dict,
9
+ List,
10
+ Mapping,
11
+ Optional,
12
+ Sequence,
13
+ Tuple,
14
+ )
15
+
16
+ from .registry import _import_class, get_filesystem_class
17
+ from .spec import AbstractFileSystem
18
+
19
+
20
+ class FilesystemJSONEncoder(json.JSONEncoder):
21
+ include_password: ClassVar[bool] = True
22
+
23
+ def default(self, o: Any) -> Any:
24
+ if isinstance(o, AbstractFileSystem):
25
+ return o.to_dict(include_password=self.include_password)
26
+ if isinstance(o, PurePath):
27
+ cls = type(o)
28
+ return {"cls": f"{cls.__module__}.{cls.__name__}", "str": str(o)}
29
+
30
+ return super().default(o)
31
+
32
+ def make_serializable(self, obj: Any) -> Any:
33
+ """
34
+ Recursively converts an object so that it can be JSON serialized via
35
+ :func:`json.dumps` and :func:`json.dump`, without actually calling
36
+ said functions.
37
+ """
38
+ if isinstance(obj, (str, int, float, bool)):
39
+ return obj
40
+ if isinstance(obj, Mapping):
41
+ return {k: self.make_serializable(v) for k, v in obj.items()}
42
+ if isinstance(obj, Sequence):
43
+ return [self.make_serializable(v) for v in obj]
44
+
45
+ return self.default(obj)
46
+
47
+
48
+ class FilesystemJSONDecoder(json.JSONDecoder):
49
+ def __init__(
50
+ self,
51
+ *,
52
+ object_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
53
+ parse_float: Optional[Callable[[str], Any]] = None,
54
+ parse_int: Optional[Callable[[str], Any]] = None,
55
+ parse_constant: Optional[Callable[[str], Any]] = None,
56
+ strict: bool = True,
57
+ object_pairs_hook: Optional[Callable[[List[Tuple[str, Any]]], Any]] = None,
58
+ ) -> None:
59
+ self.original_object_hook = object_hook
60
+
61
+ super().__init__(
62
+ object_hook=self.custom_object_hook,
63
+ parse_float=parse_float,
64
+ parse_int=parse_int,
65
+ parse_constant=parse_constant,
66
+ strict=strict,
67
+ object_pairs_hook=object_pairs_hook,
68
+ )
69
+
70
+ @classmethod
71
+ def try_resolve_path_cls(cls, dct: Dict[str, Any]):
72
+ with suppress(Exception):
73
+ fqp = dct["cls"]
74
+
75
+ path_cls = _import_class(fqp)
76
+
77
+ if issubclass(path_cls, PurePath):
78
+ return path_cls
79
+
80
+ return None
81
+
82
+ @classmethod
83
+ def try_resolve_fs_cls(cls, dct: Dict[str, Any]):
84
+ with suppress(Exception):
85
+ if "cls" in dct:
86
+ try:
87
+ fs_cls = _import_class(dct["cls"])
88
+ if issubclass(fs_cls, AbstractFileSystem):
89
+ return fs_cls
90
+ except Exception:
91
+ if "protocol" in dct: # Fallback if cls cannot be imported
92
+ return get_filesystem_class(dct["protocol"])
93
+
94
+ raise
95
+
96
+ return None
97
+
98
+ def custom_object_hook(self, dct: Dict[str, Any]):
99
+ if "cls" in dct:
100
+ if (obj_cls := self.try_resolve_fs_cls(dct)) is not None:
101
+ return AbstractFileSystem.from_dict(dct)
102
+ if (obj_cls := self.try_resolve_path_cls(dct)) is not None:
103
+ return obj_cls(dct["str"])
104
+
105
+ if self.original_object_hook is not None:
106
+ return self.original_object_hook(dct)
107
+
108
+ return dct
109
+
110
+ def unmake_serializable(self, obj: Any) -> Any:
111
+ """
112
+ Inverse function of :meth:`FilesystemJSONEncoder.make_serializable`.
113
+ """
114
+ if isinstance(obj, dict):
115
+ obj = self.custom_object_hook(obj)
116
+ if isinstance(obj, dict):
117
+ return {k: self.unmake_serializable(v) for k, v in obj.items()}
118
+ if isinstance(obj, (list, tuple)):
119
+ return [self.unmake_serializable(v) for v in obj]
120
+
121
+ return obj
@@ -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):