Skip to content

Commit 4d406cb

Browse files
authored
Finalize version 3.1.0 (#212)
1 parent dd37c9c commit 4d406cb

File tree

15 files changed

+154
-121
lines changed

15 files changed

+154
-121
lines changed

.github/workflows/linting.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Set up Python
1818
uses: actions/setup-python@v4
1919
with:
20-
python-version: "3.10"
20+
python-version: "3.11"
2121

2222
- name: Cache dependencies
2323
uses: actions/cache@v3
@@ -30,7 +30,7 @@ jobs:
3030
- name: Install dependencies
3131
run: |
3232
pip install virtualenv
33-
make venv reqs-install
33+
make venv install-dev
3434
3535
- name: Static analysis of the code
3636
run: |

.github/workflows/test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@ jobs:
4040
4141
- name: Install
4242
run: |
43-
pip install -e .
43+
pip install virtualenv
44+
make venv install
4445
4546
- name: Run in debug and color mode
4647
run: |
48+
source .venv/bin/activate
4749
make test
4850
4951
- name: Compare image processing output
5052
run: |
53+
source .venv/bin/activate
5154
make test-diff
5255

.pylintrc

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@ ignore-paths=
5959
# Emacs file locks
6060
ignore-patterns=^\.#
6161

62-
# List of module names for which member attributes should not be checked
63-
# (useful for modules/projects where namespaces are manipulated during runtime
64-
# and thus existing member attributes cannot be deduced by static analysis). It
65-
# supports qualified module names, as well as Unix pattern matching.
62+
# List of module names for which member attributes should not be checked and
63+
# will not be imported (useful for modules/projects where namespaces are
64+
# manipulated during runtime and thus existing member attributes cannot be
65+
# deduced by static analysis). It supports qualified module names, as well as
66+
# Unix pattern matching.
6667
ignored-modules=
6768

6869
# Python code to execute, usually for sys.path manipulation such as
@@ -86,6 +87,10 @@ load-plugins=
8687
# Pickle collected data for later comparisons.
8788
persistent=yes
8889

90+
# Resolve imports to .pyi stubs if available. May reduce no-member messages and
91+
# increase not-an-iterable messages.
92+
prefer-stubs=no
93+
8994
# Minimum Python version to use for version dependent checks. Will default to
9095
# the version used to run pylint.
9196
py-version=3.7
@@ -190,10 +195,7 @@ good-names=i,
190195
k,
191196
ex,
192197
Run,
193-
_,
194-
s,
195-
fh,
196-
EXIF
198+
_
197199

198200
# Good variable names regexes, separated by a comma. If names match any regex,
199201
# they will always be accepted
@@ -305,6 +307,9 @@ max-locals=25
305307
# Maximum number of parents for a class (see R0901).
306308
max-parents=7
307309

310+
# Maximum number of positional arguments for function / method.
311+
max-positional-arguments=10
312+
308313
# Maximum number of public methods for a class (see R0904).
309314
max-public-methods=20
310315

@@ -429,13 +434,12 @@ disable=raw-checker-failed,
429434
locally-disabled,
430435
file-ignored,
431436
suppressed-message,
432-
useless-suppression,
433437
deprecated-pragma,
434438
use-symbolic-message-instead,
435439
use-implicit-booleaness-not-comparison-to-string,
436440
use-implicit-booleaness-not-comparison-to-zero,
437-
consider-using-f-string,
438441
missing-function-docstring,
442+
consider-using-f-string,
439443
fixme,
440444

441445
# Enable the message, report, category or checker with the given id(s). You can
@@ -474,6 +478,11 @@ max-nested-blocks=5
474478
# printed.
475479
never-returning-functions=sys.exit,argparse.parse_error
476480

481+
# Let 'consider-using-join' be raised when the separator to join on would be
482+
# non-empty (resulting in expected fixes of the type: ``"- " + " -
483+
# ".join(items)``)
484+
suggest-join-with-non-empty-separator=yes
485+
477486

478487
[REPORTS]
479488

@@ -488,10 +497,10 @@ evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor
488497
# used to format the message information. See doc for all details.
489498
msg-template=
490499

491-
# Set the output format. Available formats are: text, parseable, colorized,
492-
# json2 (improved json format), json (old json format) and msvs (visual
493-
# studio). You can also give a reporter class, e.g.
494-
# mypackage.mymodule.MyReporterClass.
500+
# Set the output format. Available formats are: 'text', 'parseable',
501+
# 'colorized', 'json2' (improved json format), 'json' (old json format), msvs
502+
# (visual studio) and 'github' (GitHub actions). You can also give a reporter
503+
# class, e.g. mypackage.mymodule.MyReporterClass.
495504
#output-format=
496505

497506
# Tells whether to display a full report or only the messages.

ChangeLog.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
EXIF.py Change Log
22
##################
33

4-
3.1.0 — 2023-05-??
4+
3.1.0 — 2025-04-25
5+
* Add more typing definitions (#181)
6+
* Put all test files in the repo (#208)
7+
* use pre-commit to run black, isort, pylint, mypy (#209)
8+
* Canon MakerNote: Allow callable to process tag value (#189) by Daan van Gorkum
9+
* Added missing HEIC box names and handling of a TIFF header inside HEIC (#173) by Antti Ketola
10+
* don't let debug logging trigger an exception (#196) by David Bonner
11+
* fix for certain box names not handled, but skipping would generate valid output by Anand Mahesh
512
* Add DJI makernotes, extract_thumbnail parameter (#168) by Piero Toffanin
613
* Fix endianess bug while reading DJI makernotes, add Make tag (#169) by Piero Toffanin
714
* Make CI pass (#178) by Nick Dimitroff

LICENSE.txt

Lines changed: 0 additions & 31 deletions
This file was deleted.

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ test-diff: ## Run and compare exif dump
3737
analyze: ## Run all static analysis tools
3838
$(PRE_COMMIT_BIN) run --all
3939

40-
reqs-install: ## Install with all requirements
41-
$(PIP_INSTALL) .[dev]
40+
install-dev: ## Install with all development requirements
41+
$(PIP_INSTALL) -U -e .[dev]
42+
43+
install: ## Install with basic requirements
44+
$(PIP_INSTALL) -U -e .
4245

4346
build: ## build distribution
4447
rm -fr ./dist

README.rst

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ EXIF.py
44

55
Easy to use Python module to extract Exif metadata from digital image files.
66

7+
Pure Python, lightweight, no dependencies.
8+
79
Supported formats: TIFF, JPEG, PNG, Webp, HEIC
810

911

@@ -12,10 +14,6 @@ Compatibility
1214

1315
EXIF.py is tested and officially supported on Python 3.7 to 3.13
1416

15-
Starting with version ``3.0.0``, Python2 compatibility is dropped *completely* (syntax errors due to type hinting).
16-
17-
https://pythonclock.org/
18-
1917

2018
Installation
2119
************
@@ -36,9 +34,9 @@ Development Version
3634

3735
After cloning the repo, use the provided Makefile::
3836

39-
make venv reqs-install
37+
make venv install-dev
4038

41-
Which will install a virtual environment and install development dependencies.
39+
Which will create a virtual environment and install development dependencies.
4240

4341
Usage
4442
*****
@@ -163,7 +161,8 @@ Pass the ``-s`` or ``--strict`` argument, or as:
163161
Built-in Types
164162
==============
165163

166-
For easier serialization and programmatic use, this option returns a dictionary with values in built-in Python types (int, float, str, bytes, list, None) instead of `IfdTag` objects.
164+
For easier serialization and programmatic use, this option returns a dictionary with values in built-in Python types
165+
(int, float, str, bytes, list, None) instead of `IfdTag` objects.
167166

168167
Pass the ``-b`` or ``--builtin`` argument, or as:
169168

@@ -226,7 +225,7 @@ License
226225

227226
Copyright © 2002-2007 Gene Cash
228227

229-
Copyright © 2007-2023 Ianaré Sévi and contributors
228+
Copyright © 2007-2025 Ianaré Sévi and contributors
230229

231230
A **huge** thanks to all the contributors over the years!
232231

exifread/__init__.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
import struct
7-
from typing import BinaryIO, Dict, Tuple
7+
from typing import Any, BinaryIO, Dict, Tuple
88

99
from exifread.classes import ExifHeader
1010
from exifread.exceptions import ExifNotFound, InvalidExif
@@ -37,8 +37,13 @@ def _find_tiff_exif(fh: BinaryIO) -> Tuple[int, bytes]:
3737
return offset, endian
3838

3939

40-
def _find_heic_tiff(fh: BinaryIO) -> tuple:
41-
"""In some HEIC files, the Exif offset is 0 and there is a plain TIFF header near end of the file."""
40+
def _find_heic_tiff(fh: BinaryIO) -> Tuple[int, bytes]:
41+
"""
42+
Look for TIFF header in HEIC files.
43+
44+
In some HEIC files, the Exif offset is 0,
45+
and yet there is a plain TIFF header near end of the file.
46+
"""
4247

4348
data = fh.read(4)
4449
if data[0:2] in [b"II", b"MM"] and data[2] == 42 and data[3] == 0:
@@ -126,7 +131,7 @@ def _get_xmp(fh: BinaryIO) -> bytes:
126131
return xmp_bytes
127132

128133

129-
def _determine_type(fh: BinaryIO) -> tuple:
134+
def _determine_type(fh: BinaryIO) -> Tuple[int, bytes, int]:
130135
# by default do not fake an EXIF beginning
131136
fake_exif = 0
132137

@@ -163,7 +168,7 @@ def process_file(
163168
auto_seek=True,
164169
extract_thumbnail=True,
165170
builtin_types=False,
166-
) -> dict:
171+
) -> Dict[str, Any]:
167172
"""
168173
Process an image file (expects an open file object).
169174
@@ -175,22 +180,22 @@ def process_file(
175180
fh.seek(0)
176181

177182
try:
178-
offset, endian, fake_exif = _determine_type(fh)
183+
offset, endian_bytes, fake_exif = _determine_type(fh)
179184
except ExifNotFound as err:
180185
logger.warning(err)
181186
return {}
182187
except InvalidExif as err:
183188
logger.debug(err)
184189
return {}
185190

186-
endian = chr(ord_(endian[0]))
191+
endian_str = chr(ord_(endian_bytes[0]))
187192
# deal with the EXIF info we found
188193
logger.debug(
189-
"Endian format is %s (%s)", endian, ENDIAN_TYPES.get(endian, "Unknown")
194+
"Endian format is %s (%s)", endian_str, ENDIAN_TYPES.get(endian_str, "Unknown")
190195
)
191196

192197
hdr = ExifHeader(
193-
fh, endian, offset, fake_exif, strict, debug, details, truncate_tags
198+
fh, endian_str, offset, fake_exif, strict, debug, details, truncate_tags
194199
)
195200
ifd_list = hdr.list_ifd()
196201
thumb_ifd = 0

exifread/__main__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Main entrypoint for the module."""
2+
3+
from exifread.cli import main
4+
5+
if __name__ == "__main__":
6+
main()

exifread/classes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def __init__(
8383
file_handle: BinaryIO,
8484
endian: str,
8585
offset: int,
86-
fake_exif,
86+
fake_exif: int,
8787
strict: bool,
8888
debug=False,
8989
detailed=True,
@@ -431,7 +431,9 @@ def extract_tiff_thumbnail(self, thumb_ifd: int) -> None:
431431
old_offset = self.s2n(entry + 8, 4)
432432
# start of the 4-byte pointer area in entry
433433
ptr = i * 12 + 18
434-
# remember strip offsets location
434+
435+
# remember strip offsets
436+
strip_len = 0
435437
if tag == 0x0111:
436438
strip_off = ptr
437439
strip_len = count * type_length

0 commit comments

Comments
 (0)