remove temporary file the symbol django core files namedtemporaryfile is assigned as a different class on different operating systems in any case the result should minimally mock some of the api of tempfile namedtemporaryfile from the python standard library should not set mode to none if it is not present see 14681 stdlib gzip module crashes if mode is set to none file objects should yield lines when iterated over refs 22107 8149 file objects with rn line endings should yield lines when iterated over 8149 file objects with r line endings should yield lines when iterated over set chunk size to create a boundary after n b onen set chunk size to create a boundary between r and n b onern set chunk size to create a boundary after r b oner other examples of unnamed files may be tempfile spooledtemporaryfile or urllib urlopen the constructor of contentfile accepts name 16590 contentfile can accept both bytes and strings and the retrieved content is of the same type contentfile size changes after a write f contentfile self assertequalf size 0 f writetest f writestring self assertequalf size 11 with f open as fh self assertequalfh read test string class inmemoryuploadedfiletestsunittest testcase def testopenresetsfiletostartandreturnscontextmanagerself uf inmemoryuploadedfilestringio1 test textplain 1 utf8 uf read with uf open as f self assertequalf read 1 class temporaryuploadedfiletestsunittest testcase def testextensionkeptself getimagedimensions properly closes files 8817 open files passed into getimagedimensions should stay opened getimagedimensions called with a filename should closed the file we need to inject a modified open builtin into the images module that checks if the file was closed properly if the function is called with a filename instead of a file object getimagedimensions will call our catchingopen instead of the regular builtin one getimagedimensions works properly after various calls using a file handler 11158 multiple calls of getimagedimensions should return the same size regression test for 19457 getimagedimensions fails on some pngs while image size is working good on them getimagedimensions should return none none for the dimensions of invalid images 24441 brokenimg png is not a valid image and it has been generated by echo 123 brokenimg png getimagedimensions should catch struct error while feeding the pil image parser 24544 emulates the parser feed error since the error is raised on every feed attempt the resulting image size should be invalid none none filemovesafe raises oserror if the destination file exists and allowoverwrite is false should allow it and continue on if allowoverwrite is true filemovesafe ignores permissionerror thrown by copystat and copymode for example permissionerror can be raised when the destination filesystem is cifs or when relabel is disabled by selinux across filesystems this exception is required to reach the copystat call in filesafemove an error besides permissionerror isn t ignored when copystat throws permissionerror copymode error besides permissionerror isn t ignored permissionerror raised by copystat is ignored permissionerror raised by copymode is ignored too remove temporary file the symbol django core files namedtemporaryfile is assigned as a different class on different operating systems in any case the result should minimally mock some of the api of tempfile namedtemporaryfile from the python standard library should not set mode to none if it is not present see 14681 stdlib gzip module crashes if mode is set to none file objects should yield lines when iterated over refs 22107 8149 file objects with r n line endings should yield lines when iterated over 8149 file objects with r line endings should yield lines when iterated over set chunk size to create a boundary after n b one n set chunk size to create a boundary between r and n b one r n set chunk size to create a boundary after r b one r other examples of unnamed files may be tempfile spooledtemporaryfile or urllib urlopen the constructor of contentfile accepts name 16590 contentfile can accept both bytes and strings and the retrieved content is of the same type contentfile size changes after a write the temporary file name has the same suffix as the original file get_image_dimensions properly closes files 8817 open files passed into get_image_dimensions should stay opened get_image_dimensions called with a filename should closed the file we need to inject a modified open builtin into the images module that checks if the file was closed properly if the function is called with a filename instead of a file object get_image_dimensions will call our catching_open instead of the regular builtin one get_image_dimensions works properly after various calls using a file handler 11158 multiple calls of get_image_dimensions should return the same size regression test for 19457 get_image_dimensions fails on some pngs while image size is working good on them get_image_dimensions should return none none for the dimensions of invalid images 24441 brokenimg png is not a valid image and it has been generated by echo 123 brokenimg png get_image_dimensions should catch struct error while feeding the pil image parser 24544 emulates the parser feed error since the error is raised on every feed attempt the resulting image size should be invalid none none file_move_safe raises oserror if the destination file exists and allow_overwrite is false should allow it and continue on if allow_overwrite is true file_move_safe ignores permissionerror thrown by copystat and copymode for example permissionerror can be raised when the destination filesystem is cifs or when relabel is disabled by selinux across filesystems this exception is required to reach the copystat call in file_safe_move an error besides permissionerror isn t ignored when copystat throws permissionerror copymode error besides permissionerror isn t ignored permissionerror raised by copystat is ignored permissionerror raised by copymode is ignored too
import errno
import gzip
import os
import struct
import tempfile
import unittest
from io import BytesIO, StringIO, TextIOWrapper
from pathlib import Path
from unittest import mock

