tuf.api: drop use of dateutil

All of the functionality we need is available from the standard library
which reduces our dependency footprint. Having minimal dependencies is
especially important for update clients which often have to vendor their
dependencies.

However, dateutil.relativedelta is richer than timedelta and helps to
provide a clearer API. For example, with relativedelta it's possible
to specify a delta in years *and* dateutil will do the right thing for
leap years.

Signed-off-by: Joshua Lock <jlock@vmware.com>
This commit is contained in:
Joshua Lock 2020-07-06 23:21:43 +01:00
parent fd5732a024
commit 54e1f9c03b
2 changed files with 12 additions and 12 deletions

View file

@ -35,11 +35,11 @@
import sys
import errno
import os
from datetime import timedelta
from tuf.api import metadata
from tuf.api import keys
from dateutil.relativedelta import relativedelta
import iso8601
import six
@ -108,7 +108,7 @@ def test_metadata_base(self):
md.bump_expiration()
self.assertEqual(md.expiration,
iso8601.parse_date("2030-01-02").replace(tzinfo=None))
md.bump_expiration(relativedelta(years=1))
md.bump_expiration(timedelta(days=365))
self.assertEqual(md.expiration,
iso8601.parse_date("2031-01-02").replace(tzinfo=None))
@ -157,7 +157,7 @@ def test_metadata_timestamp(self):
timestamp.bump_expiration()
self.assertEqual(timestamp.expiration,
iso8601.parse_date("2030-01-02").replace(tzinfo=None))
timestamp.bump_expiration(relativedelta(years=1))
timestamp.bump_expiration(timedelta(days=365))
self.assertEqual(timestamp.expiration,
iso8601.parse_date("2031-01-02").replace(tzinfo=None))

View file

@ -4,14 +4,14 @@
from tuf.api.keys import KeyRing
# 2nd-party.
from datetime import datetime
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional
import json
import tempfile
# 3rd-party.
from dateutil.relativedelta import relativedelta
import iso8601
from securesystemslib.formats import encode_canonical
from securesystemslib.util import load_json_file, persist_temp_file
from securesystemslib.storage import StorageBackendInterface
@ -24,8 +24,6 @@
generate_timestamp_metadata,
)
import iso8601
# Types.
JsonDict = Dict[str, Any]
@ -34,7 +32,7 @@
class Metadata:
# By default, a Metadata would be a rather empty one.
def __init__(self, consistent_snapshot: bool = True, expiration: relativedelta = relativedelta(), keyring: Optional[KeyRing] = None, version: int = 1) -> None:
def __init__(self, consistent_snapshot: bool = True, expiration: datetime = datetime.today(), keyring: Optional[KeyRing] = None, version: int = 1) -> None:
self.consistent_snapshot = consistent_snapshot
self.keyring = keyring
@ -56,6 +54,8 @@ def read_from_json(cls, filename: str, storage_backend: StorageBackendInterface
signed = signable['signed']
# We always intend times to be UTC
# NOTE: we could do this with datetime.fromisoformat() but that is not
# available in Python 2.7's datetime
expiration = iso8601.parse_date(signed['expires']).replace(tzinfo=None)
version = signed['version']
@ -109,7 +109,7 @@ def expiration(self, datetime) -> None:
def bump_version(self) -> None:
self.version = self.version + 1
def bump_expiration(self, delta: relativedelta = relativedelta(days=1)) -> None:
def bump_expiration(self, delta: timedelta = timedelta(days=1)) -> None:
self._expiration = self._expiration + delta
def sign(self) -> JsonDict:
@ -161,7 +161,7 @@ def write_to_json(self, filename: str, storage_backend: StorageBackendInterface
persist_temp_file(f, filename, storage_backend)
class Timestamp(Metadata):
def __init__(self, consistent_snapshot: bool = True, expiration: relativedelta = relativedelta(days=1), keyring: KeyRing = None, version: int = 1):
def __init__(self, consistent_snapshot: bool = True, expiration: datetime = datetime.today(), keyring: KeyRing = None, version: int = 1):
super().__init__(consistent_snapshot, expiration, keyring, version)
self.snapshot_fileinfo = {}
@ -189,7 +189,7 @@ def update(self, version: int, length: int, hashes: JsonDict):
self.snapshot_fileinfo['snapshot.json'] = fileinfo
class Snapshot(Metadata):
def __init__(self, consistent_snapshot: bool = True, expiration: relativedelta = relativedelta(days=1), keyring: KeyRing = None, version: int = 1):
def __init__(self, consistent_snapshot: bool = True, expiration: datetime = datetime.today(), keyring: KeyRing = None, version: int = 1):
super().__init__(consistent_snapshot, expiration, keyring, version)
self.targets_fileinfo = {}
@ -218,7 +218,7 @@ def update(self, rolename: str, version: int, length: Optional[int] = None, hash
self.targets_fileinfo[f'{rolename}.json'] = tuf.formats.make_metadata_fileinfo(version, length, hashes)
class Targets(Metadata):
def __init__(self, consistent_snapshot: bool = True, expiration: relativedelta = relativedelta(days=1), keyring: KeyRing = None, version: int = 1):
def __init__(self, consistent_snapshot: bool = True, expiration: datetime = datetime.today(), keyring: KeyRing = None, version: int = 1):
super().__init__(consistent_snapshot, expiration, keyring, version)
self.targets = {}
self.delegations = {}