Work on issues found by pylint (#896)

* Look at possibly-used-before-assignment.

* Use latest beta releases of ansible-core 2.19 for mypy and pylint.

* Look at unsupported-*.

* Look at unknown-option-value.

* Look at redefined-builtin.

* Look at superfluous-parens.

* Look at unspecified-encoding.

* Adjust to new cryptography version and to ansible-core 2.17's pylint.

* Look at super-with-arguments.

* Look at no-else-*.

* Look at try-except-raise.

* Look at inconsistent-return-statements.

* Look at redefined-outer-name.

* Look at redefined-argument-from-local.

* Look at attribute-defined-outside-init.

* Look at unused-variable.

* Look at protected-access.

* Look at raise-missing-from.

* Look at arguments-differ.

* Look at useless-suppression and use-symbolic-message-instead.

* Look at consider-using-dict-items.

* Look at consider-using-in.

* Look at consider-using-set-comprehension.

* Look at consider-using-with.

* Look at use-dict-literal.
This commit is contained in:
Felix Fontein
2025-05-18 00:57:28 +02:00
committed by GitHub
parent a3a5284f97
commit 318462fa24
96 changed files with 1748 additions and 1598 deletions

View File

@@ -45,8 +45,7 @@ def restore_on_failure(
if backup_file is not None:
module.atomic_move(os.path.abspath(backup_file), os.path.abspath(path))
raise
else:
module.add_cleanup_file(backup_file)
module.add_cleanup_file(backup_file)
return backup_and_restore
@@ -91,9 +90,8 @@ def _restore_all_on_failure(
os.path.abspath(backup), os.path.abspath(destination)
)
raise
else:
for destination, backup in backups:
self.module.add_cleanup_file(backup)
for destination, backup in backups:
self.module.add_cleanup_file(backup)
return backup_and_restore
@@ -126,7 +124,7 @@ class OpensshModule(metaclass=abc.ABCMeta):
result["changed"] = self.changed
if self.module._diff:
if self.module._diff: # pylint: disable=protected-access
result["diff"] = self.diff
return result
@@ -219,7 +217,7 @@ class KeygenCommand:
serial_number: int | None,
signature_algorithm: str | None,
signing_key_path: str,
type: t.Literal["host", "user"] | None,
cert_type: t.Literal["host", "user"] | None,
time_parameters: OpensshCertificateTimeParameters,
use_agent: bool,
**kwargs,
@@ -235,7 +233,7 @@ class KeygenCommand:
args.extend(["-n", ",".join(principals)])
if serial_number is not None:
args.extend(["-z", str(serial_number)])
if type == "host":
if cert_type == "host":
args.extend(["-h"])
if use_agent:
args.extend(["-U"])
@@ -252,7 +250,7 @@ class KeygenCommand:
*,
private_key_path: str,
size: int,
type: str,
key_type: str,
comment: str | None,
**kwargs,
) -> tuple[int, str, str]:
@@ -264,7 +262,7 @@ class KeygenCommand:
"-b",
str(size),
"-t",
type,
key_type,
"-f",
private_key_path,
"-C",
@@ -313,7 +311,7 @@ class KeygenCommand:
except (IOError, OSError) as e:
raise ValueError(
f"The private key at {private_key_path} is not writeable preventing a comment update ({e})"
)
) from e
command = [self._bin_path, "-q"]
if force_new_format:
@@ -327,12 +325,12 @@ _PrivateKey = t.TypeVar("_PrivateKey", bound="PrivateKey")
class PrivateKey:
def __init__(
self, *, size: int, key_type: str, fingerprint: str, format: str = ""
self, *, size: int, key_type: str, fingerprint: str, key_format: str = ""
) -> None:
self._size = size
self._type = key_type
self._fingerprint = fingerprint
self._format = format
self._format = key_format
@property
def size(self) -> int:
@@ -428,11 +426,8 @@ class PublicKey:
@classmethod
def load(cls: t.Type[_PublicKey], path: str | os.PathLike) -> _PublicKey | None:
try:
with open(path, "r") as f:
properties = f.read().strip(" \n").split(" ", 2)
except (IOError, OSError):
raise
with open(path, "r", encoding="utf-8") as f:
properties = f.read().strip(" \n").split(" ", 2)
if len(properties) < 2:
return None
@@ -454,14 +449,14 @@ def parse_private_key_format(
*,
path: str | os.PathLike,
) -> t.Literal["SSH", "PKCS8", "PKCS1", ""]:
with open(path, "r") as file:
with open(path, "r", encoding="utf-8") as file:
header = file.readline().strip()
if header == "-----BEGIN OPENSSH PRIVATE KEY-----":
return "SSH"
elif header == "-----BEGIN PRIVATE KEY-----":
if header == "-----BEGIN PRIVATE KEY-----":
return "PKCS8"
elif header == "-----BEGIN RSA PRIVATE KEY-----":
if header == "-----BEGIN RSA PRIVATE KEY-----":
return "PKCS1"
return ""

