1717
1818SLUG_RE = re .compile (r"^[a-z0-9]+(?:-[a-z0-9]+)*$" )
1919
20- BRAND_REQUIRED = {"slug" , "name" , "country" , "categories" }
20+ BRAND_REQUIRED = {"slug" , "name" , "country" , "categories" , "source_urls" }
2121BRAND_CATEGORIES = {
2222 "smartphone-oem" ,
2323 "soc-designer" ,
@@ -78,7 +78,7 @@ def _load(subdir: str) -> list[tuple[str, dict[str, Any]]]:
7878 if not path .exists ():
7979 return []
8080 return [
81- (str (f .relative_to (DATA_DIR )), json .loads (f .read_text (encoding = "utf-8" )))
81+ (str (f .relative_to (DATA_DIR )), json .loads (f .read_text (encoding = "utf-8-sig " )))
8282 for f in sorted (path .rglob ("*.json" )) # recurse into brand subfolders
8383 ]
8484
@@ -127,6 +127,14 @@ def _check_unique_slugs(
127127 seen [slug ] = fname
128128
129129
130+ def _check_source_urls (name : str , record : dict [str , Any ], errors : list [str ]) -> None :
131+ urls = record .get ("source_urls" )
132+ if not isinstance (urls , list ) or not urls or not all (
133+ isinstance (url , str ) and url .startswith (("http://" , "https://" )) for url in urls
134+ ):
135+ errors .append (f"{ name } : source_urls must be a non-empty list of http(s) URL strings" )
136+
137+
130138def validate () -> list [str ]:
131139 errors : list [str ] = []
132140
@@ -150,6 +158,7 @@ def validate() -> list[str]:
150158
151159 for fname , rec in brands :
152160 _check_required (fname , rec , BRAND_REQUIRED , errors )
161+ _check_source_urls (fname , rec , errors )
153162 _check_slug (fname , rec .get ("slug" ), errors )
154163 if "founded_year" in rec :
155164 _check_range (fname , "founded_year" , rec ["founded_year" ], 1800 , 2100 , errors )
@@ -182,6 +191,7 @@ def validate() -> list[str]:
182191
183192 for fname , rec in socs :
184193 _check_required (fname , rec , SOC_REQUIRED , errors )
194+ _check_source_urls (fname , rec , errors )
185195 _check_slug (fname , rec .get ("slug" ), errors )
186196 if "release_date" in rec :
187197 _check_date (fname , rec ["release_date" ], errors )
@@ -191,6 +201,7 @@ def validate() -> list[str]:
191201
192202 for fname , rec in phones :
193203 _check_required (fname , rec , PHONE_REQUIRED , errors )
204+ _check_source_urls (fname , rec , errors )
194205 _check_slug (fname , rec .get ("slug" ), errors )
195206 if "release_date" in rec :
196207 _check_date (fname , rec ["release_date" ], errors )
@@ -206,6 +217,7 @@ def validate() -> list[str]:
206217
207218 for fname , rec in gpus :
208219 _check_required (fname , rec , GPU_REQUIRED , errors )
220+ _check_source_urls (fname , rec , errors )
209221 _check_slug (fname , rec .get ("slug" ), errors )
210222 if "release_date" in rec :
211223 _check_date (fname , rec ["release_date" ], errors )
@@ -219,6 +231,7 @@ def validate() -> list[str]:
219231 valid_segments = {"desktop" , "laptop" , "hedt" , "server" }
220232 for fname , rec in cpus :
221233 _check_required (fname , rec , CPU_REQUIRED , errors )
234+ _check_source_urls (fname , rec , errors )
222235 _check_slug (fname , rec .get ("slug" ), errors )
223236 if "release_date" in rec :
224237 _check_date (fname , rec ["release_date" ], errors )
@@ -236,6 +249,10 @@ def validate() -> list[str]:
236249
237250
238251def run () -> int :
252+ try :
253+ sys .stdout .reconfigure (encoding = "utf-8" ) # type: ignore[union-attr]
254+ except Exception :
255+ pass
239256 errors = validate ()
240257 if errors :
241258 print (f"❌ Data validation failed ({ len (errors )} issue(s)):" )
0 commit comments