Working on making export CLI threadsafe

This commit is contained in:
Rhet Turnbull
2023-04-02 12:36:51 -07:00
parent 81127b6d89
commit 030191be96

View File

@@ -1,5 +1,7 @@
"""export command for osxphotos CLI""" """export command for osxphotos CLI"""
from __future__ import annotations
import atexit import atexit
import inspect import inspect
import os import os
@@ -9,7 +11,8 @@ import shlex
import subprocess import subprocess
import sys import sys
import time import time
from typing import Iterable, List, Optional, Tuple from typing import Iterable, List, Optional, Tuple, Any, Callable
import concurrent.futures
import click import click
from osxmetadata import ( from osxmetadata import (
@@ -1426,46 +1429,27 @@ def export(
photo_num = 0 photo_num = 0
num_exported = 0 num_exported = 0
# hack to avoid passing all the options to export_photo
kwargs = locals().copy()
kwargs["export_dir"] = dest
kwargs["export_preview"] = preview
limit_str = f" (limit = [num]{limit}[/num])" if limit else "" limit_str = f" (limit = [num]{limit}[/num])" if limit else ""
with rich_progress(console=get_verbose_console(), mock=no_progress) as progress: with rich_progress(console=get_verbose_console(), mock=no_progress) as progress:
task = progress.add_task( task = progress.add_task(
f"Exporting [num]{num_photos}[/] photos{limit_str}", total=num_photos f"Exporting [num]{num_photos}[/] photos{limit_str}", total=num_photos
) )
futures = []
with concurrent.futures.ThreadPoolExecutor(
# max_workers=os.cpu_count()
max_workers=1,
) as executor:
for p in photos: for p in photos:
photo_num += 1 photo_num += 1
# hack to avoid passing all the options to export_photo kwargs["photo_num"] = photo_num
kwargs = { futures.append(executor.submit(export_worker, p, **kwargs))
k: v
for k, v in locals().items()
if k in inspect.getfullargspec(export_photo).args
}
kwargs["photo"] = p
kwargs["export_dir"] = dest
kwargs["export_preview"] = preview
export_results = export_photo(**kwargs)
if post_function:
for function in post_function:
# post function is tuple of (function, filename.py::function_name)
verbose(f"Calling post-function [bold]{function[1]}")
if not dry_run:
try:
function[0](p, export_results, verbose)
except Exception as e:
rich_echo_error(
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
)
run_post_command(
photo=p,
post_command=post_command,
export_results=export_results,
export_dir=dest,
dry_run=dry_run,
exiftool_path=exiftool_path,
export_db=export_db,
verbose=verbose,
)
for future in concurrent.futures.as_completed(futures):
p, export_results = future.result()
if album_export and export_results.exported: if album_export and export_results.exported:
try: try:
album_export.add(p) album_export.add(p)
@@ -1524,7 +1508,9 @@ def export(
if finder_tag_keywords or finder_tag_template: if finder_tag_keywords or finder_tag_template:
if dry_run: if dry_run:
for filepath in photo_files: for filepath in photo_files:
verbose(f"Writing Finder tags to [filepath]{filepath}[/]") verbose(
f"Writing Finder tags to [filepath]{filepath}[/]"
)
else: else:
tags_written, tags_skipped = write_finder_tags( tags_written, tags_skipped = write_finder_tags(
p, p,
@@ -1682,6 +1668,45 @@ def export(
export_db.close() export_db.close()
def export_worker(
photo: osxphotos.PhotoInfo, **kwargs
) -> tuple[osxphotos.PhotoInfo, ExportResults]:
"""Export worker function for multi-threaded export of photos"""
dry_run = kwargs["dry_run"]
verbose: Callable[[str], Any] = kwargs["verbose"]
export_args = {
k: v
for k, v in kwargs.items()
if k in inspect.getfullargspec(export_photo).args
}
export_args["photo"] = photo
export_results = export_photo(**export_args)
if post_function := kwargs["post_function"]:
for function in post_function:
# post function is tuple of (function, filename.py::function_name)
verbose(f"Calling post-function [bold]{function[1]}")
if not dry_run:
try:
function[0](photo, export_results, verbose)
except Exception as e:
rich_echo_error(
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
)
run_post_command(
photo=photo,
post_command=kwargs["post_command"],
export_results=export_results,
export_dir=kwargs["dest"],
dry_run=dry_run,
exiftool_path=kwargs["exiftool_path"],
export_db=kwargs["export_db"],
verbose=verbose,
)
return photo, export_results
def export_photo( def export_photo(
photo=None, photo=None,
dest=None, dest=None,