Skip to content
Merged
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
18 changes: 9 additions & 9 deletions negpy/services/export/linear_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ def _read_source_meta_exif(file_path: str) -> _SourceMeta:
tif = _tifffile.TiffFile(io.BytesIO(tiff_bytes))
finally:
logging.getLogger("tifffile").setLevel(prev)
tags = tif.pages[0].tags
make = tags.get("Make")
model = tags.get("Model")
dt = tags.get("DateTime")
return _SourceMeta(
make=str(make.value).strip() if make else None,
model=str(model.value).strip() if model else None,
datetime=str(dt.value).strip() if dt else None,
)
tags = tif.pages[0].tags
make = tags.get("Make")
model = tags.get("Model")
dt = tags.get("DateTime")
return _SourceMeta(
make=str(make.value).strip() if make else None,
model=str(model.value).strip() if model else None,
datetime=str(dt.value).strip() if dt else None,
)
except Exception:
return _SourceMeta()

Expand Down
58 changes: 58 additions & 0 deletions tests/test_linear_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -2109,3 +2109,61 @@ def test_a_tiff_bracket_decodes_merged(self, tmp_path: str) -> None:
# there, so the shadow arrives at ~15/16 of its converted level.
assert float(merged[:, :4].min()) > 0.9 * (400.0 / 65535.0) / 4.0, "the bracket decoded unmerged"
assert float(merged.max()) <= 1.0


class TestSourceMetaExifFallback:
"""`_read_source_meta_exif` is the fallback path for RAF, ORF and friends."""

_EXIF_MARKER = b"Exif" + bytes(2)

def test_module_compiles_without_syntaxwarning(self) -> None:
"""`return` inside `finally` discards any in-flight exception.

Python 3.14 warns about it, so every import emitted a SyntaxWarning. The
parsing that used to sit in that `finally` also read `tif`, which is
unbound when `TiffFile()` raises, so a malformed EXIF block surfaced as
`UnboundLocalError` rather than the real tifffile error.
"""
import warnings

from negpy.services.export import linear_output

with open(linear_output.__file__, encoding="utf-8") as fh:
source = fh.read()
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
compile(source, linear_output.__file__, "exec")
assert [w for w in caught if issubclass(w.category, SyntaxWarning)] == []

def test_malformed_exif_block_returns_empty_meta(self, tmp_path: str) -> None:
from negpy.services.export.linear_output import _read_source_meta_exif

path = os.path.join(str(tmp_path), "bad-exif.raf")
with open(path, "wb") as fh:
fh.write(self._EXIF_MARKER + b"garbage that is not a TIFF header")

assert _read_source_meta_exif(path) == _SourceMeta()

def test_file_without_exif_marker_returns_empty_meta(self, tmp_path: str) -> None:
from negpy.services.export.linear_output import _read_source_meta_exif

path = os.path.join(str(tmp_path), "no-exif.bin")
with open(path, "wb") as fh:
fh.write(bytes(512))

assert _read_source_meta_exif(path) == _SourceMeta()

def test_tifffile_log_level_is_restored_on_failure(self, tmp_path: str) -> None:
"""The `finally` exists to undo the CRITICAL silencing; keep it doing that."""
import logging

from negpy.services.export.linear_output import _read_source_meta_exif

path = os.path.join(str(tmp_path), "bad-exif.bin")
with open(path, "wb") as fh:
fh.write(self._EXIF_MARKER + b"\xff" * 256)

logger = logging.getLogger("tifffile")
before = logger.level
_read_source_meta_exif(path)
assert logger.level == before
Loading