From 060729c4c4255651c6ee8149989d9de541d0a6aa Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Sun, 13 Feb 2022 14:51:31 -0800 Subject: [PATCH] Added --debug and crash reporter to export, #628 --- osxphotos/cli.py | 48 +++++++++++++++---- osxphotos/photosdb/_photosdb_process_exif.py | 2 +- .../photosdb/_photosdb_process_searchinfo.py | 13 +---- osxphotos/photosdb/photosdb.py | 1 - tests/test_cli.py | 17 +++---- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/osxphotos/cli.py b/osxphotos/cli.py index 822d29e1..328d37d9 100644 --- a/osxphotos/cli.py +++ b/osxphotos/cli.py @@ -61,6 +61,7 @@ from .configoptions import ( ConfigOptionsInvalidError, ConfigOptionsLoadError, ) +from .crash_reporter import crash_reporter from .datetime_formatter import DateTimeFormatter from .exiftool import get_exiftool_path from .export_db import ExportDB, ExportDBInMemory @@ -135,12 +136,19 @@ __all__ = [ VERBOSE = False VERBOSE_TIMESTAMP = False +# global variable to control debug output +# set via --debug +DEBUG = False + # used to show/hide hidden commands OSXPHOTOS_HIDDEN = not bool(os.getenv("OSXPHOTOS_SHOW_HIDDEN", default=False)) # used by snap and diff commands OSXPHOTOS_SNAPSHOT_DIR = "/private/tmp/osxphotos_snapshots" +# where to write the crash report if osxphotos crashes +OSXPHOTOS_CRASH_LOG = os.getcwd() + "/osxphotos_crash.log" + rich.traceback.install() @@ -674,7 +682,7 @@ def QUERY_OPTIONS(f): @click.version_option(__version__, "--version", "-v") @click.pass_context def cli(ctx, db, json_, debug): - ctx.obj = CLI_Obj(db=db, json=json_, debug=debug) + ctx.obj = CLI_Obj(db=db, json=json_) @cli.command(cls=ExportCommand) @@ -1205,10 +1213,25 @@ def cli(ctx, db, json_, debug): f"Can be specified multiple times. Valid options are: {PROFILE_SORT_KEYS}. " "Default = 'cumulative'.", ) +@click.option( + "--debug", + required=False, + is_flag=True, + default=False, + hidden=OSXPHOTOS_HIDDEN, + help="Enable debug output.", +) @DB_ARGUMENT @click.argument("dest", nargs=1, type=click.Path(exists=True)) @click.pass_obj @click.pass_context +@crash_reporter( + OSXPHOTOS_CRASH_LOG, + "[red]Something went wrong and osxphotos encountered an error:[/red]", + "osxphotos crash log", + "Please file a bug report at https://github.com/RhetTbull/osxphotos/issues with the crash log attached.", + f"osxphotos version: {__version__}", +) def export( ctx, cli_obj, @@ -1347,6 +1370,7 @@ def export( preview_if_missing, profile, profile_sort, + debug, ): """Export photos from the Photos database. Export path DEST is required. @@ -1360,6 +1384,11 @@ def export( to modify this behavior. """ + global DEBUG + if debug: + DEBUG = True + osxphotos._set_debug(True) + if profile: click.echo("Profiling...") profile_sort = profile_sort or ["cumulative"] @@ -1403,7 +1432,7 @@ def export( ), err=True, ) - raise click.Abort() + sys.exit(1) # re-set the local vars to the corresponding config value # this isn't elegant but avoids having to rewrite this function to use cfg.varname for every parameter @@ -1589,7 +1618,7 @@ def export( ), err=True, ) - raise click.Abort() + sys.exit(1) if all(x in [s.lower() for s in sidecar] for x in ["json", "exiftool"]): click.echo( @@ -1599,7 +1628,7 @@ def export( ), err=True, ) - raise click.Abort() + sys.exit(1) if xattr_template: for attr, _ in xattr_template: @@ -1612,7 +1641,7 @@ def export( ), err=True, ) - raise click.Abort() + sys.exit(1) if save_config: verbose_(f"Saving options to file {save_config}") @@ -1633,7 +1662,7 @@ def export( click.echo( click.style(f"DEST {dest} must be valid path", fg=CLI_COLOR_ERROR), err=True ) - raise click.Abort() + sys.exit(1) dest = str(pathlib.Path(dest).resolve()) @@ -1644,7 +1673,7 @@ def export( ), err=True, ) - raise click.Abort() + sys.exit(1) # if use_photokit and not check_photokit_authorization(): # click.echo( @@ -3148,6 +3177,9 @@ def export_photo_to_directory( f"Retrying export for photo ({photo.uuid}: {photo.original_filename})" ) except Exception as e: + if DEBUG: + # if debug mode, don't swallow the exceptions + raise e click.echo( click.style( f"Error exporting photo ({photo.uuid}: {photo.original_filename}) as {filename}: {e}", @@ -3516,7 +3548,7 @@ def write_export_report(report_file, results): click.style("Could not open output file for writing", fg=CLI_COLOR_ERROR), err=True, ) - raise click.Abort() + sys.exit(1) def cleanup_files(dest_path, files_to_keep, fileutil): diff --git a/osxphotos/photosdb/_photosdb_process_exif.py b/osxphotos/photosdb/_photosdb_process_exif.py index 2ed7dade..72c623f1 100644 --- a/osxphotos/photosdb/_photosdb_process_exif.py +++ b/osxphotos/photosdb/_photosdb_process_exif.py @@ -4,7 +4,7 @@ import logging from .._constants import _DB_TABLE_NAMES, _PHOTOS_4_VERSION -from ..utils import _db_is_locked, _debug, _open_sql_file +from ..utils import _db_is_locked, _open_sql_file from .photosdb_utils import get_db_version diff --git a/osxphotos/photosdb/_photosdb_process_searchinfo.py b/osxphotos/photosdb/_photosdb_process_searchinfo.py index 8a3ccf5e..09999c14 100644 --- a/osxphotos/photosdb/_photosdb_process_searchinfo.py +++ b/osxphotos/photosdb/_photosdb_process_searchinfo.py @@ -10,7 +10,7 @@ import uuid as uuidlib from pprint import pformat from .._constants import _PHOTOS_4_VERSION, SEARCH_CATEGORY_LABEL -from ..utils import _db_is_locked, _debug, _open_sql_file, normalize_unicode +from ..utils import _db_is_locked, _open_sql_file, normalize_unicode """ This module should be imported in the class defintion of PhotosDB in photosdb.py @@ -139,17 +139,6 @@ def _process_searchinfo(self): _db_searchinfo_labels[label] = [uuid] _db_searchinfo_labels_normalized[label_norm] = [uuid] - if _debug(): - logging.debug( - "_db_searchinfo_categories: \n" + pformat(self._db_searchinfo_categories) - ) - logging.debug("_db_searchinfo_uuid: \n" + pformat(self._db_searchinfo_uuid)) - logging.debug("_db_searchinfo_labels: \n" + pformat(self._db_searchinfo_labels)) - logging.debug( - "_db_searchinfo_labels_normalized: \n" - + pformat(self._db_searchinfo_labels_normalized) - ) - conn.close() diff --git a/osxphotos/photosdb/photosdb.py b/osxphotos/photosdb/photosdb.py index 05764f3f..c05a25eb 100644 --- a/osxphotos/photosdb/photosdb.py +++ b/osxphotos/photosdb/photosdb.py @@ -2456,7 +2456,6 @@ class PhotosDB: verbose("Processing moments.") self._process_moments() - # done processing, dump debug data if requested verbose("Done processing details from Photos library.") def _process_moments(self): diff --git a/tests/test_cli.py b/tests/test_cli.py index 0a522990..aa513fc1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3883,8 +3883,8 @@ def test_export_directory_template_3(): "{created.year}/{foo}", ], ) - assert result.exit_code == 2 - assert "Error: Invalid template" in result.output + assert result.exit_code != 0 + assert "Invalid template" in result.output def test_export_directory_template_album_1(): @@ -4203,8 +4203,8 @@ def test_export_filename_template_3(): "{foo}-{original_filename}", ], ) - assert result.exit_code == 2 - assert "Error: Invalid template" in result.output + assert result.exit_code != 0 + assert "Invalid template" in result.output def test_export_album(): @@ -4896,6 +4896,7 @@ def test_export_force_update(): in result.output ) + @pytest.mark.skipif( "OSXPHOTOS_TEST_EXPORT" not in os.environ, reason="Skip if not running on author's personal library.", @@ -6073,7 +6074,7 @@ def test_export_report_not_a_file(): export, [os.path.join(cwd, CLI_PHOTOS_DB), ".", "-V", "--report", "."] ) assert result.exit_code != 0 - assert "Aborted!" in result.output + assert "report is a directory, must be file name" in result.output def test_export_as_hardlink_download_missing(): @@ -6101,7 +6102,7 @@ def test_export_as_hardlink_download_missing(): ], ) assert result.exit_code != 0 - assert "Aborted!" in result.output + assert "Incompatible export options" in result.output def test_export_missing(): @@ -6149,7 +6150,7 @@ def test_export_missing_not_download_missing(): export, [os.path.join(cwd, CLI_PHOTOS_DB), ".", "-V", "--missing", "."] ) assert result.exit_code != 0 - assert "Aborted!" in result.output + assert "Incompatible export options" in result.output def test_export_cleanup(): @@ -7466,7 +7467,7 @@ def test_bad_query_eval(): ], ) assert result.exit_code != 0 - assert "Error: Invalid query-eval CRITERIA" in result.output + assert "Invalid query-eval CRITERIA" in result.output def test_query_min_size_1():