Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add more typing definitions #181

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 57 additions & 32 deletions EXIF.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,68 @@
Runs Exif tag extraction in command line.
"""

import sys
import argparse
import sys
import timeit

from exifread import __version__, exif_log, process_file
from exifread.tags import FIELD_TYPES
from exifread import process_file, exif_log, __version__

logger = exif_log.get_logger()


def get_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog='EXIF.py',
description='Extract EXIF information from digital image files.'
prog="EXIF.py", description="Extract EXIF information from digital image files."
)
parser.add_argument(
'files', metavar='FILE', type=str, nargs='+',
help='files to process',
"files",
metavar="FILE",
type=str,
nargs="+",
help="files to process",
)
parser.add_argument(
'-v', '--version', action='version',
version='EXIF.py Version %s on Python%s' % (__version__, sys.version_info[0]),
help='Display version information and exit'
"-v",
"--version",
action="version",
version="EXIF.py Version %s on Python%s" % (__version__, sys.version_info[0]),
help="Display version information and exit",
)
parser.add_argument(
'-q', '--quick', action='store_false', dest='detailed',
help='Do not process MakerNotes and do not extract thumbnails',
"-q",
"--quick",
action="store_false",
dest="detailed",
help="Do not process MakerNotes and do not extract thumbnails",
)
parser.add_argument(
'-t', '--tag', type=str, dest='stop_tag',
help='Stop processing when this tag is retrieved.',
"-t",
"--tag",
type=str,
dest="stop_tag",
help="Stop processing when this tag is retrieved.",
)
parser.add_argument(
'-s', '--strict', action='store_true', dest='strict',
help='Run in strict mode (stop on errors).',
"-s",
"--strict",
action="store_true",
dest="strict",
help="Run in strict mode (stop on errors).",
)
parser.add_argument(
'-d', '--debug', action='store_true', dest='debug',
help='Run in debug mode (display extra info).',
"-d",
"--debug",
action="store_true",
dest="debug",
help="Run in debug mode (display extra info).",
)
parser.add_argument(
'-c', '--color', action='store_true', dest='color',
help='Output in color (only works with debug on POSIX).',
"-c",
"--color",
action="store_true",
dest="color",
help="Output in color (only works with debug on POSIX).",
)
args = parser.parse_args()
return args
Expand All @@ -72,16 +92,16 @@ def main(args) -> None:
for filename in args.files:
# avoid errors when printing to console
escaped_fn = escaped_fn = filename.encode(
sys.getfilesystemencoding(), 'surrogateescape'
sys.getfilesystemencoding(), "surrogateescape"
).decode()

file_start = timeit.default_timer()
try:
img_file = open(escaped_fn, 'rb')
img_file = open(escaped_fn, "rb")
except IOError:
logger.error("'%s' is unreadable", escaped_fn)
continue
logger.info('Opening: %s', escaped_fn)
logger.info("Opening: %s", escaped_fn)

tag_start = timeit.default_timer()

Expand All @@ -92,29 +112,34 @@ def main(args) -> None:
details=args.detailed,
strict=args.strict,
debug=args.debug,
extract_thumbnail=args.detailed
extract_thumbnail=args.detailed,
)

tag_stop = timeit.default_timer()

if not data:
logger.warning('No EXIF information found')
logger.warning("No EXIF information found")
print()
continue

if 'JPEGThumbnail' in data:
logger.info('File has JPEG thumbnail')
del data['JPEGThumbnail']
if 'TIFFThumbnail' in data:
logger.info('File has TIFF thumbnail')
del data['TIFFThumbnail']
if "JPEGThumbnail" in data:
logger.info("File has JPEG thumbnail")
del data["JPEGThumbnail"]
if "TIFFThumbnail" in data:
logger.info("File has TIFF thumbnail")
del data["TIFFThumbnail"]

tag_keys = list(data.keys())
tag_keys.sort()

for i in tag_keys:
try:
logger.info('%s (%s): %s', i, FIELD_TYPES[data[i].field_type][2], data[i].printable)
logger.info(
"%s (%s): %s",
i,
FIELD_TYPES[data[i].field_type][2],
data[i].printable,
)
except:
logger.error("%s : %s", i, str(data[i]))

Expand All @@ -125,5 +150,5 @@ def main(args) -> None:
print()


if __name__ == '__main__':
if __name__ == "__main__":
main(get_args())
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Supported formats: TIFF, JPEG, PNG, Webp, HEIC
Compatibility
*************

EXIF.py is tested and officially supported on Python 3.5 to 3.11
EXIF.py is tested and officially supported on Python 3.6 to 3.11

Starting with version ``3.0.0``, Python2 compatibility is dropped *completely* (syntax errors due to type hinting).

Expand Down
44 changes: 27 additions & 17 deletions exifread/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@
"""

