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