@@ -164,7 +164,7 @@ def from_bytes(
164164 return deserializer .deserialize (data )
165165
166166 def to_dict (self ) -> Dict [str , Any ]:
167- """Returns the dict representation of self. """
167+ """Returns the dict representation of self."""
168168
169169 signatures = []
170170 for sig in self .signatures :
@@ -320,24 +320,31 @@ class Signed:
320320 spec_version: The TUF specification version number (semver) the
321321 metadata format adheres to.
322322 expires: The metadata expiration datetime object.
323+ unrecognized_fields: Dictionary of all unrecognized fields.
323324
324325 """
325326
326327 # NOTE: Signed is a stupid name, because this might not be signed yet, but
327328 # we keep it to match spec terminology (I often refer to this as "payload",
328329 # or "inner metadata")
329330 def __init__ (
330- self , _type : str , version : int , spec_version : str , expires : datetime
331+ self ,
332+ _type : str ,
333+ version : int ,
334+ spec_version : str ,
335+ expires : datetime ,
336+ unrecognized_fields : Optional [Mapping [str , Any ]] = None ,
331337 ) -> None :
332338
333339 self ._type = _type
334340 self .spec_version = spec_version
335341 self .expires = expires
336342
337343 # TODO: Should we separate data validation from constructor?
338- if version < 0 :
339- raise ValueError (f"version must be >= 0, got { version } " )
344+ if version <= 0 :
345+ raise ValueError (f"version must be > 0, got { version } " )
340346 self .version = version
347+ self .unrecognized_fields = unrecognized_fields or {}
341348
342349 @staticmethod
343350 def _common_fields_from_dict (signed_dict : Mapping [str , Any ]) -> list :
@@ -369,6 +376,7 @@ def _common_fields_to_dict(self) -> Dict[str, Any]:
369376 "version" : self .version ,
370377 "spec_version" : self .spec_version ,
371378 "expires" : self .expires .isoformat () + "Z" ,
379+ ** self .unrecognized_fields ,
372380 }
373381
374382 def is_expired (self , reference_time : datetime = None ) -> bool :
@@ -389,7 +397,7 @@ def is_expired(self, reference_time: datetime = None) -> bool:
389397
390398 # Modification.
391399 def bump_expiration (self , delta : timedelta = timedelta (days = 1 )) -> None :
392- """Increments the expires attribute by the passed timedelta. """
400+ """Increments the expires attribute by the passed timedelta."""
393401 self .expires += delta
394402
395403 def bump_version (self ) -> None :
@@ -445,24 +453,28 @@ def __init__(
445453 consistent_snapshot : bool ,
446454 keys : Mapping [str , Any ],
447455 roles : Mapping [str , Any ],
456+ unrecognized_fields : Optional [Mapping [str , Any ]] = None ,
448457 ) -> None :
449- super ().__init__ (_type , version , spec_version , expires )
458+ super ().__init__ (
459+ _type , version , spec_version , expires , unrecognized_fields
460+ )
450461 # TODO: Add classes for keys and roles
451462 self .consistent_snapshot = consistent_snapshot
452463 self .keys = keys
453464 self .roles = roles
454465
455466 @classmethod
456467 def from_dict (cls , root_dict : Mapping [str , Any ]) -> "Root" :
457- """Creates Root object from its dict representation. """
468+ """Creates Root object from its dict representation."""
458469 common_args = cls ._common_fields_from_dict (root_dict )
459470 consistent_snapshot = root_dict .pop ("consistent_snapshot" )
460471 keys = root_dict .pop ("keys" )
461472 roles = root_dict .pop ("roles" )
462- return cls (* common_args , consistent_snapshot , keys , roles )
473+ # All fields left in the root_dict are unrecognized.
474+ return cls (* common_args , consistent_snapshot , keys , roles , root_dict )
463475
464476 def to_dict (self ) -> Dict [str , Any ]:
465- """Returns the dict representation of self. """
477+ """Returns the dict representation of self."""
466478 root_dict = self ._common_fields_to_dict ()
467479 root_dict .update (
468480 {
@@ -477,14 +489,14 @@ def to_dict(self) -> Dict[str, Any]:
477489 def add_key (
478490 self , role : str , keyid : str , key_metadata : Mapping [str , Any ]
479491 ) -> None :
480- """Adds new key for 'role' and updates the key store. """
492+ """Adds new key for 'role' and updates the key store."""
481493 if keyid not in self .roles [role ]["keyids" ]:
482494 self .roles [role ]["keyids" ].append (keyid )
483495 self .keys [keyid ] = key_metadata
484496
485497 # Remove key for a role.
486498 def remove_key (self , role : str , keyid : str ) -> None :
487- """Removes key for 'role' and updates the key store. """
499+ """Removes key for 'role' and updates the key store."""
488500 if keyid in self .roles [role ]["keyids" ]:
489501 self .roles [role ]["keyids" ].remove (keyid )
490502 for keyinfo in self .roles .values ():
@@ -521,20 +533,24 @@ def __init__(
521533 spec_version : str ,
522534 expires : datetime ,
523535 meta : Mapping [str , Any ],
536+ unrecognized_fields : Optional [Mapping [str , Any ]] = None ,
524537 ) -> None :
525- super ().__init__ (_type , version , spec_version , expires )
538+ super ().__init__ (
539+ _type , version , spec_version , expires , unrecognized_fields
540+ )
526541 # TODO: Add class for meta
527542 self .meta = meta
528543
529544 @classmethod
530545 def from_dict (cls , timestamp_dict : Mapping [str , Any ]) -> "Timestamp" :
531- """Creates Timestamp object from its dict representation. """
546+ """Creates Timestamp object from its dict representation."""
532547 common_args = cls ._common_fields_from_dict (timestamp_dict )
533548 meta = timestamp_dict .pop ("meta" )
534- return cls (* common_args , meta )
549+ # All fields left in the timestamp_dict are unrecognized.
550+ return cls (* common_args , meta , timestamp_dict )
535551
536552 def to_dict (self ) -> Dict [str , Any ]:
537- """Returns the dict representation of self. """
553+ """Returns the dict representation of self."""
538554 timestamp_dict = self ._common_fields_to_dict ()
539555 timestamp_dict .update ({"meta" : self .meta })
540556 return timestamp_dict
@@ -543,7 +559,7 @@ def to_dict(self) -> Dict[str, Any]:
543559 def update (
544560 self , version : int , length : int , hashes : Mapping [str , Any ]
545561 ) -> None :
546- """Assigns passed info about snapshot metadata to meta dict. """
562+ """Assigns passed info about snapshot metadata to meta dict."""
547563 self .meta ["snapshot.json" ] = {
548564 "version" : version ,
549565 "length" : length ,
@@ -585,20 +601,24 @@ def __init__(
585601 spec_version : str ,
586602 expires : datetime ,
587603 meta : Mapping [str , Any ],
604+ unrecognized_fields : Optional [Mapping [str , Any ]] = None ,
588605 ) -> None :
589- super ().__init__ (_type , version , spec_version , expires )
606+ super ().__init__ (
607+ _type , version , spec_version , expires , unrecognized_fields
608+ )
590609 # TODO: Add class for meta
591610 self .meta = meta
592611
593612 @classmethod
594613 def from_dict (cls , snapshot_dict : Mapping [str , Any ]) -> "Snapshot" :
595- """Creates Snapshot object from its dict representation. """
614+ """Creates Snapshot object from its dict representation."""
596615 common_args = cls ._common_fields_from_dict (snapshot_dict )
597616 meta = snapshot_dict .pop ("meta" )
598- return cls (* common_args , meta )
617+ # All fields left in the snapshot_dict are unrecognized.
618+ return cls (* common_args , meta , snapshot_dict )
599619
600620 def to_dict (self ) -> Dict [str , Any ]:
601- """Returns the dict representation of self. """
621+ """Returns the dict representation of self."""
602622 snapshot_dict = self ._common_fields_to_dict ()
603623 snapshot_dict .update ({"meta" : self .meta })
604624 return snapshot_dict
@@ -611,7 +631,7 @@ def update(
611631 length : Optional [int ] = None ,
612632 hashes : Optional [Mapping [str , Any ]] = None ,
613633 ) -> None :
614- """Assigns passed (delegated) targets role info to meta dict. """
634+ """Assigns passed (delegated) targets role info to meta dict."""
615635 metadata_fn = f"{ rolename } .json"
616636
617637 self .meta [metadata_fn ] = {"version" : version }
@@ -688,22 +708,26 @@ def __init__(
688708 expires : datetime ,
689709 targets : Mapping [str , Any ],
690710 delegations : Mapping [str , Any ],
711+ unrecognized_fields : Optional [Mapping [str , Any ]] = None ,
691712 ) -> None :
692- super ().__init__ (_type , version , spec_version , expires )
713+ super ().__init__ (
714+ _type , version , spec_version , expires , unrecognized_fields
715+ )
693716 # TODO: Add class for meta
694717 self .targets = targets
695718 self .delegations = delegations
696719
697720 @classmethod
698721 def from_dict (cls , targets_dict : Mapping [str , Any ]) -> "Targets" :
699- """Creates Targets object from its dict representation. """
722+ """Creates Targets object from its dict representation."""
700723 common_args = cls ._common_fields_from_dict (targets_dict )
701724 targets = targets_dict .pop ("targets" )
702725 delegations = targets_dict .pop ("delegations" )
703- return cls (* common_args , targets , delegations )
726+ # All fields left in the targets_dict are unrecognized.
727+ return cls (* common_args , targets , delegations , targets_dict )
704728
705729 def to_dict (self ) -> Dict [str , Any ]:
706- """Returns the dict representation of self. """
730+ """Returns the dict representation of self."""
707731 targets_dict = self ._common_fields_to_dict ()
708732 targets_dict .update (
709733 {
@@ -715,5 +739,5 @@ def to_dict(self) -> Dict[str, Any]:
715739
716740 # Modification.
717741 def update (self , filename : str , fileinfo : Mapping [str , Any ]) -> None :
718- """Assigns passed target file info to meta dict. """
742+ """Assigns passed target file info to meta dict."""
719743 self .targets [filename ] = fileinfo
0 commit comments