Removed debug code from exiftool, fixed #641

This commit is contained in:
Rhet Turnbull
2022-02-24 05:09:42 -08:00
parent 9964fd0635
commit 173e3ccc37
16 changed files with 79 additions and 74 deletions

View File

@@ -1741,7 +1741,7 @@ Substitution Description
{lf} A line feed: '\n', alias for {newline} {lf} A line feed: '\n', alias for {newline}
{cr} A carriage return: '\r' {cr} A carriage return: '\r'
{crlf} a carriage return + line feed: '\r\n' {crlf} a carriage return + line feed: '\r\n'
{osxphotos_version} The osxphotos version, e.g. '0.46.2' {osxphotos_version} The osxphotos version, e.g. '0.46.4'
{osxphotos_cmd_line} The full command line used to run osxphotos {osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified for The following substitutions may result in multiple values. Thus if specified for
@@ -3645,7 +3645,7 @@ The following template field substitutions are availabe for use the templating s
|{lf}|A line feed: '\n', alias for {newline}| |{lf}|A line feed: '\n', alias for {newline}|
|{cr}|A carriage return: '\r'| |{cr}|A carriage return: '\r'|
|{crlf}|a carriage return + line feed: '\r\n'| |{crlf}|a carriage return + line feed: '\r\n'|
|{osxphotos_version}|The osxphotos version, e.g. '0.46.2'| |{osxphotos_version}|The osxphotos version, e.g. '0.46.4'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos| |{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{album}|Album(s) photo is contained in| |{album}|Album(s) photo is contained in|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder| |{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|

View File

@@ -1,4 +1,4 @@
# Sphinx build info version 1 # Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 3bdc7daae06c46fa0e6357e497a27088 config: 4fd4a10e261cc9bab3c5f7edf97d5f38
tags: 645f666f9bcd5a90fca523b33c5a78b7 tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Overview: module code &#8212; osxphotos 0.46.2 documentation</title> <title>Overview: module code &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="../_static/alabaster.css" />
<script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script> <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.photoinfo &#8212; osxphotos 0.46.1 documentation</title> <title>osxphotos.photoinfo &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="../../_static/alabaster.css" />
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script> <script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
@@ -87,7 +87,7 @@
<span class="kn">from</span> <span class="nn">.searchinfo</span> <span class="kn">import</span> <span class="n">SearchInfo</span> <span class="kn">from</span> <span class="nn">.searchinfo</span> <span class="kn">import</span> <span class="n">SearchInfo</span>
<span class="kn">from</span> <span class="nn">.text_detection</span> <span class="kn">import</span> <span class="n">detect_text</span> <span class="kn">from</span> <span class="nn">.text_detection</span> <span class="kn">import</span> <span class="n">detect_text</span>
<span class="kn">from</span> <span class="nn">.uti</span> <span class="kn">import</span> <span class="n">get_preferred_uti_extension</span><span class="p">,</span> <span class="n">get_uti_for_extension</span> <span class="kn">from</span> <span class="nn">.uti</span> <span class="kn">import</span> <span class="n">get_preferred_uti_extension</span><span class="p">,</span> <span class="n">get_uti_for_extension</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">_debug</span><span class="p">,</span> <span class="n">_get_resource_loc</span><span class="p">,</span> <span class="n">list_directory</span> <span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">_debug</span><span class="p">,</span> <span class="n">_get_resource_loc</span><span class="p">,</span> <span class="n">list_directory</span><span class="p">,</span> <span class="n">_debug</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="s2">&quot;PhotoInfoNone&quot;</span><span class="p">]</span> <span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="s2">&quot;PhotoInfoNone&quot;</span><span class="p">]</span>
@@ -621,7 +621,7 @@
<span class="nd">@property</span> <span class="nd">@property</span>
<span class="k">def</span> <span class="nf">ismissing</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">def</span> <span class="nf">ismissing</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns true if photo is missing from disk (which means it&#39;s not been downloaded from iCloud)</span> <span class="sd">&quot;&quot;&quot;returns true if photo is missing from disk (which means it&#39;s not been downloaded from iCloud)</span>
<span class="sd"> </span>
<span class="sd"> NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos</span> <span class="sd"> NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos</span>
<span class="sd"> do not immediately get written to disk. In particular, I&#39;ve noticed that downloading</span> <span class="sd"> do not immediately get written to disk. In particular, I&#39;ve noticed that downloading</span>
<span class="sd"> an image from the cloud does not force the database to be updated until something else</span> <span class="sd"> an image from the cloud does not force the database to be updated until something else</span>

View File

@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = { var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.46.2', VERSION: '0.46.4',
LANGUAGE: 'None', LANGUAGE: 'None',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
BUILDER: 'html', BUILDER: 'html',

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos command line interface (CLI) &#8212; osxphotos 0.46.2 documentation</title> <title>osxphotos command line interface (CLI) &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; osxphotos 0.46.2 documentation</title> <title>Index &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.46.2 documentation</title> <title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos &#8212; osxphotos 0.46.2 documentation</title> <title>osxphotos &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos package &#8212; osxphotos 0.46.2 documentation</title> <title>osxphotos package &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; osxphotos 0.46.2 documentation</title> <title>Search &#8212; osxphotos 0.46.4 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.46.3" __version__ = "0.46.4"

View File

@@ -105,10 +105,9 @@ class _ExifToolProc:
return cls.instance return cls.instance
def __init__(self, exiftool=None, debug=False): def __init__(self, exiftool=None):
"""construct _ExifToolProc singleton object or return instance of already created object """construct _ExifToolProc singleton object or return instance of already created object
exiftool: optional path to exiftool binary (if not provided, will search path to find it) exiftool: optional path to exiftool binary (if not provided, will search path to find it)
debug: optional bool to enable debugging output
""" """
if hasattr(self, "_process_running") and self._process_running: if hasattr(self, "_process_running") and self._process_running:
@@ -119,11 +118,8 @@ class _ExifToolProc:
f"ignoring exiftool={exiftool}" f"ignoring exiftool={exiftool}"
) )
return return
self.debug = debug
self._process_running = False self._process_running = False
self._exiftool = exiftool or get_exiftool_path() self._exiftool = exiftool or get_exiftool_path()
if self.debug:
logging.debug(f"exiftool={self._exiftool}")
self._start_proc() self._start_proc()
@property @property
@@ -177,17 +173,10 @@ class _ExifToolProc:
self._process_running = True self._process_running = True
EXIFTOOL_PROCESSES.append(self) EXIFTOOL_PROCESSES.append(self)
if self.debug:
logging.debug(
"exiftool process started: {self._process} {self._process_running}"
)
def _stop_proc(self): def _stop_proc(self):
"""stop the exiftool process if it's running, otherwise, do nothing""" """stop the exiftool process if it's running, otherwise, do nothing"""
if self.debug:
logging.debug(f"exiftool process stopping: {self._process}")
if not self._process_running: if not self._process_running:
return return
@@ -207,16 +196,11 @@ class _ExifToolProc:
del self._process del self._process
self._process_running = False self._process_running = False
if self.debug:
logging.debug(f"exiftool process stopped: {self._process}")
class ExifTool: class ExifTool:
"""Basic exiftool interface for reading and writing EXIF tags""" """Basic exiftool interface for reading and writing EXIF tags"""
def __init__( def __init__(self, filepath, exiftool=None, overwrite=True, flags=None):
self, filepath, exiftool=None, overwrite=True, flags=None, debug=False
):
"""Create ExifTool object """Create ExifTool object
Args: Args:
@@ -224,7 +208,6 @@ class ExifTool:
exiftool: path to exiftool, if not specified will look in path exiftool: path to exiftool, if not specified will look in path
overwrite: if True, will overwrite image file without creating backup, default=False overwrite: if True, will overwrite image file without creating backup, default=False
flags: optional list of exiftool flags to prepend to exiftool command when writing metadata (e.g. -m or -F) flags: optional list of exiftool flags to prepend to exiftool command when writing metadata (e.g. -m or -F)
debug: if True, enables debug output
Returns: Returns:
ExifTool instance ExifTool instance
@@ -232,13 +215,12 @@ class ExifTool:
self.file = filepath self.file = filepath
self.overwrite = overwrite self.overwrite = overwrite
self.flags = flags or [] self.flags = flags or []
self.debug = debug
self.data = {} self.data = {}
self.warning = None self.warning = None
self.error = None self.error = None
# if running as a context manager, self._context_mgr will be True # if running as a context manager, self._context_mgr will be True
self._context_mgr = False self._context_mgr = False
self._exiftoolproc = _ExifToolProc(exiftool=exiftool, debug=debug) self._exiftoolproc = _ExifToolProc(exiftool=exiftool)
self._read_exif() self._read_exif()
@property @property
@@ -366,9 +348,6 @@ class ExifTool:
+ b"-execute\n" + b"-execute\n"
) )
if self.debug:
logging.debug(f"running exiftool command: {command_str}")
# send the command # send the command
self._process.stdin.write(command_str) self._process.stdin.write(command_str)
self._process.stdin.flush() self._process.stdin.flush()
@@ -389,10 +368,6 @@ class ExifTool:
error = "" if error == b"" else error.decode("utf-8") error = "" if error == b"" else error.decode("utf-8")
self.warning = warning self.warning = warning
self.error = error self.error = error
if self.debug:
logging.debug(
f"run_commands: output={output[:-EXIFTOOL_STAYOPEN_EOF_LEN]}, warning={warning}, error={error}"
)
return output[:-EXIFTOOL_STAYOPEN_EOF_LEN], warning, error return output[:-EXIFTOOL_STAYOPEN_EOF_LEN], warning, error
@@ -417,8 +392,6 @@ class ExifTool:
""" """
json_str, _, _ = self.run_commands("-json") json_str, _, _ = self.run_commands("-json")
if not json_str: if not json_str:
if self.debug:
logging.debug(f"empty json_str")
return dict() return dict()
json_str = unescape_str(json_str.decode("utf-8")) json_str = unescape_str(json_str.decode("utf-8"))
@@ -427,8 +400,7 @@ class ExifTool:
except Exception as e: except Exception as e:
# will fail with some commands, e.g --ext AVI which produces # will fail with some commands, e.g --ext AVI which produces
# 'No file with specified extension' instead of json # 'No file with specified extension' instead of json
if self.debug: logging.warning(f"error loading json returned by exiftool: {e} {json_str}")
logging.debug(f"json.loads error {e}")
return dict() return dict()
exifdict = exifdict[0] exifdict = exifdict[0]
if not tag_groups: if not tag_groups:
@@ -442,25 +414,18 @@ class ExifTool:
if normalized: if normalized:
exifdict = {k.lower(): v for (k, v) in exifdict.items()} exifdict = {k.lower(): v for (k, v) in exifdict.items()}
if self.debug:
logging.debug(f"asdict: {exifdict}")
return exifdict return exifdict
def json(self): def json(self):
"""returns JSON string containing all EXIF tags and values from exiftool""" """returns JSON string containing all EXIF tags and values from exiftool"""
json, _, _ = self.run_commands("-json") json, _, _ = self.run_commands("-json")
json = unescape_str(json.decode("utf-8")) json = unescape_str(json.decode("utf-8"))
if self.debug:
logging.debug(f"json: {json}")
return json return json
def _read_exif(self): def _read_exif(self):
"""read exif data from file""" """read exif data from file"""
data = self.asdict() data = self.asdict()
self.data = {k: v for k, v in data.items()} self.data = {k: v for k, v in data.items()}
if self.debug:
logging.debug(f"_read_exif: {self.data}")
def __str__(self): def __str__(self):
return f"file: {self.file}\nexiftool: {self._exiftoolproc._exiftool}" return f"file: {self.file}\nexiftool: {self._exiftoolproc._exiftool}"
@@ -486,17 +451,15 @@ class ExifToolCaching(ExifTool):
_singletons = {} _singletons = {}
def __new__(cls, filepath, exiftool=None, debug=False): def __new__(cls, filepath, exiftool=None):
"""create new object or return instance of already created singleton""" """create new object or return instance of already created singleton"""
if filepath not in cls._singletons: if filepath not in cls._singletons:
cls._singletons[filepath] = _ExifToolCaching( cls._singletons[filepath] = _ExifToolCaching(filepath, exiftool=exiftool)
filepath, exiftool=exiftool, debug=debug
)
return cls._singletons[filepath] return cls._singletons[filepath]
class _ExifToolCaching(ExifTool): class _ExifToolCaching(ExifTool):
def __init__(self, filepath, exiftool=None, debug=False): def __init__(self, filepath, exiftool=None):
"""Create read-only ExifTool object that caches values """Create read-only ExifTool object that caches values
Args: Args:
@@ -506,12 +469,9 @@ class _ExifToolCaching(ExifTool):
Returns: Returns:
ExifTool instance ExifTool instance
""" """
self.debug = debug
self._json_cache = None self._json_cache = None
self._asdict_cache = {} self._asdict_cache = {}
super().__init__( super().__init__(filepath, exiftool=exiftool, overwrite=False, flags=None)
filepath, exiftool=exiftool, overwrite=False, flags=None, debug=debug
)
def run_commands(self, *commands, no_file=False): def run_commands(self, *commands, no_file=False):
if commands[0] not in ["-json", "-ver"]: if commands[0] not in ["-json", "-ver"]:

