diff --git a/osxphotos/_version.py b/osxphotos/_version.py index be4c736c..eb41159a 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.42.21" \ No newline at end of file +__version__ = "0.42.22" diff --git a/osxphotos/exiftool.py b/osxphotos/exiftool.py index 53b4f885..97e5d9ea 100644 --- a/osxphotos/exiftool.py +++ b/osxphotos/exiftool.py @@ -6,19 +6,28 @@ If these aren't important to you, I highly recommend you use Sven Marnach's excellent pyexiftool: https://github.com/smarnach/pyexiftool which provides more functionality """ +import atexit import json import logging import os import re import shutil import subprocess -from functools import lru_cache # pylint: disable=syntax-error from abc import ABC, abstractmethod +from functools import lru_cache # pylint: disable=syntax-error # exiftool -stay_open commands outputs this EOF marker after command is run EXIFTOOL_STAYOPEN_EOF = "{ready}" EXIFTOOL_STAYOPEN_EOF_LEN = len(EXIFTOOL_STAYOPEN_EOF) +# list of exiftool processes to cleanup when exiting or when terminate is called +EXIFTOOL_PROCESSES = [] + +@atexit.register +def terminate_exiftool(): + """Terminate any running ExifTool subprocesses; call this to cleanup when done using ExifTool """ + for proc in EXIFTOOL_PROCESSES: + proc._stop_proc() @lru_cache(maxsize=1) def get_exiftool_path(): @@ -61,6 +70,8 @@ class _ExifToolProc: self._exiftool = exiftool or get_exiftool_path() self._start_proc() + EXIFTOOL_PROCESSES.append(self) + @property def process(self): """ return the exiftool subprocess """ @@ -117,9 +128,6 @@ class _ExifToolProc: try: self._process.communicate(timeout=5) except subprocess.TimeoutExpired: - logging.warning( - f"exiftool pid {self._process.pid} did not exit, killing it" - ) self._process.kill() self._process.communicate() diff --git a/tests/test_exiftool.py b/tests/test_exiftool.py index f131544a..bef5dad2 100644 --- a/tests/test_exiftool.py +++ b/tests/test_exiftool.py @@ -417,3 +417,16 @@ def test_photoinfo_exiftool_none(): photo = photosdb.photos(uuid=[uuid])[0] exiftool = photo.exiftool assert exiftool is None + + +def test_exiftool_terminate(): + """ Test that exiftool process is terminated when exiftool.terminate() is called """ + import osxphotos.exiftool + import subprocess + + exif1 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD) + osxphotos.exiftool.terminate_exiftool() + + ps = subprocess.run(["ps"], capture_output=True) + stdout = ps.stdout.decode("utf-8") + assert "exiftool -stay_open" not in stdout