from django.core.files import File, locks
from django.core.files.base import ContentFile
from django.core.files.move import file_move_safe
from django.core.files.temp import NamedTemporaryFile
from django.core.files.uploadedfile import (
    InMemoryUploadedFile,
    SimpleUploadedFile,
    TemporaryUploadedFile,
    UploadedFile,
)
from django.test import override_settings

try:
    from PIL import Image, features

    HAS_WEBP = features.check("webp")
except ImportError:
    Image = None
    HAS_WEBP = False
else:
    from django.core.files import images


class FileTests(unittest.TestCase):
    def test_unicode_uploadedfile_name(self):
        uf = UploadedFile(name="¿Cómo?", content_type="text")
        self.assertIs(type(repr(uf)), str)

    def test_unicode_file_name(self):
        f = File(None, "djángö")
        self.assertIs(type(repr(f)), str)

    def test_context_manager(self):
        orig_file = tempfile.TemporaryFile()
        base_file = File(orig_file)
        with base_file as f:
            self.assertIs(base_file, f)
            self.assertFalse(f.closed)
        self.assertTrue(f.closed)
        self.assertTrue(orig_file.closed)

    def test_open_resets_opened_file_to_start_and_returns_context_manager(self):
        file = File(BytesIO(b"content"))
        file.read()
        with file.open() as f:
            self.assertEqual(f.read(), b"content")

    def test_open_reopens_closed_file_and_returns_context_manager(self):
        temporary_file = tempfile.NamedTemporaryFile(delete=False)
        file = File(temporary_file)
        try:
            file.close()
            with file.open() as f:
                self.assertFalse(f.closed)
        finally:
            
            os.unlink(file.name)

    def test_namedtemporaryfile_closes(self):
        
        tempfile = NamedTemporaryFile()
        self.assertTrue(hasattr(tempfile, "closed"))
        self.assertFalse(tempfile.closed)

        tempfile.close()
        self.assertTrue(tempfile.closed)

    def test_file_mode(self):
        
        
        file = SimpleUploadedFile("mode_test.txt", b"content")
        self.assertFalse(hasattr(file, "mode"))
        gzip.GzipFile(fileobj=file)

    def test_file_iteration(self):
        
        file = File(BytesIO(b"one\ntwo\nthree"))
        self.assertEqual(list(file), [b"one\n", b"two\n", b"three"])

    def test_file_iteration_windows_newlines(self):
        
        f = File(BytesIO(b"one\r\ntwo\r\nthree"))
        self.assertEqual(list(f), [b"one\r\n", b"two\r\n", b"three"])

    def test_file_iteration_mac_newlines(self):
        
        f = File(BytesIO(b"one\rtwo\rthree"))
        self.assertEqual(list(f), [b"one\r", b"two\r", b"three"])

    def test_file_iteration_mixed_newlines(self):
        f = File(BytesIO(b"one\rtwo\nthree\r\nfour"))
        self.assertEqual(list(f), [b"one\r", b"two\n", b"three\r\n", b"four"])

    def test_file_iteration_with_unix_newline_at_chunk_boundary(self):
        f = File(BytesIO(b"one\ntwo\nthree"))
        
        
        
        f.DEFAULT_CHUNK_SIZE = 4
        self.assertEqual(list(f), [b"one\n", b"two\n", b"three"])

    def test_file_iteration_with_windows_newline_at_chunk_boundary(self):
        f = File(BytesIO(b"one\r\ntwo\r\nthree"))
        
        
        
        f.DEFAULT_CHUNK_SIZE = 4
        self.assertEqual(list(f), [b"one\r\n", b"two\r\n", b"three"])

    def test_file_iteration_with_mac_newline_at_chunk_boundary(self):
        f = File(BytesIO(b"one\rtwo\rthree"))
        
        
        
        f.DEFAULT_CHUNK_SIZE = 4
        self.assertEqual(list(f), [b"one\r", b"two\r", b"three"])

    def test_file_iteration_with_text(self):
        f = File(StringIO("one\ntwo\nthree"))
        self.assertEqual(list(f), ["one\n", "two\n", "three"])

    def test_readable(self):
        with tempfile.TemporaryFile() as temp, File(
            temp, name="something.txt"
        ) as test_file:
            self.assertTrue(test_file.readable())
        self.assertFalse(test_file.readable())

    def test_writable(self):
        with tempfile.TemporaryFile() as temp, File(
            temp, name="something.txt"
        ) as test_file:
            self.assertTrue(test_file.writable())
        self.assertFalse(test_file.writable())
        with tempfile.TemporaryFile("rb") as temp, File(
            temp, name="something.txt"
        ) as test_file:
            self.assertFalse(test_file.writable())

    def test_seekable(self):
        with tempfile.TemporaryFile() as temp, File(
            temp, name="something.txt"
        ) as test_file:
            self.assertTrue(test_file.seekable())
        self.assertFalse(test_file.seekable())

    def test_io_wrapper(self):
        content = "vive l'été\n"
        with tempfile.TemporaryFile() as temp, File(
            temp, name="something.txt"
        ) as test_file:
            test_file.write(content.encode())
            test_file.seek(0)
            wrapper = TextIOWrapper(test_file, "utf-8", newline="\n")
            self.assertEqual(wrapper.read(), content)
            wrapper.write(content)
            wrapper.seek(0)
            self.assertEqual(wrapper.read(), content * 2)
            test_file = wrapper.detach()
            test_file.seek(0)
            self.assertEqual(test_file.read(), (content * 2).encode())

    def test_exclusive_lock(self):
        file_path = Path(__file__).parent / "test.png"
        with open(file_path) as f1, open(file_path) as f2:
            self.assertIs(locks.lock(f1, locks.LOCK_EX), True)
            self.assertIs(locks.lock(f2, locks.LOCK_EX | locks.LOCK_NB), False)
            self.assertIs(locks.lock(f2, locks.LOCK_SH | locks.LOCK_NB), False)
            self.assertIs(locks.unlock(f1), True)

    def test_shared_lock(self):
        file_path = Path(__file__).parent / "test.png"
        with open(file_path) as f1, open(file_path) as f2:
            self.assertIs(locks.lock(f1, locks.LOCK_SH), True)
            self.assertIs(locks.lock(f2, locks.LOCK_SH | locks.LOCK_NB), True)
            self.assertIs(locks.unlock(f1), True)
            self.assertIs(locks.unlock(f2), True)

    def test_open_supports_full_signature(self):
        called = False

        def opener(path, flags):
            nonlocal called
            called = True
            return os.open(path, flags)

        file_path = Path(__file__).parent / "test.png"
        with open(file_path) as f:
            test_file = File(f)

        with test_file.open(opener=opener):
            self.assertIs(called, True)


