Skip to content

Commit c97a6bc

Browse files
MakerNetworkclaude
andcommitted
Fix OKHManifest.from_dict crashing on string items in list fields
Two list fields accepted strings in practice but the parser called .get() on each item unconditionally, raising AttributeError and producing an unactionable "Failed to parse content as OKHManifest: 'str' object has no attribute 'get'" error in both validate and fix. making_instructions / design_files / manufacturing_files etc.: A plain string (e.g. an instruction step like "Heat oven to 350°F") is now coerced into a DocumentRef(title=<string>, path="", type=<field type>). The default doc type is now field-specific (making-instructions, operating-instructions, etc.) rather than always falling back to design-files. standards_used: A plain string (e.g. "CC0-1.0") is coerced into Standard(standard_title=<string>). Both cases now also raise a clear ValueError naming the offending field and received type when an item is neither str nor dict. materials already handled strings; this brings the other list fields in line with that pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 13b9897 commit c97a6bc

1 file changed

Lines changed: 46 additions & 23 deletions

File tree

src/core/models/okh.py

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -833,26 +833,40 @@ def from_dict(cls, data: Dict) -> "OKHManifest":
833833
)
834834

835835
# Handle document references
836-
for doc_field in [
837-
"manufacturing_files",
838-
"design_files",
839-
"making_instructions",
840-
"operating_instructions",
841-
"technical_specifications",
842-
"publications",
843-
]:
836+
_FIELD_DOC_TYPE = {
837+
"manufacturing_files": DocumentationType.MANUFACTURING_FILES,
838+
"design_files": DocumentationType.DESIGN_FILES,
839+
"making_instructions": DocumentationType.MAKING_INSTRUCTIONS,
840+
"operating_instructions": DocumentationType.OPERATING_INSTRUCTIONS,
841+
"technical_specifications": DocumentationType.TECHNICAL_SPECIFICATIONS,
842+
"publications": DocumentationType.PUBLICATIONS,
843+
}
844+
for doc_field in _FIELD_DOC_TYPE:
844845
if doc_field in data and data[doc_field] is not None:
845846
doc_list = []
846847
for doc_data in data[doc_field]:
847-
doc_type = DocumentationType(
848-
doc_data.get("type", DocumentationType.DESIGN_FILES.value)
849-
)
850-
doc = DocumentRef(
851-
title=doc_data.get("title", ""),
852-
path=doc_data.get("path", ""),
853-
type=doc_type,
854-
metadata=doc_data.get("metadata", {}),
855-
)
848+
if isinstance(doc_data, str):
849+
# Plain string (e.g. an instruction step) → title with no file path
850+
doc = DocumentRef(
851+
title=doc_data,
852+
path="",
853+
type=_FIELD_DOC_TYPE[doc_field],
854+
)
855+
elif isinstance(doc_data, dict):
856+
doc_type = DocumentationType(
857+
doc_data.get("type", _FIELD_DOC_TYPE[doc_field].value)
858+
)
859+
doc = DocumentRef(
860+
title=doc_data.get("title", ""),
861+
path=doc_data.get("path", ""),
862+
type=doc_type,
863+
metadata=doc_data.get("metadata", {}),
864+
)
865+
else:
866+
raise ValueError(
867+
f"'{doc_field}' items must be a string or dict, "
868+
f"got {type(doc_data).__name__!r}"
869+
)
856870
doc_list.append(doc)
857871
setattr(instance, doc_field, doc_list)
858872

@@ -955,12 +969,21 @@ def from_dict(cls, data: Dict) -> "OKHManifest":
955969
if "standards_used" in data and data["standards_used"] is not None:
956970
instance.standards_used = []
957971
for std_data in data["standards_used"]:
958-
std = Standard(
959-
standard_title=std_data.get("standard_title", ""),
960-
publisher=std_data.get("publisher"),
961-
reference=std_data.get("reference"),
962-
certifications=std_data.get("certifications", []),
963-
)
972+
if isinstance(std_data, str):
973+
# Plain string (e.g. "CC0-1.0") → standard title
974+
std = Standard(standard_title=std_data)
975+
elif isinstance(std_data, dict):
976+
std = Standard(
977+
standard_title=std_data.get("standard_title", ""),
978+
publisher=std_data.get("publisher"),
979+
reference=std_data.get("reference"),
980+
certifications=std_data.get("certifications", []),
981+
)
982+
else:
983+
raise ValueError(
984+
f"'standards_used' items must be a string or dict, "
985+
f"got {type(std_data).__name__!r}"
986+
)
964987
instance.standards_used.append(std)
965988

966989
# Handle software

0 commit comments

Comments
 (0)