diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b518ea..9b6c777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog], [markdownlint], and this project adheres to [Semantic Versioning]. +## [0.0.10] - 2025-08-11 + +### Added in 0.0.10 + +- New Python snippets + ## [0.0.9] - 2025-07-23 ### Changed in 0.0.9 diff --git a/python/deleting/delete_futures.py b/python/deleting/delete_futures.py index 564f3ea..2b75f68 100755 --- a/python/deleting/delete_futures.py +++ b/python/deleting/delete_futures.py @@ -64,7 +64,7 @@ def futures_del(engine, input_file): del futures[f] - print(f"\nSuccessfully deleted {success_recs:,} records, with" f" {error_recs:,} errors") + print(f"\nSuccessfully deleted {success_recs:,} records, with {error_recs:,} errors") try: diff --git a/python/deleting/delete_with_info_futures.py b/python/deleting/delete_with_info_futures.py index 7fc3ebc..4eb9372 100755 --- a/python/deleting/delete_with_info_futures.py +++ b/python/deleting/delete_with_info_futures.py @@ -74,7 +74,7 @@ def futures_del(engine, input_file, output_file): del futures[f] - print(f"\nSuccessfully deleted {success_recs:,} records, with" f" {error_recs:,} errors") + print(f"\nSuccessfully deleted {success_recs:,} records, with {error_recs:,} errors") print(f"\nWith info responses written to {output_file}") diff --git a/python/information/get_stats.py b/python/information/get_stats.py index ab2f430..5c2d5c4 100755 --- a/python/information/get_stats.py +++ b/python/information/get_stats.py @@ -77,7 +77,7 @@ def futures_add(engine, input_file): del futures[f] - print(f"\nSuccessfully loaded {success_recs:,} records, with" f" {error_recs:,} errors") + print(f"\nSuccessfully loaded {success_recs:,} records, with {error_recs:,} errors") try: diff --git a/python/initialization/README.md b/python/initialization/README.md index 68e5fd4..c322049 100644 --- a/python/initialization/README.md +++ b/python/initialization/README.md @@ -18,12 +18,17 @@ - Priming the Senzing engine before use loads resource intensive assets upfront. - Without priming the first SDK call to the engine will appear slower than usual as it causes these assets to be loaded. - **factory_destroy.py** - - Calls `destroy` on the abstract factory destroying the abstract factory and any Senzing objects it has created. + - Calls `destroy()` on the abstract factory destroying the abstract factory and any Senzing objects it has created. - The abstract factory must exist for the life of Senzing objects it has created. - - If the abstract factory goes out of scope `destroy` is automatically called + - If the abstract factory goes out of scope `destroy()` is automatically called - **purge_repository.py** - **WARNING** This script will remove all data from a Senzing repository, use with caution! **WARNING**. - It will prompt first, still use with caution!. + **signal_handler.py** + - Catches signal.SIGINT (ctrl + c) and exits cleanly + - Exiting cleanly on a signal ensures resource cleanup for the abstract factory is automatically called + - If sz_abstract_factory goes out of scope 'destroy()` is automatically called, if signals are not caught and handled automatic resource cleanup does not happen + - `destroy()` could also be called directly on the abstract factory by the signal handler - **sz_engine_config_ini_to_json.py** - The snippets herein utilize the `SENZING_ENGINE_CONFIGURATION_JSON` environment variable for Senzing abstract factory creation. - If you are familiar with working with a Senzing project you may be aware the same configuration data is held in the sz_engine_config.ini file. diff --git a/python/initialization/signal_handler.py b/python/initialization/signal_handler.py new file mode 100755 index 0000000..0b910e7 --- /dev/null +++ b/python/initialization/signal_handler.py @@ -0,0 +1,29 @@ +#! /usr/bin/env python3 + +import os +import signal +import sys +from pathlib import Path + +from senzing import SzError +from senzing_core import SzAbstractFactoryCore + +INSTANCE_NAME = Path(__file__).stem +SETTINGS = os.getenv("SENZING_ENGINE_CONFIGURATION_JSON", "{}") + + +def handler(signum, frame): + print("\nCaught ctrl-c, exiting") + sys.exit(0) + + +signal.signal(signal.SIGINT, handler) + +try: + sz_abstract_factory = SzAbstractFactoryCore(INSTANCE_NAME, SETTINGS) + sz_engine = sz_abstract_factory.create_engine() + # Do work... + print("\nSimulating work, press ctrl-c to exit...") + signal.pause() +except SzError as err: + print(f"\n{err.__class__.__name__} - {err}") diff --git a/python/loading/add_futures.py b/python/loading/add_futures.py index ce9af9d..096472e 100755 --- a/python/loading/add_futures.py +++ b/python/loading/add_futures.py @@ -64,7 +64,7 @@ def futures_add(engine, input_file): del futures[f] - print(f"\nSuccessfully loaded {success_recs:,} records, with" f" {error_recs:,} errors") + print(f"\nSuccessfully loaded {success_recs:,} records, with {error_recs:,} errors") try: diff --git a/python/loading/add_with_info_futures.py b/python/loading/add_with_info_futures.py index 9d6e5bb..f765161 100755 --- a/python/loading/add_with_info_futures.py +++ b/python/loading/add_with_info_futures.py @@ -87,7 +87,7 @@ def futures_add(engine, input_file, output_file): del futures[f] - print(f"\nSuccessfully loaded {success_recs:,} records, with" f" {error_recs:,} errors") + print(f"\nSuccessfully loaded {success_recs:,} records, with {error_recs:,} errors") print(f"\nWith info responses written to {output_file}") diff --git a/python/redo/add_with_redo.py b/python/redo/add_with_redo.py index d567397..cb6362a 100755 --- a/python/redo/add_with_redo.py +++ b/python/redo/add_with_redo.py @@ -66,7 +66,7 @@ def process_redo(engine): success_recs += 1 if success_recs % 1 == 0: - print(f"Processed {success_recs:,} redo records, with" f" {error_recs:,} errors") + print(f"Processed {success_recs:,} redo records, with {error_recs:,} errors") except SzBadInputError as err: mock_logger("ERROR", err) error_recs += 1 @@ -77,7 +77,7 @@ def process_redo(engine): mock_logger("CRITICAL", err) raise err - print(f"\nSuccessfully processed {success_recs:,} redo records, with" f" {error_recs:,} errors") + print(f"\nSuccessfully processed {success_recs:,} redo records, with {error_recs:,} errors") try: diff --git a/python/redo/redo_continuous.py b/python/redo/redo_continuous.py index e4a325b..330cff3 100755 --- a/python/redo/redo_continuous.py +++ b/python/redo/redo_continuous.py @@ -1,6 +1,7 @@ #! /usr/bin/env python3 import os +import signal import sys import time from pathlib import Path @@ -12,6 +13,11 @@ SETTINGS = os.getenv("SENZING_ENGINE_CONFIGURATION_JSON", "{}") +def handler(signum, frame): + print("\nCaught ctrl-c, exiting") + sys.exit(0) + + def mock_logger(level, error, error_record=None): print(f"\n{level}: {error.__class__.__name__} - {error}", file=sys.stderr) if error_record: @@ -27,7 +33,7 @@ def process_redo(engine): if not (response := engine.get_redo_record()): print( "No redo records to process, pausing for 30 seconds. Total" - f" processed {success_recs:,} . (CTRL-C to exit)..." + f" processed: {success_recs:,} (ctrl-c to exit)..." ) time.sleep(30) continue @@ -36,7 +42,7 @@ def process_redo(engine): success_recs += 1 if success_recs % 100 == 0: - print(f"Processed {success_recs:,} redo records, with" f" {error_recs:,} errors") + print(f"Processed {success_recs:,} redo records, with {error_recs:,} errors") except SzBadInputError as err: mock_logger("ERROR", err) error_recs += 1 @@ -47,6 +53,8 @@ def process_redo(engine): raise err +signal.signal(signal.SIGINT, handler) + try: sz_factory = SzAbstractFactoryCore(INSTANCE_NAME, SETTINGS, verbose_logging=False) sz_engine = sz_factory.create_engine() diff --git a/python/redo/redo_continuous_futures.py b/python/redo/redo_continuous_futures.py index 06c33c8..6e1e411 100755 --- a/python/redo/redo_continuous_futures.py +++ b/python/redo/redo_continuous_futures.py @@ -2,6 +2,7 @@ import concurrent.futures import os +import signal import sys import time from pathlib import Path @@ -13,6 +14,11 @@ SETTINGS = os.getenv("SENZING_ENGINE_CONFIGURATION_JSON", "{}") +def handler(signum, frame): + print("\nCaught ctrl-c, exiting") + sys.exit(0) + + def mock_logger(level, error, error_record=None): print(f"\n{level}: {error.__class__.__name__} - {error}", file=sys.stderr) if error_record: @@ -50,7 +56,7 @@ def redo_count(engine): def redo_pause(success): - print("No redo records to process, pausing for 30 seconds. Total processed:" f" {success:,} (CTRL-C to exit)...") + print(f"No redo records to process, pausing for 30 seconds. Total processed: {success:,} (ctrl-c to exit)...") time.sleep(30) @@ -88,7 +94,7 @@ def futures_redo(engine): else: success_recs += 1 if success_recs % 100 == 0: - print(f"Processed {success_recs:,} redo records, with" f" {error_recs:,} errors") + print(f"Processed {success_recs:,} redo records, with {error_recs:,} errors") finally: if not shutdown and (record := get_redo_record(engine)): futures[executor.submit(process_redo_record, engine, record)] = record @@ -106,6 +112,8 @@ def futures_redo(engine): futures[executor.submit(process_redo_record, engine, record)] = record +signal.signal(signal.SIGINT, handler) + try: sz_factory = SzAbstractFactoryCore(INSTANCE_NAME, SETTINGS, verbose_logging=False) sz_engine = sz_factory.create_engine() diff --git a/python/redo/redo_with_info_continuous.py b/python/redo/redo_with_info_continuous.py index f62514d..23b837c 100755 --- a/python/redo/redo_with_info_continuous.py +++ b/python/redo/redo_with_info_continuous.py @@ -20,8 +20,13 @@ SETTINGS = os.getenv("SENZING_ENGINE_CONFIGURATION_JSON", "{}") -def responses_message(signum, frame): +def handler(signum, frame): + print("\nCaught ctrl-c, exiting") print(f"\nWith info responses written to {OUTPUT_FILE}") + sys.exit(0) + + +def responses_message(signum, frame): sys.exit() @@ -32,7 +37,7 @@ def mock_logger(level, error, error_record=None): def redo_pause(success): - print("No redo records to process, pausing for 30 seconds. Total processed:" f" {success:,} (CTRL-C to exit)...") + print(f"No redo records to process, pausing for 30 seconds. Total processed: {success:,} (ctrl-c to exit)...") time.sleep(30) @@ -53,7 +58,7 @@ def process_redo(engine, output_file): out_file.write(f"{response}\n") if success_recs % 100 == 0: - print(f"Processed {success_recs:,} redo records, with" f" {error_recs:,} errors") + print(f"Processed {success_recs:,} redo records, with {error_recs:,} errors") except SzBadInputError as err: mock_logger("ERROR", err, redo_record) error_recs += 1 @@ -65,7 +70,7 @@ def process_redo(engine, output_file): raise err -signal.signal(signal.SIGINT, responses_message) +signal.signal(signal.SIGINT, handler) try: sz_factory = SzAbstractFactoryCore(INSTANCE_NAME, SETTINGS, verbose_logging=False) diff --git a/python/searching/search_futures.py b/python/searching/search_futures.py index bfe5d43..c7cd67e 100755 --- a/python/searching/search_futures.py +++ b/python/searching/search_futures.py @@ -64,7 +64,7 @@ def futures_search(engine, input_file): del futures[f] - print(f"\nSuccessfully searched {success_recs:,} records, with" f" {error_recs:,} errors") + print(f"\nSuccessfully searched {success_recs:,} records, with {error_recs:,} errors") try: