Frequently Asked Questions
PyExifTool output is different from the exiftool command line
One of the most frequently asked questions relates to the default output of PyExifTool.
For example, using the rose.jpg in tests, let’s get all JFIF tags:
Default exiftool output
$ exiftool -JFIF:all rose.jpg
JFIF Version : 1.01
Resolution Unit : inches
X Resolution : 72
Y Resolution : 72
Default PyExifTool output
from PyExifTool, using the following code:
import exiftool
with exiftool.ExifTool() as et:
print(et.execute("-JFIF:all", "rose.jpg"))
Output:
[JFIF] JFIF Version : 1 1
[JFIF] Resolution Unit : 1
[JFIF] X Resolution : 72
[JFIF] Y Resolution : 72
What’s going on?
The reason for the different default output is that PyExifTool, by default, includes two arguments which make exiftool easier to use: -G, -n
.
Note
The -n
disables print conversion which displays raw tag values, making the output more machine-parseable.
When print conversion is enabled, some raw values may be translated to prettier human-readable text.
Note
The -G
enables group name (level 1) option which displays a group in the output to help disambiguate tags with the same name in different groups.
For example, -DateCreated can be ambiguous if both -IPTC:DateCreated and -XMP:DateCreated exists and have different values. -G
would display which one was returned by exiftool.
Read the documentation for the ExifTool constructor common_args
parameter for more details: exiftool.ExifTool.__init__()
.
(You can also change common_args
on an existing instance using exiftool.ExifTool.common_args
, as long as the subprocess is not exiftool.ExifTool.running
)
Ways to make the ouptut match
So if you want to have the ouput match (useful for debugging) between PyExifTool and exiftool, either:
Enable print conversion on exiftool command line:
$
exiftool -G -n -JFIF:all rose.jpg
[JFIF] JFIF Version : 1 1 [JFIF] Resolution Unit : 1 [JFIF] X Resolution : 72 [JFIF] Y Resolution : 72
Disable print conversion and group name in PyExifTool:
import exiftool with exiftool.ExifTool(common_args=None) as et: print(et.execute("-JFIF:all", "rose.jpg"))
Output:
JFIF Version : 1.01 Resolution Unit : inches X Resolution : 72 Y Resolution : 72
I can run this on the command-line but it doesn’t work in PyExifTool
A frequent problem encountered by first-time users, is figuring out how to properly split their arguments into a call to PyExifTool.
As noted in the Quick Start Examples:
If there is an unquoted space on the command line to exiftool, it’s a separate argument to the method in PyExifTool.
So, what does this look like in practice?
Use Python’s shlex library as a quick and easy way to figure out what the parameters to exiftool.ExifTool.execute()
or exiftool.ExifTool.execute_json()
should be.
Sample exiftool command line (with multiple quoted and unquoted parameters):
exiftool -v0 -preserve -overwrite_original -api largefilesupport=1 -api "QuickTimeUTC=1" "-EXIF:DateTimeOriginal+=1:2:3 4:5:6" -XMP:DateTimeOriginal="2006:05:04 03:02:01" -gpsaltituderef="Above Sea Level" -make= test.mov
Using
shlex
to figure out the right argument list:import shlex, exiftool with exiftool.ExifToolHelper() as et: params = shlex.split('-v0 -preserve -overwrite_original -api largefilesupport=1 "-EXIF:DateTimeOriginal+=1:2:3 4:5:6" -XMP:DateTimeOriginal="2006:05:04 03:02:01" -gpsaltituderef="Above Sea Level" -make= test.mov') print(params) # Output: ['-v0', '-preserve', '-overwrite_original', '-api', 'largefilesupport=1', '-api', 'QuickTimeUTC=1', '-EXIF:DateTimeOriginal+=1:2:3 4:5:6', '-XMP:DateTimeOriginal=2006:05:04 03:02:01', '-gpsaltituderef=Above Sea Level', '-make=', 'test.mov'] et.execute(*params)
Note
shlex.split()
is a useful tool to simplify discovery of the correct arguments needed to call PyExifTool.However, since spliting and constructing immutable strings in Python is slower than building the parameter list properly, this method is only recommended for debugging!
PyExifTool json turns some text fields into numbers
A strange behavior of exiftool is documented in the exiftool documentation:
-j[[+]=JSONFILE] (-json)
Note that ExifTool quotes JSON values only if they don't look like numbers
(regardless of the original storage format or the relevant metadata specification).
This causes a peculiar behavior if you set a text metadata field to a string that looks like a number:
import exiftool
with exiftool.ExifToolHelper() as et:
# Comment is a STRING field
et.set_tags("rose.jpg", {"Comment": "1.10"}) # string: "1.10" != "1.1"
# FocalLength is a FLOAT field
et.set_tags("rose.jpg", {"FocalLength": 1.10}) # float: 1.10 == 1.1
print(et.get_tags("rose.jpg", ["Comment", "FocalLength"]))
# Prints: [{'SourceFile': 'rose.jpg', 'File:Comment': 1.1, 'EXIF:FocalLength': 1.1}]
Workaround to enable output as string
There is no universal fix which wouldn’t affect other behaviors in PyExifTool, so this is an advanced workaround if you encounter this specific problem.
PyExifTool does not do any processing on the fields returned by exiftool. In effect, what is returned is processed directly by json.loads()
by default.
You can change the behavior of the json string parser, or specify a different one using exiftool.ExifTool.set_json_loads()
.
The documentation of CPython’s json.load allows parse_float
to be any parser of choice when a float is encountered in a JSON file. Thus, you can force the float to be interpreted as a string.
However, as you can see below, it also changes the behavior of all float fields.
import exiftool, json
with exiftool.ExifToolHelper() as et:
et.set_json_loads(json.loads, parse_float=str)
# Comment is a STRING field
et.set_tags("rose.jpg", {"Comment": "1.10"}) # string: "1.10" == "1.10"
# FocalLength is a FLOAT field
et.set_tags("rose.jpg", {"FocalLength": 1.10}) # float: 1.1 != "1.1"
print(et.get_tags("rose.jpg", ["Comment", "FocalLength"]))
# Prints: [{'SourceFile': 'rose.jpg', 'File:Comment': '1.10', 'EXIF:FocalLength': '1.1'}]
Warning
Unfortunately you can either change all float fields to a string, or possibly lose some float precision when working with floats in string metadata fields.
There isn’t any known universal workaround which wouldn’t break one thing or the other, as it is an underlying exiftool quirk.
There are other edge cases which may exhibit quirky behavior when storing numbers and whitespace only to text fields (See test cases related to numeric tags). Since PyExifTool cannot accommodate all possible edge cases, this workaround will allow you to configure PyExifTool to work in your environment!
I would like to use a faster json string parser
By default, PyExifTool uses the built-in json
library to load the json string returned by exiftool. If you would like to use an alternate library, set it manually using exiftool.ExifTool.set_json_loads()
import exiftool, json
with exiftool.ExifToolHelper() as et:
et.set_json_loads(ujson.loads)
...
Note
In PyExifTool version before 0.5.6, ujson
was supported automatically if the package was installed.
To support any possible alternative JSON library, this behavior has now been changed and it must be enabled manually.
I’m getting an error! How do I debug PyExifTool output?
To assist debugging, ExifTool has a logger
in the constructor exiftool.ExifTool.__init__()
. You can also specify the logger after constructing the object by using the exiftool.ExifTool.logger
property.
First construct the logger object. The example below using the most common way to construct using getLogger(__name__)
. See more examples on Python logging - Advanced Logging Tutorial
Example usage:
import logging
import exiftool
logging.basicConfig(level=logging.DEBUG)
with exiftool.ExifToolHelper(logger=logging.getLogger(__name__)) as et:
et.execute("missingfile.jpg",)