View File

@@ -560,11 +560,15 @@ class PhotoExporter:
touch_results = [] touch_results = []
for touch_file in set(touch_files): for touch_file in set(touch_files):
ts = int(self.photo.date.timestamp()) ts = int(self.photo.date.timestamp())
stat = os.stat(touch_file) try:
if stat.st_mtime != ts: stat = os.stat(touch_file)
if not options.dry_run: if stat.st_mtime != ts:
fileutil.utime(touch_file, (ts, ts)) fileutil.utime(touch_file, (ts, ts))
touch_results.append(touch_file) touch_results.append(touch_file)
except FileNotFoundError as e:
# ignore errors if in dry_run as file may not be present
if not options.dry_run:
raise e from e
return ExportResults(touched=touch_results) return ExportResults(touched=touch_results)
def _get_edited_filename(self, original_filename): def _get_edited_filename(self, original_filename):
@@ -669,8 +673,8 @@ class PhotoExporter:
if file_record.export_options != options.bit_flags: if file_record.export_options != options.bit_flags:
# exporting with different set of options (e.g. exiftool), should update # exporting with different set of options (e.g. exiftool), should update
# need to check this before exiftool in case exiftool options are different # need to check this before exiftool in case exiftool options are different
# and export database is missing; this will always be True if database is missing # and export database is missing; this will always be True if database is missing
# as it'll be None and bit_flags will be an int # as it'll be None and bit_flags will be an int
return True return True