class NoNameFileTestCase(unittest.TestCase):
    

    def test_noname_file_default_name(self):
        self.assertIsNone(File(BytesIO(b"A file with no name")).name)

    def test_noname_file_get_size(self):
        self.assertEqual(File(BytesIO(b"A file with no name")).size, 19)


class ContentFileTestCase(unittest.TestCase):
    def test_content_file_default_name(self):
        self.assertIsNone(ContentFile(b"content").name)

    def test_content_file_custom_name(self):
        
        name = "I can have a name too!"
        self.assertEqual(ContentFile(b"content", name=name).name, name)

    def test_content_file_input_type(self):
        
        self.assertIsInstance(ContentFile(b"content").read(), bytes)
        self.assertIsInstance(ContentFile("español").read(), str)

    def test_open_resets_file_to_start_and_returns_context_manager(self):
        file = ContentFile(b"content")
        with file.open() as f:
            self.assertEqual(f.read(), b"content")
        with file.open() as f:
            self.assertEqual(f.read(), b"content")

    def test_size_changing_after_writing(self):
        
        f = ContentFile("")
        self.assertEqual(f.size, 0)
        f.write("Test ")
        f.write("string")
        self.assertEqual(f.size, 11)
        with f.open() as fh:
            self.assertEqual(fh.read(), "Test string")


