#8817 closed (fixed)
Accessing ImageField's dimensions doesn't close file
Reported by: | tripediac | Owned by: | Armin Ronacher |
---|---|---|---|
Component: | File uploads/storage | Version: | dev |
Severity: | Keywords: | ||
Cc: | bthomas@…, simon@… | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
If you access an ImageField's width or height property, the corresponding image file doesn't seem to be closed after retrieving its dimensions: After retrieving a lot of image dimensions (of different images), Django produces strange errors ("cannot open template file ..." etc.) which seem to result from too many files being open by the process. Manually calling the close() method of the ImageFieldFile object (after accessing the width and height properties) resolves these errors, so it seems it isn't not called automatically...
Attachments (4)
Change History (20)
comment:1 by , 16 years ago
milestone: | → 1.1 |
---|---|
Triage Stage: | Unreviewed → Accepted |
by , 16 years ago
comment:2 by , 16 years ago
Has patch: | set |
---|---|
Needs tests: | set |
I have a possible fix for this. There are several problems here that make a clean solution difficult. The patch I attached solves the problem by opening a new file handle to read the image dimensions, instead of reading from the open file-like object passed to it. This avoids the problem of the FileField.file attribute transparently opening the file, which can't be easily detected inside of get_image_dimensions. A method like get_image_dimensions shouldn't have side effects, but it will alter the file pointer's position by reading from it. This patch prevents any side effects in most cases, but it will still alter a file object that is not based on django.core.files.base.File (this should be fixed, but it's a slightly separate issue).
No unit tests yet, but it passes the existing file_storage tests.
Calling file.close() at the end may not be necessary (the interpreter should clean up when it drops out of scope), but explicit is probably better than implicit here.
comment:3 by , 16 years ago
Cc: | added |
---|---|
Needs tests: | unset |
Added unit tests. Easier than I thought, thanks to the existing tests for #8534
by , 16 years ago
Attachment: | 8817_2.diff added |
---|
changed patch to reset the file position before and after reading the dimensions. Without these changes the Model.save() would fail if the file position was non zero, for example by validating the image dimensions in a forms clean method.
comment:4 by , 16 years ago
This bug is also causing the file storage regression tests to fail on Windows, because an open file handle will cause the shutil.rmtree(temp_storage_dir)
at the end to fail.
comment:5 by , 16 years ago
I don't think that doing a seek(0) at the end is really the correct behavior. It would still modify the position on the stream if it was nonzero before. Getting image dimensions should have *no* side effects. However, this is really a separate bug from leaving the file handle open, and I think there is already a separate bug for doing a seek(0) before attempting to save.
comment:6 by , 16 years ago
Cc: | added |
---|
comment:7 by , 16 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
Here an updated version with a proper fix, no seek (which is against the common interface of functions accepting both strings and files) and a testcase.
comment:8 by , 16 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:9 by , 16 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:10 by , 16 years ago
comment:11 by , 16 years ago
The fix for this should have fixed #11032 but for some reason it hasn't. Using trunk r10707 on Windows with PIL installed, running the file_storage tests fails due to mug being open, therefore the attempt to delete the temp directory fails. Removing lines 36-43 of regressiontests/file_storage/models.py (the lines that access the dimensions) makes the test pass. Apparently the dimensions are read from an already-open file but the access of them is causing the file to not be subsequently closed? I don't understand what's going on here, and unfortunately I've run out of time to look at this for several hours, so I'll settle for leaving this pesky comment.
comment:12 by , 16 years ago
I'm looking into the test failure on windows right now. I have a semi-finished patch for the temporary file that could fix that problem.
comment:13 by , 16 years ago
I personally think the problem should better be solved by replacing the custom NamedTemporaryFile
with the standard one from the tempfile
module. I can't really see what the one in django is used for. The docstring says this:
The temp module provides a NamedTemporaryFile that can be re-opened on any platform. Most platforms use the standard Python tempfile.TemporaryFile class, but MS Windows users are given a custom class. This is needed because in Windows NT, the default implementation of NamedTemporaryFile uses the O_TEMPORARY flag, and thus cannot be reopened [1]. 1: http://mail.python.org/pipermail/python-list/2005-December/359474.html
The point of the temporary file is that it disappears if close()
is called. Why is there code doing something like open(f.name, 'r')
on a temporary file in django? Maybe that could be refactored that it does not open the file a second time. If not it would probably make sense to open the file using the windows API and by passing all FILE_SHARE_
flags to it and the FILE_ATTRIBUTE_TEMPORARY
and FILE_FLAG_DELETE_ON_CLOSE
flags. That should give a posix like behavior.
Open a new file handle to read image dimensions