coolblaze03 commited on
Commit
f534783
·
verified ·
1 Parent(s): 3486ce0

fix(harness): correct docstring_of — a slot-0 string is a docstring only in function-like scopes; genexps do not reserve slot 0, modules/class bodies store __doc__ explicitly. 0 spurious / 0 missed over 2,158 code objects vs ast.get_docstring, 13 tests. disassemble_v2 defaults to doc_rule=published so published inputs stay reproducible.

Browse files
harness/pybytecode_core/rep.py CHANGED
@@ -96,18 +96,87 @@ def signature(co: types.CodeType) -> str:
96
  return ", ".join(parts)
97
 
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  def docstring_of(co: types.CodeType) -> str | None:
100
- """co_consts[0] iff it is the implicit docstring slot. This is the information v1 destroyed."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  if co.co_consts and isinstance(co.co_consts[0], str):
102
  return co.co_consts[0]
103
  return None
104
 
105
 
106
- def disassemble_v2(co: types.CodeType, out: list[str] | None = None) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  out = [] if out is None else out
108
  labels, kids = _labels_for(co), _child_names(co)
109
  out.append(f"CODE {co.co_qualname}({signature(co)})")
110
- doc = docstring_of(co)
111
  if doc is not None:
112
  out.append(f" DOC {doc!r}")
113
  for i in dis.get_instructions(co):
@@ -132,5 +201,5 @@ def disassemble_v2(co: types.CodeType, out: list[str] | None = None) -> str:
132
  out.append("END")
133
  for c in co.co_consts:
134
  if isinstance(c, types.CodeType):
135
- disassemble_v2(c, out)
136
  return "\n".join(out)
 
96
  return ", ".join(parts)
97
 
98
 
99
+ CO_OPTIMIZED = 0x01
100
+ _CONST_OPS = ("LOAD_CONST", "RETURN_CONST", "KW_NAMES")
101
+
102
+
103
+ def docstring_of_published(co: types.CodeType) -> str | None:
104
+ """The rule used to build the PUBLISHED benchmarks. Kept verbatim; do not 'fix' in place.
105
+
106
+ It is wrong -- see docstring_of() -- but the published `bench.jsonl` inputs, the v3 training
107
+ corpus and every scored generation were produced with it. Reproducing a published number
108
+ requires reproducing the input that produced it, so this stays.
109
+ """
110
+ if co.co_consts and isinstance(co.co_consts[0], str):
111
+ return co.co_consts[0]
112
+ return None
113
+
114
+
115
  def docstring_of(co: types.CodeType) -> str | None:
116
+ """co_consts[0] iff it is the IMPLICIT docstring slot -- the information v1 destroyed.
117
+
118
+ `docstring_of_published` asked only "is co_consts[0] a string", which is not the question:
119
+ whether a slot-0 string is a docstring depends on WHAT KIND OF SCOPE the code object is.
120
+ Measured over the two published benchmarks, the published rule emitted a DOC line for 98 code
121
+ objects that have no implicit docstring: 94 on csn-600 (13.4% of its DOC lines) and all 4 on
122
+ mbpp-383. A DOC line is presented to the model as recovered ground truth, so a wrong one is
123
+ not noise -- it is an instruction to reproduce a docstring that was never in the source.
124
+
125
+ What CPython 3.12 actually does, measured rather than assumed:
126
+
127
+ * A FUNCTION / lambda / async function with no docstring RESERVES slot 0 and fills it with
128
+ `None`; with a docstring, slot 0 is the docstring. So for these scopes "slot 0 is a string"
129
+ is already exactly right, and the published rule was never wrong here. (This is why the
130
+ defect is invisible at function level: 2 of 605 function-scope emissions.)
131
+
132
+ * A GENERATOR EXPRESSION is CO_OPTIMIZED like a function but does NOT reserve slot 0 -- its
133
+ first ordinary literal lands there. `sum(x for x in xs if x != '=')` yields
134
+ `co_consts[0] == '='`, and the published rule reports `'='` as a docstring. A genexp has no
135
+ `__doc__` at all. 8 such emissions on csn-600, 3 on mbpp-383.
136
+
137
+ * A MODULE or CLASS BODY stores `__doc__` EXPLICITLY (`LOAD_CONST <doc>; STORE_NAME __doc__`),
138
+ so its docstring is already visible in the instruction stream and nothing was destroyed --
139
+ emitting DOC duplicates it. And when there is no docstring, slot 0 is simply the first
140
+ const, which may be a DEFAULT ARGUMENT value reached only inside the defaults tuple
141
+ (`co_consts = ('WMAP5', <code>, None, ('WMAP5',))`) -- so it is not even referenced
142
+ directly, and an "is slot 0 loaded" test would still call it a docstring. 86 emissions on
143
+ csn-600, 1 on mbpp-383.
144
+
145
+ Hence: function-like scope, and not one of the angle-bracketed compiler-generated scopes.
146
+ `<genexpr>`, `<listcomp>`, `<setcomp>`, `<dictcomp>` and `<lambda>` can never collide with a
147
+ user identifier, so co_name is a sound discriminator. A lambda cannot carry a docstring
148
+ either (its body is a single expression), so excluding it costs nothing.
149
+
150
+ Exact on both published benchmarks against `ast.get_docstring` ground truth: 0 spurious,
151
+ 0 missed, over 2,158 code objects. See test_rep_docstring.py.
152
+ """
153
+ if not (co.co_flags & CO_OPTIMIZED):
154
+ return None # module / class body: explicit, nothing was lost
155
+ if co.co_name.startswith("<"):
156
+ return None # <genexpr>/<listcomp>/<lambda>: no __doc__ exists
157
  if co.co_consts and isinstance(co.co_consts[0], str):
158
  return co.co_consts[0]
159
  return None
160
 
161
 
162
+ def disassemble_v2(co: types.CodeType, out: list[str] | None = None, *,
163
+ doc_rule: str = "published") -> str:
164
+ """Render the code object.
165
+
166
+ `doc_rule` selects which DOC rule to apply, and DEFAULTS TO THE PUBLISHED ONE ON PURPOSE.
167
+ The DOC line is part of the model's INPUT: the shipped v3 weights were trained on inputs
168
+ built with `docstring_of_published`, and every published score was measured against those
169
+ inputs. Silently switching the default would change what the public benchmark asks of the
170
+ model and would invalidate the numbers on its own card. Pass doc_rule="fixed" to use the
171
+ corrected rule -- see docstring_of() -- which is the right default only from a retrain
172
+ onward.
173
+ """
174
+ if doc_rule not in ("published", "fixed"):
175
+ raise ValueError(f"doc_rule must be 'published' or 'fixed', got {doc_rule!r}")
176
  out = [] if out is None else out
177
  labels, kids = _labels_for(co), _child_names(co)
178
  out.append(f"CODE {co.co_qualname}({signature(co)})")
179
+ doc = (docstring_of if doc_rule == "fixed" else docstring_of_published)(co)
180
  if doc is not None:
181
  out.append(f" DOC {doc!r}")
182
  for i in dis.get_instructions(co):
 
201
  out.append("END")
202
  for c in co.co_consts:
203
  if isinstance(c, types.CodeType):
204
+ disassemble_v2(c, out, doc_rule=doc_rule)
205
  return "\n".join(out)
harness/test_rep_docstring.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Regression tests for rep.docstring_of -- the fabricated-DOC defect.
3
+
4
+ The DOC line is part of the model's INPUT and is presented as recovered ground truth, so a wrong
5
+ one instructs the model to invent a docstring that was never in the source. The published rule
6
+ (`docstring_of_published`) asked only "is co_consts[0] a string", which emitted a DOC line for 98
7
+ code objects across the two published benchmarks that have no implicit docstring at all.
8
+
9
+ Ground truth here is `ast.get_docstring` on the source, not a hand-written expectation, so these
10
+ tests measure the rule against Python's own definition of a docstring.
11
+
12
+ python3 -m unittest test_rep_docstring -v # unit cases only
13
+ python3 test_rep_docstring.py --benchmarks # + exhaustive sweep of both benchmarks
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import ast
18
+ import json
19
+ import marshal
20
+ import sys
21
+ import types
22
+ import unittest
23
+ from pathlib import Path
24
+
25
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
26
+ from pybytecode_core.rep import (CO_OPTIMIZED, disassemble_v2, docstring_of, # noqa: E402
27
+ docstring_of_published)
28
+
29
+ ROOT = Path(__file__).resolve().parent.parent
30
+
31
+
32
+ def compile_src(src: str) -> types.CodeType:
33
+ return compile(src, "<t>", "exec", dont_inherit=True, optimize=0)
34
+
35
+
36
+ def walk(co: types.CodeType):
37
+ yield co
38
+ for c in co.co_consts:
39
+ if isinstance(c, types.CodeType):
40
+ yield from walk(c)
41
+
42
+
43
+ def by_qualname(src: str) -> dict[str, types.CodeType]:
44
+ return {c.co_qualname: c for c in walk(compile_src(src))}
45
+
46
+
47
+ class FabricatedDoc(unittest.TestCase):
48
+ """Each case is a construct where the published rule emits a DOC line and must not."""
49
+
50
+ def test_function_with_docstring_is_still_emitted(self):
51
+ co = by_qualname("def f():\n 'the doc'\n return 1\n")["f"]
52
+ self.assertEqual(docstring_of(co), "the doc")
53
+
54
+ def test_function_without_docstring_reserves_slot_zero(self):
55
+ """CPython fills slot 0 with None for a docstring-less function, so BOTH rules agree.
56
+
57
+ This is why the defect is invisible at function level and only shows up in genexps,
58
+ modules and class bodies. Asserted so the claim is checked, not assumed.
59
+ """
60
+ co = by_qualname("def f():\n return 'hello'\n")["f"]
61
+ self.assertIsNone(co.co_consts[0])
62
+ self.assertIsNone(docstring_of_published(co))
63
+ self.assertIsNone(docstring_of(co))
64
+
65
+ def test_generator_expression_slot_zero_is_a_literal(self):
66
+ """A genexp is CO_OPTIMIZED but does NOT reserve slot 0 -- the real function-level defect."""
67
+ src = "def f(xs):\n return sum(1 for x in xs if x != '=')\n"
68
+ gen = [c for c in by_qualname(src).values() if c.co_name == "<genexpr>"]
69
+ self.assertTrue(gen, "no genexpr code object on this interpreter")
70
+ co = gen[0]
71
+ self.assertEqual(co.co_consts[0], "=")
72
+ self.assertEqual(docstring_of_published(co), "=") # the defect
73
+ self.assertIsNone(docstring_of(co)) # fixed
74
+
75
+ def test_class_body_docstring_is_explicit_not_lost(self):
76
+ # a class stores __doc__ explicitly, so DOC would duplicate the instruction stream
77
+ co = by_qualname("class C:\n 'cdoc'\n x = 1\n")["C"]
78
+ self.assertIsNotNone(docstring_of_published(co))
79
+ self.assertIsNone(docstring_of(co))
80
+
81
+ def test_class_body_without_docstring_emits_nothing(self):
82
+ co = by_qualname("class C:\n x = 1\n")["C"]
83
+ self.assertIsNone(docstring_of(co))
84
+
85
+ def test_module_docstring_is_explicit_not_lost(self):
86
+ co = compile_src("'mdoc'\nx = 1\n")
87
+ self.assertIsNotNone(docstring_of_published(co))
88
+ self.assertIsNone(docstring_of(co))
89
+
90
+ def test_module_first_const_is_a_default_argument(self):
91
+ # the case that makes an "is slot 0 referenced" test insufficient: slot 0 is never loaded
92
+ # directly, only inside the defaults tuple
93
+ co = compile_src("def f(c='WMAP5'):\n return c\n")
94
+ self.assertEqual(co.co_consts[0], "WMAP5")
95
+ self.assertIsNotNone(docstring_of_published(co))
96
+ self.assertIsNone(docstring_of(co))
97
+
98
+ def test_generator_expression_has_no_docstring(self):
99
+ src = "def f(xs):\n return tuple('a' for _ in xs)\n"
100
+ for q, co in by_qualname(src).items():
101
+ if co.co_name == "<genexpr>":
102
+ self.assertIsNone(docstring_of(co), q)
103
+ break
104
+ else:
105
+ self.skipTest("no genexpr code object on this interpreter")
106
+
107
+ def test_docstring_equal_to_a_body_literal_is_not_lost(self):
108
+ """CPython de-duplicates consts, so one slot serves both the docstring and the literal.
109
+
110
+ A rule that reasoned from "is slot 0 loaded" would drop this docstring -- reintroducing the
111
+ v1 information loss this module exists to undo. Scope kind gets it right.
112
+ """
113
+ src = "def f():\n 'pass'\n x = 'pass'\n return x\n"
114
+ co = by_qualname(src)["f"]
115
+ self.assertEqual(ast.get_docstring(ast.parse(src).body[0]), "pass")
116
+ self.assertEqual(co.co_consts, ("pass",))
117
+ self.assertEqual(docstring_of(co), "pass")
118
+
119
+ def test_lambda_has_no_docstring(self):
120
+ co = by_qualname("f = lambda: 'a'\n")["<lambda>"]
121
+ self.assertIsNone(docstring_of(co))
122
+
123
+
124
+ class RepWiring(unittest.TestCase):
125
+ def test_default_rule_is_the_published_one(self):
126
+ """Changing this default changes the public benchmark's input. It must be deliberate."""
127
+ co = compile_src("def f(xs):\n return sum(1 for x in xs if x != '=')\n")
128
+ self.assertIn("DOC '='", disassemble_v2(co))
129
+ self.assertIn("DOC '='", disassemble_v2(co, doc_rule="published"))
130
+ self.assertNotIn("DOC '='", disassemble_v2(co, doc_rule="fixed"))
131
+
132
+ def test_bad_rule_rejected(self):
133
+ with self.assertRaises(ValueError):
134
+ disassemble_v2(compile_src("x = 1\n"), doc_rule="v2")
135
+
136
+ def test_fixed_rule_propagates_into_nested_code_objects(self):
137
+ src = "class C:\n 'cdoc'\n def m(self):\n return 'lit'\n"
138
+ co = compile_src(src)
139
+ self.assertNotIn("DOC", disassemble_v2(co, doc_rule="fixed"))
140
+ self.assertIn("DOC", disassemble_v2(co, doc_rule="published"))
141
+
142
+
143
+ def ast_truth(src: str) -> dict[str, bool]:
144
+ """qualname -> has an implicit docstring, mirroring co_qualname. Ground truth."""
145
+ out: dict[str, bool] = {}
146
+
147
+ def rec(node, prefix):
148
+ for ch in ast.iter_child_nodes(node):
149
+ if isinstance(ch, (ast.FunctionDef, ast.AsyncFunctionDef)):
150
+ q = f"{prefix}{ch.name}"
151
+ out[q] = ast.get_docstring(ch) is not None
152
+ rec(ch, f"{q}.<locals>.")
153
+ elif isinstance(ch, ast.ClassDef):
154
+ q = f"{prefix}{ch.name}"
155
+ out[q] = ast.get_docstring(ch) is not None
156
+ rec(ch, f"{q}.")
157
+ else:
158
+ rec(ch, prefix)
159
+
160
+ tree = ast.parse(src)
161
+ out["<module>"] = ast.get_docstring(tree) is not None
162
+ rec(tree, "")
163
+ return out
164
+
165
+
166
+ def sweep_benchmarks() -> int:
167
+ """Exhaustive: every code object of both published benchmarks against ast.get_docstring."""
168
+ fails = 0
169
+ for name, d in (("csn-3.12-licensed", ROOT / "benchmarks" / "csn-3.12-licensed"),
170
+ ("mbpp-ood", ROOT / "benchmarks" / "mbpp-ood")):
171
+ if not (d / "bench.jsonl").exists():
172
+ print(f" SKIP {name} (not present)")
173
+ continue
174
+ rows = [json.loads(l) for l in (d / "bench.jsonl").read_text().splitlines() if l.strip()]
175
+ n = spurious_old = spurious_new = missed_new = 0
176
+ for r in rows:
177
+ src = (d / r["src_path"]).read_text()
178
+ truth = ast_truth(src)
179
+ co = marshal.loads((d / r["pyc_path"]).read_bytes()[16:])
180
+ for c in walk(co):
181
+ n += 1
182
+ real = truth.get(c.co_qualname) is True and bool(c.co_flags & CO_OPTIMIZED)
183
+ spurious_old += (docstring_of_published(c) is not None) and not real
184
+ got = docstring_of(c) is not None
185
+ spurious_new += got and not real
186
+ missed_new += real and not got
187
+ ok = spurious_new == 0 and missed_new == 0
188
+ print(f" {'OK ' if ok else 'FAIL'} {name}: {n} code objects | spurious DOC "
189
+ f"published={spurious_old} fixed={spurious_new} | real docstrings missed="
190
+ f"{missed_new}")
191
+ fails += 0 if ok else 1
192
+ return fails
193
+
194
+
195
+ if __name__ == "__main__":
196
+ if "--benchmarks" in sys.argv:
197
+ sys.argv.remove("--benchmarks")
198
+ print("=== exhaustive sweep of both published benchmarks ===")
199
+ rc = sweep_benchmarks()
200
+ print()
201
+ r = unittest.main(exit=False, verbosity=2).result
202
+ raise SystemExit(1 if rc or not r.wasSuccessful() else 0)
203
+ unittest.main()