class InMemoryUploadedFileTests(unittest.TestCase):
    def test_open_resets_file_to_start_and_returns_context_manager(self):
        uf = InMemoryUploadedFile(StringIO("1"), "", "test", "text/plain", 1, "utf8")
        uf.read()
        with uf.open() as f:
            self.assertEqual(f.read(), "1")


class TemporaryUploadedFileTests(unittest.TestCase):
    def test_extension_kept(self):
        
        with TemporaryUploadedFile("test.txt", "text/plain", 1, "utf8") as temp_file:
            self.assertTrue(temp_file.file.name.endswith(".upload.txt"))

    def test_file_upload_temp_dir_pathlib(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            with override_settings(FILE_UPLOAD_TEMP_DIR=Path(tmp_dir)):
                with TemporaryUploadedFile(
                    "test.txt", "text/plain", 1, "utf-8"
                ) as temp_file:
                    self.assertTrue(os.path.exists(temp_file.file.name))


class DimensionClosingBug(unittest.TestCase):
    

    @unittest.skipUnless(Image, "Pillow not installed")
    def test_not_closing_of_files(self):
        
        empty_io = BytesIO()
        try:
            images.get_image_dimensions(empty_io)
        finally:
            self.assertTrue(not empty_io.closed)

    @unittest.skipUnless(Image, "Pillow not installed")
    def test_closing_of_filenames(self):
        
        
        
        
        
        

        class FileWrapper:
            _closed = []

            def __init__(self, f):
                self.f = f

            def __getattr__(self, name):
                return getattr(self.f, name)

            def close(self):
                self._closed.append(True)
                self.f.close()

        def catching_open(*args):
            return FileWrapper(open(*args))

        images.open = catching_open
        try:
            images.get_image_dimensions(
                os.path.join(os.path.dirname(__file__), "test1.png")
            )
        finally:
            del images.open
        self.assertTrue(FileWrapper._closed)


class InconsistentGetImageDimensionsBug(unittest.TestCase):
    

    @unittest.skipUnless(Image, "Pillow not installed")
    def test_multiple_calls(self):
        
        img_path = os.path.join(os.path.dirname(__file__), "test.png")
        with open(img_path, "rb") as fh:
            image = images.ImageFile(fh)
            image_pil = Image.open(fh)
            size_1 = images.get_image_dimensions(image)
            size_2 = images.get_image_dimensions(image)
        self.assertEqual(image_pil.size, size_1)
        self.assertEqual(size_1, size_2)

    @unittest.skipUnless(Image, "Pillow not installed")
    def test_bug_19457(self):
        
        img_path = os.path.join(os.path.dirname(__file__), "magic.png")
        size = images.get_image_dimensions(img_path)
        with open(img_path, "rb") as fh:
            self.assertEqual(size, Image.open(fh).size)


@unittest.skipUnless(Image, "Pillow not installed")
class GetImageDimensionsTests(unittest.TestCase):
    def test_invalid_image(self):
        
        img_path = os.path.join(os.path.dirname(__file__), "brokenimg.png")
        with open(img_path, "rb") as fh:
            size = images.get_image_dimensions(fh)
            self.assertEqual(size, (None, None))

    def test_valid_image(self):
        
        img_path = os.path.join(os.path.dirname(__file__), "test.png")
        with mock.patch("PIL.ImageFile.Parser.feed", side_effect=struct.error):
            with open(img_path, "rb") as fh:
                size = images.get_image_dimensions(fh)
                self.assertEqual(size, (None, None))

    def test_missing_file(self):
        size = images.get_image_dimensions("missing.png")
        self.assertEqual(size, (None, None))

    @unittest.skipUnless(HAS_WEBP, "WEBP not installed")
    def test_webp(self):
        img_path = os.path.join(os.path.dirname(__file__), "test.webp")
        with open(img_path, "rb") as fh:
            size = images.get_image_dimensions(fh)
        self.assertEqual(size, (540, 405))


class FileMoveSafeTests(unittest.TestCase):
    def test_file_move_overwrite(self):
        handle_a, self.file_a = tempfile.mkstemp()
        handle_b, self.file_b = tempfile.mkstemp()

        
        
        with self.assertRaises(FileExistsError):
            file_move_safe(self.file_a, self.file_b, allow_overwrite=False)

        
        self.assertIsNone(
            file_move_safe(self.file_a, self.file_b, allow_overwrite=True)
        )

        os.close(handle_a)
        os.close(handle_b)

    def test_file_move_permissionerror(self):
        
        permission_error = PermissionError(errno.EPERM, "msg")
        os_error = OSError("msg")
        handle_a, self.file_a = tempfile.mkstemp()
        handle_b, self.file_b = tempfile.mkstemp()
        handle_c, self.file_c = tempfile.mkstemp()
        try:
            
            
            with mock.patch("django.core.files.move.os.rename", side_effect=OSError()):
                
                with mock.patch(
                    "django.core.files.move.copystat", side_effect=os_error
                ):
                    with self.assertRaises(OSError):
                        file_move_safe(self.file_a, self.file_b, allow_overwrite=True)
                
                
                with mock.patch(
                    "django.core.files.move.copystat", side_effect=permission_error
                ):
                    with mock.patch(
                        "django.core.files.move.copymode", side_effect=os_error
                    ):
                        with self.assertRaises(OSError):
                            file_move_safe(
                                self.file_a, self.file_b, allow_overwrite=True
                            )
                
                with mock.patch(
                    "django.core.files.move.copystat", side_effect=permission_error
                ):
                    self.assertIsNone(
                        file_move_safe(self.file_a, self.file_b, allow_overwrite=True)
                    )
                    
                    with mock.patch(
                        "django.core.files.move.copymode", side_effect=permission_error
                    ):
                        self.assertIsNone(
                            file_move_safe(
                                self.file_c, self.file_b, allow_overwrite=True
                            )
                        )
        finally:
            os.close(handle_a)
            os.close(handle_b)
            os.close(handle_c)


class SpooledTempTests(unittest.TestCase):
    def test_in_memory_spooled_temp(self):
        with tempfile.SpooledTemporaryFile() as temp:
            temp.write(b"foo bar baz quux\n")
            django_file = File(temp, name="something.txt")
            self.assertEqual(django_file.size, 17)

    def test_written_spooled_temp(self):
        with tempfile.SpooledTemporaryFile(max_size=4) as temp:
            temp.write(b"foo bar baz quux\n")
            django_file = File(temp, name="something.txt")
            self.assertEqual(django_file.size, 17)