6969from mypy_extensions import u8
7070
7171# High-level cache layout format
72- CACHE_VERSION : Final = 0
72+ CACHE_VERSION : Final = 1
73+
74+ SerializedError : _TypeAlias = tuple [str | None , int , int , int , int , str , str , str | None ]
7375
7476
7577class CacheMeta :
@@ -92,7 +94,7 @@ def __init__(
9294 dep_lines : list [int ],
9395 dep_hashes : list [bytes ],
9496 interface_hash : bytes ,
95- error_lines : list [str ],
97+ error_lines : list [SerializedError ],
9698 version_id : str ,
9799 ignore_all : bool ,
98100 plugin_data : Any ,
@@ -157,7 +159,7 @@ def deserialize(cls, meta: dict[str, Any], data_file: str) -> CacheMeta | None:
157159 dep_lines = meta ["dep_lines" ],
158160 dep_hashes = [bytes .fromhex (dep ) for dep in meta ["dep_hashes" ]],
159161 interface_hash = bytes .fromhex (meta ["interface_hash" ]),
160- error_lines = meta ["error_lines" ],
162+ error_lines = [ tuple ( err ) for err in meta ["error_lines" ] ],
161163 version_id = meta ["version_id" ],
162164 ignore_all = meta ["ignore_all" ],
163165 plugin_data = meta ["plugin_data" ],
@@ -179,7 +181,7 @@ def write(self, data: WriteBuffer) -> None:
179181 write_int_list (data , self .dep_lines )
180182 write_bytes_list (data , self .dep_hashes )
181183 write_bytes (data , self .interface_hash )
182- write_str_list (data , self .error_lines )
184+ write_errors (data , self .error_lines )
183185 write_str (data , self .version_id )
184186 write_bool (data , self .ignore_all )
185187 # Plugin data may be not a dictionary, so we use
@@ -204,7 +206,7 @@ def read(cls, data: ReadBuffer, data_file: str) -> CacheMeta | None:
204206 dep_lines = read_int_list (data ),
205207 dep_hashes = read_bytes_list (data ),
206208 interface_hash = read_bytes (data ),
207- error_lines = read_str_list (data ),
209+ error_lines = read_errors (data ),
208210 version_id = read_str (data ),
209211 ignore_all = read_bool (data ),
210212 plugin_data = read_json_value (data ),
@@ -231,6 +233,7 @@ def read(cls, data: ReadBuffer, data_file: str) -> CacheMeta | None:
231233LIST_INT : Final [Tag ] = 21
232234LIST_STR : Final [Tag ] = 22
233235LIST_BYTES : Final [Tag ] = 23
236+ TUPLE_GEN : Final [Tag ] = 24
234237DICT_STR_GEN : Final [Tag ] = 30
235238
236239# Misc classes.
@@ -391,12 +394,11 @@ def write_str_opt_list(data: WriteBuffer, value: list[str | None]) -> None:
391394
392395
393396Value : _TypeAlias = None | int | str | bool
394- JsonValue : _TypeAlias = Value | list ["JsonValue" ] | dict [str , "JsonValue" ]
395397
396- # Currently tuples are used by mypyc plugin. They will be normalized to
397- # JSON lists after a roundtrip .
398- JsonValueEx : _TypeAlias = (
399- Value | list ["JsonValueEx " ] | dict [str , "JsonValueEx " ] | tuple ["JsonValueEx " , ...]
398+ # Our JSON format is somewhat non-standard as we distinguish lists and tuples.
399+ # This is convenient for some internal things, like mypyc plugin and error serialization .
400+ JsonValue : _TypeAlias = (
401+ Value | list ["JsonValue " ] | dict [str , "JsonValue " ] | tuple ["JsonValue " , ...]
400402)
401403
402404
@@ -415,13 +417,16 @@ def read_json_value(data: ReadBuffer) -> JsonValue:
415417 if tag == LIST_GEN :
416418 size = read_int_bare (data )
417419 return [read_json_value (data ) for _ in range (size )]
420+ if tag == TUPLE_GEN :
421+ size = read_int_bare (data )
422+ return tuple (read_json_value (data ) for _ in range (size ))
418423 if tag == DICT_STR_GEN :
419424 size = read_int_bare (data )
420425 return {read_str_bare (data ): read_json_value (data ) for _ in range (size )}
421426 assert False , f"Invalid JSON tag: { tag } "
422427
423428
424- def write_json_value (data : WriteBuffer , value : JsonValueEx ) -> None :
429+ def write_json_value (data : WriteBuffer , value : JsonValue ) -> None :
425430 if value is None :
426431 write_tag (data , LITERAL_NONE )
427432 elif isinstance (value , bool ):
@@ -432,11 +437,16 @@ def write_json_value(data: WriteBuffer, value: JsonValueEx) -> None:
432437 elif isinstance (value , str ):
433438 write_tag (data , LITERAL_STR )
434439 write_str_bare (data , value )
435- elif isinstance (value , ( list , tuple ) ):
440+ elif isinstance (value , list ):
436441 write_tag (data , LIST_GEN )
437442 write_int_bare (data , len (value ))
438443 for val in value :
439444 write_json_value (data , val )
445+ elif isinstance (value , tuple ):
446+ write_tag (data , TUPLE_GEN )
447+ write_int_bare (data , len (value ))
448+ for val in value :
449+ write_json_value (data , val )
440450 elif isinstance (value , dict ):
441451 write_tag (data , DICT_STR_GEN )
442452 write_int_bare (data , len (value ))
@@ -461,3 +471,38 @@ def write_json(data: WriteBuffer, value: dict[str, Any]) -> None:
461471 for key in sorted (value ):
462472 write_str_bare (data , key )
463473 write_json_value (data , value [key ])
474+
475+
476+ def write_errors (data : WriteBuffer , errs : list [SerializedError ]) -> None :
477+ write_tag (data , LIST_GEN )
478+ write_int_bare (data , len (errs ))
479+ for path , line , column , end_line , end_column , severity , message , code in errs :
480+ write_tag (data , TUPLE_GEN )
481+ write_str_opt (data , path )
482+ write_int (data , line )
483+ write_int (data , column )
484+ write_int (data , end_line )
485+ write_int (data , end_column )
486+ write_str (data , severity )
487+ write_str (data , message )
488+ write_str_opt (data , code )
489+
490+
491+ def read_errors (data : ReadBuffer ) -> list [SerializedError ]:
492+ assert read_tag (data ) == LIST_GEN
493+ result = []
494+ for _ in range (read_int_bare (data )):
495+ assert read_tag (data ) == TUPLE_GEN
496+ result .append (
497+ (
498+ read_str_opt (data ),
499+ read_int (data ),
500+ read_int (data ),
501+ read_int (data ),
502+ read_int (data ),
503+ read_str (data ),
504+ read_str (data ),
505+ read_str_opt (data ),
506+ )
507+ )
508+ return result
0 commit comments