def test_invalid_file(): with open("Tests/images/flower.jpg", "rb") as fp: with pytest.raises(SyntaxError): IcoImagePlugin.IcoImageFile(fp)
def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: self.assertRaises(InvalidFileType, lambda: IcoImagePlugin.IcoImageFile(fp))
def look_for_icon(self, icon_hash: str) -> Optional[Union['Image.Image', str]]: icon_hash = icon_hash.lower() for icon_path in self.icon_folder.iterdir(): if icon_path.stem.lower() == icon_hash and icon_path.suffix in {'.ico', '.png', '.zip'}: is_zip = zipfile.is_zipfile(icon_path) #Can't just rely on the extension because some zip files like to hide and pretend to be .ico files for some reason with icon_path.open('rb') as test: magic = test.read(4) if magic == b'Rar!': raise IconError('icon {0} is secretly a RAR file and cannot be opened'.format(icon_hash)) if icon_path.suffix == '.ico' and not is_zip: if have_pillow: #.ico files can be a bit flaky with Tumbler thumbnails and some other image-reading stuff, so if we can convert them, that might be a good idea just in case (well, there definitely are some icons that don't thumbnail properly so yeah) try: image = Image.open(icon_path) return image except (ValueError, OSError) as ex: #Try and handle the "This is not one of the allowed sizes of this image" error caused by .ico files having incorrect headers which I guess happens more often than I would have thought otherwise #This is gonna get ugly with icon_path.open('rb') as f: try: #Use BytesIO here to prevent "seeking a closed file" errors, which is probably a sign that I don't actually know what I'm doing ico = IcoImagePlugin.IcoFile(io.BytesIO(f.read())) biggest_size = (0, 0) for size in ico.sizes(): if size[0] > biggest_size[0] and size[1] > biggest_size[1]: biggest_size = size if biggest_size == (0, 0): raise IconError('.ico file {0} has no valid sizes'.format(icon_path)) from ex return ico.getimage(biggest_size) except SyntaxError as syntax_error: #Of all the errors it throws, it throws this one? Well, okay fine whatever raise IconError('.ico file {0} is not actually an .ico file at all'.format(icon_path)) from syntax_error except Exception as ex: #Guess it's still broken raise IconError('.ico file {0} has some annoying error: {1}'.format(icon_path, str(ex))) from ex return icon_path if not is_zip: return icon_path with zipfile.ZipFile(icon_path, 'r') as zip_file: #TODO: Should just make this a comprehension I think, and for that matter could just be a generator since we are only passing it to max icon_files: set[zipfile.ZipInfo] = set() for zip_info in zip_file.infolist(): if zip_info.is_dir(): continue if zip_info.filename.startswith('__MACOSX'): #Yeah that happens with retail Linux games apparently continue if zip_info.filename.lower().endswith(('.ico', '.png')): icon_files.add(zip_info) #Get the biggest image file and assume that's the best icon we can have #extracted_icon_file = sorted(icon_files, key=lambda zip_info: zip_info.file_size, reverse=True)[0] extracted_icon_file = max(icon_files, key=attrgetter('file_size')) extracted_icon_folder = main_config.image_folder.joinpath('Icon', 'extracted_from_zip', icon_hash) return zip_file.extract(extracted_icon_file, path=extracted_icon_folder) raise IconNotFoundError('{0} not found'.format(icon_hash))