mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
@@ -918,6 +918,68 @@ it("accepts code objects as set members and dictionary keys", () => {
|
||||
expect(pythonPycSchema.safeParse(report).success).toBe(true);
|
||||
});
|
||||
|
||||
it("uses the code equality fields for each CPython layout", () => {
|
||||
const codeCollections = (
|
||||
layout: "py38" | "py311",
|
||||
different: boolean,
|
||||
): [Buffer, Buffer] => {
|
||||
const lineA = marshalCode(layout, {
|
||||
name: "f",
|
||||
filename: "fixture.py",
|
||||
firstlineno: 1,
|
||||
linetable: Buffer.from([2, 1]),
|
||||
});
|
||||
const lineB = marshalCode(layout, {
|
||||
name: "f",
|
||||
filename: "fixture.py",
|
||||
firstlineno: 1,
|
||||
linetable: Buffer.from([2, different ? 2 : 1]),
|
||||
});
|
||||
const frozenSet = Buffer.concat([
|
||||
Buffer.from([0x3e]),
|
||||
u32(2),
|
||||
lineA,
|
||||
lineB,
|
||||
]);
|
||||
const dict = Buffer.concat([
|
||||
Buffer.from([0x7b]),
|
||||
lineA,
|
||||
Buffer.from([0x4e]),
|
||||
lineB,
|
||||
Buffer.from([0x54, 0x30]),
|
||||
]);
|
||||
return [frozenSet, dict];
|
||||
};
|
||||
for (const [magic, layout] of [
|
||||
[3413, "py38"],
|
||||
[3439, "py38"],
|
||||
] as const) {
|
||||
const [frozenSet, dict] = codeCollections(layout, true);
|
||||
const report = parse(
|
||||
Buffer.concat([
|
||||
pythonPycHeader(magic),
|
||||
marshalCode(layout, { consts: [frozenSet, dict] }),
|
||||
]),
|
||||
);
|
||||
expect(root(report).consts).toEqual([
|
||||
{ kind: "frozenset", length: 1 },
|
||||
{ kind: "dict", length: 1 },
|
||||
]);
|
||||
}
|
||||
|
||||
const [frozenSet, dict] = codeCollections("py311", true);
|
||||
const py312 = parse(
|
||||
Buffer.concat([
|
||||
pythonPycHeader(3531),
|
||||
marshalCode("py311", { consts: [frozenSet, dict] }),
|
||||
]),
|
||||
);
|
||||
expect(root(py312).consts).toEqual([
|
||||
{ kind: "frozenset", length: 2 },
|
||||
{ kind: "dict", length: 2 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps the first dictionary key when a later key compares equal", () => {
|
||||
const key = (filename: string): Buffer =>
|
||||
marshalCode("py311", {
|
||||
|
||||
@@ -1156,8 +1156,12 @@ class MarshalReader {
|
||||
`${node.bytecode.bytes}:${node.bytecode.sha256}`,
|
||||
this.#constKeyItems(material.consts),
|
||||
...nameTables,
|
||||
material.linetable,
|
||||
material.exceptiontable,
|
||||
// CPython started including line and exception tables in code equality
|
||||
// with the 3.11 code layout; earlier layouts still retain their digests
|
||||
// in CodeEquality, but those fields do not affect container identity.
|
||||
...(this.layout === "py311"
|
||||
? [material.linetable, material.exceptiontable]
|
||||
: []),
|
||||
];
|
||||
const key = this.#boundedJoin(
|
||||
"",
|
||||
|
||||
@@ -61,6 +61,62 @@ cliTest(
|
||||
},
|
||||
);
|
||||
|
||||
cliTest(
|
||||
"counts code keys using the selected CPython equality rules",
|
||||
async ({ cli }) => {
|
||||
const root = await createTestTempDirectory("rea-python-pyc-equality-");
|
||||
const cases = [
|
||||
{ magic: 3413, layout: "py38" as const, lengths: [1, 1] },
|
||||
{ magic: 3439, layout: "py38" as const, lengths: [1, 1] },
|
||||
{ magic: 3531, layout: "py311" as const, lengths: [2, 2] },
|
||||
];
|
||||
for (const [index, testCase] of cases.entries()) {
|
||||
const code = (linetable: Buffer) =>
|
||||
marshalCode(testCase.layout, {
|
||||
name: "f",
|
||||
filename: "fixture.py",
|
||||
firstlineno: 1,
|
||||
linetable,
|
||||
});
|
||||
const frozenSet = Buffer.concat([
|
||||
Buffer.from([0x3e]),
|
||||
Buffer.from([2, 0, 0, 0]),
|
||||
code(Buffer.from([2, 1])),
|
||||
code(Buffer.from([2, 2])),
|
||||
]);
|
||||
const dict = Buffer.concat([
|
||||
Buffer.from([0x7b]),
|
||||
code(Buffer.from([2, 1])),
|
||||
Buffer.from([0x4e]),
|
||||
code(Buffer.from([2, 2])),
|
||||
Buffer.from([0x54, 0x30]),
|
||||
]);
|
||||
const path = join(root, `case-${index}.pyc`);
|
||||
await writeFile(
|
||||
path,
|
||||
Buffer.concat([
|
||||
pythonPycHeader(testCase.magic),
|
||||
marshalCode(testCase.layout, { consts: [frozenSet, dict] }),
|
||||
]),
|
||||
);
|
||||
const result = await cli.run({
|
||||
arguments: ["inspect-python-pyc", path, "--json"],
|
||||
});
|
||||
expect(result.exitCode, result.stderr).toBe(0);
|
||||
const evidence = toolContract("inspect_python_pyc").outputSchema.parse(
|
||||
result.json,
|
||||
);
|
||||
expect(
|
||||
evidence.normalized_result.code_objects[0]?.consts.map((value) =>
|
||||
value.kind === "frozenset" || value.kind === "dict"
|
||||
? value.length
|
||||
: null,
|
||||
),
|
||||
).toEqual(testCase.lengths);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cliTest(
|
||||
"resolves a relative pyc path against the working directory",
|
||||
async ({ cli }) => {
|
||||
|
||||
Reference in New Issue
Block a user