@@ -125,6 +125,15 @@ class ReadableBinaryFile(Protocol):
125
125
def read (self , size : int = ...) -> bytes : ...
126
126
127
127
128
+ class NonCloseableBufferedReader (io .BufferedReader ):
129
+ def __init__ (self , raw : ReadableBinaryFile , * args : Any , ** kwargs : Any ):
130
+ super ().__init__ (cast (io .RawIOBase , raw ), * args , ** kwargs )
131
+
132
+ def close (self ) -> None :
133
+ # Don't close the underlying file object, just flush the buffer.
134
+ self .flush ()
135
+
136
+
128
137
class StreamingBodyIOAdapter (io .RawIOBase ):
129
138
"""
130
139
Wrapper to adapt a boto3 S3 object to a standard Python "file-like" object
@@ -465,7 +474,7 @@ def store_file(
465
474
_log_existing_file_returned (key_path )
466
475
return self .bucket_name , existing_boto_object .key
467
476
468
- readable = _to_stream (contents = contents )
477
+ readable = NonCloseableBufferedReader ( _to_stream (contents = contents ) )
469
478
470
479
# `boto_client.upload_fileobj` is type annotated with `Fileobj: BinaryIO`. However, in
471
480
# practice the only file-like method it needs is `read(size=...)`. This cast allows us to
@@ -489,6 +498,11 @@ def store_file(
489
498
ExtraArgs = extra_args ,
490
499
)
491
500
501
+ # we call readable.detach instead of file_obj.detach because mypy
502
+ # assumes file_obj is a BinaryIO and not a BufferedReader thus
503
+ # doesn't have a detach method.
504
+ readable .detach ()
505
+
492
506
return self .bucket_name , key_path
493
507
494
508
def store_versioned_file (
0 commit comments