Python wrapper for exiftool to extract and manipulate metadata from image, video, and other media files
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Low-level interface to the exiftool subprocess providing direct command execution, JSON parsing, and subprocess lifecycle management. The ExifTool class offers maximum control and flexibility for advanced users who need fine-grained control over the exiftool process.
Control the exiftool subprocess lifecycle with start, stop, and context manager support.
class ExifTool:
def __init__(self, executable=None, common_args=["-G", "-n"], win_shell=False, config_file=None, encoding=None, logger=None):
"""
Initialize ExifTool instance.
Parameters:
- executable: str or Path, path to exiftool executable (default: searches PATH)
- common_args: list of str, arguments included in every command (default: ["-G", "-n"])
- win_shell: bool, Windows shell visibility (default: False)
- config_file: str or Path, path to exiftool config file
- encoding: str, text encoding for subprocess communication (default: utf-8)
- logger: logger instance for debug output
"""
def run(self):
"""
Start the exiftool subprocess in batch mode.
Raises:
- ExifToolRunning: if subprocess is already running
- Various subprocess errors if startup fails
"""
def terminate(self, timeout=30, _del=False):
"""
Terminate the exiftool subprocess.
Parameters:
- timeout: int, seconds to wait for clean shutdown (default: 30)
- _del: bool, internal flag for destructor cleanup
"""
def __enter__(self):
"""Context manager entry - automatically calls run()"""
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit - automatically calls terminate()"""
def __del__(self):
"""Destructor - automatically terminates subprocess if still running"""Execute exiftool commands directly with raw output or JSON parsing.
def execute(self, *params, raw_bytes=False):
"""
Execute parameters with the exiftool subprocess.
Parameters:
- *params: str or bytes, command parameters to send to exiftool
- raw_bytes: bool, return bytes instead of str (default: False)
Returns:
str or bytes: command stdout output
Raises:
- ExifToolNotRunning: if subprocess is not running
- ExifToolVersionError: if version compatibility issues
- TypeError: if parameter types are invalid
"""
def execute_json(self, *params):
"""
Execute parameters and parse JSON output.
Parameters:
- *params: str or bytes, command parameters to send to exiftool
Returns:
list: parsed JSON as list of dictionaries
Raises:
- ExifToolOutputEmptyError: if no output received when expected
- ExifToolJSONInvalidError: if invalid JSON received
"""Configure the ExifTool instance behavior and access runtime information.
# Properties (read/write)
@property
def executable(self) -> Union[str, Path]:
"""Path to exiftool executable (can only be set when not running)"""
@property
def encoding(self) -> str:
"""Text encoding for subprocess communication (can only be set when not running)"""
@property
def block_size(self) -> int:
"""Block size for reading from subprocess pipes (default: 4096)"""
@property
def common_args(self) -> List[str]:
"""Common arguments included in every command (can only be set when not running)"""
@property
def config_file(self) -> Union[str, Path]:
"""Path to exiftool config file (can only be set when not running)"""
# Properties (read-only)
@property
def running(self) -> bool:
"""Whether the exiftool subprocess is currently running"""
@property
def version(self) -> str:
"""Version string of the running exiftool process (only available when running)"""
@property
def last_stdout(self) -> Union[str, bytes]:
"""STDOUT from most recent execute() call"""
@property
def last_stderr(self) -> Union[str, bytes]:
"""STDERR from most recent execute() call"""
@property
def last_status(self) -> int:
"""Exit status code from most recent execute() call"""
# Properties (write-only)
@property
def logger(self):
"""Set logger instance for debug output"""Override the default JSON parser for performance or compatibility.
def set_json_loads(self, json_loads, **kwargs):
"""
Override default JSON parser method.
Parameters:
- json_loads: callable, alternative JSON loading function (e.g., ujson.loads)
- **kwargs: additional arguments passed to the JSON loader
"""import exiftool
# Manual process control
et = exiftool.ExifTool()
et.run()
try:
result = et.execute('-ver')
print(f"ExifTool version: {result}")
finally:
et.terminate()
# Context manager (recommended)
with exiftool.ExifTool() as et:
result = et.execute('-ver')
print(f"ExifTool version: {result}")with exiftool.ExifTool() as et:
# Get basic info
output = et.execute('-n', '-g', 'image.jpg')
# Get JSON output
metadata = et.execute_json('-j', 'image.jpg')
# Set tags
et.execute('-overwrite_original', '-EXIF:Artist=John Doe', 'image.jpg')import ujson
import exiftool
with exiftool.ExifTool() as et:
# Use ujson for better performance
et.set_json_loads(ujson.loads)
metadata = et.execute_json('-j', 'large_file.jpg')import exiftool
# Custom configuration
et = exiftool.ExifTool(
executable='/usr/local/bin/exiftool',
common_args=['-G', '-n', '-q'], # Add quiet flag
encoding='utf-8',
config_file='my_exiftool.config'
)
with et:
# All commands will include common_args
result = et.execute('image.jpg') # Equivalent to: exiftool -G -n -q image.jpgInstall with Tessl CLI
npx tessl i tessl/pypi-pyexiftool