Merge pull request #1590 from jku/docs-tweaking

API doc improvements
This commit is contained in:
Jussi Kukkonen 2021-10-01 15:13:07 +03:00 committed by GitHub
commit d021331890
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 248 additions and 161 deletions

View file

@ -0,0 +1,4 @@
Metadata class
---------------------------------
.. autoclass:: tuf.api.metadata.Metadata

View file

@ -0,0 +1,4 @@
Root class
---------------------------------
.. autoclass:: tuf.api.metadata.Root

View file

@ -1,7 +0,0 @@
Metadata
---------------------------------
.. automodule:: tuf.api.metadata
:members:
:undoc-members:
:show-inheritance:

View file

@ -0,0 +1,4 @@
Snapshot class
---------------------------------
.. autoclass:: tuf.api.metadata.Snapshot

View file

@ -0,0 +1,27 @@
Supporting classes
---------------------------------
The Metadata API includes multiple classes that are used by the top-level
ones (Root, Timestamp, Snapshot, Targets):
.. autosummary::
:nosignatures:
tuf.api.metadata.DelegatedRole
tuf.api.metadata.Delegations
tuf.api.metadata.Key
tuf.api.metadata.MetaFile
tuf.api.metadata.Role
tuf.api.metadata.TargetFile
.. autoclass:: tuf.api.metadata.DelegatedRole
.. autoclass:: tuf.api.metadata.Delegations
.. autoclass:: tuf.api.metadata.Key
.. autoclass:: tuf.api.metadata.MetaFile
.. autoclass:: tuf.api.metadata.Role
.. autoclass:: tuf.api.metadata.TargetFile

View file

@ -0,0 +1,4 @@
Targets class
---------------------------------
.. autoclass:: tuf.api.metadata.Targets

View file

@ -0,0 +1,4 @@
Timestamp class
---------------------------------
.. autoclass:: tuf.api.metadata.Timestamp

View file

@ -1,18 +1,19 @@
Metadata API
===============
The low-level Metadata API contains two modules:
.. toctree::
* :doc:`tuf.api.metadata` contains the actual Metadata abstraction
that higher level libraries and application code should use to interact
with TUF metadata. This abstraction provides safe reading and writing to
supported file formats and helper functions for accessing and modifying
the metadata contents.
* :doc:`tuf.api.serialization` covers serializing the metadata into
specific wire formats (like json).
tuf.api.metadata.metadata
tuf.api.metadata.root
tuf.api.metadata.timestamp
tuf.api.metadata.snapshot
tuf.api.metadata.targets
.. toctree::
:hidden:
tuf.api.metadata
tuf.api.metadata.supporting
tuf.api.serialization
.. automodule:: tuf.api.metadata
:no-members:

View file

@ -1,10 +1,10 @@
Serialization
=============================
.. automodule:: tuf.api.serialization
JSON serialization
-----------------------------
.. automodule:: tuf.api.serialization.json
:members:
:undoc-members:
:show-inheritance:

View file

@ -2,6 +2,4 @@ Configuration
=============
.. automodule:: tuf.ngclient.config
:members:
:undoc-members:
:show-inheritance:

View file

@ -2,6 +2,4 @@ Fetcher
============
.. automodule:: tuf.ngclient.fetcher
:members:
:undoc-members:
:show-inheritance:

View file

@ -2,5 +2,3 @@ Updater
=========
.. automodule:: tuf.ngclient.updater
:members:
:special-members: __init__

View file

@ -30,7 +30,7 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.napoleon']
extensions = ['sphinx.ext.napoleon', 'sphinx.ext.autosummary']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -52,5 +52,19 @@
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']
# -- Autodoc configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
autodoc_mock_imports = ['securesystemslib']
# Tone down the "tuf.api.metadata." repetition
add_module_names = False
python_use_unqualified_type_names = True
# Show typehints in argument doc lines, but not in signatures
autodoc_typehints = "description"
autodoc_default_options = {
'members': True,
'exclude-members': 'to_dict, from_dict'
}

