depyo 1.0.1 → 1.0.3

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.
package/lib/PycReader.js CHANGED
@@ -93,14 +93,29 @@ const MagicToVersion = {
93
93
  0x0A0D0D33: {major: 3, minor: 6, IsUnicode: true, opcode: require('./bytecode/python_3_6')},
94
94
  0x0A0D0D41: {major: 3, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_3_7')},
95
95
  0x0A0D0D42: {major: 3, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_3_7')},
96
- 0x0A0D0D49: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
96
+ // Python 3.8a1 (3400) and 3.8a2 (3401) predate PEP 570 — no posonly field in marshal.
97
+ 0x0A0D0D48: {major: 3, minor: 8, preReleaseMarshal: true, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
98
+ 0x0A0D0D49: {major: 3, minor: 8, preReleaseMarshal: true, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
99
+ // 3.8a3 (3410) and later include posonly.
100
+ 0x0A0D0D52: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
101
+ 0x0A0D0D53: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
102
+ 0x0A0D0D54: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
97
103
  0x0A0D0D55: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
98
104
  0x0A0D0D61: {major: 3, minor: 9, IsUnicode: true, opcode: require('./bytecode/python_3_9')},
99
105
  0x0A0D0D6F: {major: 3, minor: 10, IsUnicode: true, opcode: require('./bytecode/python_3_10')},
100
106
  0x0A0D0DA7: {major: 3, minor: 11, IsUnicode: true, opcode: require('./bytecode/python_3_11')},
101
107
  0x0A0D0DCB: {major: 3, minor: 12, IsUnicode: true, opcode: require('./bytecode/python_3_12')},
102
108
  0x0A0D0DF3: {major: 3, minor: 13, IsUnicode: true, opcode: require('./bytecode/python_3_13')},
103
- 0x0A0D0E2B: {major: 3, minor: 14, IsUnicode: true, opcode: require('./bytecode/python_3_14')}
109
+ 0x0A0D0E2B: {major: 3, minor: 14, IsUnicode: true, opcode: require('./bytecode/python_3_14')},
110
+ 0x0A0D0E4F: {major: 3, minor: 15, IsUnicode: true, opcode: require('./bytecode/python_3_15')},
111
+
112
+ // PyPy magics (shares opcode tables with matching CPython release).
113
+ // PyPy adds a handful of custom opcodes — unknowns degrade decode rather than fail.
114
+ 0x0A0DF30A: {major: 2, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_2_7'), pypy: true},
115
+ 0x0A0D0030: {major: 3, minor: 2, IsUnicode: true, opcode: require('./bytecode/python_3_2'), pypy: true},
116
+ 0x0A0D0070: {major: 3, minor: 5, IsUnicode: true, opcode: require('./bytecode/python_3_5'), pypy: true},
117
+ 0x0A0D00A0: {major: 3, minor: 6, IsUnicode: true, opcode: require('./bytecode/python_3_6'), pypy: true},
118
+ 0x0A0D00F0: {major: 3, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_3_7'), pypy: true}
104
119
  };
105
120
 
106
121
  const VersionAliases = {
@@ -109,6 +124,9 @@ const VersionAliases = {
109
124
 
110
125
  const VersionToInfo = {};
111
126
  for (const [magic, info] of Object.entries(MagicToVersion)) {
127
+ if (info.pypy) {
128
+ continue;
129
+ }
112
130
  const key = `${info.major}.${info.minor}`;
113
131
  const existing = VersionToInfo[key];
114
132
  const candidate = {...info, magic: Number(magic)};
@@ -280,6 +298,34 @@ class PycReader
280
298
  return best ? best.candidate : null;
281
299
  }
282
300
 
301
+ static ScanMarshalCandidates(buffer) {
302
+ const candidates = PycReader.ListSupportedVersions(false);
303
+ const results = [];
304
+ for (const candidate of candidates) {
305
+ const trial = PycReader.TryParseMarshal(buffer, candidate);
306
+ if (!trial) {
307
+ continue;
308
+ }
309
+ results.push({...trial, versionInfo: candidate});
310
+ }
311
+ results.sort((a, b) => {
312
+ if (a.unknownRatio !== b.unknownRatio) {
313
+ return a.unknownRatio - b.unknownRatio;
314
+ }
315
+ if (a.remaining !== b.remaining) {
316
+ return a.remaining - b.remaining;
317
+ }
318
+ if (a.unknown !== b.unknown) {
319
+ return a.unknown - b.unknown;
320
+ }
321
+ if (a.versionInfo.major !== b.versionInfo.major) {
322
+ return a.versionInfo.major - b.versionInfo.major;
323
+ }
324
+ return a.versionInfo.minor - b.versionInfo.minor;
325
+ });
326
+ return results;
327
+ }
328
+
283
329
  static CountUnknownOpcodes(codeObject, reader, opCodeList) {
284
330
  const code = codeObject?.Code?.Value;
285
331
  if (!code || !opCodeList) {
@@ -356,7 +402,7 @@ class PycReader
356
402
  if (obj.Names?.ClassName === "Py_Tuple") {
357
403
  score += 1;
358
404
  }
359
- return {score, remaining, unknown, unknownRatio};
405
+ return {score, remaining, unknown, total, unknownRatio};
360
406
  } catch (ex) {
361
407
  return null;
362
408
  }
@@ -598,7 +644,8 @@ class PycReader
598
644
  }
599
645
  codeObject.ArgCount = argCount;
600
646
 
601
- codeObject.PosOnlyArgCount = this.versionCompare(3, 8) >= 0 ? this.m_rdr.readUInt32() : 0;
647
+ const hasPosOnly = this.versionCompare(3, 8) >= 0 && !this.m_version?.preReleaseMarshal;
648
+ codeObject.PosOnlyArgCount = hasPosOnly ? this.m_rdr.readUInt32() : 0;
602
649
  codeObject.KWOnlyArgCount = this.versionCompare(3, 0) >= 0 ? this.m_rdr.readUInt32() : 0;
603
650
 
604
651
  codeObject.NumLocals = 0;
@@ -48,6 +48,29 @@ class PythonObject {
48
48
 
49
49
  // TODO: Refactor to use Symbol.toPrimitive() and Object.valueOf()
50
50
 
51
+ toReprString() {
52
+ if (this.ClassName === "Py_String" || this.ClassName === "Py_Unicode") {
53
+ let raw = this.Value;
54
+ if (raw == null || raw.length === 0) return '""';
55
+ raw = raw.toString();
56
+ let escaped = raw
57
+ .replace(/\\/g, '\\\\')
58
+ .replace(/\n/g, '\\n')
59
+ .replace(/\r/g, '\\r')
60
+ .replace(/\t/g, '\\t');
61
+ let quote = '"';
62
+ if (escaped.includes('"')) {
63
+ if (!escaped.includes("'")) {
64
+ quote = "'";
65
+ } else {
66
+ escaped = escaped.replace(/"/g, '\\"');
67
+ }
68
+ }
69
+ return quote + escaped + quote;
70
+ }
71
+ return this.toString();
72
+ }
73
+
51
74
  toString()
52
75
  {
53
76
  switch(this.ClassName) {
@@ -74,8 +97,16 @@ class PythonObject {
74
97
  case "Py_Interned":
75
98
  return this.Value !== null ? this.Value.toString() : "0";
76
99
 
77
- case "Py_Float":
78
- return this.Value !== null ? `${this.Value}${Number.isInteger(this.Value) ? ".0" : ""}` : "0.0";
100
+ case "Py_Float": {
101
+ if (this.Value === null) return "0.0";
102
+ let s = `${this.Value}`;
103
+ // Python rejects `1e+300.0`; only append `.0` when the
104
+ // printed form is pure digits (optional sign).
105
+ if (/^-?\d+$/.test(s)) {
106
+ s += ".0";
107
+ }
108
+ return s;
109
+ }
79
110
 
80
111
  case "Py_VeryLong":
81
112
  if (this.Value) {
@@ -123,10 +154,11 @@ class PythonObject {
123
154
  let res = "(";
124
155
  if (this.Value) {
125
156
  for (let obj of this.Value) {
157
+ const part = obj instanceof PythonObject ? obj.toReprString() : String(obj);
126
158
  if (res != "(") {
127
- res += ", " + obj;
159
+ res += ", " + part;
128
160
  } else {
129
- res += obj;
161
+ res += part;
130
162
  }
131
163
  }
132
164
  res += ")";
@@ -144,8 +176,10 @@ class PythonObject {
144
176
  for (let pair of this.Value) {
145
177
  if (res != "(") {
146
178
  res += ", ";
147
- }
148
- res += pair.key + ": " + pair.value;
179
+ }
180
+ const kPart = pair.key instanceof PythonObject ? pair.key.toReprString() : String(pair.key);
181
+ const vPart = pair.value instanceof PythonObject ? pair.value.toReprString() : String(pair.value);
182
+ res += kPart + ": " + vPart;
149
183
  }
150
184
  res += ")";
151
185
  return res;