Coverage for klayout_pex/pex25d/codec.py: 86%

44 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-17 19:08 +0000

1# 

2# -------------------------------------------------------------------------------- 

3# SPDX-FileCopyrightText: 2024-2026 Martin Jan Köhler and Harald Pretl 

4# Johannes Kepler University, Institute for Integrated Circuits. 

5# 

6# This file is part of KPEX  

7# (see https://github.com/iic-jku/klayout-pex). 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21# SPDX-License-Identifier: GPL-3.0-or-later 

22# -------------------------------------------------------------------------------- 

23# 

24 

25""" 

26Loading and saving PEX25D artifacts in any of the three encodings. 

27 

28The two protobuf encodings are handled here — they are a 

29``SerializeToString`` away. The normative *text* format is delegated to 

30:mod:`~klayout_pex.pex25d.reader` and :mod:`~klayout_pex.pex25d.writer`. 

31""" 

32 

33from __future__ import annotations 

34 

35from typing import * 

36 

37from .artifact import ( 

38 ArtifactFormat, 

39 ArtifactKind, 

40 ArtifactSpec, 

41 open_artifact_read, 

42 open_artifact_write, 

43) 

44from .diagnostics import DiagnosticsReport 

45from .protobuf import kind_for_message, message_class_for_kind 

46 

47 

48def load_artifact(spec: ArtifactSpec, 

49 report: Optional[DiagnosticsReport] = None, 

50 with_source_refs: bool = False) -> Any: 

51 """ 

52 Read an artifact and return the generated protobuf message it holds 

53 (``PEX25DFile`` or ``PEX25DScene``, per ``spec.kind``). 

54 

55 Syntax- and semantic-tier findings from the text reader are appended to 

56 ``report`` when one is given; a fatal parse failure raises. 

57 """ 

58 # Read first: a missing or unreadable input is the more likely mistake, and 

59 # is a better error than 'the generated protobuf modules are not built yet'. 

60 with open_artifact_read(spec) as f: 

61 data = f.read() 

62 

63 match spec.format: 

64 case ArtifactFormat.PB: 

65 message = message_class_for_kind(spec.kind)() 

66 message.ParseFromString(data) 

67 return message 

68 

69 case ArtifactFormat.TEXTPB: 

70 from google.protobuf import text_format 

71 message = message_class_for_kind(spec.kind)() 

72 text_format.Parse(data.decode('utf-8'), message) 

73 return message 

74 

75 case ArtifactFormat.TEXT: 

76 if spec.kind != ArtifactKind.FILE: 

77 raise ValueError("The PEX25D text format only spells PEX25DFile") 

78 from .reader import read_pex25d_text 

79 return read_pex25d_text(data, 

80 source_name=spec.path, 

81 report=report, 

82 with_source_refs=with_source_refs) 

83 

84 case _: 

85 raise ValueError(f"Unknown artifact format {spec.format}") 

86 

87 

88def save_artifact(message: Any, spec: ArtifactSpec, comments: bool = False) -> None: 

89 """ 

90 Write a ``PEX25DFile`` / ``PEX25DScene`` in the encoding ``spec`` asks for. 

91 

92 :param comments: emit the specification's syntax hints. Text format only — 

93 the protobuf encodings have no comments. 

94 :raises ValueError: if the message is not the kind the spec describes. The 

95 text format spells a ``PEX25DFile`` only, and a scene written under a 

96 spec that says file would otherwise fail deep inside the writer. 

97 """ 

98 kind = kind_for_message(message) 

99 if kind != spec.kind: 

100 raise ValueError(f"The artifact spec for '{spec.path}' says " 

101 f"{spec.kind}, but the message is a {kind}") 

102 

103 match spec.format: 

104 case ArtifactFormat.PB: 

105 data = message.SerializeToString() 

106 

107 case ArtifactFormat.TEXTPB: 

108 from google.protobuf import text_format 

109 data = text_format.MessageToString(message, as_utf8=True).encode('utf-8') 

110 

111 case ArtifactFormat.TEXT: 

112 if spec.kind != ArtifactKind.FILE: 

113 raise ValueError("The PEX25D text format only spells PEX25DFile") 

114 from .writer import write_pex25d_text 

115 data = write_pex25d_text(message, comments=comments) 

116 

117 case _: 

118 raise ValueError(f"Unknown artifact format {spec.format}") 

119 

120 with open_artifact_write(spec) as f: 

121 f.write(data)