diff --git a/requirements/lint.txt b/requirements/lint.txt index 2626c6a233..1ab9845122 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -6,7 +6,7 @@ # Lint tools # (We are not so interested in the specific versions of the tools: the versions # are pinned to prevent unexpected linting failures when tools update) -ruff==0.11.13 +ruff==0.12.0 mypy==1.16.1 zizmor==1.9.0 diff --git a/tuf/api/_payload.py b/tuf/api/_payload.py index 89fcbfe812..8a8c40ffdb 100644 --- a/tuf/api/_payload.py +++ b/tuf/api/_payload.py @@ -181,6 +181,17 @@ def __eq__(self, other: object) -> bool: and self.unrecognized_fields == other.unrecognized_fields ) + def __hash__(self) -> int: + return hash( + ( + self.type, + self.version, + self.spec_version, + self.expires, + self.unrecognized_fields, + ) + ) + @abc.abstractmethod def to_dict(self) -> dict[str, Any]: """Serialize and return a dict representation of self.""" @@ -299,6 +310,9 @@ def __eq__(self, other: object) -> bool: and self.unrecognized_fields == other.unrecognized_fields ) + def __hash__(self) -> int: + return hash((self.keyids, self.threshold, self.unrecognized_fields)) + @classmethod def from_dict(cls, role_dict: dict[str, Any]) -> Role: """Create ``Role`` object from its json/dict representation. @@ -551,6 +565,17 @@ def __eq__(self, other: object) -> bool: and self.consistent_snapshot == other.consistent_snapshot ) + def __hash__(self) -> int: + return hash( + ( + super().__hash__(), + self.keys, + self.roles, + self.consistent_snapshot, + self.unrecognized_fields, + ) + ) + @classmethod def from_dict(cls, signed_dict: dict[str, Any]) -> Root: """Create ``Root`` object from its json/dict representation. @@ -826,6 +851,11 @@ def __eq__(self, other: object) -> bool: and self.unrecognized_fields == other.unrecognized_fields ) + def __hash__(self) -> int: + return hash( + (self.version, self.length, self.hashes, self.unrecognized_fields) + ) + @classmethod def from_dict(cls, meta_dict: dict[str, Any]) -> MetaFile: """Create ``MetaFile`` object from its json/dict representation. @@ -940,6 +970,9 @@ def __eq__(self, other: object) -> bool: super().__eq__(other) and self.snapshot_meta == other.snapshot_meta ) + def __hash__(self) -> int: + return hash((super().__hash__(), self.snapshot_meta)) + @classmethod def from_dict(cls, signed_dict: dict[str, Any]) -> Timestamp: """Create ``Timestamp`` object from its json/dict representation. @@ -1001,6 +1034,9 @@ def __eq__(self, other: object) -> bool: return super().__eq__(other) and self.meta == other.meta + def __hash__(self) -> int: + return hash((super().__hash__(), self.meta)) + @classmethod def from_dict(cls, signed_dict: dict[str, Any]) -> Snapshot: """Create ``Snapshot`` object from its json/dict representation. @@ -1098,6 +1134,17 @@ def __eq__(self, other: object) -> bool: and self.path_hash_prefixes == other.path_hash_prefixes ) + def __hash__(self) -> int: + return hash( + ( + super().__hash__(), + self.name, + self.terminating, + self.path, + self.path_hash_prefixes, + ) + ) + @classmethod def from_dict(cls, role_dict: dict[str, Any]) -> DelegatedRole: """Create ``DelegatedRole`` object from its json/dict representation. @@ -1256,6 +1303,9 @@ def __eq__(self, other: object) -> bool: and self.name_prefix == other.name_prefix ) + def __hash__(self) -> int: + return hash((super().__hash__(), self.bit_length, self.name_prefix)) + @classmethod def from_dict(cls, role_dict: dict[str, Any]) -> SuccinctRoles: """Create ``SuccinctRoles`` object from its json/dict representation. @@ -1408,6 +1458,16 @@ def __eq__(self, other: object) -> bool: return all_attributes_check + def __hash__(self) -> int: + return hash( + ( + self.keys, + self.roles, + self.succinct_roles, + self.unrecognized_fields, + ) + ) + @classmethod def from_dict(cls, delegations_dict: dict[str, Any]) -> Delegations: """Create ``Delegations`` object from its json/dict representation. @@ -1529,6 +1589,11 @@ def __eq__(self, other: object) -> bool: and self.unrecognized_fields == other.unrecognized_fields ) + def __hash__(self) -> int: + return hash( + (self.length, self.hashes, self.path, self.unrecognized_fields) + ) + @classmethod def from_dict(cls, target_dict: dict[str, Any], path: str) -> TargetFile: """Create ``TargetFile`` object from its json/dict representation. @@ -1672,6 +1737,9 @@ def __eq__(self, other: object) -> bool: and self.delegations == other.delegations ) + def __hash__(self) -> int: + return hash((super().__hash__(), self.targets, self.delegations)) + @classmethod def from_dict(cls, signed_dict: dict[str, Any]) -> Targets: """Create ``Targets`` object from its json/dict representation. diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index d03a501546..85433e73a7 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -147,12 +147,15 @@ def __eq__(self, other: object) -> bool: and self.unrecognized_fields == other.unrecognized_fields ) + def __hash__(self) -> int: + return hash((self.signatures, self.signed, self.unrecognized_fields)) + @property def signed_bytes(self) -> bytes: """Default canonical json byte representation of ``self.signed``.""" # Use local scope import to avoid circular import errors - from tuf.api.serialization.json import CanonicalJSONSerializer + from tuf.api.serialization.json import CanonicalJSONSerializer # noqa: I001, PLC0415 return CanonicalJSONSerializer().serialize(self.signed) @@ -261,7 +264,7 @@ def from_bytes( if deserializer is None: # Use local scope import to avoid circular import errors - from tuf.api.serialization.json import JSONDeserializer + from tuf.api.serialization.json import JSONDeserializer # noqa: I001, PLC0415 deserializer = JSONDeserializer() @@ -288,7 +291,7 @@ def to_bytes(self, serializer: MetadataSerializer | None = None) -> bytes: if serializer is None: # Use local scope import to avoid circular import errors - from tuf.api.serialization.json import JSONSerializer + from tuf.api.serialization.json import JSONSerializer # noqa: I001, PLC0415 serializer = JSONSerializer(compact=True) diff --git a/tuf/api/serialization/json.py b/tuf/api/serialization/json.py index f311907149..9b411eb99f 100644 --- a/tuf/api/serialization/json.py +++ b/tuf/api/serialization/json.py @@ -8,9 +8,6 @@ verification. """ -# We should not have shadowed stdlib json but that milk spilled already -# ruff: noqa: A005 - from __future__ import annotations import json diff --git a/verify_release b/verify_release index 0c7cdaa81c..7bf43e345e 100755 --- a/verify_release +++ b/verify_release @@ -108,7 +108,7 @@ def verify_github_release(version: str, compare_dir: str) -> bool: "GET", url, preload_content=False, timeout=HTTP_TIMEOUT ) with open(os.path.join(github_dir, filename), "wb") as f: - for data in response.stream(): + for data in response.stream(): # noqa: FURB122 f.write(data) return cmp(