View File

@@ -54,7 +54,7 @@ if t.TYPE_CHECKING:
class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
def __init__(self, *, module: AnsibleModule) -> None:
super(KeypairBackend, self).__init__(module=module)
super().__init__(module=module)
self.comment: str | None = self.module.params["comment"]
self.private_key_path: str = self.module.params["path"]
@@ -189,9 +189,9 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
def _should_generate(self) -> bool:
if self.original_private_key is None:
return True
elif self.regenerate == "never":
if self.regenerate == "never":
return False
elif self.regenerate == "fail":
if self.regenerate == "fail":
if not self._private_key_valid():
self.module.fail_json(
msg="Key has wrong type and/or size. Will not proceed. "
@@ -199,10 +199,9 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
+ "`partial_idempotence`, `full_idempotence` or `always`, or with `force=true`."
)
return False
elif self.regenerate in ("partial_idempotence", "full_idempotence"):
if self.regenerate in ("partial_idempotence", "full_idempotence"):
return not self._private_key_valid()
else:
return True
return True
def _private_key_valid(self) -> bool:
if self.original_private_key is None:
@@ -358,7 +357,7 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
class KeypairBackendOpensshBin(KeypairBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(KeypairBackendOpensshBin, self).__init__(module=module)
super().__init__(module=module)
if self.module.params["private_key_format"] != "auto":
self.module.fail_json(
@@ -371,7 +370,7 @@ class KeypairBackendOpensshBin(KeypairBackend):
self.ssh_keygen.generate_keypair(
private_key_path=private_key_path,
size=self.size,
type=self.type,
key_type=self.type,
comment=self.comment,
check_rc=True,
)
@@ -391,7 +390,7 @@ class KeypairBackendOpensshBin(KeypairBackend):
return PublicKey.from_string(public_key_content)
def _private_key_readable(self) -> bool:
rc, stdout, stderr = self.ssh_keygen.get_matching_public_key(
rc, _stdout, stderr = self.ssh_keygen.get_matching_public_key(
private_key_path=self.private_key_path, check_rc=False
)
return not (
@@ -425,7 +424,7 @@ class KeypairBackendOpensshBin(KeypairBackend):
class KeypairBackendCryptography(KeypairBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(KeypairBackendCryptography, self).__init__(module=module)
super().__init__(module=module)
if self.type == "rsa1":
self.module.fail_json(
@@ -489,7 +488,7 @@ class KeypairBackendCryptography(KeypairBackend):
size=keypair.size,
key_type=keypair.key_type,
fingerprint=keypair.fingerprint,
format=parse_private_key_format(path=self.private_key_path),
key_format=parse_private_key_format(path=self.private_key_path),
)
def _get_public_key(self) -> PublicKey | t.Literal[""]:
@@ -522,10 +521,9 @@ class KeypairBackendCryptography(KeypairBackend):
OpensshKeypair.load(
path=self.private_key_path, passphrase=None, no_public_key=True
)
return False
except (InvalidPrivateKeyFileError, InvalidPassphraseError):
return True
else:
return False
return True

View File

@@ -124,11 +124,9 @@ class OpensshCertificateTimeParameters:
def __eq__(self, other: object) -> bool:
if not isinstance(other, type(self)):
return NotImplemented
else:
return (
self._valid_from == other._valid_from
and self._valid_to == other._valid_to
)
return (
self._valid_from == other._valid_from and self._valid_to == other._valid_to
)
def __ne__(self, other: object) -> bool:
return not self == other
@@ -188,12 +186,11 @@ class OpensshCertificateTimeParameters:
return "always"
if dt == _FOREVER:
return "forever"
else:
return (
dt.isoformat().replace("+00:00", "")
if date_format == "human_readable"
else dt.strftime("%Y%m%d%H%M%S")
)
return (
dt.isoformat().replace("+00:00", "")
if date_format == "human_readable"
else dt.strftime("%Y%m%d%H%M%S")
)
if date_format == "timestamp":
td = dt - _ALWAYS
return int(
@@ -203,22 +200,17 @@ class OpensshCertificateTimeParameters:
@staticmethod
def to_datetime(time_string_or_timestamp: str | bytes | int) -> datetime:
try:
if isinstance(time_string_or_timestamp, (str, bytes)):
result = OpensshCertificateTimeParameters._time_string_to_datetime(
to_text(time_string_or_timestamp.strip())
)
elif isinstance(time_string_or_timestamp, int):
result = OpensshCertificateTimeParameters._timestamp_to_datetime(
time_string_or_timestamp
)
else:
raise ValueError(
f"Value must be of type (str, unicode, int) not {type(time_string_or_timestamp)}"
)
except ValueError:
raise
return result
if isinstance(time_string_or_timestamp, (str, bytes)):
return OpensshCertificateTimeParameters._time_string_to_datetime(
to_text(time_string_or_timestamp.strip())
)
if isinstance(time_string_or_timestamp, int):
return OpensshCertificateTimeParameters._timestamp_to_datetime(
time_string_or_timestamp
)
raise ValueError(
f"Value must be of type (str, unicode, int) not {type(time_string_or_timestamp)}"
)
@staticmethod
def _timestamp_to_datetime(timestamp: int) -> datetime:
@@ -228,8 +220,8 @@ class OpensshCertificateTimeParameters:
return _FOREVER
try:
return datetime.fromtimestamp(timestamp, tz=_datetime.timezone.utc)
except OverflowError:
raise ValueError
except OverflowError as e:
raise ValueError from e
@staticmethod
def _time_string_to_datetime(time_string: str) -> datetime:
@@ -382,16 +374,15 @@ class OpensshCertificateInfo(metaclass=abc.ABCMeta):
def cert_type(self) -> t.Literal["user", "host", ""]:
if self._cert_type == _USER_TYPE:
return "user"
elif self._cert_type == _HOST_TYPE:
if self._cert_type == _HOST_TYPE:
return "host"
else:
return ""
return ""
@cert_type.setter
def cert_type(self, cert_type: t.Literal["user", "host"] | int) -> None:
if cert_type == "user" or cert_type == _USER_TYPE:
if cert_type in ("user", _USER_TYPE):
self._cert_type = _USER_TYPE
elif cert_type == "host" or cert_type == _HOST_TYPE:
elif cert_type in ("host", _HOST_TYPE):
self._cert_type = _HOST_TYPE
else:
raise ValueError(f"{cert_type} is not a valid certificate type")
@@ -412,7 +403,7 @@ class OpensshCertificateInfo(metaclass=abc.ABCMeta):
class OpensshRSACertificateInfo(OpensshCertificateInfo):
def __init__(self, *, e: int | None = None, n: int | None = None, **kwargs) -> None:
super(OpensshRSACertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self.type_string = _SSH_TYPE_STRINGS["rsa"] + _CERT_SUFFIX_V01
self.e = e
self.n = n
@@ -444,7 +435,7 @@ class OpensshDSACertificateInfo(OpensshCertificateInfo):
y: int | None = None,
**kwargs,
) -> None:
super(OpensshDSACertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self.type_string = _SSH_TYPE_STRINGS["dsa"] + _CERT_SUFFIX_V01
self.p = p
self.q = q
@@ -476,7 +467,7 @@ class OpensshECDSACertificateInfo(OpensshCertificateInfo):
def __init__(
self, *, curve: bytes | None = None, public_key: bytes | None = None, **kwargs
):
super(OpensshECDSACertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self._curve = None
if curve is not None:
self.curve = curve
@@ -519,7 +510,7 @@ class OpensshECDSACertificateInfo(OpensshCertificateInfo):
class OpensshED25519CertificateInfo(OpensshCertificateInfo):
def __init__(self, *, pk: bytes | None = None, **kwargs) -> None:
super(OpensshED25519CertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self.type_string = _SSH_TYPE_STRINGS["ed25519"] + _CERT_SUFFIX_V01
self.pk = pk
@@ -559,13 +550,13 @@ class OpensshCertificate:
with open(path, "rb") as cert_file:
data = cert_file.read()
except (IOError, OSError) as e:
raise ValueError(f"{path} cannot be opened for reading: {e}")
raise ValueError(f"{path} cannot be opened for reading: {e}") from e
try:
format_identifier, b64_cert = data.split(b" ")[:2]
cert = binascii.a2b_base64(b64_cert)
except (binascii.Error, ValueError):
raise ValueError("Certificate not in OpenSSH format")
except (binascii.Error, ValueError) as e:
raise ValueError("Certificate not in OpenSSH format") from e
for key_type, string in _SSH_TYPE_STRINGS.items():
if format_identifier == string + _CERT_SUFFIX_V01:
@@ -585,7 +576,7 @@ class OpensshCertificate:
cert_info = cls._parse_cert_info(pub_key_type, parser)
signature = parser.string()
except (TypeError, ValueError) as e:
raise ValueError(f"Invalid certificate data: {e}")
raise ValueError(f"Invalid certificate data: {e}") from e
if parser.remaining_bytes():
raise ValueError(
@@ -751,10 +742,9 @@ def apply_directives(directives: t.Iterable[str]) -> list[OpensshCertificateOpti
if "clear" in directives:
return []
else:
return list(
set(default_options()) - set(directive_to_option[d] for d in directives)
)
return list(
set(default_options()) - set(directive_to_option[d] for d in directives)
)
def default_options() -> list[OpensshCertificateOption]:

View File

@@ -19,6 +19,7 @@ try:
from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding, rsa
from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PrivateKey,
Ed25519PublicKey,
@@ -68,6 +69,10 @@ except ImportError:
CRYPTOGRAPHY_VERSION = "0.0"
_ALGORITHM_PARAMETERS = {}
from ansible_collections.community.crypto.plugins.module_utils._crypto.cryptography_support import (
is_potential_certificate_issuer_private_key,
)
if t.TYPE_CHECKING:
KeyFormat = t.Literal["SSH", "PKCS8", "PKCS1"]
@@ -309,10 +314,10 @@ class AsymmetricKeypair:
try:
self.verify(signature=self.sign(b"message"), data=b"message")
except InvalidSignatureError:
except InvalidSignatureError as e:
raise InvalidPublicKeyFileError(
"The private key and public key of this keypair do not match"
)
) from e
def __eq__(self, other: object) -> bool:
if not isinstance(other, AsymmetricKeypair):
@@ -368,7 +373,7 @@ class AsymmetricKeypair:
data, **_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"] # type: ignore
)
except TypeError as e:
raise InvalidDataError(e)
raise InvalidDataError(e) from e
def verify(self, *, signature: bytes, data: bytes) -> None:
"""Verifies that the signature associated with the provided data was signed
@@ -383,8 +388,8 @@ class AsymmetricKeypair:
data,
**_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"], # type: ignore
)
except InvalidSignature:
raise InvalidSignatureError
except InvalidSignature as e:
raise InvalidSignatureError from e
def update_passphrase(self, passphrase: bytes | None = None) -> None:
"""Updates the encryption algorithm of this key pair
@@ -661,10 +666,10 @@ def load_privatekey(
try:
privatekey_loader = privatekey_loaders[key_format]
except KeyError:
except KeyError as e:
raise InvalidKeyFormatError(
f"{key_format} is not a valid key format ({','.join(privatekey_loaders)})"
)
) from e
if not os.path.exists(path):
raise InvalidPrivateKeyFileError(f"No file was found at {path}")
@@ -673,32 +678,33 @@ def load_privatekey(
with open(path, "rb") as f:
content = f.read()
privatekey = privatekey_loader( # type: ignore
try:
privatekey = privatekey_loader(
data=content,
password=passphrase,
)
except ValueError as exc:
# Revert to PEM if key could not be loaded in SSH format
if key_format == "SSH":
try:
privatekey = privatekey_loaders["PEM"]( # type: ignore
except ValueError as exc:
# Revert to PEM if key could not be loaded in SSH format
if key_format == "SSH":
privatekey = privatekey_loaders["PEM"](
data=content,
password=passphrase,
)
except ValueError as e:
raise InvalidPrivateKeyFileError(e)
except TypeError as e:
raise InvalidPassphraseError(e)
except UnsupportedAlgorithm as e:
raise InvalidAlgorithmError(e)
else:
raise InvalidPrivateKeyFileError(exc)
else:
raise InvalidPrivateKeyFileError(exc) from exc
except ValueError as e:
raise InvalidPrivateKeyFileError(e) from e
except TypeError as e:
raise InvalidPassphraseError(e)
raise InvalidPassphraseError(e) from e
except UnsupportedAlgorithm as e:
raise InvalidAlgorithmError(e)
raise InvalidAlgorithmError(e) from e
if not is_potential_certificate_issuer_private_key(privatekey) or isinstance(
privatekey, Ed448PrivateKey
):
raise InvalidPrivateKeyFileError(
f"{privatekey} is not a supported private key type"
)
return privatekey
@@ -713,10 +719,10 @@ def load_publickey(
try:
publickey_loader = publickey_loaders[key_format]
except KeyError:
except KeyError as e:
raise InvalidKeyFormatError(
f"{key_format} is not a valid key format ({','.join(publickey_loaders)})"
)
) from e
if not os.path.exists(path):
raise InvalidPublicKeyFileError(f"No file was found at {path}")
@@ -729,9 +735,9 @@ def load_publickey(
data=content,
)
except ValueError as e:
raise InvalidPublicKeyFileError(e)
raise InvalidPublicKeyFileError(e) from e
except UnsupportedAlgorithm as e:
raise InvalidAlgorithmError(e)
raise InvalidAlgorithmError(e) from e
return publickey
@@ -749,8 +755,7 @@ def compare_publickeys(pk1: PublicKeyTypes, pk2: PublicKeyTypes) -> bool:
serialization.Encoding.Raw, serialization.PublicFormat.Raw
)
return a_bytes == b_bytes
else:
return pk1.public_numbers() == pk2.public_numbers() # type: ignore
return pk1.public_numbers() == pk2.public_numbers() # type: ignore
def compare_encryption_algorithms(
@@ -761,12 +766,11 @@ def compare_encryption_algorithms(
ea2, serialization.NoEncryption
):
return True
elif isinstance(ea1, serialization.BestAvailableEncryption) and isinstance(
if isinstance(ea1, serialization.BestAvailableEncryption) and isinstance(
ea2, serialization.BestAvailableEncryption
):
return ea1.password == ea2.password
else:
return False
return False
def get_encryption_algorithm(
@@ -775,7 +779,7 @@ def get_encryption_algorithm(
try:
return serialization.BestAvailableEncryption(passphrase)
except ValueError as e:
raise InvalidPassphraseError(e)
raise InvalidPassphraseError(e) from e
def validate_comment(comment: str) -> None:
@@ -796,7 +800,7 @@ def extract_comment(path: str | os.PathLike) -> str:
else:
comment = ""
except (IOError, OSError) as e:
raise InvalidPublicKeyFileError(e)
raise InvalidPublicKeyFileError(e) from e
return comment

View File

@@ -177,10 +177,9 @@ class OpensshParser:
def _check_position(self, offset: int) -> int:
if self._pos + offset > len(self._data):
raise ValueError(f"Insufficient data remaining at position: {self._pos}")
elif self._pos + offset < 0:
if self._pos + offset < 0:
raise ValueError("Position cannot be less than zero.")
else:
return self._pos + offset
return self._pos + offset
@classmethod
def signature_data(cls, *, signature_string: bytes) -> dict[str, bytes | int]:
@@ -306,7 +305,9 @@ class _OpensshWriter:
try:
self.string(",".join(value).encode("ASCII"))
except UnicodeEncodeError as e:
raise ValueError(f"Name-list's must consist of US-ASCII characters: {e}")
raise ValueError(
f"Name-list's must consist of US-ASCII characters: {e}"
) from e
return self