Skip to content

Commit

Permalink
For Python 3.13: A drop-in replacement for imghdr.what()
Browse files Browse the repository at this point in the history
  • Loading branch information
cclauss committed May 18, 2024
1 parent dbd8ff4 commit 56238d5
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
build:

strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
Expand Down
29 changes: 29 additions & 0 deletions puremagic/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
"multi_part_dict",
]

# Convert puremagic extensions to imghdr extensions
imghdr_exts = {"dib": "bmp", "jfif": "jpeg", "jpg": "jpeg", "rst": "rast", "sun": "rast", "tif": "tiff"}


here = os.path.abspath(os.path.dirname(__file__))

PureMagic = namedtuple(
Expand Down Expand Up @@ -387,5 +391,30 @@ def command_line_entry(*args):
print("'{0}' : could not be Identified".format(fn))


def what(file: Union[os.PathLike, str, None], h: Union[str, bytes, None]) -> str:
"""A drop-in replacement for `imghdr.what()` which was removed from the standard
library in Python 3.13.
Usage:
```python
# Replace...
import imghdr
ext = imghdr.what(...)
# with...
import puremagic
ext = puremagic.what(...)
# ---
# Or replace...
from imghdr import what
# with...
from puremagic import what
```
Source code for `imghdr`: https://github.com/python/cpython/blob/3.12/Lib/imghdr.py
"""
ext = (_magic(h, b"", False) if h else from_file(file, False)).lstrip(".")
return imghdr_exts.get(ext, ext)


if __name__ == "__main__":
command_line_entry()
116 changes: 116 additions & 0 deletions test/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from pathlib import Path
from sys import version_info
from warnings import filterwarnings

try: # imghdr was removed from the standard library in Python 3.13
filterwarnings("ignore", category=DeprecationWarning)
from imghdr import what as imghdr_what
except ModuleNotFoundError:
imghdr_what = None

import pytest

from puremagic.main import PureError, what

files = "bmp exr gif jpg pbm pgm png ppm ras rgb tif webp xbm".split()
files = "bmp gif jpg png tif webp".split() # TODO: (cclauss) Add the remainin files


@pytest.mark.skipif(imghdr_what is None, reason="imghdr was removed from the standard library in Python 3.13")
@pytest.mark.parametrize("file", files)
def test_what_from_file(file, h=None):
file = f"test/resources/images/test.{file}"
assert what(file, h) == imghdr_what(file, h)
file = Path(file).resolve()
assert what(file, h) == imghdr_what(file, h)


string_tests = [
("bmp", b"BM"),
("bmp", "424d"),
("bmp", "424d787878785c3030305c303030"),
("exr", "762f3101"),
("exr", b"\x76\x2f\x31\x01"),
("gif", "474946383761"),
("gif", b"GIF87a"),
("gif", b"GIF89a"),
("pbm", b"P1 "),
("pbm", b"P1\n"),
("pbm", b"P1\r"),
("pbm", b"P1\t"),
("pbm", b"P4 "),
("pbm", b"P4\n"),
("pbm", b"P4\r"),
("pbm", b"P4\t"),
("pgm", b"P2 "),
("pgm", b"P2\n"),
("pgm", b"P2\r"),
("pgm", b"P2\t"),
("pgm", b"P5 "),
("pgm", b"P5\n"),
("pgm", b"P5\r"),
("pgm", b"P5\t"),
("png", "89504e470d0a1a0a"),
("png", b"\211PNG\r\n\032\n"),
("ppm", b"P3 "),
("ppm", b"P3\n"),
("ppm", b"P3\r"),
("ppm", b"P3\t"),
("ppm", b"P6 "),
("ppm", b"P6\n"),
("ppm", b"P6\r"),
("ppm", b"P6\t"),
("rast", b"\x59\xA6\x6A\x95"),
("webp", b"RIFF____WEBP"),
]


@pytest.mark.skipif(imghdr_what is None, reason="imghdr was removed from the standard library in Python 3.13")
@pytest.mark.parametrize("expected, h", string_tests)
def test_what_from_string(expected, h):
if isinstance(h, str):
h = bytes.fromhex(h)
assert imghdr_what(None, h) == what(None, h) == expected


@pytest.mark.skipif(imghdr_what is None, reason="imghdr was removed from the standard library in Python 3.13")
@pytest.mark.parametrize(
"expected, h",
[
("jpeg", "ffd8ffdb"),
("jpeg", b"\xff\xd8\xff\xdb"),
],
)
def test_puremagic_what_from_string_py311(expected, h):
"""
These tests fail with imghdr on Python < 3.11.
TODO: (cclauss) Document these imghdr fails on Python < 3.11
"""
if isinstance(h, str):
h = bytes.fromhex(h)
assert what(None, h) == expected
if version_info < (3, 11): # TODO: Document these imghdr fails
expected = None
assert imghdr_what(None, h) == expected


@pytest.mark.skipif(imghdr_what is None, reason="imghdr was removed from the standard library in Python 3.13")
@pytest.mark.parametrize(
"expected, h",
[
("jpeg", b"______JFIF"),
("jpeg", b"______Exif"),
("rgb", b"\001\332"),
("tiff", b"II"),
("tiff", b"MM"),
("xbm", b"#define "),
],
)
def test_puremagic_what_from_string_todo(expected, h):
"""
These tests pass with imghdr but fail with puremagic.
TODO: (cclauss) Fix these puremagic fails
"""
assert imghdr_what(None, h) == expected
with pytest.raises(PureError): # TODO: Fix these puremagic fails
what(None, h)

0 comments on commit 56238d5

Please sign in to comment.