From 54e1f9c03b09bdf05fd0a3cd3e4db35c6480c5a7 Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Mon, 6 Jul 2020 23:21:43 +0100 Subject: [PATCH] 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 --- tests/test_tuf_api.py | 6 +++--- tuf/api/metadata.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_tuf_api.py b/tests/test_tuf_api.py index e9796cab..14aa0d00 100644 --- a/tests/test_tuf_api.py +++ b/tests/test_tuf_api.py @@ -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)) diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index 3f93ddae..a1dc2c18 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -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 = {}