Check that the file-upload field data contains a valid image (GIF, JPG, PNG, etc. -- whatever Pillow supports).
(self, data)
| 720 | } |
| 721 | |
| 722 | def to_python(self, data): |
| 723 | """ |
| 724 | Check that the file-upload field data contains a valid image (GIF, JPG, |
| 725 | PNG, etc. -- whatever Pillow supports). |
| 726 | """ |
| 727 | f = super().to_python(data) |
| 728 | if f is None: |
| 729 | return None |
| 730 | |
| 731 | from PIL import Image |
| 732 | |
| 733 | # We need to get a file object for Pillow. We might have a path or we |
| 734 | # might have to read the data into memory. |
| 735 | if hasattr(data, "temporary_file_path"): |
| 736 | file = data.temporary_file_path() |
| 737 | else: |
| 738 | if hasattr(data, "read"): |
| 739 | file = BytesIO(data.read()) |
| 740 | else: |
| 741 | file = BytesIO(data["content"]) |
| 742 | |
| 743 | try: |
| 744 | # load() could spot a truncated JPEG, but it loads the entire |
| 745 | # image in memory, which is a DoS vector. See #3848 and #18520. |
| 746 | image = Image.open(file) |
| 747 | # verify() must be called immediately after the constructor. |
| 748 | image.verify() |
| 749 | |
| 750 | # Annotating so subclasses can reuse it for their own validation |
| 751 | f.image = image |
| 752 | # Pillow doesn't detect the MIME type of all formats. In those |
| 753 | # cases, content_type will be None. |
| 754 | f.content_type = Image.MIME.get(image.format) |
| 755 | except Exception as exc: |
| 756 | # Pillow doesn't recognize it as an image. |
| 757 | raise ValidationError( |
| 758 | self.error_messages["invalid_image"], |
| 759 | code="invalid_image", |
| 760 | ) from exc |
| 761 | if hasattr(f, "seek") and callable(f.seek): |
| 762 | f.seek(0) |
| 763 | return f |
| 764 | |
| 765 | def widget_attrs(self, widget): |
| 766 | attrs = super().widget_attrs(widget) |
nothing calls this directly
no test coverage detected