Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements/lint.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
68 changes: 68 additions & 0 deletions tuf/api/_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
9 changes: 6 additions & 3 deletions tuf/api/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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()

Expand All @@ -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)

Expand Down
3 changes: 0 additions & 3 deletions tuf/api/serialization/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion verify_release
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down