refactor: improve metadata module
This commit is contained in:
@@ -18,7 +18,14 @@ from re import Pattern, compile
|
||||
from typing import Any, Callable
|
||||
from semver import Version
|
||||
from . import utils
|
||||
from .utils import Triple, VERSION, dict_chain_get, dict_typed_get, dict_typed_get_required
|
||||
from .utils import (
|
||||
Triple,
|
||||
VERSION,
|
||||
dict_chain_get,
|
||||
dict_typed_get,
|
||||
dict_typed_get_required,
|
||||
list_typed_iter,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -104,15 +111,14 @@ class MetadataCMake:
|
||||
def from_dict(d: dict[str, Any]) -> "MetadataCMake":
|
||||
"""Build a :class:`MetadataCMake` from its raw TOML table."""
|
||||
raw_dependencies = dict_typed_get(d, "dependencies", list)
|
||||
if raw_dependencies is not None:
|
||||
dependencies_list: list[MetadataCMakeDependency] = []
|
||||
for i, item in enumerate(raw_dependencies):
|
||||
if not isinstance(item, dict):
|
||||
raise TypeError(f"dependencies[{i}] must be a table")
|
||||
dependencies_list.append(MetadataCMakeDependency.from_dict(item))
|
||||
dependencies = tuple(dependencies_list)
|
||||
else:
|
||||
dependencies = None
|
||||
dependencies = (
|
||||
tuple(
|
||||
MetadataCMakeDependency.from_dict(item)
|
||||
for item in list_typed_iter(raw_dependencies, dict)
|
||||
)
|
||||
if raw_dependencies is not None
|
||||
else None
|
||||
)
|
||||
|
||||
return MetadataCMake(
|
||||
namespace_name=dict_typed_get(d, "namespace_name", str),
|
||||
@@ -157,15 +163,11 @@ class MetadataPkgConfig:
|
||||
def from_dict(d: dict[str, Any]) -> "MetadataPkgConfig":
|
||||
"""Build a :class:`MetadataPkgConfig` from its raw TOML table."""
|
||||
raw_requires = dict_typed_get(d, "requires", list)
|
||||
if raw_requires is not None:
|
||||
requires_list: list[str] = []
|
||||
for i, item in enumerate(raw_requires):
|
||||
if not isinstance(item, str):
|
||||
raise TypeError(f"requires[{i}] must be a string")
|
||||
requires_list.append(item)
|
||||
requires = tuple(requires_list)
|
||||
else:
|
||||
requires = None
|
||||
requires = (
|
||||
tuple(list_typed_iter(raw_requires, str))
|
||||
if raw_requires is not None
|
||||
else None
|
||||
)
|
||||
|
||||
return MetadataPkgConfig(
|
||||
id=dict_typed_get(d, "id", str),
|
||||
@@ -210,13 +212,12 @@ class Metadata:
|
||||
else:
|
||||
min_version = None
|
||||
|
||||
raw_headers = dict_typed_get_required(d, "headers", list)
|
||||
headers_list: list[MetadataHeader] = []
|
||||
for i, item in enumerate(raw_headers):
|
||||
if not isinstance(item, dict):
|
||||
raise TypeError(f"headers[{i}] must be a table")
|
||||
headers_list.append(MetadataHeader.from_dict(item))
|
||||
headers = tuple(headers_list)
|
||||
headers = tuple(
|
||||
MetadataHeader.from_dict(item)
|
||||
for item in list_typed_iter(
|
||||
dict_typed_get_required(d, "headers", list), dict
|
||||
)
|
||||
)
|
||||
|
||||
raw_cmake = dict_typed_get(d, "cmake", dict)
|
||||
cmake = MetadataCMake.from_dict(raw_cmake) if raw_cmake is not None else None
|
||||
@@ -260,6 +261,7 @@ _SUBPROCESS_TIMEOUT_SEC: int = 10
|
||||
_HOST_TRIPLE_PATTERN: Pattern = compile(r"(?m)^host:\s*(\S+)$")
|
||||
"""The pattern for match host triple in ``rustc -vV``. The group 1 is the triple result."""
|
||||
|
||||
|
||||
class MetadataExtractor:
|
||||
"""Access layer over ``cargo metadata`` and the OMRF metadata table.
|
||||
|
||||
@@ -313,8 +315,7 @@ class MetadataExtractor:
|
||||
raise RuntimeError("fail to fetch host triple: timed out")
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(
|
||||
"fail to fetch host triple: "
|
||||
+ stderr.decode("utf-8", errors="ignore")
|
||||
"fail to fetch host triple: " + stderr.decode("utf-8", errors="ignore")
|
||||
)
|
||||
m = _HOST_TRIPLE_PATTERN.search(stdout.decode("utf-8", errors="strict"))
|
||||
if m is None:
|
||||
@@ -367,11 +368,8 @@ class MetadataExtractor:
|
||||
:returns: The matching package item.
|
||||
:raises RuntimeError: if no matching package is found.
|
||||
"""
|
||||
packages: list[Any] = dict_typed_get_required(cargo_metadata, "packages", list)
|
||||
for i, package in enumerate(packages):
|
||||
if not isinstance(package, dict):
|
||||
raise TypeError(f"packages[{i}] must be a table")
|
||||
|
||||
packages = dict_typed_get_required(cargo_metadata, "packages", list)
|
||||
for package in list_typed_iter(packages, dict):
|
||||
raw_manifest_path = dict_typed_get_required(package, "manifest_path", str)
|
||||
manifest_path = Path(raw_manifest_path)
|
||||
if manifest_path == cargo_toml_path:
|
||||
@@ -393,11 +391,8 @@ class MetadataExtractor:
|
||||
# build the path to lib.rs for comparing
|
||||
librs = cargo_toml_path.parent / "src" / "lib.rs"
|
||||
# start checking
|
||||
targets: list[Any] = dict_typed_get_required(cargo_package, "targets", list)
|
||||
for i, target in enumerate(targets):
|
||||
if not isinstance(target, dict):
|
||||
raise TypeError(f"targets[{i}] must be a table")
|
||||
|
||||
targets = dict_typed_get_required(cargo_package, "targets", list)
|
||||
for target in list_typed_iter(targets, dict):
|
||||
raw_src_path = dict_typed_get_required(target, "src_path", str)
|
||||
src_path = Path(raw_src_path)
|
||||
if src_path == librs:
|
||||
|
||||
@@ -7,7 +7,7 @@ validation and template-directory resolution.
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from re import Pattern, compile
|
||||
from typing import Any, overload
|
||||
from typing import Any, Iterator, overload
|
||||
from semver import Version
|
||||
|
||||
VERSION: Version = Version(1, 0, 0)
|
||||
@@ -106,6 +106,20 @@ def dict_chain_get(d: dict[str, Any], *args: str) -> dict[str, Any]:
|
||||
return d
|
||||
|
||||
|
||||
def list_typed_iter[T](lst: list[Any], ty: type[T]) -> Iterator[T]:
|
||||
"""Yield each element of ``lst`` after checking it is an instance of ``ty``.
|
||||
|
||||
:param lst: The list to iterate.
|
||||
:param ty: Expected type of every element.
|
||||
:returns: An iterator over the validated elements, typed ``T``.
|
||||
:raises TypeError: if an element is not an instance of ``ty``.
|
||||
"""
|
||||
for i, item in enumerate(lst):
|
||||
if not isinstance(item, ty):
|
||||
raise TypeError(f"item {i} of the given list is not a {ty.__name__}")
|
||||
yield item
|
||||
|
||||
|
||||
_NAME_PATTERN: Pattern = compile(r"[a-zA-Z0-9_+-]+")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user