python2ts 1.4.1 → 1.4.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.
@@ -314,15 +314,12 @@ var JS_RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
314
314
  "async",
315
315
  "await",
316
316
  "declare",
317
- "from",
318
- "get",
319
317
  "is",
320
318
  "module",
321
319
  "namespace",
322
- "of",
323
320
  "require",
324
- "set",
325
321
  "type"
322
+ // Note: 'get', 'set', 'from', 'of' are contextual keywords and valid as identifiers
326
323
  ]);
327
324
  function escapeReservedKeyword(name) {
328
325
  return JS_RESERVED_KEYWORDS.has(name) ? `_${name}` : name;
@@ -336,6 +333,7 @@ function createContext(source) {
336
333
  // Start with one global scope
337
334
  definedClasses: /* @__PURE__ */ new Set(),
338
335
  insideFunctionBody: 0,
336
+ insideTryBlock: 0,
339
337
  hoistedImports: []
340
338
  };
341
339
  }
@@ -1022,7 +1020,7 @@ function transformAssignStatement(node, ctx) {
1022
1020
  }
1023
1021
  return `${targetCode} = ${valueCode}`;
1024
1022
  } else {
1025
- const valuesCodes = values.map((v) => transformNode(v, ctx));
1023
+ const valuesCodes = transformValuesWithSpread(values, ctx);
1026
1024
  if (needsDeclaration) {
1027
1025
  return `${declarationKeyword} ${targetCode}${typeAnnotation} = [${valuesCodes.join(", ")}]`;
1028
1026
  }
@@ -1044,10 +1042,32 @@ function transformAssignStatement(node, ctx) {
1044
1042
  const valueCode = transformNode(value, ctx);
1045
1043
  return allDeclaredAtAccessibleScope ? `${targetPattern} = ${valueCode}` : `let ${targetPattern} = ${valueCode}`;
1046
1044
  } else {
1047
- const valuesCodes = values.map((v) => transformNode(v, ctx));
1045
+ const valuesCodes = transformValuesWithSpread(values, ctx);
1048
1046
  return allDeclaredAtAccessibleScope ? `${targetPattern} = [${valuesCodes.join(", ")}]` : `let ${targetPattern} = [${valuesCodes.join(", ")}]`;
1049
1047
  }
1050
1048
  }
