@@ -1466,33 +1466,33 @@ def open_stream(
14661466 s3_signature_version ,
14671467 )
14681468
1469- # Always run the close + upload procedure
1470- # before any code from Python's NamedTemporaryFile wrapper.
1471- # It isn't safe to call a bound method from a weakref finalizer,
1472- # but calling a weakref finalizer alongside a bound method
1473- # creates no problems, other than that the code outside the
1474- # finalizer is not guaranteed to be run at any point.
1475- # In this case, the weakref finalizer performs all necessary
1476- # cleanup itself, but the original NamedTemporaryFile methods
1477- # are invoked as well, just in case.
1478- wrapped_close = temp_file . close
1479-
1480- def close_wrapper ():
1481- guaranteed_closer ()
1482- return wrapped_close ()
1483-
1484- # Python 3.12+ doesn't call NamedTemporaryFile.close() during
1485- # .__exit__(), so it must be wrapped separately.
1486- # Since guaranteed_closer is idempotent, it's fine to call it in
1487- # both methods, even if both are called back-to-back.
1488- wrapped_exit = temp_file .__exit__
1489-
1490- def exit_wrapper ( exc , value , tb ):
1491- guaranteed_closer ()
1492- return wrapped_exit ( exc , value , tb )
1493-
1494- temp_file . close = close_wrapper
1495- temp_file .__exit__ = exit_wrapper
1469+ # Create a class dynamically to wrap methods, as __exit__ is always
1470+ # looked up on the class of an object even if a function with the
1471+ # same name exists in the instance dictionary.
1472+ # noinspection PyAbstractClass
1473+ class S3TemporaryFileWrapper ( temp_file . __class__ ):
1474+ # Always run the close + upload procedure
1475+ # before any code from Python's NamedTemporaryFile wrapper.
1476+ # It isn't safe to call a bound method from a weakref finalizer,
1477+ # but calling a weakref finalizer alongside a bound method
1478+ # creates no problems, other than that the code outside the
1479+ # finalizer is not guaranteed to be run at any point.
1480+ # In this case, the weakref finalizer performs all necessary
1481+ # cleanup itself, but the original NamedTemporaryFile methods
1482+ # are invoked as well, just in case.
1483+ def close ( self ):
1484+ guaranteed_closer ()
1485+ return super (). close ()
1486+
1487+ # Python 3.12+ doesn't call NamedTemporaryFile.close() during
1488+ # .__exit__(), so it must be wrapped separately.
1489+ # Since guaranteed_closer is idempotent, it's fine to call it in
1490+ # both methods, even if both are called back-to-back.
1491+ def __exit__ ( self , exc , value , tb ):
1492+ guaranteed_closer ( )
1493+ return super (). __exit__ ( exc , value , tb )
1494+
1495+ temp_file .__class__ = S3TemporaryFileWrapper
14961496
14971497 return temp_file
14981498 else :
0 commit comments