View file

@ -1,38 +1,28 @@
# Copyright New York University and the TUF contributors
# SPDX-License-Identifier: MIT OR Apache-2.0
"""TUF role metadata model.
This module contains low-level API through container classes for TUF role
metadata. The API aims to provide:
"""
The low-level Metadata API in ``tuf.api.metadata`` module contains:
* Safe de/serialization of metadata to and from files.
* Access to and modification of signed metadata content.
* Signing metadata and verifying signatures.
Each of the top level metadata roles is an instance of the Metadata[T] class
where the "signed" portion of each of the roles (or the "T") is an instance
of one of the classes Root, Timestamp, Snapshot or Targets.
For example, Metadata[Root] represents the TUF root role and that in practice
means that this is a Metadata object with a signed attribute of type Root.
Metadata API implements functionality at the metadata file level, it does
not provide TUF repository or client functionality on its own (but can be used
to implement them).
Additionally, there are helper classes providing abstractions over the complex
metadata fields inside the four top level classes - Root, Timestamp, Snapshot
and Targets.
The API design is based on the file format defined in the `TUF specification
<https://theupdateframework.github.io/specification/latest/>`_ and the object
attributes generally follow the JSON format used in the specification.
Note: the metadata module provides a low-level API and as such it doesn't use
concepts like "repository" or "trusted collection of metadata".
In this file there is no implementation of the repository-side logic or client
update workflows, but instead it provides solid base for other components to do
so.
The metadata model supports any custom serialization format, defaulting to JSON
as wireline format and Canonical JSON for reproducible signature creation and
verification.
Custom serializers must implement the abstract serialization interface defined
in 'tuf.api.serialization', and may use the [to|from]_dict convenience methods
available in the class model.
The above principle means that a ``Metadata`` object represents a single
metadata file, and has a ``signed`` attribute that is an instance of one of the
four top level signed classes (Root, Timestamp, Snapshot and Targets). To make
Python type annotations useful Metadata can be type constrained: e.g. the
signed attribute of ``Metadata[Root]`` is known to be ``Root``.
Currently Metadata API supports JSON as the file format.
"""
import abc
import fnmatch
@ -104,9 +94,12 @@ class Metadata(Generic[T]):
"[Root]" is not validated at runtime (as pure annotations are not available
then).
Attributes:
signed: A subclass of Signed, which has the actual metadata payload,
i.e. one of Targets, Snapshot, Timestamp or Root.
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
signed: The actual metadata payload, i.e. one of Targets, Snapshot,
Timestamp or Root.
signatures: An ordered dictionary of keyids to Signature objects, each
signing the canonical serialized representation of 'signed'.
"""
@ -205,9 +198,9 @@ def from_bytes(
"""Loads TUF metadata from raw data.
Arguments:
data: metadata content as bytes.
deserializer: Optional; A MetadataDeserializer instance that
implements deserialization. Default is JSONDeserializer.
data: metadata content.
deserializer: MetadataDeserializer implementation to use. Default
is JSONDeserializer.
Raises:
tuf.api.serialization.DeserializationError:
@ -340,7 +333,7 @@ def verify_delegate(
Args:
delegated_role: Name of the delegated role to verify
delegated_metadata: The Metadata object for the delegated role
signed_serializer: Optional; serializer used for delegate
signed_serializer: serializer used for delegate
serialization. Default is CanonicalJSONSerializer.
Raises:
@ -390,12 +383,13 @@ class Signed(metaclass=abc.ABCMeta):
on the signed attribute. This class provides attributes and methods that
are common for all TUF metadata types (roles).
Attributes:
_type: The metadata type string. Also available without underscore.
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
version: The metadata version number.
spec_version: The TUF specification version number (semver) the
metadata format adheres to.
expires: The metadata expiration datetime object.
spec_version: The supported TUF specification version number.
expires: The metadata expiry date.
unrecognized_fields: Dictionary of all unrecognized fields.
"""
@ -409,6 +403,7 @@ def _type(self) -> str:
@property
def type(self) -> str:
"""Metadata type as string."""
return self._signed_type
# NOTE: Signed is a stupid name, because this might not be signed yet, but
@ -420,7 +415,7 @@ def __init__(
spec_version: str,
expires: datetime,
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
spec_list = spec_version.split(".")
if (
len(spec_list) != 3
@ -492,9 +487,8 @@ def is_expired(self, reference_time: Optional[datetime] = None) -> bool:
"""Checks metadata expiration against a reference time.
Args:
reference_time: Optional; The time to check expiration date against.
A naive datetime in UTC expected.
If not provided, checks against the current UTC date and time.
reference_time: The time to check expiration date against. A naive
datetime in UTC expected. Default is current UTC date and time.
Returns:
True if expiration time is less than the reference time.
@ -517,18 +511,20 @@ def bump_version(self) -> None:
class Key:
"""A container class representing the public portion of a Key.
Please note that "Key" instances are not semanticly validated during
initialization: this only happens at signature verification time.
Supported key content (type, scheme and keyval) is defined in
Securesystemslib.
Attributes:
keyid: An identifier string that must uniquely identify a key within
the metadata it is used in. This implementation does not verify
that keyid is the hash of a specific representation of the key.
keytype: A string denoting a public key signature system,
such as "rsa", "ed25519", "ecdsa" and "ecdsa-sha2-nistp256".
scheme: A string denoting a corresponding signature scheme. For example:
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
keyid: Key identifier that is unique within the metadata it is used in.
Keyid is not verified to be the hash of a specific representation
of the key.
keytype: key type, e.g. "rsa", "ed25519" or "ecdsa-sha2-nistp256".
scheme: signature scheme. For example:
"rsassa-pss-sha256", "ed25519", and "ecdsa-sha2-nistp256".
keyval: A dictionary containing the public portion of the key.
keyval: Opaque key content
unrecognized_fields: Dictionary of all unrecognized fields.
"""
@ -539,7 +535,7 @@ def __init__(
scheme: str,
keyval: Dict[str, str],
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
if not all(
isinstance(at, str) for at in [keyid, keytype, scheme]
) or not isinstance(keyval, Dict):
@ -605,7 +601,7 @@ def verify_signature(
Arguments:
metadata: Metadata to verify
signed_serializer: Optional; SignedSerializer to serialize
signed_serializer: SignedSerializer to serialize
'metadata.signed' with. Default is CanonicalJSONSerializer.
Raises:
@ -653,8 +649,11 @@ class Role:
Role defines how many keys are required to successfully sign the roles
metadata, and which keys are accepted.
Attributes:
keyids: A set of strings representing signing keys for this role.
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
keyids: The roles signing key identifiers.
threshold: Number of keys required to sign this role's metadata.
unrecognized_fields: Dictionary of all unrecognized fields.
"""
@ -664,7 +663,7 @@ def __init__(
keyids: List[str],
threshold: int,
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
keyids_set = set(keyids)
if len(keyids_set) != len(keyids):
raise ValueError(
@ -697,12 +696,17 @@ def to_dict(self) -> Dict[str, Any]:
class Root(Signed):
"""A container for the signed part of root metadata.
Attributes:
consistent_snapshot: An optional boolean indicating whether the
repository supports consistent snapshots.
Parameters listed below are also instance attributes.
Args:
version: The metadata version number.
spec_version: The supported TUF specification version number.
expires: The metadata expiry date.
keys: Dictionary of keyids to Keys. Defines the keys used in 'roles'.
roles: Dictionary of role names to Roles. Defines which keys are
required to sign the metadata for a specific role.
consistent_snapshot: Does repository support consistent snapshots.
unrecognized_fields: Dictionary of all unrecognized fields.
"""
_signed_type = "root"
@ -718,7 +722,7 @@ def __init__(
roles: Dict[str, Role],
consistent_snapshot: Optional[bool] = None,
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
super().__init__(version, spec_version, expires, unrecognized_fields)
self.consistent_snapshot = consistent_snapshot
self.keys = keys
@ -859,10 +863,13 @@ def _validate_length(length: int) -> None:
class MetaFile(BaseFile):
"""A container with information about a particular metadata file.
Attributes:
version: An integer indicating the version of the metadata file.
length: An optional integer indicating the length of the metadata file.
hashes: An optional dictionary of hash algorithm names to hash values.
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
version: Version of the metadata file.
length: Length of the metadata file.
hashes: Dictionary of hash algorithm names to hash values.
unrecognized_fields: Dictionary of all unrecognized fields.
"""
@ -872,7 +879,7 @@ def __init__(
length: Optional[int] = None,
hashes: Optional[Dict[str, str]] = None,
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
if version <= 0:
raise ValueError(f"Metafile version must be > 0, got {version}")
@ -934,8 +941,15 @@ class Timestamp(Signed):
TUF file format uses a dictionary to contain the snapshot information:
this is not the case with Timestamp.snapshot_meta which is a MetaFile.
Attributes:
snapshot_meta: MetaFile instance with the snapshot meta information.
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
version: The metadata version number.
spec_version: The supported TUF specification version number.
expires: The metadata expiry date.
unrecognized_fields: Dictionary of all unrecognized fields.
snapshot_meta: Meta information for snapshot metadata.
"""
_signed_type = "timestamp"
@ -947,7 +961,7 @@ def __init__(
expires: datetime,
snapshot_meta: MetaFile,
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
super().__init__(version, spec_version, expires, unrecognized_fields)
self.snapshot_meta = snapshot_meta
@ -977,7 +991,14 @@ class Snapshot(Signed):
Snapshot contains information about all target Metadata files.
Attributes:
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
version: The metadata version number.
spec_version: The supported TUF specification version number.
expires: The metadata expiry date.
unrecognized_fields: Dictionary of all unrecognized fields.
meta: A dictionary of target metadata filenames to MetaFile objects.
"""
@ -990,7 +1011,7 @@ def __init__(
expires: datetime,
meta: Dict[str, MetaFile],
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
super().__init__(version, spec_version, expires, unrecognized_fields)
self.meta = meta
@ -1034,13 +1055,17 @@ class DelegatedRole(Role):
paths and path_hash_prefixes are mutually exclusive: both cannot be set,
at least one of them must be set.
Attributes:
name: A string giving the name of the delegated role.
terminating: A boolean indicating whether subsequent delegations
should be considered during a target lookup.
paths: An optional list of path pattern strings. See note above.
path_hash_prefixes: An optional list of hash prefixes. See note above.
unrecognized_fields: Dictionary of all unrecognized fields.
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
name: Delegated role name.
keyids: Delegated role signing key identifiers.
threshold: Number of keys required to sign this role's metadata.
terminating: Does this delegation terminate a target lookup.
paths: Path patterns. See note above.
path_hash_prefixes: Hash prefixes. See note above.
unrecognized_fields: Attributes not managed by TUF Metadata API.
"""
def __init__(
@ -1052,7 +1077,7 @@ def __init__(
paths: Optional[List[str]] = None,
path_hash_prefixes: Optional[List[str]] = None,
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
super().__init__(keyids, threshold, unrecognized_fields)
self.name = name
self.terminating = terminating
@ -1152,12 +1177,15 @@ def is_delegated_path(self, target_filepath: str) -> bool:
class Delegations:
"""A container object storing information about all delegations.
Attributes:
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
keys: Dictionary of keyids to Keys. Defines the keys used in 'roles'.
roles: Ordered dictionary of role names to DelegatedRoles instances. It
defines which keys are required to sign the metadata for a specific
role. The roles order also defines the order that role delegations
are considered in.
are considered during target searches.
unrecognized_fields: Dictionary of all unrecognized fields.
"""
@ -1166,7 +1194,7 @@ def __init__(
keys: Dict[str, Key],
roles: "OrderedDict[str, DelegatedRole]",
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
self.keys = keys
self.roles = roles
self.unrecognized_fields = unrecognized_fields or {}
@ -1202,11 +1230,13 @@ def to_dict(self) -> Dict[str, Any]:
class TargetFile(BaseFile):
"""A container with information about a particular target file.
Attributes:
length: An integer indicating the length of the target file.
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
length: Length in bytes.
hashes: A dictionary of hash algorithm names to hash values.
path: A string denoting the path to a target file relative to a base
URL of targets.
path: URL path to a target file, relative to a base targets URL.
unrecognized_fields: Dictionary of all unrecognized fields.
"""
@ -1216,7 +1246,7 @@ def __init__(
hashes: Dict[str, str],
path: str,
unrecognized_fields: Optional[Mapping[str, Any]] = None,
) -> None:
):
self._validate_length(length)
self._validate_hashes(hashes)
@ -1255,12 +1285,13 @@ def from_file(
hash_algorithms: Optional[List[str]] = None,
) -> "TargetFile":
"""Creates TargetFile object from a file.
Arguments:
target_file_path: The TargetFile path.
local_path: The local path to the file to create TargetFile from.
hash_algorithms: An optional list of hash algorithms to create
the hashes with. If not specified the securesystemslib default
hash algorithm is used.
target_file_path: URL path to a target file, relative to a base
targets URL.
local_path: The local path to target file content.
hash_algorithms: hash algorithms to calculate hashes with. If not
specified the securesystemslib default hash algorithm is used.
Raises:
FileNotFoundError: The file doesn't exist.
UnsupportedAlgorithmError: The hash algorithms list
@ -1277,12 +1308,14 @@ def from_data(
hash_algorithms: Optional[List[str]] = None,
) -> "TargetFile":
"""Creates TargetFile object from bytes.
Arguments:
target_file_path: The TargetFile path.
data: The data to create TargetFile from.
hash_algorithms: An optional list of hash algorithms to create
the hashes with. If not specified the securesystemslib default
hash algorithm is used.
target_file_path: URL path to a target file, relative to a base
targets URL.
data: The target file content.
hash_algorithms: Hash algorithms to create the hashes with. If not
specified the securesystemslib default hash algorithm is used.
Raises:
UnsupportedAlgorithmError: The hash algorithms list
contains an unsupported algorithm.
@ -1339,10 +1372,17 @@ class Targets(Signed):
Targets contains verifying information about target files and also
delegates responsibility to other Targets roles.
Attributes:
*All parameters named below are not just constructor arguments but also
instance attributes.*
Args:
version: The metadata version number.
spec_version: The supported TUF specification version number.
expires: The metadata expiry date.
targets: A dictionary of target filenames to TargetFiles
delegations: An optional Delegations that defines how this Targets
further delegates responsibility to other Targets Metadata files.
delegations: Defines how this Targets delegates responsibility to other
Targets Metadata files.
unrecognized_fields: Dictionary of all unrecognized fields.
"""
_signed_type = "targets"

View file

@ -1,19 +1,19 @@
# Copyright New York University and the TUF contributors
# SPDX-License-Identifier: MIT OR Apache-2.0
"""TUF role metadata de/serialization.
This sub-package provides abstract base classes and concrete implementations to
serialize and deserialize TUF role metadata and metadata parts.
"""``tuf.api.serialization`` module provides abstract base classes and concrete
implementations to serialize and deserialize TUF metadata.
Any custom de/serialization implementations should inherit from the abstract
base classes defined in this __init__.py module.
base classes defined in this module. The implementations can use the
``to_dict()``/``from_dict()`` implementations available in the Metadata
API objects.
- Metadata de/serializers are used to convert to and from wireline formats.
- Signed serializers are used to canonicalize data for cryptographic signatures
generation and verification.
"""
import abc
from typing import TYPE_CHECKING
@ -36,7 +36,7 @@ class MetadataDeserializer(metaclass=abc.ABCMeta):
@abc.abstractmethod
def deserialize(self, raw_data: bytes) -> "Metadata":
"""Deserialize passed bytes to Metadata object."""
"""Deserialize bytes to Metadata object."""
raise NotImplementedError
@ -45,7 +45,7 @@ class MetadataSerializer(metaclass=abc.ABCMeta):
@abc.abstractmethod
def serialize(self, metadata_obj: "Metadata") -> bytes:
"""Serialize passed Metadata object to bytes."""
"""Serialize Metadata object to bytes."""
raise NotImplementedError
@ -54,5 +54,5 @@ class SignedSerializer(metaclass=abc.ABCMeta):
@abc.abstractmethod
def serialize(self, signed_obj: "Signed") -> bytes:
"""Serialize passed Signed object to bytes."""
"""Serialize Signed object to bytes."""
raise NotImplementedError

View file

@ -1,14 +1,13 @@
# Copyright New York University and the TUF contributors
# SPDX-License-Identifier: MIT OR Apache-2.0
"""TUF role metadata JSON serialization and deserialization.
This module provides concrete implementations to serialize and deserialize TUF
role metadata to and from the JSON wireline format for transportation, and
to serialize the 'signed' part of TUF role metadata to the OLPC Canonical JSON
format for signature generation and verification.
"""``tuf.api.serialization.json`` module provides concrete implementations to
serialize and deserialize TUF role metadata to and from the JSON wireline
format for transportation, and to serialize the 'signed' part of TUF role
metadata to the OLPC Canonical JSON format for signature generation and
verification.
"""
import json
from securesystemslib.formats import encode_canonical
@ -51,7 +50,7 @@ class JSONSerializer(MetadataSerializer):
"""
def __init__(self, compact: bool = False) -> None:
def __init__(self, compact: bool = False):
self.compact = compact
def serialize(self, metadata_obj: Metadata) -> bytes:

View file

@ -18,11 +18,11 @@ class UpdaterConfig:
timestamp_max_length: The maximum length of a timestamp metadata file.
snapshot_max_length: The maximum length of a snapshot metadata file.
targets_max_length: The maximum length of a targets metadata file.
prefix_targets_with_hash: When consistent snapshots are used
(see https://theupdateframework.github.io/specification/latest/#consistent-snapshots), #pylint: disable=line-too-long
target download URLs are formed by prefixing the filename with a
hash digest of file content by default. This can be overridden by
setting prefix_targets_with_hash to False.
prefix_targets_with_hash: When `consistent snapshots
<https://theupdateframework.github.io/specification/latest/#consistent-snapshots>`_
are used, target download URLs are formed by prefixing the filename
with a hash digest of file content by default. This can be
overridden by setting prefix_targets_with_hash to False.
"""

View file

@ -78,7 +78,21 @@
class Updater:
"""Implementation of the TUF client workflow."""
"""Creates a new Updater instance and loads trusted root metadata.
Args:
repository_dir: Local metadata directory. Directory must be
writable and it must contain a trusted root.json file.
metadata_base_url: Base URL for all remote metadata downloads
target_base_url: Optional; Default base URL for all remote target
downloads. Can be individually set in download_target()
fetcher: Optional; FetcherInterface implementation used to download
both metadata and targets. Default is RequestsFetcher
Raises:
OSError: Local root.json cannot be read
RepositoryError: Local root.json is invalid
"""
def __init__(
self,
@ -88,21 +102,6 @@ def __init__(
fetcher: Optional[FetcherInterface] = None,
config: Optional[UpdaterConfig] = None,
):
"""Creates a new Updater instance and loads trusted root metadata.
Args:
repository_dir: Local metadata directory. Directory must be
writable and it must contain a trusted root.json file.
metadata_base_url: Base URL for all remote metadata downloads
target_base_url: Optional; Default base URL for all remote target
downloads. Can be individually set in download_target()
fetcher: Optional; FetcherInterface implementation used to download
both metadata and targets. Default is RequestsFetcher
Raises:
OSError: Local root.json cannot be read
RepositoryError: Local root.json is invalid
"""
self._dir = repository_dir
self._metadata_base_url = _ensure_trailing_slash(metadata_base_url)
if target_base_url is None: