gcf-python 0.5.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gcf/__init__.py CHANGED
@@ -65,4 +65,4 @@ __all__ = [
65
65
  "encode_with_session",
66
66
  ]
67
67
 
68
- __version__ = "0.1.3"
68
+ __version__ = "1.0.0"
gcf/decode.py CHANGED
@@ -35,7 +35,11 @@ def decode(input_text: str) -> Payload:
35
35
  _parse_header(header[4:], p)
36
36
 
37
37
  if not p.tool:
38
- raise DecodeError("header missing required 'tool' field")
38
+ raise DecodeError("missing_tool: header missing required 'tool' field")
39
+
40
+ # Detect delta mode.
41
+ is_delta = "delta=true" in header
42
+ valid_delta_sections = {"removed", "added", "edges_removed", "edges_added"}
39
43
 
40
44
  # Parse body: symbols and edges.
41
45
  symbols: list[Symbol] = []
@@ -48,6 +52,10 @@ def decode(input_text: str) -> Payload:
48
52
  if not line:
49
53
  continue
50
54
 
55
+ # Skip ##! summary trailer.
56
+ if line.startswith("##! "):
57
+ continue
58
+
51
59
  # Group header.
52
60
  if line.startswith("## "):
53
61
  group = line[3:]
@@ -55,6 +63,8 @@ def decode(input_text: str) -> Payload:
55
63
  bracket_idx = group.find(" [")
56
64
  if bracket_idx >= 0:
57
65
  group = group[:bracket_idx]
66
+ if is_delta and group not in valid_delta_sections:
67
+ raise DecodeError(f"malformed_delta: invalid delta section {group!r}")
58
68
  in_edges = group == "edges"
59
69
  if not in_edges:
60
70
  if group == "targets":
@@ -113,19 +123,19 @@ def _parse_header(fields: str, p: Payload) -> None:
113
123
  def _parse_symbol_line(line: str, distance: int) -> tuple[Symbol, int]:
114
124
  """Parse a symbol line into a Symbol and its local ID."""
115
125
  if not line.startswith("@"):
116
- raise DecodeError(f"expected symbol line starting with @, got {line!r}")
126
+ raise DecodeError(f"invalid_node_line: expected symbol line starting with @, got {line!r}")
117
127
 
118
128
  parts = line.split()
119
129
  if len(parts) < 5:
120
130
  raise DecodeError(
121
- f"symbol line needs at least 5 fields, got {len(parts)} in {line!r}"
131
+ f"invalid_node_line: symbol line needs at least 5 fields, got {len(parts)} in {line!r}"
122
132
  )
123
133
 
124
134
  id_str = parts[0][1:] # strip @
125
135
  try:
126
136
  sym_id = int(id_str)
127
137
  except ValueError as e:
128
- raise DecodeError(f"invalid symbol id {id_str!r}: {e}") from e
138
+ raise DecodeError(f"invalid_symbol_id: invalid symbol id {id_str!r}: {e}") from e
129
139
 
130
140
  kind = parts[1]
131
141
  kind = KIND_EXPAND.get(kind, kind)
@@ -135,7 +145,7 @@ def _parse_symbol_line(line: str, distance: int) -> tuple[Symbol, int]:
135
145
  try:
136
146
  score = float(parts[3])
137
147
  except ValueError as e:
138
- raise DecodeError(f"invalid score {parts[3]!r}: {e}") from e
148
+ raise DecodeError(f"invalid_score: invalid score {parts[3]!r}: {e}") from e
139
149
 
140
150
  provenance = parts[4]
141
151
 
@@ -157,7 +167,7 @@ def _parse_edge_line(line: str, sym_by_id: dict[int, Symbol]) -> Edge:
157
167
  ref = parts[0]
158
168
  lt_idx = ref.find("<")
159
169
  if lt_idx < 0:
160
- raise DecodeError(f"edge line missing '<' separator in {ref!r}")
170
+ raise DecodeError(f"invalid_edge_syntax: edge line missing '<' separator in {ref!r}")
161
171
 
162
172
  target_id_str = ref[1:lt_idx] # strip leading @
163
173
  source_id_str = ref[lt_idx + 2:] # strip <@
@@ -176,7 +186,7 @@ def _parse_edge_line(line: str, sym_by_id: dict[int, Symbol]) -> Edge:
176
186
  source_sym = sym_by_id.get(source_id)
177
187
  if target_sym is None or source_sym is None:
178
188
  raise DecodeError(
179
- f"edge references unknown symbol id(s): target={target_id} source={source_id}"
189
+ f"unknown_edge_reference: edge references unknown symbol id(s): target={target_id} source={source_id}"
180
190
  )
181
191
 
182
192
  edge_type = parts[1]