Download tools/score.py from Pranavz/pylexnet: direct link, hf CLI and curl.
- Browser
- Download file 4.73 kB
-
https://huggingface.co/Pranavz/pylexnet/resolve/main/tools/score.py
- Command line
-
hf download hf://Pranavz/pylexnet/tools/score.py
-
curl -L -o score.py https://huggingface.co/Pranavz/pylexnet/resolve/main/tools/score.py
4.73 kB
| """Score ./pylexnet against Pygments' PythonLexer, per byte, on the dev split of the stdlib. PC only, one process. | |
| Pygments labels are cached in dev_cache/ after the first run. usage: score.py [dev] [report file]""" | |
| import hashlib | |
| import subprocess | |
| import sys | |
| from collections import Counter | |
| from pathlib import Path | |
| import numpy as np | |
| ROOT = Path("/usr/lib/python3.12") | |
| HERE = Path(__file__).resolve().parent | |
| CACHE = HERE / "dev_cache" | |
| NAMES = ["text", "keyword", "builtin", "string", "comment", "number", "func", "class", "decorator"] | |
| def files(split): | |
| out = [] | |
| for p in sorted(ROOT.rglob("*.py")): | |
| if "site-packages" in p.parts or "dist-packages" in p.parts or "lib2to3" in p.parts: | |
| continue | |
| if p.stat().st_size > 300_000: | |
| continue | |
| h = hashlib.sha256(str(p).encode()).digest()[0] | |
| if (h < 128) == (split == "dev"): | |
| out.append(p) | |
| return out | |
| def oracle(text): | |
| from pygments.lexers import PythonLexer | |
| from pygments.token import Token as T | |
| lex = PythonLexer(stripnl=False, ensurenl=False, stripall=False) | |
| out = bytearray() | |
| for _, tok, val in lex.get_tokens_unprocessed(text): | |
| if tok in T.Comment: c = 4 | |
| elif tok in T.Literal.String: c = 3 | |
| elif tok in T.Literal.Number: c = 5 | |
| elif tok in T.Keyword or tok in T.Operator.Word: c = 1 | |
| elif tok in T.Name.Decorator: c = 8 | |
| elif tok in T.Name.Function: c = 6 | |
| elif tok in T.Name.Class: c = 7 | |
| elif tok in T.Name.Builtin or tok in T.Name.Exception: c = 2 | |
| else: c = 0 | |
| out += bytes([c]) * len(val.encode("utf-8", "surrogateescape")) | |
| return out | |
| def build_cache(split): | |
| CACHE.mkdir(exist_ok=True) | |
| idx, data, gold = [], bytearray(), bytearray() | |
| for p in files(split): | |
| try: | |
| text = p.read_text("utf-8") | |
| except UnicodeDecodeError: | |
| continue | |
| text = text.replace("\r\n", "\n") | |
| d = text.encode() | |
| g = oracle(text) | |
| if len(g) != len(d): | |
| continue | |
| idx.append(f"{p}\t{len(data)}\t{len(d)}\n") | |
| data += d; gold += g | |
| (CACHE / f"{split}_data.bin").write_bytes(data) | |
| (CACHE / f"{split}_gold.bin").write_bytes(gold) | |
| (CACHE / f"{split}_index.tsv").write_text("".join(idx)) | |
| def main(): | |
| split = sys.argv[1] if len(sys.argv) > 1 else "dev" | |
| rep = open(sys.argv[2], "w") if len(sys.argv) > 2 else None | |
| if not (CACHE / f"{split}_index.tsv").exists(): | |
| build_cache(split) | |
| data = np.memmap(CACHE / f"{split}_data.bin", dtype=np.uint8, mode="r") | |
| gold = np.memmap(CACHE / f"{split}_gold.bin", dtype=np.uint8, mode="r") | |
| conf = np.zeros((9, 9), dtype=np.int64) | |
| exact = nfiles = 0 | |
| bytext = Counter(); sample = {} | |
| for line in open(CACHE / f"{split}_index.tsv"): | |
| path, off, n = line.rstrip("\n").split("\t") | |
| off, n = int(off), int(n) | |
| d = data[off:off + n].tobytes(); g = gold[off:off + n] | |
| out = subprocess.run([str(HERE / "pylexnet"), "--classes"], input=d, capture_output=True).stdout | |
| p = np.frombuffer(out, dtype=np.uint8) | |
| assert len(p) == n, (path, len(p), n) | |
| nfiles += 1 | |
| np.add.at(conf, (g, p), 1) | |
| bad = np.nonzero(g != p)[0] | |
| if not len(bad): | |
| exact += 1 | |
| continue | |
| s = e = int(bad[0]); runs = [] | |
| for i in map(int, bad[1:]): | |
| if i == e + 1 and g[i] == g[e] and p[i] == p[e]: | |
| e = i | |
| else: | |
| runs.append((s, e)); s = e = i | |
| runs.append((s, e)) | |
| for s, e in runs: | |
| k = (f"{NAMES[g[s]]}>{NAMES[p[s]]}", d[s:e + 1][:18].decode("utf-8", "replace")) | |
| bytext[k] += e - s + 1 | |
| if k not in sample: | |
| a = d.rfind(b"\n", 0, s) + 1; z = d.find(b"\n", e); z = n if z < 0 else z | |
| sample[k] = (Path(path).name, d[a:z][:110].decode("utf-8", "replace")) | |
| tot = conf.sum() | |
| print(f"{split}: {nfiles} files, {tot / 1e6:.1f} MB") | |
| print(f"byte accuracy {np.trace(conf) / tot:.5f}, exact files {exact}/{nfiles}, wrong bytes {tot - np.trace(conf)}") | |
| for k in range(9): | |
| pr = conf[k, k] / max(1, conf[:, k].sum()); rc = conf[k, k] / max(1, conf[k].sum()) | |
| print(f" {NAMES[k]:10} P {pr:.5f} R {rc:.5f} n {conf[k].sum()}") | |
| off = conf.copy(); np.fill_diagonal(off, 0) | |
| print("top confusions:", [(f"{NAMES[i // 9]}>{NAMES[i % 9]}", int(off.ravel()[i])) for i in np.argsort(off.ravel())[::-1][:8] if off.ravel()[i]]) | |
| for k, v in bytext.most_common(25 if rep is None else 80): | |
| print(f"{v:7} {k[0]} {k[1]!r} [{sample[k][0]}] {sample[k][1]!r}", file=rep or sys.stdout) | |
| if __name__ == "__main__": | |
| main() | |