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.
- {s3fs-2025.5.1 → s3fs-2025.9.0}/PKG-INFO +6 -20
- s3fs-2025.9.0/README.md +18 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/changelog.rst +12 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/requirements.txt +1 -1
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/_version.py +3 -3
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/core.py +2 -5
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/derived/s3fs_test.py +0 -1
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/test_s3fs.py +85 -12
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/PKG-INFO +6 -20
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/SOURCES.txt +1 -1
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/requires.txt +1 -1
- {s3fs-2025.5.1 → s3fs-2025.9.0}/setup.py +2 -0
- s3fs-2025.5.1/README.rst +0 -18
- {s3fs-2025.5.1 → s3fs-2025.9.0}/LICENSE.txt +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/MANIFEST.in +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/api.rst +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/development.rst +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/index.rst +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/docs/source/install.rst +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/__init__.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/errors.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/mapping.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/__init__.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/derived/__init__.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/derived/s3fs_fixtures.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/test_mapping.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/tests/test_utils.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs/utils.py +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/dependency_links.txt +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/not-zip-safe +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/s3fs.egg-info/top_level.txt +0 -0
- {s3fs-2025.5.1 → s3fs-2025.9.0}/setup.cfg +0 -0
- {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.
|
|
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.
|
|
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
|
-
|
|
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
|
s3fs-2025.9.0/README.md
ADDED
|
@@ -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
|
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-09-02T15:17:31-0400",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2025.
|
|
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",
|
|
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
|
-
|
|
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(
|
|
2903
|
-
|
|
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
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
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.
|
|
2913
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|