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
« 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#
25"""
26Loading and saving PEX25D artifacts in any of the three encodings.
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"""
33from __future__ import annotations
35from typing import *
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
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``).
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()
63 match spec.format:
64 case ArtifactFormat.PB:
65 message = message_class_for_kind(spec.kind)()
66 message.ParseFromString(data)
67 return message
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
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)
84 case _:
85 raise ValueError(f"Unknown artifact format {spec.format}")
88def save_artifact(message: Any, spec: ArtifactSpec, comments: bool = False) -> None:
89 """
90 Write a ``PEX25DFile`` / ``PEX25DScene`` in the encoding ``spec`` asks for.
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}")
103 match spec.format:
104 case ArtifactFormat.PB:
105 data = message.SerializeToString()
107 case ArtifactFormat.TEXTPB:
108 from google.protobuf import text_format
109 data = text_format.MessageToString(message, as_utf8=True).encode('utf-8')
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)
117 case _:
118 raise ValueError(f"Unknown artifact format {spec.format}")
120 with open_artifact_write(spec) as f:
121 f.write(data)