@@ -246,17 +246,19 @@ def write_file(path, data, append=False, forced=False, backup=False, always_over
246246 # cfr. https://docs.python.org/3/library/functions.html#open
247247 mode = 'a' if append else 'w'
248248
249- is_file_like = hasattr (data , 'read' )
249+ data_is_file_obj = all ( hasattr (data , x ) for x in ( 'read' , 'seek' , 'tell' , 'write' ) )
250250
251251 # special care must be taken with binary data in Python 3
252- if sys .version_info [0 ] >= 3 and (isinstance (data , bytes ) or is_file_like ):
252+ if sys .version_info [0 ] >= 3 and (isinstance (data , bytes ) or data_is_file_obj ):
253253 mode += 'b'
254254
255255 # note: we can't use try-except-finally, because Python 2.4 doesn't support it as a single block
256256 try :
257257 mkdir (os .path .dirname (path ), parents = True )
258258 with open_file (path , mode ) as fh :
259- if is_file_like :
259+ if data_is_file_obj :
260+ # if a file-like object was provided, use copyfileobj (which reads the file in chunks)
261+ data .seek (0 )
260262 shutil .copyfileobj (data , fh )
261263 else :
262264 fh .write (data )
@@ -715,6 +717,10 @@ def download_file(filename, url, path, forced=False):
715717 url_fd = response .raw
716718 url_fd .decode_content = True
717719 _log .debug ('response code for given url %s: %s' % (url , status_code ))
720+ # note: we pass the file object to write_file rather than reading the file first,
721+ # to ensure the data is read in chunks (which prevents problems in Python 3.9+);
722+ # cfr. https://github.com/easybuilders/easybuild-framework/issues/3455
723+ # and https://bugs.python.org/issue42853
718724 write_file (path , url_fd , forced = forced , backup = True )
719725 _log .info ("Downloaded file %s from url %s to %s" % (filename , url , path ))
720726 downloaded = True
0 commit comments