Source code for asdf.extension._manifest
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import yaml
from ._extension import Extension
from ._tag import TagDefinition
if TYPE_CHECKING:
from collections.abc import Mapping
from asdf.extension import Compressor, Converter, Validator
from asdf.typing import TreeKey
[docs]
class ManifestExtension(Extension):
"""
Extension implementation that reads the extension URI, ASDF
Standard requirement, and tag list from a manifest document.
Parameters
----------
manifest : dict
Parsed manifest.
converters : iterable of asdf.extension.Converter, optional
Converter instances for the tags and Python types
supported by this extension.
compressors : iterable of asdf.extension.Compressor, optional
Compressor instances to support additional binary
block compression options.
validators : iterable of asdf.extension.Validator, optional
Validator instances to support validation of custom
schema properties.
legacy_class_names : iterable of str, optional
Fully-qualified class names used by older versions
of this extension.
"""
[docs]
@classmethod
def from_uri(cls, manifest_uri: str, **kwargs) -> ManifestExtension:
"""
Construct the extension using the manifest with the
specified URI. The manifest document must be registered
with ASDF's resource manager.
Parameters
----------
manifest_uri : str
Manifest URI.
See the class docstring for details on keyword parameters.
"""
from asdf.config import get_config
manifest = yaml.safe_load(get_config().resource_manager[manifest_uri])
return cls(manifest, **kwargs)
def __init__(
self,
manifest: Mapping[TreeKey, Any],
*,
legacy_class_names: list[str] | None = None,
converters: list[Converter[Any]] | None = None,
compressors: list[Compressor] | None = None,
validators: list[Validator] | None = None,
):
self._manifest = manifest
if legacy_class_names is None:
self._legacy_class_names = []
else:
self._legacy_class_names = legacy_class_names
if converters is None:
self._converters = []
else:
self._converters = converters
if compressors is None:
self._compressors = []
else:
self._compressors = compressors
if validators is None:
self._validators = []
else:
self._validators = validators
@property
def extension_uri(self) -> str:
return self._manifest["extension_uri"]
@property
def legacy_class_names(self) -> list[str]:
return self._legacy_class_names
@property
def asdf_standard_requirement(self) -> str | None:
version = self._manifest.get("asdf_standard_requirement", None)
if version is None:
return None
if isinstance(version, str):
return f"=={version}"
specifiers = []
for prop, operator in [("gt", ">"), ("gte", ">="), ("lt", "<"), ("lte", "<=")]:
value = version.get(prop)
if value:
specifiers.append(f"{operator}{value}")
return ",".join(specifiers)
@property
def converters(self) -> list[Converter[Any]]:
return self._converters
@property
def compressors(self) -> list[Compressor]:
return self._compressors
@property
def validators(self) -> list[Validator]:
return self._validators
@property
def tags(self) -> list[str | TagDefinition]:
result: list[str | TagDefinition] = []
for tag in self._manifest.get("tags", []):
if isinstance(tag, str):
# ExtensionProxy knows how to handle str tags.
result.append(tag)
elif isinstance(tag, dict):
result.append(
TagDefinition(
tag["tag_uri"],
schema_uris=tag.get("schema_uri"),
title=tag.get("title"),
description=tag.get("description"),
),
)
else:
msg = "Malformed manifest document"
raise TypeError(msg)
return result