Skip to content

fix: treat missing exif orientation tag as unrotated - #27

Merged
escaped merged 2 commits into
masterfrom
fix/get-orientation-missing
Sep 14, 2026
Merged

escaped merged 2 commits into
masterfrom
fix/get-orientation-missing

Conversation

@escaped

@escaped escaped commented Sep 12, 2026 •

Copy link
Copy Markdown
Owner

Why

Fix #23. get_orientation crashed with KeyError: 'Orientation' for any
file whose exif data has no orientation tag. ExifTool omits the tag when an
image is stored unrotated (common for screenshots, PNGs and processed
images), so ExifField(denormalized_fields={'orientation': get_orientation})
broke on perfectly valid files.

Walkthrough

  • Default the orientation to 1 (normal) when the tag is missing, instead of
    assuming it is present.
  • Add a regression test covering a missing tag for both landscape and
    portrait dimensions.

Changes

File(s) Change Summary
exiffield/getters.py read Orientation with a normal-orientation default
tests/test_getters.py regression test without an Orientation tag
CHANGELOG.md note the fix under [Unreleased]

Testing

Red first: the new test raised KeyError: 'Orientation' before the fix
(2 failed). After:

$ uv run pytest -q
71 passed, 1 xfailed in 5.80s
$ uv run ruff format --check . && uv run ruff check . && uv run mypy
17 files already formatted / All checks passed! / no issues found

CI on 3bb0c90: run 34694220972 — 14/14 jobs success.

PR breakdown

Stacked on the modernization branch test/e2e (PR #26). Merge in order.

# PR What it does Status
1 This PR #23: missing orientation tag crash Open
2 #28 #17: timezone-aware get_datetaken Open
3 #29 #6: document non-editable fields Open
4 #30 #1: document and test deferred extraction Open

The suite only exercised the field in isolation; no test went through a
real request. This adds a minimal upload view, model form and templates to
the test app and four tests that render the form, submit a real multipart
image, follow the redirect and assert both the rendered DOM and the
database state after exif extraction and denormalization.
get_orientation raised a KeyError for files without an Orientation tag
(e.g. screenshots and PNGs). ExifTool omits the tag when the image is
stored unrotated, so default to a normal orientation before deciding
between landscape and portrait from the pixel dimensions.

Fixes #23
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 34694220972

Warning

No base build found for commit 0a11a59 on test/e2e.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 98.276%

Details

  • Patch coverage: 1 of 1 lines across 1 file are fully covered (100%).

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 174
Covered Lines: 171
Line Coverage: 98.28%
Coverage Strength: 10.81 hits per line

💛 - Coveralls

@escaped

escaped commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Two-axis review record

Method: an independent sub-agent reviewed the four stacked diffs (this PR
through #30) on two axes — Standards (repo conventions, correctness,
test quality) and Spec (does each PR actually fix its issue as
reported) — and verified every finding by running the code. The findings
were fixed in follow-up commits; final state: 79 passed, 0 xfailed, ruff
format/check and mypy clean, all four branches CI-green (14/14 jobs each).

Findings addressed

PR Finding Disposition
#28 A present but malformed offset was silently ignored, returning a naive datetime even though the file claims to carry timezone info Fixed in 48cb745: raises ExifError (a valid GPSDateTime is still preferred when available)
#29 The editable-copy workaround read self.exif before super().save(), but extraction runs in the field's pre_save — so it never seeded on the first save, and a cleared field was re-seeded Fixed in bd00ac2: seed after the first save, guarded by created, preserving user edits including clearing
#30 README claimed commit=True fills denormalized_fields but the test model had none Fixed in 7972298: UnsyncedImage gains a denormalized camera field, asserted after the deferred save
#30 Cleanup unlinked only the base file name, leaking storage-suffixed files across re-runs Fixed in 7972298: delete the actual stored name
— Docs PRs carry no CHANGELOG entry Accepted: docs-only, consistent with repo history

Verified correct

  • Every new test was observed failing before its fix (red runs are recorded
    in each PR's Testing section) — no vacuous assertions.
  • get_datetaken preference order and fallbacks: offset-aware original →
    GPS (UTC, with or without Z) → naive original → ExifError; malformed
    offset falls back to GPS when present.
  • get_orientation default of 1 (normal) is more correct than the
    issue's suggested 0: both bypass the 90° rotation branch, but 1 is the
    valid EXIF value.

Base automatically changed from test/e2e to master September 14, 2026 21:18
@escaped
escaped merged commit 95ffd59 into master Sep 14, 2026
26 checks passed
@escaped
escaped deleted the fix/get-orientation-missing branch September 14, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

get_orientation

2 participants