import struct
from typing import BinaryIO
from typing import BinaryIO, Dict, Tuple

from exifread.exif_log import get_logger
from exifread.classes import ExifHeader
from exifread.tags import DEFAULT_STOP_TAG
from exifread.utils import ord_, make_string
from exifread.exceptions import ExifNotFound, InvalidExif
from exifread.exif_log import get_logger
from exifread.heic import HEICExifFinder
from exifread.jpeg import find_jpeg_exif
from exifread.exceptions import InvalidExif, ExifNotFound
from exifread.tags import DEFAULT_STOP_TAG
from exifread.utils import make_string, ord_

__version__ = '3.1.0'

logger = get_logger()


def _find_tiff_exif(fh: BinaryIO) -> tuple:
ENDIAN_TYPES: Dict[str, str] = {
'I': 'Intel',
'M': 'Motorola',
'\x01': 'Adobe Ducky',
'd': 'XMP/Adobe unknown'
}


def _find_tiff_exif(fh: BinaryIO) -> Tuple[int, bytes]:
logger.debug("TIFF format recognized in data[0:2]")
fh.seek(0)
endian = fh.read(1)
Expand All @@ -27,7 +35,7 @@ def _find_tiff_exif(fh: BinaryIO) -> tuple:
return offset, endian


def _find_webp_exif(fh: BinaryIO) -> tuple:
def _find_webp_exif(fh: BinaryIO) -> Tuple[int, bytes]:
logger.debug("WebP format recognized in data[0:4], data[8:12]")
# file specification: https://developers.google.com/speed/webp/docs/riff_container
data = fh.read(5)
Expand All @@ -48,7 +56,7 @@ def _find_webp_exif(fh: BinaryIO) -> tuple:
raise ExifNotFound("Webp file does not have exif data.")


def _find_png_exif(fh: BinaryIO, data: bytes) -> tuple:
def _find_png_exif(fh: BinaryIO, data: bytes) -> Tuple[int, bytes]:
logger.debug("PNG format recognized in data[0:8]=%s", data[:8].hex())
fh.seek(8)

Expand Down Expand Up @@ -120,9 +128,16 @@ def _determine_type(fh: BinaryIO) -> tuple:
return offset, endian, fake_exif


def process_file(fh: BinaryIO, stop_tag=DEFAULT_STOP_TAG,
details=True, strict=False, debug=False,
truncate_tags=True, auto_seek=True, extract_thumbnail=True):
def process_file(
fh: BinaryIO,
stop_tag=DEFAULT_STOP_TAG,
details=True,
strict=False,
debug=False,
truncate_tags=True,
auto_seek=True,
extract_thumbnail=True
):
"""
Process an image file (expects an open file object).

Expand All @@ -144,12 +159,7 @@ def process_file(fh: BinaryIO, stop_tag=DEFAULT_STOP_TAG,

endian = chr(ord_(endian[0]))
# deal with the EXIF info we found
logger.debug("Endian format is %s (%s)", endian, {
'I': 'Intel',
'M': 'Motorola',
'\x01': 'Adobe Ducky',
'd': 'XMP/Adobe unknown'
}[endian])
logger.debug("Endian format is %s (%s)", endian, ENDIAN_TYPES[endian])

hdr = ExifHeader(fh, endian, offset, fake_exif, strict, debug, details, truncate_tags)
ifd_list = hdr.list_ifd()
Expand Down
Loading