View File

@@ -588,7 +588,7 @@ class PhotoInfo:
@property @property
def ismissing(self): def ismissing(self):
"""returns true if photo is missing from disk (which means it's not been downloaded from iCloud) """returns true if photo is missing from disk (which means it's not been downloaded from iCloud)
NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos
do not immediately get written to disk. In particular, I've noticed that downloading do not immediately get written to disk. In particular, I've noticed that downloading
an image from the cloud does not force the database to be updated until something else an image from the cloud does not force the database to be updated until something else
@@ -1343,7 +1343,7 @@ class PhotoInfo:
try: try:
exiftool_path = self._db._exiftool_path or get_exiftool_path() exiftool_path = self._db._exiftool_path or get_exiftool_path()
if self.path is not None and os.path.isfile(self.path): if self.path is not None and os.path.isfile(self.path):
exiftool = ExifToolCaching(self.path, exiftool=exiftool_path, debug=_debug()) exiftool = ExifToolCaching(self.path, exiftool=exiftool_path)
else: else:
exiftool = None exiftool = None
except FileNotFoundError: except FileNotFoundError:

View File

@@ -1,5 +1,8 @@
import json
import pytest import pytest
from osxphotos.exiftool import get_exiftool_path
from osxphotos.exiftool import get_exiftool_path, unescape_str
TEST_FILE_ONE_KEYWORD = "tests/test-images/wedding.jpg" TEST_FILE_ONE_KEYWORD = "tests/test-images/wedding.jpg"
TEST_FILE_BAD_IMAGE = "tests/test-images/badimage.jpeg" TEST_FILE_BAD_IMAGE = "tests/test-images/badimage.jpeg"
@@ -89,6 +92,20 @@ EXIF_UUID_NO_GROUPS = {
} }
EXIF_UUID_NONE = ["A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C"] EXIF_UUID_NONE = ["A1DD1F98-2ECD-431F-9AC9-5AFEFE2D3A5C"]
QUOTED_JSON_BYTES = b'[{"ExifTool:ExifToolVersion": 12.37,"ExifTool:Now": "2022:02:22 18:14:31+00:00","ExifTool:NewGUID": "20220222181431005A76C1A4B4D508A2","ExifTool:FileSequence": 0,"ExifTool:Warning": "Error running &quot;xattr&quot; to extract XAttr tags","ExifTool:ProcessingTime": 0.157028}]'
QUOTED_JSON_STRING_UNESCAPED = '[{"ExifTool:ExifToolVersion": 12.37,"ExifTool:Now": "2022:02:22 18:14:31+00:00","ExifTool:NewGUID": "20220222181431005A76C1A4B4D508A2","ExifTool:FileSequence": 0,"ExifTool:Warning": "Error running \\"xattr\\" to extract XAttr tags","ExifTool:ProcessingTime": 0.157028}]'
QUOTED_JSON_LOADED = [
{
"ExifTool:ExifToolVersion": 12.37,
"ExifTool:Now": "2022:02:22 18:14:31+00:00",
"ExifTool:NewGUID": "20220222181431005A76C1A4B4D508A2",
"ExifTool:FileSequence": 0,
"ExifTool:Warning": 'Error running "xattr" to extract XAttr tags',
"ExifTool:ProcessingTime": 0.157028,
}
]
try: try:
exiftool = get_exiftool_path() exiftool = get_exiftool_path()
except: except:
@@ -126,6 +143,7 @@ def test_setvalue_1():
# test setting a tag value # test setting a tag value
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -145,6 +163,7 @@ def test_setvalue_multiline():
# test setting a tag value with embedded newline # test setting a tag value with embedded newline
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -164,6 +183,7 @@ def test_setvalue_non_alphanumeric_chars():
# test setting a tag value non-alphanumeric characters # test setting a tag value non-alphanumeric characters
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -183,6 +203,7 @@ def test_setvalue_warning():
# test setting illegal tag value generates warning # test setting illegal tag value generates warning
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -199,6 +220,7 @@ def test_setvalue_error():
# test setting tag on bad image generates error # test setting tag on bad image generates error
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -215,6 +237,7 @@ def test_setvalue_context_manager():
# test setting a tag value as context manager # test setting a tag value as context manager
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -241,6 +264,7 @@ def test_setvalue_context_manager_warning():
# test setting a tag value as context manager when warning generated # test setting a tag value as context manager when warning generated
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -257,6 +281,7 @@ def test_setvalue_context_manager_error():
# test setting a tag value as context manager when error generated # test setting a tag value as context manager when error generated
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -273,6 +298,7 @@ def test_flags():
# test that flags work # test that flags work
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -296,6 +322,7 @@ def test_clear_value():
# test clearing a tag value # test clearing a tag value
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -315,6 +342,7 @@ def test_addvalues_1():
# test setting a tag value # test setting a tag value
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -332,6 +360,7 @@ def test_addvalues_2():
# test setting a tag value where multiple values already exist # test setting a tag value where multiple values already exist
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -353,6 +382,7 @@ def test_addvalues_non_alphanumeric_multiline():
# test setting a tag value # test setting a tag value
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -373,6 +403,7 @@ def test_addvalues_unicode():
# test setting a tag value with unicode # test setting a tag value with unicode
import os.path import os.path
import tempfile import tempfile
import osxphotos.exiftool import osxphotos.exiftool
from osxphotos.fileutil import FileUtil from osxphotos.fileutil import FileUtil
@@ -444,9 +475,10 @@ def test_as_dict_no_tag_groups():
def test_json(): def test_json():
import osxphotos.exiftool
import json import json
import osxphotos.exiftool
exif1 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD) exif1 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD)
exifdata = json.loads(exif1.json()) exifdata = json.loads(exif1.json())
assert exifdata[0]["XMP:TagsList"] == "wedding" assert exifdata[0]["XMP:TagsList"] == "wedding"
@@ -498,9 +530,10 @@ def test_photoinfo_exiftool_none():
def test_exiftool_terminate(): def test_exiftool_terminate():
"""Test that exiftool process is terminated when exiftool.terminate() is called""" """Test that exiftool process is terminated when exiftool.terminate() is called"""
import osxphotos.exiftool
import subprocess import subprocess
import osxphotos.exiftool
exif1 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD) exif1 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD)
ps = subprocess.run(["ps"], capture_output=True) ps = subprocess.run(["ps"], capture_output=True)
@@ -516,3 +549,11 @@ def test_exiftool_terminate():
# verify we can create a new instance after termination # verify we can create a new instance after termination
exif2 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD) exif2 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD)
assert exif2.asdict()["IPTC:Keywords"] == "wedding" assert exif2.asdict()["IPTC:Keywords"] == "wedding"
def test_unescape_str():
"""Test unescape_str, #636"""
quoted_str = unescape_str(QUOTED_JSON_BYTES.decode("utf-8"))
assert quoted_str == QUOTED_JSON_STRING_UNESCAPED
quoted_json = json.loads(quoted_str)
assert quoted_json == QUOTED_JSON_LOADED