|
7 | 7 |
|
8 | 8 | from __future__ import annotations |
9 | 9 |
|
10 | | -from dataclasses import dataclass, field, asdict |
| 10 | +from dataclasses import dataclass, field, asdict, fields |
11 | 11 | from typing import Any, Optional |
12 | 12 | import json |
13 | 13 |
|
@@ -75,9 +75,19 @@ def to_json(self) -> str: |
75 | 75 |
|
76 | 76 | @classmethod |
77 | 77 | def from_dict(cls, data: dict[str, Any]) -> "Manifest": |
78 | | - caps_raw = data.get("capabilities", []) |
79 | | - caps = [Capability(**c) if isinstance(c, dict) else c for c in caps_raw] |
80 | | - kwargs = {k: v for k, v in data.items() if k != "capabilities"} |
| 78 | + # Drop keys we don't know about instead of crashing. schema_version |
| 79 | + # exists precisely so a newer hub can add fields; a v0.1 reader parsing |
| 80 | + # a v0.2 manifest (or any extra top-level key) must degrade gracefully, |
| 81 | + # not raise TypeError. Same for unknown keys on a capability. |
| 82 | + cap_fields = {f.name for f in fields(Capability)} |
| 83 | + caps = [ |
| 84 | + Capability(**{k: v for k, v in c.items() if k in cap_fields}) |
| 85 | + if isinstance(c, dict) else c |
| 86 | + for c in data.get("capabilities", []) |
| 87 | + ] |
| 88 | + manifest_fields = {f.name for f in fields(cls)} - {"capabilities"} |
| 89 | + kwargs = {k: v for k, v in data.items() |
| 90 | + if k in manifest_fields and k != "capabilities"} |
81 | 91 | return cls(capabilities=caps, **kwargs) |
82 | 92 |
|
83 | 93 | @classmethod |
|
0 commit comments