s3fs 2025.5.1__tar.gz → 2025.9.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 (33) hide show
  1. {s3fs-2025.5.1 → s3fs-2025.9.0}/PKG-INFO +6 -20
  2. s3fs-2025.9.0/README.md +18 -0
  3. {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/changelog.rst +12 -0
  4. {s3fs-2025.5.1 → s3fs-2025.9.0}/requirements.txt +1 -1
  5. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/_version.py +3 -3
  6. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/core.py +2 -5
  7. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/derived/s3fs_test.py +0 -1
  8. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/test_s3fs.py +85 -12
  9. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/PKG-INFO +6 -20
  10. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/SOURCES.txt +1 -1
  11. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/requires.txt +1 -1
  12. {s3fs-2025.5.1 → s3fs-2025.9.0}/setup.py +2 -0
  13. s3fs-2025.5.1/README.rst +0 -18
  14. {s3fs-2025.5.1 → s3fs-2025.9.0}/LICENSE.txt +0 -0
  15. {s3fs-2025.5.1 → s3fs-2025.9.0}/MANIFEST.in +0 -0
  16. {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/api.rst +0 -0
  17. {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/development.rst +0 -0
  18. {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/index.rst +0 -0
  19. {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/install.rst +0 -0
  20. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/__init__.py +0 -0
  21. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/errors.py +0 -0
  22. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/mapping.py +0 -0
  23. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/__init__.py +0 -0
  24. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/derived/__init__.py +0 -0
  25. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/derived/s3fs_fixtures.py +0 -0
  26. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/test_mapping.py +0 -0
  27. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/test_utils.py +0 -0
  28. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/utils.py +0 -0
  29. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/dependency_links.txt +0 -0
  30. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/not-zip-safe +0 -0
  31. {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/top_level.txt +0 -0
  32. {s3fs-2025.5.1 → s3fs-2025.9.0}/setup.cfg +0 -0
  33. {s3fs-2025.5.1 → s3fs-2025.9.0}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: s3fs
3
- Version: 2025.5.1
3
+ Version: 2025.9.0
4
4
  Summary: Convenient Filesystem interface over S3
5
5
  Home-page: http://github.com/fsspec/s3fs/
6
6
  Maintainer: Martin Durant
@@ -17,15 +17,18 @@ Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Requires-Python: >= 3.9
20
+ Description-Content-Type: text/markdown
20
21
  License-File: LICENSE.txt
21
22
  Requires-Dist: aiobotocore<3.0.0,>=2.5.4
22
- Requires-Dist: fsspec==2025.5.1
23
+ Requires-Dist: fsspec==2025.9.0
23
24
  Requires-Dist: aiohttp!=4.0.0a0,!=4.0.0a1
24
25
  Provides-Extra: awscli
25
26
  Requires-Dist: aiobotocore[awscli]<3.0.0,>=2.5.4; extra == "awscli"
26
27
  Provides-Extra: boto3
27
28
  Requires-Dist: aiobotocore[boto3]<3.0.0,>=2.5.4; extra == "boto3"
28
29
  Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
29
32
  Dynamic: home-page
30
33
  Dynamic: keywords
31
34
  Dynamic: license
@@ -37,21 +40,4 @@ Dynamic: requires-dist
37
40
  Dynamic: requires-python
38
41
  Dynamic: summary
39
42
 
40
- s3fs
41
- ====
42
-
43
- |Build Status| |Doc Status|
44
-
45
- S3FS builds on aiobotocore_ to provide a convenient Python filesystem interface for S3.
46
-
47
- View the documentation_ for s3fs.
48
-
49
- .. _documentation: http://s3fs.readthedocs.io/en/latest/
50
- .. _aiobotocore: https://aiobotocore.readthedocs.io/en/latest/
51
-
52
- .. |Build Status| image:: https://github.com/fsspec/s3fs/workflows/CI/badge.svg
53
- :target: https://github.com/fsspec/s3fs/actions
54
- :alt: Build Status
55
- .. |Doc Status| image:: https://readthedocs.org/projects/s3fs/badge/?version=latest
56
- :target: https://s3fs.readthedocs.io/en/latest/?badge=latest
57
- :alt: Documentation Status
43
+ README.md
@@ -0,0 +1,18 @@
1
+ s3fs
2
+ ====
3
+
4
+ [|Build Status|](https://github.com/fsspec/s3fs/actions)
5
+ [|Documentation|](https://s3fs.readthedocs.io/en/latest/?badge=latest)
6
+
7
+ S3FS builds on [aiobotocore](https://aiobotocore.readthedocs.io/en/latest/)
8
+ to provide a convenient Python filesystem interface for S3.
9
+
10
+
11
+ Support
12
+ -------
13
+
14
+ Work on this repository is supported in part by:
15
+
16
+ "Anaconda, Inc. - Advancing AI through open source."
17
+
18
+ <a href="https://anaconda.com/"><img src="https://camo.githubusercontent.com/b8555ef2222598ed37ce38ac86955febbd25de7619931bb7dd3c58432181d3b6/68747470733a2f2f626565776172652e6f72672f636f6d6d756e6974792f6d656d626572732f616e61636f6e64612f616e61636f6e64612d6c617267652e706e67" alt="anaconda logo" width="40%"/></a>
@@ -1,6 +1,18 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2025.9.0
5
+ --------
6
+
7
+ - update README for distribution compliance
8
+
9
+ 2025.7.0
10
+ --------
11
+
12
+ - fix exclusive write for small files (#974)
13
+ - acknowledge Anaconda support (#972)
14
+ - fix test typo (#970)
15
+
4
16
  2025.5.1
5
17
  --------
6
18
 
@@ -1,3 +1,3 @@
1
1
  aiobotocore>=2.5.4,<3.0.0
2
- fsspec==2025.5.1
2
+ fsspec==2025.9.0
3
3
  aiohttp!=4.0.0a0, !=4.0.0a1
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-05-24T08:13:30-0400",
11
+ "date": "2025-09-02T15:17:31-0400",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "2f3c76d9c36025c7daefa766a8f81c79fc4010e7",
15
- "version": "2025.5.1"
14
+ "full-revisionid": "380b2c6ad868dd90cf8967c862bac1b3154b357b",
15
+ "version": "2025.9.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -2458,6 +2458,7 @@ class S3File(AbstractBufferedFile):
2458
2458
 
2459
2459
  def commit(self):
2460
2460
  logger.debug("Commit %s" % self)
2461
+ match = {"IfNoneMatch": "*"} if "x" in self.mode else {}
2461
2462
  if self.tell() == 0:
2462
2463
  if self.buffer is not None:
2463
2464
  logger.debug("Empty file committed %s" % self)
@@ -2471,15 +2472,11 @@ class S3File(AbstractBufferedFile):
2471
2472
  kw = dict(Key=self.key, Bucket=self.bucket, Body=data, **self.kwargs)
2472
2473
  if self.acl:
2473
2474
  kw["ACL"] = self.acl
2474
- write_result = self._call_s3("put_object", **kw)
2475
+ write_result = self._call_s3("put_object", **kw, **match)
2475
2476
  else:
2476
2477
  raise RuntimeError
2477
2478
  else:
2478
2479
  logger.debug("Complete multi-part upload for %s " % self)
2479
- if "x" in self.mode:
2480
- match = {"IfNoneMatch": "*"}
2481
- else:
2482
- match = {}
2483
2480
  part_info = {"Parts": self.parts}
2484
2481
  write_result = self._call_s3(
2485
2482
  "complete_multipart_upload",
@@ -33,7 +33,6 @@ class TestS3fsPipe(abstract.AbstractPipeTests, S3fsFixtures):
33
33
 
34
34
 
35
35
  class TestS3fsOpen(abstract.AbstractOpenTests, S3fsFixtures):
36
-
37
36
  test_open_exclusive = pytest.mark.xfail(
38
37
  reason="complete_multipart_upload doesn't implement condition in moto"
39
38
  )(abstract.AbstractOpenTests.test_open_exclusive)
@@ -18,6 +18,7 @@ from itertools import chain
18
18
  import fsspec.core
19
19
  from dateutil.tz import tzutc
20
20
 
21
+ import botocore
21
22
  import s3fs.core
22
23
  from s3fs.core import S3FileSystem
23
24
  from s3fs.utils import ignoring, SSEParams
@@ -2759,7 +2760,7 @@ def test_async_stream(s3_base):
2759
2760
  )
2760
2761
  await fs._mkdir(test_bucket_name)
2761
2762
  await fs._pipe(fn, data)
2762
- f = await fs.open_async(fn, mode="rb", block_seze=1000)
2763
+ f = await fs.open_async(fn, mode="rb", block_size=1000)
2763
2764
  while True:
2764
2765
  got = await f.read(1000)
2765
2766
  assert f.size == len(data)
@@ -2888,7 +2889,14 @@ def test_exist_after_delete(s3):
2888
2889
  assert not s3.exists(test_dir)
2889
2890
 
2890
2891
 
2891
- @pytest.mark.xfail(reason="moto doesn't support conditional MPU")
2892
+ # condition: True if running on botocore < 1.36.0
2893
+ # The below tests for exclusive writes will fail on older versions of botocore.
2894
+ old_botocore = version.parse(botocore.__version__) < version.parse("1.36.0")
2895
+
2896
+
2897
+ @pytest.mark.xfail(
2898
+ reason="moto doesn't support IfNoneMatch for MPU when object created via MPU"
2899
+ )
2892
2900
  def test_pipe_exclusive_big(s3):
2893
2901
  chunksize = 5 * 2**20 # minimum allowed
2894
2902
  data = b"x" * chunksize * 3
@@ -2899,17 +2907,82 @@ def test_pipe_exclusive_big(s3):
2899
2907
  assert not s3.list_multipart_uploads(test_bucket_name)
2900
2908
 
2901
2909
 
2902
- @pytest.mark.xfail(reason="moto doesn't support conditional MPU")
2903
- def test_put_exclusive_big(s3, tempdir):
2910
+ @pytest.mark.xfail(
2911
+ old_botocore, reason="botocore<1.33.0 lacks IfNoneMatch support", strict=True
2912
+ )
2913
+ def test_pipe_exclusive_big_after_small(s3):
2914
+ """Test conditional MPU after creating object via put_object
2915
+
2916
+ This test is required because moto's implementation of IfNoneMatch for MPU
2917
+ only works when the object is initially created via put_object and not via
2918
+ MPU.
2919
+ """
2904
2920
  chunksize = 5 * 2**20 # minimum allowed
2905
- data = b"x" * chunksize * 3
2906
- fn = f"{tempdir}/afile"
2907
- with open(fn, "wb") as f:
2908
- f.write(fn)
2909
- s3.put(fn, f"{test_bucket_name}/afile", data, mode="overwrite", chunksize=chunksize)
2910
- s3.put(fn, f"{test_bucket_name}/afile", data, mode="overwrite", chunksize=chunksize)
2921
+
2922
+ # First, create object via put_object (small upload)
2923
+ s3.pipe(f"{test_bucket_name}/afile", b"small", mode="overwrite")
2924
+
2925
+ # Now try multipart upload with mode="create" (should fail)
2911
2926
  with pytest.raises(FileExistsError):
2912
- s3.put(
2913
- fn, f"{test_bucket_name}/afile", data, mode="create", chunksize=chunksize
2927
+ s3.pipe(
2928
+ f"{test_bucket_name}/afile",
2929
+ b"c" * chunksize * 3,
2930
+ mode="create",
2931
+ chunksize=chunksize,
2914
2932
  )
2933
+
2934
+ assert not s3.list_multipart_uploads(test_bucket_name)
2935
+
2936
+
2937
+ @pytest.mark.xfail(
2938
+ reason="moto doesn't support IfNoneMatch for MPU when object created via MPU"
2939
+ )
2940
+ def test_put_exclusive_big(s3, tmpdir):
2941
+ chunksize = 5 * 2**20 # minimum allowed
2942
+ fn = f"{tmpdir}/afile"
2943
+ with open(fn, "wb") as f:
2944
+ f.write(b"x" * chunksize * 3)
2945
+ s3.put(fn, f"{test_bucket_name}/afile", mode="overwrite", chunksize=chunksize)
2946
+ s3.put(fn, f"{test_bucket_name}/afile", mode="overwrite", chunksize=chunksize)
2947
+ with pytest.raises(FileExistsError):
2948
+ s3.put(fn, f"{test_bucket_name}/afile", mode="create", chunksize=chunksize)
2949
+ assert not s3.list_multipart_uploads(test_bucket_name)
2950
+
2951
+
2952
+ @pytest.mark.xfail(
2953
+ old_botocore, reason="botocore<1.33.0 lacks IfNoneMatch support", strict=True
2954
+ )
2955
+ def test_put_exclusive_big_after_small(s3, tmpdir):
2956
+ """Test conditional MPU after creating object via put_object.
2957
+
2958
+ This test is required because moto's implementation of IfNoneMatch for MPU
2959
+ only works when the object is initially created via put_object and not via
2960
+ MPU.
2961
+ """
2962
+ chunksize = 5 * 2**20 # minimum allowed
2963
+ fn = str(tmpdir.join("afile"))
2964
+ with open(fn, "wb") as f:
2965
+ f.write(b"x" * chunksize * 3)
2966
+
2967
+ # First, create object via put_object (small upload)
2968
+ s3.pipe(f"{test_bucket_name}/afile", b"small", mode="overwrite")
2969
+
2970
+ # Now try multipart upload with mode="create" (should fail)
2971
+ with pytest.raises(FileExistsError):
2972
+ s3.put(fn, f"{test_bucket_name}/afile", mode="create", chunksize=chunksize)
2973
+
2974
+ assert not s3.list_multipart_uploads(test_bucket_name)
2975
+
2976
+
2977
+ @pytest.mark.xfail(
2978
+ old_botocore, reason="botocore<1.33.0 lacks IfNoneMatch support", strict=True
2979
+ )
2980
+ def test_put_exclusive_small(s3, tmpdir):
2981
+ fn = f"{tmpdir}/afile"
2982
+ with open(fn, "wb") as f:
2983
+ f.write(b"x")
2984
+ s3.put(fn, f"{test_bucket_name}/afile", mode="overwrite")
2985
+ s3.put(fn, f"{test_bucket_name}/afile", mode="overwrite")
2986
+ with pytest.raises(FileExistsError):
2987
+ s3.put(fn, f"{test_bucket_name}/afile", mode="create")
2915
2988
  assert not s3.list_multipart_uploads(test_bucket_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: s3fs
3
- Version: 2025.5.1
3
+ Version: 2025.9.0
4
4
  Summary: Convenient Filesystem interface over S3
5
5
  Home-page: http://github.com/fsspec/s3fs/
6
6
  Maintainer: Martin Durant
@@ -17,15 +17,18 @@ Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Requires-Python: >= 3.9
20
+ Description-Content-Type: text/markdown
20
21
  License-File: LICENSE.txt
21
22
  Requires-Dist: aiobotocore<3.0.0,>=2.5.4
22
- Requires-Dist: fsspec==2025.5.1
23
+ Requires-Dist: fsspec==2025.9.0
23
24
  Requires-Dist: aiohttp!=4.0.0a0,!=4.0.0a1
24
25
  Provides-Extra: awscli
25
26
  Requires-Dist: aiobotocore[awscli]<3.0.0,>=2.5.4; extra == "awscli"
26
27
  Provides-Extra: boto3
27
28
  Requires-Dist: aiobotocore[boto3]<3.0.0,>=2.5.4; extra == "boto3"
28
29
  Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
29
32
  Dynamic: home-page
30
33
  Dynamic: keywords
31
34
  Dynamic: license
@@ -37,21 +40,4 @@ Dynamic: requires-dist
37
40
  Dynamic: requires-python
38
41
  Dynamic: summary
39
42
 
40
- s3fs
41
- ====
42
-
43
- |Build Status| |Doc Status|
44
-
45
- S3FS builds on aiobotocore_ to provide a convenient Python filesystem interface for S3.
46
-
47
- View the documentation_ for s3fs.
48
-
49
- .. _documentation: http://s3fs.readthedocs.io/en/latest/
50
- .. _aiobotocore: https://aiobotocore.readthedocs.io/en/latest/
51
-
52
- .. |Build Status| image:: https://github.com/fsspec/s3fs/workflows/CI/badge.svg
53
- :target: https://github.com/fsspec/s3fs/actions
54
- :alt: Build Status
55
- .. |Doc Status| image:: https://readthedocs.org/projects/s3fs/badge/?version=latest
56
- :target: https://s3fs.readthedocs.io/en/latest/?badge=latest
57
- :alt: Documentation Status
43
+ README.md
@@ -1,6 +1,6 @@
1
1
  LICENSE.txt
2
2
  MANIFEST.in
3
- README.rst
3
+ README.md
4
4
  requirements.txt
5
5
  setup.cfg
6
6
  setup.py
@@ -1,5 +1,5 @@
1
1
  aiobotocore<3.0.0,>=2.5.4
2
- fsspec==2025.5.1
2
+ fsspec==2025.9.0
3
3
  aiohttp!=4.0.0a0,!=4.0.0a1
4
4
 
5
5
  [awscli]
@@ -39,5 +39,7 @@ setup(
39
39
  "awscli": [f"aiobotocore[awscli]{aiobotocore_version_suffix}"],
40
40
  "boto3": [f"aiobotocore[boto3]{aiobotocore_version_suffix}"],
41
41
  },
42
+ long_description="README.md",
43
+ long_description_content_type="text/markdown",
42
44
  zip_safe=False,
43
45
  )
s3fs-2025.5.1/README.rst DELETED
@@ -1,18 +0,0 @@
1
- s3fs
2
- ====
3
-
4
- |Build Status| |Doc Status|
5
-
6
- S3FS builds on aiobotocore_ to provide a convenient Python filesystem interface for S3.
7
-
8
- View the documentation_ for s3fs.
9
-
10
- .. _documentation: http://s3fs.readthedocs.io/en/latest/
11
- .. _aiobotocore: https://aiobotocore.readthedocs.io/en/latest/
12
-
13
- .. |Build Status| image:: https://github.com/fsspec/s3fs/workflows/CI/badge.svg
14
- :target: https://github.com/fsspec/s3fs/actions
15
- :alt: Build Status
16
- .. |Doc Status| image:: https://readthedocs.org/projects/s3fs/badge/?version=latest
17
- :target: https://s3fs.readthedocs.io/en/latest/?badge=latest
18
- :alt: Documentation Status
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