From a95a8da29e1b2f5e76b4cd56f3717a929497f5ec Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 2 Mar 2023 10:25:57 +0100 Subject: [PATCH] Refs #34118 -- Used delete_on_close argument of tempfile.NamedTemporaryFile on Windows and Python 3.12. delete_on_close is available in Python 3.12: - https://github.com/python/cpython/issues/58451 - https://github.com/python/cpython/pull/97015 so we don't need a custom NamedTemporaryFile implementation anymore. --- django/core/files/temp.py | 95 ++++++++++++++++++++------------------- django/utils/version.py | 1 + 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/django/core/files/temp.py b/django/core/files/temp.py index 5bd31dd5f23a..26465fbdf430 100644 --- a/django/core/files/temp.py +++ b/django/core/files/temp.py @@ -18,8 +18,10 @@ import os import tempfile +from functools import partial from django.core.files.utils import FileProxyMixin +from django.utils.version import PY312 __all__ = ( "NamedTemporaryFile", @@ -28,51 +30,54 @@ if os.name == "nt": - - class TemporaryFile(FileProxyMixin): - """ - Temporary file object constructor that supports reopening of the - temporary file in Windows. - - Unlike tempfile.NamedTemporaryFile from the standard library, - __init__() doesn't support the 'delete', 'buffering', 'encoding', or - 'newline' keyword arguments. - """ - - def __init__(self, mode="w+b", bufsize=-1, suffix="", prefix="", dir=None): - fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir) - self.name = name - self.file = os.fdopen(fd, mode, bufsize) - self.close_called = False - - # Because close can be called during shutdown - # we need to cache os.unlink and access it - # as self.unlink only - unlink = os.unlink - - def close(self): - if not self.close_called: - self.close_called = True - try: - self.file.close() - except OSError: - pass - try: - self.unlink(self.name) - except OSError: - pass - - def __del__(self): - self.close() - - def __enter__(self): - self.file.__enter__() - return self - - def __exit__(self, exc, value, tb): - self.file.__exit__(exc, value, tb) - - NamedTemporaryFile = TemporaryFile + if PY312: + NamedTemporaryFile = partial(tempfile.NamedTemporaryFile, delete_on_close=False) + else: + # TODO: Remove when dropping support for PY311. + class TemporaryFile(FileProxyMixin): + """ + Temporary file object constructor that supports reopening of the + temporary file in Windows. + + Unlike tempfile.NamedTemporaryFile from the standard library, + __init__() doesn't support the 'delete', 'buffering', 'encoding', or + 'newline' keyword arguments. + """ + + def __init__(self, mode="w+b", bufsize=-1, suffix="", prefix="", dir=None): + fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir) + self.name = name + self.file = os.fdopen(fd, mode, bufsize) + self.close_called = False + + # Because close can be called during shutdown + # we need to cache os.unlink and access it + # as self.unlink only + unlink = os.unlink + + def close(self): + if not self.close_called: + self.close_called = True + try: + self.file.close() + except OSError: + pass + try: + self.unlink(self.name) + except OSError: + pass + + def __del__(self): + self.close() + + def __enter__(self): + self.file.__enter__() + return self + + def __exit__(self, exc, value, tb): + self.file.__exit__(exc, value, tb) + + NamedTemporaryFile = TemporaryFile else: NamedTemporaryFile = tempfile.NamedTemporaryFile diff --git a/django/utils/version.py b/django/utils/version.py index 5b6a435b518f..1e9c7202990f 100644 --- a/django/utils/version.py +++ b/django/utils/version.py @@ -16,6 +16,7 @@ PY39 = sys.version_info >= (3, 9) PY310 = sys.version_info >= (3, 10) PY311 = sys.version_info >= (3, 11) +PY312 = sys.version_info >= (3, 12) def get_version(version=None):