From 40aea793ee6534f1c4bddcc649bdebfbbc0b686e Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 12 Nov 2025 21:00:17 +0100 Subject: [PATCH] Use raise from in modules (#11097) * Use raise from. * Add changelog fragment. * Add comment. --- changelogs/fragments/11097-raise-from.yml | 21 +++++++++++++++++++ plugins/module_utils/cmd_runner.py | 2 +- plugins/module_utils/csv.py | 2 +- plugins/module_utils/database.py | 8 +++---- plugins/module_utils/hwc_utils.py | 6 +++--- .../identity/keycloak/keycloak.py | 8 +++---- plugins/module_utils/ipa.py | 2 +- plugins/module_utils/lxd.py | 2 +- plugins/module_utils/net_tools/pritunl/api.py | 2 +- plugins/modules/cronvar.py | 8 +++---- plugins/modules/jenkins_plugin.py | 2 +- plugins/modules/jira.py | 2 +- plugins/modules/layman.py | 2 +- plugins/modules/listen_ports_facts.py | 4 ++-- plugins/modules/osx_defaults.py | 8 +++---- plugins/modules/packet_ip_subnet.py | 4 ++-- plugins/modules/packet_sshkey.py | 2 +- plugins/modules/pids.py | 2 +- plugins/modules/rhevm.py | 4 ++-- plugins/modules/vertica_schema.py | 4 ++-- plugins/modules/vertica_user.py | 4 ++-- 21 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 changelogs/fragments/11097-raise-from.yml diff --git a/changelogs/fragments/11097-raise-from.yml b/changelogs/fragments/11097-raise-from.yml new file mode 100644 index 0000000000..273a978a5f --- /dev/null +++ b/changelogs/fragments/11097-raise-from.yml @@ -0,0 +1,21 @@ +minor_changes: + - cmd_runner module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - csv module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - database module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - hwc_utils module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - identity.keycloak.keycloak module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - ipa module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - lxd module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - net_tools.pritunl.api module utils - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - cronvar - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - jenkins_plugin - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - jira - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - layman - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - listen_ports_facts - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - osx_defaults - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - packet_ip_subnet - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - packet_sshkey - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - pids - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - rhevm - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - vertica_schema - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). + - vertica_user - use ``raise ... from ...`` when passing on exceptions (https://github.com/ansible-collections/community.general/pull/11097). diff --git a/plugins/module_utils/cmd_runner.py b/plugins/module_utils/cmd_runner.py index be81ca1fda..44bd9fc6ec 100644 --- a/plugins/module_utils/cmd_runner.py +++ b/plugins/module_utils/cmd_runner.py @@ -194,7 +194,7 @@ class _CmdRunnerContext: except MissingArgumentValue: raise except Exception as e: - raise FormatError(arg_name, value, runner.arg_formats[arg_name], e) + raise FormatError(arg_name, value, runner.arg_formats[arg_name], e) from e if self.check_mode_skip and module.check_mode: return self.check_mode_return diff --git a/plugins/module_utils/csv.py b/plugins/module_utils/csv.py index 8246be14ab..452ab6ee96 100644 --- a/plugins/module_utils/csv.py +++ b/plugins/module_utils/csv.py @@ -45,7 +45,7 @@ def initialize_dialect(dialect, **kwargs): try: csv.register_dialect("custom", dialect, **dialect_params) except TypeError as e: - raise CustomDialectFailureError(f"Unable to create custom dialect: {e}") + raise CustomDialectFailureError(f"Unable to create custom dialect: {e}") from e dialect = "custom" return dialect diff --git a/plugins/module_utils/database.py b/plugins/module_utils/database.py index 86d8bd5231..c2ddfb3c2b 100644 --- a/plugins/module_utils/database.py +++ b/plugins/module_utils/database.py @@ -56,8 +56,8 @@ def _find_end_quote(identifier, quote_char): while True: try: quote = identifier.index(quote_char) - except ValueError: - raise UnclosedQuoteError + except ValueError as e: + raise UnclosedQuoteError from e accumulate = accumulate + quote try: next_char = identifier[quote + 1] @@ -67,8 +67,8 @@ def _find_end_quote(identifier, quote_char): try: identifier = identifier[quote + 2 :] accumulate = accumulate + 2 - except IndexError: - raise UnclosedQuoteError + except IndexError as e: + raise UnclosedQuoteError from e else: return accumulate diff --git a/plugins/module_utils/hwc_utils.py b/plugins/module_utils/hwc_utils.py index a23f339b51..926351655c 100644 --- a/plugins/module_utils/hwc_utils.py +++ b/plugins/module_utils/hwc_utils.py @@ -59,14 +59,14 @@ def session_method_wrapper(f): url = self.endpoint + url r = f(self, url, *args, **kwargs) except Exception as ex: - raise HwcClientException(0, f"Sending request failed, error={ex}") + raise HwcClientException(0, f"Sending request failed, error={ex}") from ex result = None if r.content: try: result = r.json() except Exception as ex: - raise HwcClientException(0, f"Parsing response to json failed, error: {ex}") + raise HwcClientException(0, f"Parsing response to json failed, error: {ex}") from ex code = r.status_code if code not in [200, 201, 202, 203, 204, 205, 206, 207, 208, 226]: @@ -184,7 +184,7 @@ class Config: try: url = client.get_endpoint(service_type=service_type, region_name=region, interface="public") except Exception as ex: - raise HwcClientException(0, f"Getting endpoint failed, error={ex}") + raise HwcClientException(0, f"Getting endpoint failed, error={ex}") from ex if url == "": raise HwcClientException(0, f"Cannot find the endpoint for {service_type}") diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index b961d4c614..4f6360b179 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -214,11 +214,11 @@ def _token_request(module_params, payload): return r["access_token"] except ValueError as e: - raise KeycloakError(f"API returned invalid JSON when trying to obtain access token from {auth_url}: {e}") - except KeyError: - raise KeycloakError(f"API did not include access_token field in response from {auth_url}") + raise KeycloakError(f"API returned invalid JSON when trying to obtain access token from {auth_url}: {e}") from e + except KeyError as e: + raise KeycloakError(f"API did not include access_token field in response from {auth_url}") from e except Exception as e: - raise KeycloakError(f"Could not obtain access token from {auth_url}: {e}", authError=e) + raise KeycloakError(f"Could not obtain access token from {auth_url}: {e}", authError=e) from e def _request_token_using_credentials(module_params): diff --git a/plugins/module_utils/ipa.py b/plugins/module_utils/ipa.py index 7a2fa6b264..f0be48437c 100644 --- a/plugins/module_utils/ipa.py +++ b/plugins/module_utils/ipa.py @@ -37,7 +37,7 @@ def _env_then_dns_fallback(*args, **kwargs): try: return socket.gethostbyaddr(socket.gethostbyname("ipa-ca"))[0] except Exception: - raise AnsibleFallbackNotFound + raise AnsibleFallbackNotFound from None # no need to pass the original exception's context since this is basically a special return value class IPAClient: diff --git a/plugins/module_utils/lxd.py b/plugins/module_utils/lxd.py index cc3836b3e4..5189263db0 100644 --- a/plugins/module_utils/lxd.py +++ b/plugins/module_utils/lxd.py @@ -116,7 +116,7 @@ class LXDClient: self._raise_err_from_json(resp_json) return resp_json except socket.error as e: - raise LXDClientException("cannot connect to the LXD server", err=e) + raise LXDClientException("cannot connect to the LXD server", err=e) from e def _raise_err_from_json(self, resp_json): err_params = {} diff --git a/plugins/module_utils/net_tools/pritunl/api.py b/plugins/module_utils/net_tools/pritunl/api.py index 13847869bd..343dbab707 100644 --- a/plugins/module_utils/net_tools/pritunl/api.py +++ b/plugins/module_utils/net_tools/pritunl/api.py @@ -322,4 +322,4 @@ def pritunl_auth_request( validate_certs=validate_certs, ) except Exception as e: - raise PritunlException(e) + raise PritunlException(e) from e diff --git a/plugins/modules/cronvar.py b/plugins/modules/cronvar.py index 4384a7d093..147891e58e 100644 --- a/plugins/modules/cronvar.py +++ b/plugins/modules/cronvar.py @@ -161,8 +161,8 @@ class CronVar: except IOError: # cron file does not exist return - except Exception: - raise CronVarError("Unexpected error:", sys.exc_info()[0]) + except Exception as e: + raise CronVarError("Unexpected error:", sys.exc_info()[0]) from e else: # using safely quoted shell for now, but this really should be two non-shell calls instead. FIXME (rc, out, err) = self.module.run_command(self._read_user_execute(), use_unsafe_shell=True) @@ -218,8 +218,8 @@ class CronVar: except OSError: # cron file does not exist return False - except Exception: - raise CronVarError("Unexpected error:", sys.exc_info()[0]) + except Exception as e: + raise CronVarError("Unexpected error:", sys.exc_info()[0]) from e def parse_for_var(self, line): lexer = shlex.shlex(line) diff --git a/plugins/modules/jenkins_plugin.py b/plugins/modules/jenkins_plugin.py index 65105ffa60..f30175626d 100644 --- a/plugins/modules/jenkins_plugin.py +++ b/plugins/modules/jenkins_plugin.py @@ -475,7 +475,7 @@ class JenkinsPlugin: self.module.fail_json(msg=msg_status, details=info["msg"]) except Exception as e: if dont_fail: - raise FailedInstallingWithPluginManager(e) + raise FailedInstallingWithPluginManager(e) from e else: self.module.fail_json(msg=msg_exception, details=to_native(e)) diff --git a/plugins/modules/jira.py b/plugins/modules/jira.py index f1e56a65d7..efa991eba1 100644 --- a/plugins/modules/jira.py +++ b/plugins/modules/jira.py @@ -775,7 +775,7 @@ class JIRA(StateModuleHelper): try: content = base64.b64decode(content) except binascii.Error as e: - raise Exception(f"Unable to base64 decode file content: {e}") + raise Exception(f"Unable to base64 decode file content: {e}") from e lines = [ f"--{boundary}", diff --git a/plugins/modules/layman.py b/plugins/modules/layman.py index 4c1da7d35a..e12a984b74 100644 --- a/plugins/modules/layman.py +++ b/plugins/modules/layman.py @@ -136,7 +136,7 @@ def download_url(module, url, dest): with open(dest, "w") as f: shutil.copyfileobj(response, f) except IOError as e: - raise ModuleError(f"Failed to write: {e}") + raise ModuleError(f"Failed to write: {e}") from e def install_overlay(module, name, list_url=None): diff --git a/plugins/modules/listen_ports_facts.py b/plugins/modules/listen_ports_facts.py index bff3933216..5d0f7a33d3 100644 --- a/plugins/modules/listen_ports_facts.py +++ b/plugins/modules/listen_ports_facts.py @@ -296,12 +296,12 @@ def ss_parse(raw): protocol, state, recv_q, send_q, local_addr_port, peer_addr_port = cells else: protocol, state, recv_q, send_q, local_addr_port, peer_addr_port, process = cells - except ValueError: + except ValueError as e: # unexpected stdout from ss raise EnvironmentError( 'Expected `ss` table layout "Netid, State, Recv-Q, Send-Q, Local Address:Port, Peer Address:Port" and' f'optionally "Process", but got something else: {line}' - ) + ) from e conns = regex_conns.search(local_addr_port) pids = regex_pid.findall(process) diff --git a/plugins/modules/osx_defaults.py b/plugins/modules/osx_defaults.py index c30597e6d6..c799be2b13 100644 --- a/plugins/modules/osx_defaults.py +++ b/plugins/modules/osx_defaults.py @@ -210,8 +210,8 @@ class OSXDefaults: elif data_type == "date": try: return datetime.strptime(value.split("+")[0].strip(), "%Y-%m-%d %H:%M:%S") - except ValueError: - raise OSXDefaultsException(f"Invalid date value: {value!r}. Required format yyy-mm-dd hh:mm:ss.") + except ValueError as e: + raise OSXDefaultsException(f"Invalid date value: {value!r}. Required format yyy-mm-dd hh:mm:ss.") from e elif data_type in ["int", "integer"]: if not OSXDefaults.is_int(value): raise OSXDefaultsException(f"Invalid integer value: {value!r}") @@ -219,8 +219,8 @@ class OSXDefaults: elif data_type == "float": try: value = float(value) - except ValueError: - raise OSXDefaultsException(f"Invalid float value: {value!r}") + except ValueError as e: + raise OSXDefaultsException(f"Invalid float value: {value!r}") from e return value elif data_type == "array": if not isinstance(value, list): diff --git a/plugins/modules/packet_ip_subnet.py b/plugins/modules/packet_ip_subnet.py index 52248e5baf..58d0f84304 100644 --- a/plugins/modules/packet_ip_subnet.py +++ b/plugins/modules/packet_ip_subnet.py @@ -212,8 +212,8 @@ def parse_subnet_cidr(cidr): addr, prefixlen = cidr.split("/") try: prefixlen = int(prefixlen) - except ValueError: - raise Exception(f"Wrong prefix length in CIDR expression {cidr}") + except ValueError as e: + raise Exception(f"Wrong prefix length in CIDR expression {cidr}") from e return addr, prefixlen diff --git a/plugins/modules/packet_sshkey.py b/plugins/modules/packet_sshkey.py index a9e221a864..3067c133fa 100644 --- a/plugins/modules/packet_sshkey.py +++ b/plugins/modules/packet_sshkey.py @@ -210,7 +210,7 @@ def act_on_sshkeys(target_state, module, packet_conn): changed = True except Exception as e: _msg = f"while trying to remove sshkey {k.label}, id {k.id} {target_state}, got error: {e}" - raise Exception(_msg) + raise Exception(_msg) from e return {"changed": changed, "sshkeys": [serialize_sshkey(k) for k in matching_sshkeys]} diff --git a/plugins/modules/pids.py b/plugins/modules/pids.py index 9dafeface8..5e3bfeebea 100644 --- a/plugins/modules/pids.py +++ b/plugins/modules/pids.py @@ -126,7 +126,7 @@ class PSAdapter(metaclass=abc.ABCMeta): try: regex = re.compile(pattern, flags) except re.error as e: - raise PSAdapterError(f"'{pattern}' is not a valid regular expression: {e}") + raise PSAdapterError(f"'{pattern}' is not a valid regular expression: {e}") from e return [p.pid for p in self._process_iter(*self.PATTERN_ATTRS) if self._matches_regex(p, regex)] diff --git a/plugins/modules/rhevm.py b/plugins/modules/rhevm.py index b84826fdd8..b4ff5a0a7a 100644 --- a/plugins/modules/rhevm.py +++ b/plugins/modules/rhevm.py @@ -371,8 +371,8 @@ class RHEVConn: api = API(url=url, username=user, password=password, insecure=str(insecure_api)) api.test() self.conn = api - except Exception: - raise Exception("Failed to connect to RHEV-M.") + except Exception as e: + raise Exception("Failed to connect to RHEV-M.") from e def __del__(self): self.conn.disconnect() diff --git a/plugins/modules/vertica_schema.py b/plugins/modules/vertica_schema.py index 944515d643..d26740ecf6 100644 --- a/plugins/modules/vertica_schema.py +++ b/plugins/modules/vertica_schema.py @@ -242,8 +242,8 @@ def absent(schema_facts, cursor, schema, usage_roles, create_roles): ) try: cursor.execute(f"drop schema {schema_facts[schema_key]['name']} restrict") - except pyodbc.Error: - raise CannotDropError("Dropping schema failed due to dependencies.") + except pyodbc.Error as e: + raise CannotDropError("Dropping schema failed due to dependencies.") from e del schema_facts[schema_key] return True else: diff --git a/plugins/modules/vertica_user.py b/plugins/modules/vertica_user.py index 58b9e5a6fd..6575fed8a0 100644 --- a/plugins/modules/vertica_user.py +++ b/plugins/modules/vertica_user.py @@ -282,8 +282,8 @@ def absent(user_facts, cursor, user, roles): update_roles(user_facts, cursor, user, user_facts[user_key]["roles"], user_facts[user_key]["default_roles"], []) try: cursor.execute(f"drop user {user_facts[user_key]['name']}") - except pyodbc.Error: - raise CannotDropError("Dropping user failed due to dependencies.") + except pyodbc.Error as e: + raise CannotDropError("Dropping user failed due to dependencies.") from e del user_facts[user_key] return True else: