Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New metadata API: add MetadataInfo and TargetFile classes #1223

Closed
88 changes: 58 additions & 30 deletions tuf/api/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,6 @@ def __init__(
self.expires = expires

# TODO: Should we separate data validation from constructor?
if version < 0:
raise ValueError(f'version must be < 0, got {version}')
self.version = version


Expand All @@ -291,22 +289,24 @@ def __init__(
def from_dict(cls, signed_dict: JsonDict) -> 'Signed':
"""Creates Signed object from its JSON/dict representation. """

# Create empty object with default or parametrized constructor with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Create empty object with default or parametrized constructor with
# Create empty object with default or parameterized constructor with

# default arguments.
obj = cls()
obj._type = signed_dict['_type']
obj.version = signed_dict['version']
obj.spec_version = signed_dict['spec_version']
# Convert 'expires' TUF metadata string to a datetime object, which is
# what the constructor expects and what we store. The inverse operation
# is implemented in 'to_dict'.
signed_dict['expires'] = tuf.formats.expiry_string_to_datetime(
obj.expires = tuf.formats.expiry_string_to_datetime(
signed_dict['expires'])
# NOTE: We write the converted 'expires' back into 'signed_dict' above
# so that we can pass it to the constructor as '**signed_dict' below,
# along with other fields that belong to Signed subclasses.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the removal of this comment indicate we should no longer be doing the conversion of the expires string to a datetime object here?

# Any 'from_dict'(-like) conversions of fields that correspond to a
# subclass should be performed in the 'from_dict' method of that
# subclass and also be written back into 'signed_dict' before calling
# super().from_dict.
# NOTE: Any 'from_dict'(-like) conversions of fields that correspond
# to a subclass should be performed in the 'from_dict' method of that
# subclass and assigned to the below returned object.

# NOTE: cls might be a subclass of Signed, if 'from_dict' was called on
# that subclass (see e.g. Metadata.from_dict).
return cls(**signed_dict)
return obj


# Serialization.
Expand Down Expand Up @@ -378,16 +378,28 @@ class Root(Signed):
# default max-args value for pylint is 5
# pylint: disable=too-many-arguments
def __init__(
self, _type: str, version: int, spec_version: str,
expires: datetime, consistent_snapshot: bool,
keys: JsonDict, roles: JsonDict) -> None:
self, _type: str=None, version: int=None, spec_version: str=None,
expires: datetime=None, consistent_snapshot: bool=None,
keys: JsonDict=None, roles: JsonDict=None) -> None:
MVrachev marked this conversation as resolved.
Show resolved Hide resolved
super().__init__(_type, version, spec_version, expires)
# TODO: Add classes for keys and roles
self.consistent_snapshot = consistent_snapshot
self.keys = keys
self.roles = roles


@classmethod
def from_dict(cls, signed_dict: JsonDict) -> 'Root':
"""Creates Root object from its JSON/dict representation. """

# Get a parent object with its attributes already assigned.
obj = super().from_dict(signed_dict)
obj.consistent_snapshot = signed_dict['consistent_snapshot']
obj.keys = signed_dict['keys']
obj.roles = signed_dict['roles']
return obj


# Serialization.
def to_dict(self) -> JsonDict:
"""Returns the JSON-serializable dictionary representation of self. """
Expand Down Expand Up @@ -478,8 +490,9 @@ class Timestamp(Signed):

"""
def __init__(
self, _type: str, version: int, spec_version: str,
expires: datetime, meta: Dict[str, MetadataInfo]) -> None:
self, _type: str=None, version: int=None, spec_version: str=None,
expires: datetime=None, meta: Dict[str, MetadataInfo]=None
) -> None:
super().__init__(_type, version, spec_version, expires)
self.meta = meta

Expand All @@ -500,10 +513,14 @@ def to_dict(self) -> JsonDict:
def from_dict(cls, signed_dict: JsonDict) -> 'Timestamp':
"""Creates Timestamp object from its JSON/dict representation. """

signed_dict['meta']['snapshot.json'] = MetadataInfo(
**signed_dict['meta']['snapshot.json'])
# Get a parent object with its attributes already assigned.
obj = super().from_dict(signed_dict)
obj.meta = {}
meta = signed_dict['meta']['snapshot.json']
obj.meta['snapshot.json'] = MetadataInfo(meta['version'],
meta.get('length'), meta.get('hashes'))

return super().from_dict(signed_dict)
return obj


# Modification.
Expand All @@ -529,8 +546,9 @@ class Snapshot(Signed):

"""
def __init__(
self, _type: str, version: int, spec_version: str,
expires: datetime, meta: Dict[str, MetadataInfo]) -> None:
self, _type: str=None, version: int=None, spec_version: str=None,
expires: datetime=None, meta: Dict[str, MetadataInfo]=None
) -> None:
super().__init__(_type, version, spec_version, expires)
self.meta = meta

Expand All @@ -539,11 +557,15 @@ def __init__(
def from_dict(cls, signed_dict: JsonDict) -> 'Snapshot':
"""Creates Snapshot object from its JSON/dict representation. """

# Get a parent object with its attributes already assigned.
obj = super().from_dict(signed_dict)
obj.meta = {}
for meta_path in signed_dict['meta'].keys():
signed_dict['meta'][meta_path] = MetadataInfo(
**signed_dict['meta'][meta_path])
meta = signed_dict['meta'][meta_path]
obj.meta[meta_path] = MetadataInfo(meta['version'],
meta.get('length'), meta.get('hashes'))

return super().from_dict(signed_dict)
return obj

MVrachev marked this conversation as resolved.
Show resolved Hide resolved

# Serialization.
Expand Down Expand Up @@ -672,9 +694,9 @@ class Targets(Signed):
# default max-args value for pylint is 5
# pylint: disable=too-many-arguments
def __init__(
self, _type: str, version: int, spec_version: str,
expires: datetime, targets: Dict[str, TargetInfo],
delegations: JsonDict) -> None:
self, _type: str=None, version: int=None, spec_version: str=None,
expires: datetime=None, targets: Dict[str, TargetInfo]=None,
delegations: JsonDict=None) -> None:
super().__init__(_type, version, spec_version, expires)
self.targets = targets

Expand All @@ -685,11 +707,17 @@ def __init__(
@classmethod
def from_dict(cls, signed_dict: JsonDict) -> 'Targets':
"""Creates Targets object from its JSON/dict representation. """

# Get a parent object with its attributes already assigned.
obj = super().from_dict(signed_dict)
obj.targets = {}
for target_path in signed_dict['targets'].keys():
signed_dict['targets'][target_path] = TargetInfo(
**signed_dict['targets'][target_path])
info = signed_dict['targets'][target_path]
obj.targets[target_path] = TargetInfo(info['length'],
info['hashes'], info.get('custom'))

return super().from_dict(signed_dict)
obj.delegations = signed_dict['delegations']
return obj


# Serialization.
Expand Down