fix(python): classify historical headers and signed flags

Fixes #6568, #6487
This commit is contained in:
morluto committed 2026-10-23 17:43:07 +08:00
1 parent d53584a10a
commit 81de82a1a4
9 files changed
+281 -27

No files matched your search

+12
View File
@@ -9,18 +9,30 @@ it("maps pinned CPython 3.7–3.13 magics and leaves gaps unknown", () => {
expect(pythonPycMagicEntry(3394)).toEqual({
python_version: "3.7",
code_layout: "py37",
header_bytes: 16,
});
expect(pythonPycMagicEntry(3390)).toMatchObject({
python_version: "3.7",
header_bytes: 12,
});
expect(pythonPycMagicEntry(3391)).toMatchObject({
python_version: "3.7",
header_bytes: 12,
});
expect(pythonPycMagicEntry(3413)).toEqual({
python_version: "3.8",
code_layout: "py38",
header_bytes: 16,
});
expect(pythonPycMagicEntry(3531)).toEqual({
python_version: "3.12",
code_layout: "py311",
header_bytes: 16,
});
expect(pythonPycMagicEntry(3571)).toEqual({
python_version: "3.13",
code_layout: "py311",
header_bytes: 16,
});
expect(pythonPycMagicEntry(3450)).toBeNull();
expect(pythonPycMagicEntry(62171)).toBeNull();
+13 -10
View File
@@ -9,8 +9,8 @@ export const PYTHON_PYC_CHECK_SOURCE = 0x2;
const uint = z.number().int().min(0).max(0xffffffff);
const index = z.number().int().nonnegative();
const signedInt32 = z.number().int().min(-0x80000000).max(0x7fffffff);
/** Non-negative CPython `int` counts. `0xffffffff` is -1 and is not evidence. */
const signedCount = z.number().int().min(0).max(0x7fffffff);
/** Non-negative CPython `int` counts. */
const nonNegativeInt32 = z.number().int().min(0).max(0x7fffffff);
/** Marshal TYPE_CODE field order for CPython 3.7, 3.8–3.10, and 3.11+. */
export const PYTHON_PYC_CODE_LAYOUTS = ["py37", "py38", "py311"] as const;
@@ -19,14 +19,16 @@ export type PythonPycCodeLayout = (typeof PYTHON_PYC_CODE_LAYOUTS)[number];
type MagicEntry = {
readonly python_version: string;
readonly code_layout: PythonPycCodeLayout;
readonly header_bytes: 12 | 16;
};
const magics = (
values: readonly number[],
python_version: string,
code_layout: PythonPycCodeLayout,
header_bytes: 12 | 16 = PYTHON_PYC_HEADER_BYTES,
): ReadonlyArray<readonly [number, MagicEntry]> =>
values.map((magic) => [magic, { python_version, code_layout }]);
values.map((magic) => [magic, { python_version, code_layout, header_bytes }]);
const consecutive = (
start: number,
@@ -46,7 +48,8 @@ const consecutive = (
* bytecode opcode tables.
*/
export const PYTHON_PYC_MAGIC_TABLE: ReadonlyMap<number, MagicEntry> = new Map([
...magics([3390, 3391, 3392, 3393, 3394], "3.7", "py37"),
...magics([3390, 3391], "3.7", "py37", 12),
...magics([3392, 3393, 3394], "3.7", "py37"),
...magics([3400, 3401], "3.8", "py37"),
...magics([3410, 3411, 3412, 3413], "3.8", "py38"),
...magics([3420, 3421, 3422, 3423, 3424, 3425], "3.9", "py38"),
@@ -159,12 +162,12 @@ const pythonCodeFields = {
qualname: z.string().nullable(),
filename: z.string(),
firstlineno: signedInt32,
argcount: signedCount,
posonlyargcount: signedCount.nullable(),
kwonlyargcount: signedCount,
nlocals: signedCount.nullable(),
stacksize: signedCount,
flags: uint,
argcount: nonNegativeInt32,
posonlyargcount: nonNegativeInt32.nullable(),
kwonlyargcount: nonNegativeInt32,
nlocals: nonNegativeInt32.nullable(),
stacksize: nonNegativeInt32,
flags: signedInt32,
names: z.array(z.string()),
varnames: z.array(z.string()).nullable(),
freevars: z.array(z.string()).nullable(),