diff --git a/src/pyinfra/api/facts.py b/src/pyinfra/api/facts.py index e9550263e..fcc65bb67 100644 --- a/src/pyinfra/api/facts.py +++ b/src/pyinfra/api/facts.py @@ -269,7 +269,22 @@ def _get_fact( if status: if stdout_lines: - data = fact.process(stdout_lines) + try: + data = fact.process(stdout_lines) + except Exception as e: + log_error_or_warning( + host, + global_kwargs["_ignore_errors"], + description=("could not process fact: {0} {1}").format( + name, get_kwargs_str(fact_kwargs) + ), + exception=e, + ) + + # Check we've not failed + if apply_failed_hosts and not global_kwargs["_ignore_errors"]: + state.fail_hosts({host}) + elif stderr_lines: # If we have error output and that error is sudo or su stating the user # does not exist, do not fail but instead return the default fact value. diff --git a/src/pyinfra/api/util.py b/src/pyinfra/api/util.py index fddc462be..07ea1322b 100644 --- a/src/pyinfra/api/util.py +++ b/src/pyinfra/api/util.py @@ -220,7 +220,11 @@ def log_operation_start( def log_error_or_warning( - host: "Host", ignore_errors: bool, description: str = "", continue_on_error: bool = False + host: "Host", + ignore_errors: bool, + description: str = "", + continue_on_error: bool = False, + exception: Exception | None = None, ) -> None: log_func = logger.error log_color = "red" @@ -235,6 +239,15 @@ def log_error_or_warning( if description: log_text = f"{log_text}: " + if exception: + exc_text = "{0}: {1}".format(type(exception).__name__, exception) + log_func( + "{0}{1}".format( + host.print_prefix, + click.style(exc_text, log_color), + ), + ) + log_func( "{0}{1}{2}".format( host.print_prefix,