1049
+ function transformValuesWithSpread(values, ctx) {
1050
+ const result = [];
1051
+ let i = 0;
1052
+ while (i < values.length) {
1053
+ const value = values[i];
1054
+ if (!value) {
1055
+ i++;
1056
+ continue;
1057
+ }
1058
+ if (value.name === "*" || getNodeText(value, ctx.source) === "*") {
1059
+ const nextValue = values[i + 1];
1060
+ if (nextValue) {
1061
+ result.push(`...${transformNode(nextValue, ctx)}`);
1062
+ i += 2;
1063
+ continue;
1064
+ }
1065
+ }
1066
+ result.push(transformNode(value, ctx));
1067
+ i++;
1068
+ }
1069
+ return result;
1070
+ }
1051
1071
  function extractVariableNames(nodes, source) {
1052
1072
  const names = [];
1053
1073
  for (const node of nodes) {
@@ -1095,46 +1115,80 @@ function transformSliceAssignment(target, values, ctx) {
1095
1115
  const bracketEnd = children.findIndex((c) => c.name === "]");
1096
1116
  if (bracketStart === -1 || bracketEnd === -1) return `/* slice assignment error */`;
1097
1117
  const sliceParts = children.slice(bracketStart + 1, bracketEnd);
1098
- let start;
1099
- let end;
1100
- let step;
1118
+ const hasComma = sliceParts.some((p) => p.name === ",");
1119
+ const firstValue = values[0];
1120
+ const valuesCode = values.length === 1 && firstValue ? transformNode(firstValue, ctx) : `[${values.map((v) => transformNode(v, ctx)).join(", ")}]`;
1121
+ if (hasComma) {
1122
+ const dimensions = [];
1123
+ let currentDim = [];
1124
+ for (const part of sliceParts) {
1125
+ if (part.name === ",") {
1126
+ dimensions.push(currentDim);
1127
+ currentDim = [];
1128
+ } else {
1129
+ currentDim.push(part);
1130
+ }
1131
+ }
1132
+ dimensions.push(currentDim);
1133
+ const dimCodes = dimensions.map((dim) => {
1134
+ const hasColon = dim.some((p) => p.name === ":");
1135
+ if (hasColon) {
1136
+ const { start: start2, end: end2, step: step2 } = parseSliceDimension(dim, ctx);
1137
+ return `ndarray.slice(${start2 ?? "undefined"}, ${end2 ?? "undefined"}${step2 ? `, ${step2}` : ""})`;
1138
+ } else {
1139
+ const indexParts = dim.filter((p) => p.name !== ":" && p.name !== ",");
1140
+ if (indexParts.length > 0 && indexParts[0]) {
1141
+ return transformNode(indexParts[0], ctx);
1142
+ }
1143
+ return "undefined";
1144
+ }
1145
+ });
1146
+ ctx.usesRuntime.add("ndarray.set");
1147
+ return `ndarray.set(${objCode}, [${dimCodes.join(", ")}], ${valuesCode})`;
1148
+ }
1149
+ const { start, end, step } = parseSliceDimension(sliceParts, ctx);
1150
+ ctx.usesRuntime.add("list.sliceAssign");
1151
+ return `list.sliceAssign(${objCode}, ${start ?? "undefined"}, ${end ?? "undefined"}, ${step ?? "undefined"}, ${valuesCode})`;
1152
+ }
1153
+ function parseSliceDimension(parts, ctx) {
1101
1154
  const colonIndices = [];
1102
- for (let i = 0; i < sliceParts.length; i++) {
1103
- if (sliceParts[i]?.name === ":") {
1155
+ for (let i = 0; i < parts.length; i++) {
1156
+ if (parts[i]?.name === ":") {
1104
1157
  colonIndices.push(i);
1105
1158
  }
1106
1159
  }
1107
- if (colonIndices.length >= 1) {
1108
- const beforeFirst = sliceParts.slice(0, colonIndices[0]);
1109
- if (beforeFirst.length > 0 && beforeFirst[0]?.name !== ":") {
1110
- start = beforeFirst.map((n) => transformNode(n, ctx)).join("");
1160
+ if (colonIndices.length === 0) {
1161
+ return {};
1162
+ }
1163
+ let start;
1164
+ let end;
1165
+ let step;
1166
+ const firstColon = colonIndices[0] ?? 0;
1167
+ const beforeFirst = parts.slice(0, firstColon);
1168
+ if (beforeFirst.length > 0 && beforeFirst[0]?.name !== ":") {
1169
+ start = beforeFirst.map((n) => transformNode(n, ctx)).join("");
1170
+ }
1171
+ const secondColon = colonIndices[1];
1172
+ if (colonIndices.length === 1) {
1173
+ const afterFirst = parts.slice(firstColon + 1);
1174
+ if (afterFirst.length > 0) {
1175
+ end = afterFirst.map((n) => transformNode(n, ctx)).join("");
1111
1176
  }
1112
- const firstColon = colonIndices[0] ?? 0;
1113
- const secondColon = colonIndices[1];
1114
- if (colonIndices.length === 1) {
1115
- const afterFirst = sliceParts.slice(firstColon + 1);
1116
- if (afterFirst.length > 0) {
1117
- end = afterFirst.map((n) => transformNode(n, ctx)).join("");
1118
- }
1119
- } else if (secondColon !== void 0) {
1120
- const betweenColons = sliceParts.slice(firstColon + 1, secondColon);
1121
- if (betweenColons.length > 0) {
1122
- end = betweenColons.map((n) => transformNode(n, ctx)).join("");
1123
- }
1124
- const afterSecond = sliceParts.slice(secondColon + 1);
1125
- if (afterSecond.length > 0) {
1126
- step = afterSecond.map((n) => transformNode(n, ctx)).join("");
1127
- }
1177
+ } else if (secondColon !== void 0) {
1178
+ const betweenColons = parts.slice(firstColon + 1, secondColon);
1179
+ if (betweenColons.length > 0) {
1180
+ end = betweenColons.map((n) => transformNode(n, ctx)).join("");
1181
+ }
1182
+ const afterSecond = parts.slice(secondColon + 1);
1183
+ if (afterSecond.length > 0) {
1184
+ step = afterSecond.map((n) => transformNode(n, ctx)).join("");
1128
1185
  }
1129
1186
  }
1130
- const firstValue = values[0];
1131
- const valuesCode = values.length === 1 && firstValue ? transformNode(firstValue, ctx) : `[${values.map((v) => transformNode(v, ctx)).join(", ")}]`;
1132
- ctx.usesRuntime.add("list.sliceAssign");
1133
- return `list.sliceAssign(${objCode}, ${start ?? "undefined"}, ${end ?? "undefined"}, ${step ?? "undefined"}, ${valuesCode})`;
1187
+ return { start, end, step };
1134
1188
  }
1135
1189
  function transformAssignTarget(node, ctx) {
1136
1190
  if (node.name === "VariableName") {
1137
- return getNodeText(node, ctx.source);
1191
+ return escapeReservedKeyword(getNodeText(node, ctx.source));
1138
1192
  } else if (node.name === "TupleExpression") {
1139
1193
  const children = getChildren(node);
1140
1194
  const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
@@ -1149,6 +1203,20 @@ function transformBinaryExpression(node, ctx) {
1149
1203
  const op = children[1];
1150
1204
  if (!left || !op) return getNodeText(node, ctx.source);
1151
1205
  const opText = getNodeText(op, ctx.source);
1206
+ if ((opText === "-" || opText === "+") && left.name === "MemberExpression") {
1207
+ const memberChildren = getChildren(left);
1208
+ const numNode = memberChildren.find((c) => c.name === "Number");
1209
+ const propNode = memberChildren.find((c) => c.name === "PropertyName");
1210
+ const right2 = children[2];
1211
+ if (numNode && propNode && right2?.name === "Number") {
1212
+ const propName = getNodeText(propNode, ctx.source).toLowerCase();
1213
+ if (propName === "e") {
1214
+ const numText = getNodeText(numNode, ctx.source);
1215
+ const expText = getNodeText(right2, ctx.source);
1216
+ return `${numText}e${opText}${expText}`;
1217
+ }
1218
+ }
1219
+ }
1152
1220
  if ((opText === "is" || opText === "not") && children.length >= 4) {
1153
1221
  const secondOp = children[2];
1154
1222
  const secondOpText = secondOp ? getNodeText(secondOp, ctx.source) : "";
@@ -1281,7 +1349,9 @@ function transformNamedExpression(node, ctx) {
1281
1349
  }
1282
1350
  function transformConditionalExpression(node, ctx) {
1283
1351
  const children = getChildren(node);
1284
- const exprs = children.filter((c) => c.name !== "if" && c.name !== "else" && c.name !== "Keyword");
1352
+ const exprs = children.filter(
1353
+ (c) => c.name !== "if" && c.name !== "else" && c.name !== "Keyword" && c.name !== "Comment"
1354
+ );
1285
1355
  if (exprs.length >= 3) {
1286
1356
  const trueExpr = exprs[0];
1287
1357
  const condition = exprs[1];
@@ -1301,12 +1371,36 @@ function transformNumber(node, ctx) {
1301
1371
  }
1302
1372
  function transformString(node, ctx) {
1303
1373
  const text = getNodeText(node, ctx.source);
1374
+ if (text.startsWith('b"') || text.startsWith("b'")) {
1375
+ return text.slice(1);
1376
+ }
1377
+ if (text.startsWith('B"') || text.startsWith("B'")) {
1378
+ return text.slice(1);
1379
+ }
1380
+ if (text.startsWith('br"') || text.startsWith("br'") || text.startsWith('rb"') || text.startsWith("rb'")) {
1381
+ return text.slice(2);
1382
+ }
1383
+ if (text.startsWith('BR"') || text.startsWith("BR'") || text.startsWith('RB"') || text.startsWith("RB'")) {
1384
+ return text.slice(2);
1385
+ }
1386
+ if (text.startsWith('Br"') || text.startsWith("Br'") || text.startsWith('rB"') || text.startsWith("rB'")) {
1387
+ return text.slice(2);
1388
+ }
1389
+ if (text.startsWith('bR"') || text.startsWith("bR'") || text.startsWith('Rb"') || text.startsWith("Rb'")) {
1390
+ return text.slice(2);
1391
+ }
1304
1392
  if (text.startsWith('r"') || text.startsWith("r'")) {
1305
1393
  return text.slice(1);
1306
1394
  }
1307
1395
  if (text.startsWith('R"') || text.startsWith("R'")) {
1308
1396
  return text.slice(1);
1309
1397
  }
1398
+ if (text.startsWith('u"') || text.startsWith("u'")) {
1399
+ return text.slice(1);
1400
+ }
1401
+ if (text.startsWith('U"') || text.startsWith("U'")) {
1402
+ return text.slice(1);
1403
+ }
1310
1404
  if (text.startsWith('"""') || text.startsWith("'''")) {
1311
1405
  const content = text.slice(3, -3);
1312
1406
  return "`" + content.replace(/`/g, "\\`") + "`";
@@ -1337,18 +1431,22 @@ function transformFormatString(node, ctx) {
1337
1431
  let expr;
1338
1432
  let formatSpec;
1339
1433
  let conversion;
1434
+ let selfDoc = false;
1340
1435
  for (const child of replChildren) {
1341
1436
  if (child.name === "{" || child.name === "}") continue;
1342
1437
  if (child.name === "FormatSpec") {
1343
1438
  formatSpec = getNodeText(child, ctx.source).slice(1);
1344
1439
  } else if (child.name === "FormatConversion") {
1345
1440
  conversion = getNodeText(child, ctx.source).slice(1);
1441
+ } else if (child.name === "FormatSelfDoc") {
1442
+ selfDoc = true;
1346
1443
  } else {
1347
1444
  expr = child;
1348
1445
  }
1349
1446
  }
1350
1447
  if (expr) {
1351
1448
  let exprCode = transformNode(expr, ctx);
1449
+ const exprText = getNodeText(expr, ctx.source);
1352
1450
  if (conversion === "r") {
1353
1451
  exprCode = `repr(${exprCode})`;
1354
1452
  ctx.usesRuntime.add("repr");
@@ -1361,7 +1459,13 @@ function transformFormatString(node, ctx) {
1361
1459
  }
1362
1460
  if (formatSpec) {
1363
1461
  ctx.usesRuntime.add("format");
1364
- result += `\${format(${exprCode}, "${formatSpec}")}`;
1462
+ if (selfDoc) {
1463
+ result += `${exprText}=\${format(${exprCode}, "${formatSpec}")}`;
1464
+ } else {
1465
+ result += `\${format(${exprCode}, "${formatSpec}")}`;
1466
+ }
1467
+ } else if (selfDoc) {
1468
+ result += `${exprText}=\${${exprCode}}`;
1365
1469
  } else {
1366
1470
  result += `\${${exprCode}}`;
1367
1471
  }
@@ -2110,12 +2214,16 @@ function transformMethodCall(callee, args, ctx) {
2110
2214
  ctx.usesRuntime.add("set");
2111
2215
  return `set.issuperset(${objCode}, ${args})`;
2112
2216
  // Hash object methods (hashlib) - async
2217
+ // Skip if objCode is 'self' or 'this' - user-defined class, not a Hash object
2113
2218
  case "digest":
2219
+ if (objCode === "self" || objCode === "this") return null;
2114
2220
  return `await ${objCode}.digest()`;
2115
2221
  case "hexdigest":
2222
+ if (objCode === "self" || objCode === "this") return null;
2116
2223
  return `await ${objCode}.hexdigest()`;
2117
2224
  // Path object methods (pathlib) - async
2118
2225
  // Only handle snake_case methods that are unique to Path
2226
+ // Skip if objCode is 'self' or 'this' - user-defined class, not a Path object
2119
2227
  case "is_file":
2120
2228
  case "is_dir":
2121
2229
  case "is_symlink":
@@ -2126,6 +2234,9 @@ function transformMethodCall(callee, args, ctx) {
2126
2234
  case "symlink_to":
2127
2235
  case "link_to":
2128
2236
  case "iterdir": {
2237
+ if (objCode === "self" || objCode === "this") {
2238
+ return null;
2239
+ }
2129
2240
  const jsMethod = toJsName(methodName);
2130
2241
  return `await ${objCode}.${jsMethod}(${args})`;
2131
2242
  }
@@ -2136,7 +2247,9 @@ function transformMethodCall(callee, args, ctx) {
2136
2247
  }
2137
2248
  function transformArgList(node, ctx) {
2138
2249
  const children = getChildren(node);
2139
- const items = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== "ArgList");
2250
+ const items = children.filter(
2251
+ (c) => c.name !== "(" && c.name !== ")" && c.name !== "ArgList" && c.name !== "Comment"
2252
+ );
2140
2253
  const hasForKeyword = items.some(
2141
2254
  (c) => c.name === "for" || c.name === "Keyword" && getNodeText(c, ctx.source) === "for"
2142
2255
  );
@@ -2273,11 +2386,10 @@ function transformMemberExpression(node, ctx) {
2273
2386
  const objCode = transformNode(obj, ctx);
2274
2387
  const attrMap = {
2275
2388
  __name__: "name",
2276
- __doc__: "undefined",
2277
- // JS functions don't have docstrings
2278
2389
  __class__: "constructor",
2279
2390
  __dict__: "this"
2280
2391
  // Rough equivalent
2392
+ // Note: __doc__ is kept as-is since it's a valid JS property name
2281
2393
  };
2282
2394
  const mappedProp = attrMap[propName] ?? propName;
2283
2395
  return `${objCode}.${mappedProp}`;
@@ -2292,14 +2404,42 @@ function transformSliceFromMember(obj, children, ctx) {
2292
2404
  return `slice(${objCode})`;
2293
2405
  }
2294
2406
  const sliceElements = children.slice(bracketStart + 1, bracketEnd);
2407
+ const hasComma = sliceElements.some((el) => el.name === ",");
2408
+ if (hasComma) {
2409
+ const dimensions = [];
2410
+ let currentDim = [];
2411
+ for (const el of sliceElements) {
2412
+ if (el.name === ",") {
2413
+ dimensions.push(currentDim);
2414
+ currentDim = [];
2415
+ } else {
2416
+ currentDim.push(el);
2417
+ }
2418
+ }
2419
+ dimensions.push(currentDim);
2420
+ const dimSlices = dimensions.map((dimElements) => {
2421
+ return parseSliceDimensionForSubscript(dimElements, ctx);
2422
+ });
2423
+ return `slice(${objCode}, ${dimSlices.join(", ")})`;
2424
+ }
2425
+ const parts = parseSliceParts(sliceElements, ctx);
2426
+ return `slice(${objCode}, ${parts.join(", ")})`;
2427
+ }
2428
+ function parseSliceParts(elements, ctx) {
2295
2429
  const colonIndices = [];
2296
- sliceElements.forEach((el, i) => {
2430
+ elements.forEach((el, i) => {
2297
2431
  if (el.name === ":") colonIndices.push(i);
2298
2432
  });
2433
+ if (colonIndices.length === 0) {
2434
+ if (elements.length > 0 && elements[0]) {
2435
+ return [transformNode(elements[0], ctx)];
2436
+ }
2437
+ return ["undefined"];
2438
+ }
2299
2439
  const parts = [];
2300
2440
  let lastIdx = 0;
2301
2441
  for (const colonIdx of colonIndices) {
2302
- const beforeColon = sliceElements.slice(lastIdx, colonIdx);
2442
+ const beforeColon = elements.slice(lastIdx, colonIdx);
2303
2443
  if (beforeColon.length > 0 && beforeColon[0]) {
2304
2444
  parts.push(transformNode(beforeColon[0], ctx));
2305
2445
  } else {
@@ -2307,27 +2447,52 @@ function transformSliceFromMember(obj, children, ctx) {
2307
2447
  }
2308
2448
  lastIdx = colonIdx + 1;
2309
2449
  }
2310
- const afterLastColon = sliceElements.slice(lastIdx);
2450
+ const afterLastColon = elements.slice(lastIdx);
2311
2451
  if (afterLastColon.length > 0 && afterLastColon[0] && afterLastColon[0].name !== ":") {
2312
2452
  parts.push(transformNode(afterLastColon[0], ctx));
2313
2453
  } else if (colonIndices.length > 0) {
2314
2454
  parts.push("undefined");
2315
2455
  }
2316
- if (colonIndices.length === 0) {
2317
- return `slice(${objCode})`;
2456
+ return parts;
2457
+ }
2458
+ function parseSliceDimensionForSubscript(elements, ctx) {
2459
+ const parts = parseSliceParts(elements, ctx);
2460
+ if (parts.length === 1) {
2461
+ return parts[0] ?? "undefined";
2318
2462
  }
2319
- return `slice(${objCode}, ${parts.join(", ")})`;
2463
+ return `[${parts.join(", ")}]`;
2320
2464
  }
2321
2465
  function transformArrayExpression(node, ctx) {
2322
2466
  const children = getChildren(node);
2323
- const elements = children.filter((c) => c.name !== "[" && c.name !== "]" && c.name !== ",");
2324
- const elementCodes = elements.map((el) => transformNode(el, ctx));
2467
+ const elementCodes = [];
2468
+ let i = 0;
2469
+ while (i < children.length) {
2470
+ const child = children[i];
2471
+ if (!child) {
2472
+ i++;
2473
+ continue;
2474
+ }
2475
+ if (child.name === "[" || child.name === "]" || child.name === "," || child.name === "Comment") {
2476
+ i++;
2477
+ continue;
2478
+ }
2479
+ if (child.name === "*" || getNodeText(child, ctx.source) === "*") {
2480
+ const nextChild = children[i + 1];
2481
+ if (nextChild && nextChild.name !== "," && nextChild.name !== "]") {
2482
+ elementCodes.push(`...${transformNode(nextChild, ctx)}`);
2483
+ i += 2;
2484
+ continue;
2485
+ }
2486
+ }
2487
+ elementCodes.push(transformNode(child, ctx));
2488
+ i++;
2489
+ }
2325
2490
  return `[${elementCodes.join(", ")}]`;
2326
2491
  }
2327
2492
  function transformDictionaryExpression(node, ctx) {
2328
2493
  const children = getChildren(node);
2329
2494
  const items = children.filter(
2330
- (c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== ":"
2495
+ (c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== ":" && c.name !== "Comment"
2331
2496
  );
2332
2497
  const keyNodes = [];
2333
2498
  for (let i = 0; i < items.length; i += 2) {
@@ -2368,9 +2533,35 @@ function transformDictionaryExpression(node, ctx) {
2368
2533
  }
2369
2534
  function transformTupleExpression(node, ctx) {
2370
2535
  const children = getChildren(node);
2371
- const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
2536
+ const elementCodes = [];
2537
+ let hasSpread = false;
2538
+ let i = 0;
2539
+ while (i < children.length) {
2540
+ const child = children[i];
2541
+ if (!child) {
2542
+ i++;
2543
+ continue;
2544
+ }
2545
+ if (child.name === "(" || child.name === ")" || child.name === "," || child.name === "Comment") {
2546
+ i++;
2547
+ continue;
2548
+ }
2549
+ if (child.name === "*" || getNodeText(child, ctx.source) === "*") {
2550
+ const nextChild = children[i + 1];
2551
+ if (nextChild && nextChild.name !== "," && nextChild.name !== ")") {
2552
+ hasSpread = true;
2553
+ elementCodes.push(`...${transformNode(nextChild, ctx)}`);
2554
+ i += 2;
2555
+ continue;
2556
+ }
2557
+ }
2558
+ elementCodes.push(transformNode(child, ctx));
2559
+ i++;
2560
+ }
2561
+ if (hasSpread) {
2562
+ return `[${elementCodes.join(", ")}]`;
2563
+ }
2372
2564
  ctx.usesRuntime.add("tuple");
2373
- const elementCodes = elements.map((el) => transformNode(el, ctx));
2374
2565
  return `tuple(${elementCodes.join(", ")})`;
2375
2566
  }
2376
2567
  function isNegativeIndexLiteral(node, ctx) {
@@ -2807,10 +2998,23 @@ function transformForStatement(node, ctx) {
2807
2998
  }
2808
2999
  let varCode;
2809
3000
  if (varNodes.length === 1 && varNodes[0]) {
2810
- varCode = transformNode(varNodes[0], ctx);
3001
+ const singleVar = varNodes[0];
3002
+ if (singleVar.name === "TupleExpression") {
3003
+ varCode = transformForLoopVar(singleVar, ctx);
3004
+ } else {
3005
+ varCode = transformNode(singleVar, ctx);
3006
+ }
2811
3007
  } else {
2812
3008
  varCode = "[" + varNodes.map((v) => transformForLoopVar(v, ctx)).join(", ") + "]";
2813
3009
  }
3010
+ const forLoopVarNames = extractForLoopVarNames(varNodes, ctx.source);
3011
+ for (const varName of forLoopVarNames) {
3012
+ declareVariable(ctx, varName);
3013
+ const escapedName = escapeReservedKeyword(varName);
3014
+ if (escapedName !== varName) {
3015
+ declareVariable(ctx, escapedName);
3016
+ }
3017
+ }
2814
3018
  let iterableCode = transformNode(iterableNode, ctx);
2815
3019
  const bodyCode = transformBody(bodyNode, ctx);
2816
3020
  if (iterableNode.name === "VariableName" && !isAsync) {
@@ -2824,7 +3028,7 @@ ${bodyCode}
2824
3028
  }
2825
3029
  function transformForLoopVar(node, ctx) {
2826
3030
  if (node.name === "VariableName") {
2827
- return getNodeText(node, ctx.source);
3031
+ return escapeReservedKeyword(getNodeText(node, ctx.source));
2828
3032
  } else if (node.name === "TupleExpression") {
2829
3033
  const children = getChildren(node);
2830
3034
  const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
@@ -2832,6 +3036,19 @@ function transformForLoopVar(node, ctx) {
2832
3036
  }
2833
3037
  return transformNode(node, ctx);
2834
3038
  }
3039
+ function extractForLoopVarNames(varNodes, source) {
3040
+ const names = [];
3041
+ for (const node of varNodes) {
3042
+ if (node.name === "VariableName") {
3043
+ names.push(getNodeText(node, source));
3044
+ } else if (node.name === "TupleExpression") {
3045
+ const children = getChildren(node);
3046
+ const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
3047
+ names.push(...extractForLoopVarNames(elements, source));
3048
+ }
3049
+ }
3050
+ return names;
3051
+ }
2835
3052
  function transformTryStatement(node, ctx) {
2836
3053
  const children = getChildren(node);
2837
3054
  const baseIndent = " ".repeat(ctx.indentLevel);
@@ -2896,7 +3113,9 @@ function transformTryStatement(node, ctx) {
2896
3113
  if (!tryBody) {
2897
3114
  return getNodeText(node, ctx.source);
2898
3115
  }
3116
+ ctx.insideTryBlock++;
2899
3117
  const tryCode = transformBody(tryBody, ctx);
3118
+ ctx.insideTryBlock--;
2900
3119
  let result = `try {
2901
3120
  ${tryCode}
2902
3121
  ${baseIndent}}`;
@@ -2988,7 +3207,7 @@ function transformRaiseStatement(node, ctx) {
2988
3207
  const children = getChildren(node);
2989
3208
  const exprNode = children.find((c) => c.name !== "raise");
2990
3209
  if (!exprNode) {
2991
- return "throw";
3210
+ return "throw e";
2992
3211
  }
2993
3212
  const expr = transformNode(exprNode, ctx);
2994
3213
  if (exprNode.name === "CallExpression") {
@@ -3043,6 +3262,13 @@ function isExceptionType(name) {
3043
3262
  function transformImportStatement(node, ctx) {
3044
3263
  const children = getChildren(node);
3045
3264
  const hasFrom = children.some((c) => c.name === "from");
3265
+ if (ctx.insideTryBlock > 0) {
3266
+ if (hasFrom) {
3267
+ return transformFromImportDynamic(children, ctx);
3268
+ } else {
3269
+ return transformSimpleImportDynamic(children, ctx);
3270
+ }
3271
+ }
3046
3272
  let importCode;
3047
3273
  if (hasFrom) {
3048
3274
  importCode = transformFromImport(children, ctx);
@@ -3098,6 +3324,92 @@ function transformSimpleImport(children, ctx) {
3098
3324
  return `import * as ${importName} from "${module}"`;
3099
3325
  }).join("\n");
3100
3326
  }
3327
+ function transformSimpleImportDynamic(children, ctx) {
3328
+ const names = [];
3329
+ let i = 0;
3330
+ while (i < children.length) {
3331
+ const child = children[i];
3332
+ if (!child) {
3333
+ i++;
3334
+ continue;
3335
+ }
3336
+ if (child.name === "import" || child.name === ",") {
3337
+ i++;
3338
+ continue;
3339
+ }
3340
+ if (child.name === "VariableName") {
3341
+ const moduleName = getNodeText(child, ctx.source);
3342
+ let alias = null;
3343
+ const nextChild = children[i + 1];
3344
+ if (nextChild?.name === "as") {
3345
+ const aliasChild = children[i + 2];
3346
+ if (aliasChild?.name === "VariableName") {
3347
+ alias = getNodeText(aliasChild, ctx.source);
3348
+ i += 3;
3349
+ names.push({ module: moduleName, alias });
3350
+ continue;
3351
+ }
3352
+ }
3353
+ names.push({ module: moduleName, alias: null });
3354
+ i++;
3355
+ continue;
3356
+ }
3357
+ i++;
3358
+ }
3359
+ const filteredNames = names.filter(
3360
+ ({ module }) => !RUNTIME_MODULES.has(module) && !TYPING_MODULES.has(module)
3361
+ );
3362
+ if (filteredNames.length === 0) {
3363
+ return "";
3364
+ }
3365
+ return filteredNames.map(({ module, alias }) => {
3366
+ const importName = alias ?? module;
3367
+ return `const ${importName} = await import("${module}")`;
3368
+ }).join("\n");
3369
+ }
3370
+ function transformFromImportDynamic(children, ctx) {
3371
+ let moduleName = "";
3372
+ for (const child of children) {
3373
+ if (child.name === "VariableName") {
3374
+ const prevChild = children[children.indexOf(child) - 1];
3375
+ if (prevChild?.name === "from" || prevChild?.name === ".") {
3376
+ moduleName = getNodeText(child, ctx.source);
3377
+ break;
3378
+ }
3379
+ }
3380
+ }
3381
+ if (TYPING_MODULES.has(moduleName) || RUNTIME_MODULES.has(moduleName)) {
3382
+ return "";
3383
+ }
3384
+ const importedNames = [];
3385
+ let afterImport = false;
3386
+ for (let i = 0; i < children.length; i++) {
3387
+ const child = children[i];
3388
+ if (!child) continue;
3389
+ if (child.name === "import") {
3390
+ afterImport = true;
3391
+ continue;
3392
+ }
3393
+ if (afterImport && child.name === "VariableName") {
3394
+ const name = getNodeText(child, ctx.source);
3395
+ const nextChild = children[i + 1];
3396
+ if (nextChild?.name === "as") {
3397
+ const aliasChild = children[i + 2];
3398
+ if (aliasChild?.name === "VariableName") {
3399
+ importedNames.push({ name, alias: getNodeText(aliasChild, ctx.source) });
3400
+ i += 2;
3401
+ continue;
3402
+ }
3403
+ }
3404
+ importedNames.push({ name, alias: null });
3405
+ }
3406
+ }
3407
+ if (importedNames.length === 0) {
3408
+ return "";
3409
+ }
3410
+ const bindings = importedNames.map(({ name, alias }) => alias ? `${name}: ${alias}` : name).join(", ");
3411
+ return `const { ${bindings} } = await import("${moduleName}")`;
3412
+ }
3101
3413
  var TYPING_MODULES = /* @__PURE__ */ new Set([
3102
3414
  "typing",
3103
3415
  "typing_extensions",
@@ -3407,7 +3719,7 @@ function transformFunctionDefinition(node, ctx) {
3407
3719
  if (child.name === "async") {
3408
3720
  isAsync = true;
3409
3721
  } else if (child.name === "VariableName") {
3410
- funcName = getNodeText(child, ctx.source);
3722
+ funcName = escapeReservedKeyword(getNodeText(child, ctx.source));
3411
3723
  } else if (child.name === "ParamList") {
3412
3724
  paramList = child;
3413
3725
  } else if (child.name === "Body") {
@@ -3482,7 +3794,8 @@ function transformClassDefinition(node, ctx) {
3482
3794
  if (firstParent === "Protocol") {
3483
3795
  return transformProtocol(className, parentClasses, body, ctx);
3484
3796
  }
3485
- if (firstParent === "ABC" || parentClasses.includes("ABC")) {
3797
+ const isAbcClass = firstParent === "ABC" || firstParent === "abc.ABC" || parentClasses.includes("ABC") || parentClasses.includes("abc.ABC");
3798
+ if (isAbcClass) {
3486
3799
  return transformAbstractClass(className, parentClasses, body, ctx, jsdoc);
3487
3800
  }
3488
3801
  const genericParams = extractGenericParams(parentClasses);
@@ -3539,6 +3852,10 @@ function transformClassBody(node, ctx, skipFirst = false) {
3539
3852
  function transformClassProperty(node, ctx, indent) {
3540
3853
  const children = getChildren(node);
3541
3854
  if (children.length < 2) return "";
3855
+ const nodeText = getNodeText(node, ctx.source);
3856
+ if (nodeText.includes(".__doc__")) {
3857
+ return "";
3858
+ }
3542
3859
  const assignOpIndex = children.findIndex((c) => c.name === "AssignOp" || c.name === "=");
3543
3860
  const typeDef = children.find((c) => c.name === "TypeDef");
3544
3861
  if (assignOpIndex === -1) {
@@ -3684,6 +4001,8 @@ function transformMethodParamListWithTypes(node, ctx) {
3684
4001
  function transformMethodParamListImpl(node, ctx, includeTypes) {
3685
4002
  const children = getChildren(node);
3686
4003
  const params = [];
4004
+ let restParam = null;
4005
+ let kwargsParam = null;
3687
4006
  let i = 0;
3688
4007
  let isFirstParam = true;
3689
4008
  while (i < children.length) {
@@ -3708,8 +4027,8 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3708
4027
  if (child.name === "*" || getNodeText(child, ctx.source) === "*") {
3709
4028
  const nextChild = children[i + 1];
3710
4029
  if (nextChild?.name === "VariableName") {
3711
- const name = getNodeText(nextChild, ctx.source);
3712
- params.push(`...${name}`);
4030
+ const name = escapeReservedKeyword(getNodeText(nextChild, ctx.source));
4031
+ restParam = `...${name}`;
3713
4032
  i += 2;
3714
4033
  continue;
3715
4034
  }
@@ -3719,8 +4038,8 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3719
4038
  if (child.name === "**" || getNodeText(child, ctx.source) === "**") {
3720
4039
  const nextChild = children[i + 1];
3721
4040
  if (nextChild?.name === "VariableName") {
3722
- const name = getNodeText(nextChild, ctx.source);
3723
- params.push(name);
4041
+ const name = escapeReservedKeyword(getNodeText(nextChild, ctx.source));
4042
+ kwargsParam = `${name}: Record<string, unknown> = {}`;
3724
4043
  i += 2;
3725
4044
  continue;
3726
4045
  }
@@ -3728,7 +4047,7 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3728
4047
  continue;
3729
4048
  }
3730
4049
  if (child.name === "VariableName") {
3731
- const nameCode = getNodeText(child, ctx.source);
4050
+ const nameCode = escapeReservedKeyword(getNodeText(child, ctx.source));
3732
4051
  let typeStr = "";
3733
4052
  let defaultStr = "";
3734
4053
  let consumed = 1;
@@ -3758,6 +4077,12 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3758
4077
  }
3759
4078
  i++;
3760
4079
  }
4080
+ if (kwargsParam) {
4081
+ params.push(kwargsParam);
4082
+ }
4083
+ if (restParam) {
4084
+ params.push(restParam);
4085
+ }
3761
4086
  return params.join(", ");
3762
4087
  }
3763
4088
  function transformClassMethodBody(node, ctx, skipFirst = false, predeclaredVars = []) {
@@ -3930,7 +4255,7 @@ function transformDecoratedStatement(node, ctx) {
3930
4255
  let body = null;
3931
4256
  for (const child of funcChildren) {
3932
4257
  if (child.name === "VariableName") {
3933
- funcName = getNodeText(child, ctx.source);
4258
+ funcName = escapeReservedKeyword(getNodeText(child, ctx.source));
3934
4259
  } else if (child.name === "ParamList") {
3935
4260
  paramList = child;
3936
4261
  } else if (child.name === "Body") {
@@ -4382,7 +4707,7 @@ ${members.join("\n")}
4382
4707
  }`;
4383
4708
  }
4384
4709
  function transformAbstractClass(className, parentClasses, body, ctx, jsdoc) {
4385
- const filteredParents = parentClasses.filter((p) => p !== "ABC");
4710
+ const filteredParents = parentClasses.filter((p) => p !== "ABC" && p !== "abc.ABC");
4386
4711
  let classHeader = `abstract class ${className}`;
4387
4712
  const firstParent = filteredParents[0];
4388
4713
  if (firstParent) {
@@ -4734,9 +5059,9 @@ function parseComprehensionClauses(children, ctx) {
4734
5059
  let variable;
4735
5060
  const firstVarNode = varNodes[0];
4736
5061
  if (varNodes.length === 1 && firstVarNode) {
4737
- variable = transformNode(firstVarNode, ctx);
5062
+ variable = transformForLoopVar(firstVarNode, ctx);
4738
5063
  } else {
4739
- variable = "[" + varNodes.map((v) => transformNode(v, ctx)).join(", ") + "]";
5064
+ variable = "[" + varNodes.map((v) => transformForLoopVar(v, ctx)).join(", ") + "]";
4740
5065
  }
4741
5066
  clauses.push({
4742
5067
  type: "for",
@@ -4849,7 +5174,9 @@ function transformDictComprehension(node, ctx) {
4849
5174
  }
4850
5175
  function transformSetExpression(node, ctx) {
4851
5176
  const children = getChildren(node);
4852
- const elements = children.filter((c) => c.name !== "{" && c.name !== "}" && c.name !== ",");
5177
+ const elements = children.filter(
5178
+ (c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== "Comment"
5179
+ );
4853
5180
  ctx.usesRuntime.add("set");
4854
5181
  const elementCodes = elements.map((el) => transformNode(el, ctx));
4855
5182
  return `set([${elementCodes.join(", ")}])`;
@@ -5131,7 +5458,7 @@ var BUILTINS = /* @__PURE__ */ new Set([
5131
5458
  "input",
5132
5459
  "format"
5133
5460
  ]);
5134
- var MODULE_NAMESPACES = /* @__PURE__ */ new Set(["string", "list", "dict", "set"]);
5461
+ var MODULE_NAMESPACES = /* @__PURE__ */ new Set(["string", "list", "dict", "set", "ndarray"]);
5135
5462
  function generate(input, options = {}) {
5136
5463
  const opts = { ...defaultOptions, ...options };
5137
5464
  const result = transform(input);
@@ -5320,7 +5647,6 @@ export {
5320
5647
  /* v8 ignore next -- defensive: checked exprs.length >= 3 above @preserve */
5321
5648
  /* v8 ignore next 4 -- defensive: items from parser are never null @preserve */
5322
5649
  /* v8 ignore next 3 -- defensive: subscript always has brackets @preserve */
5323
- /* v8 ignore next 3 -- defensive: slice detection already checked for colons @preserve */
5324
5650
  /* v8 ignore next 2 -- del obj.attr edge case @preserve */
5325
5651
  /* v8 ignore next -- fallback for complex del targets @preserve */
5326
5652
  /* v8 ignore next -- bare yield statement @preserve */
package/dist/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  transpileAsync
4
- } from "../chunk-R57A2BPG.js";
4
+ } from "../chunk-E5LEIMJI.js";
5
5
 
6
6
  // src/cli/index.ts
7
7
  import { parseArgs } from "util";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Tree, SyntaxNode } from '@lezer/common';
1
+ import { SyntaxNode, Tree } from '@lezer/common';
2
2
 
3
3
  interface ParseResult {
4
4
  tree: Tree;
@@ -82,6 +82,8 @@ interface TransformContext {
82
82
  isAbstractClass?: boolean;
83
83
  /** Depth counter for function bodies (0 = module level, 1+ = inside function) */
84
84
  insideFunctionBody: number;
85
+ /** Depth counter for try blocks (0 = not in try, 1+ = inside try block) */
86
+ insideTryBlock: number;
85
87
  /** Imports found inside function bodies that need to be hoisted to module level */
86
88
  hoistedImports: string[];
87
89
  }
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  transpile,
13
13
  transpileAsync,
14
14
  walkTree
15
- } from "./chunk-R57A2BPG.js";
15
+ } from "./chunk-E5LEIMJI.js";
16
16
  export {
17
17
  debugTree,
18
18
  formatCode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "python2ts",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "AST-based Python to TypeScript transpiler. Convert Python code to clean, idiomatic TypeScript with full type preservation.",
5
5
  "homepage": "https://sebastian-software.github.io/python2ts/",
6
6
  "repository": {
@@ -53,7 +53,7 @@
53
53
  "eslint": "^9.39.2",
54
54
  "prettier": "^3.8.0",
55
55
  "typescript-eslint": "^8.53.1",
56
- "pythonlib": "2.0.3"
56
+ "pythonlib": "2.0.4"
57
57
  },
58
58
  "devDependencies": {
59
59
  "tsup": "^8.5.1",