python2ts 1.4.2 → 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
  );
@@ -2291,14 +2404,42 @@ function transformSliceFromMember(obj, children, ctx) {
2291
2404
  return `slice(${objCode})`;
2292
2405
  }
2293
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) {
2294
2429
  const colonIndices = [];
2295
- sliceElements.forEach((el, i) => {
2430
+ elements.forEach((el, i) => {
2296
2431
  if (el.name === ":") colonIndices.push(i);
2297
2432
  });
2433
+ if (colonIndices.length === 0) {
2434
+ if (elements.length > 0 && elements[0]) {
2435
+ return [transformNode(elements[0], ctx)];
2436
+ }
2437
+ return ["undefined"];
2438
+ }
2298
2439
  const parts = [];
2299
2440
  let lastIdx = 0;
2300
2441
  for (const colonIdx of colonIndices) {
2301
- const beforeColon = sliceElements.slice(lastIdx, colonIdx);
2442
+ const beforeColon = elements.slice(lastIdx, colonIdx);
2302
2443
  if (beforeColon.length > 0 && beforeColon[0]) {
2303
2444
  parts.push(transformNode(beforeColon[0], ctx));
2304
2445
  } else {
@@ -2306,27 +2447,52 @@ function transformSliceFromMember(obj, children, ctx) {
2306
2447
  }
2307
2448
  lastIdx = colonIdx + 1;
2308
2449
  }
2309
- const afterLastColon = sliceElements.slice(lastIdx);
2450
+ const afterLastColon = elements.slice(lastIdx);
2310
2451
  if (afterLastColon.length > 0 && afterLastColon[0] && afterLastColon[0].name !== ":") {
2311
2452
  parts.push(transformNode(afterLastColon[0], ctx));
2312
2453
  } else if (colonIndices.length > 0) {
2313
2454
  parts.push("undefined");
2314
2455
  }
2315
- if (colonIndices.length === 0) {
2316
- 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";
2317
2462
  }
2318
- return `slice(${objCode}, ${parts.join(", ")})`;
2463
+ return `[${parts.join(", ")}]`;
2319
2464
  }
2320
2465
  function transformArrayExpression(node, ctx) {
2321
2466
  const children = getChildren(node);
2322
- const elements = children.filter((c) => c.name !== "[" && c.name !== "]" && c.name !== ",");
2323
- 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
+ }
2324
2490
  return `[${elementCodes.join(", ")}]`;
2325
2491
  }
2326
2492
  function transformDictionaryExpression(node, ctx) {
2327
2493
  const children = getChildren(node);
2328
2494
  const items = children.filter(
2329
- (c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== ":"
2495
+ (c) => c.name !== "{" && c.name !== "}" && c.name !== "," && c.name !== ":" && c.name !== "Comment"
2330
2496
  );
2331
2497
  const keyNodes = [];
2332
2498
  for (let i = 0; i < items.length; i += 2) {
@@ -2376,7 +2542,7 @@ function transformTupleExpression(node, ctx) {
2376
2542
  i++;
2377
2543
  continue;
2378
2544
  }
2379
- if (child.name === "(" || child.name === ")" || child.name === ",") {
2545
+ if (child.name === "(" || child.name === ")" || child.name === "," || child.name === "Comment") {
2380
2546
  i++;
2381
2547
  continue;
2382
2548
  }
@@ -2832,10 +2998,23 @@ function transformForStatement(node, ctx) {
2832
2998
  }
2833
2999
  let varCode;
2834
3000
  if (varNodes.length === 1 && varNodes[0]) {
2835
- 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
+ }
2836
3007
  } else {
2837
3008
  varCode = "[" + varNodes.map((v) => transformForLoopVar(v, ctx)).join(", ") + "]";
2838
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
+ }
2839
3018
  let iterableCode = transformNode(iterableNode, ctx);
2840
3019
  const bodyCode = transformBody(bodyNode, ctx);
2841
3020
  if (iterableNode.name === "VariableName" && !isAsync) {
@@ -2849,7 +3028,7 @@ ${bodyCode}
2849
3028
  }
2850
3029
  function transformForLoopVar(node, ctx) {
2851
3030
  if (node.name === "VariableName") {
2852
- return getNodeText(node, ctx.source);
3031
+ return escapeReservedKeyword(getNodeText(node, ctx.source));
2853
3032
  } else if (node.name === "TupleExpression") {
2854
3033
  const children = getChildren(node);
2855
3034
  const elements = children.filter((c) => c.name !== "(" && c.name !== ")" && c.name !== ",");
@@ -2857,6 +3036,19 @@ function transformForLoopVar(node, ctx) {
2857
3036
  }
2858
3037
  return transformNode(node, ctx);
2859
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
+ }
2860
3052
  function transformTryStatement(node, ctx) {
2861
3053
  const children = getChildren(node);
2862
3054
  const baseIndent = " ".repeat(ctx.indentLevel);
@@ -2921,7 +3113,9 @@ function transformTryStatement(node, ctx) {
2921
3113
  if (!tryBody) {
2922
3114
  return getNodeText(node, ctx.source);
2923
3115
  }
3116
+ ctx.insideTryBlock++;
2924
3117
  const tryCode = transformBody(tryBody, ctx);
3118
+ ctx.insideTryBlock--;
2925
3119
  let result = `try {
2926
3120
  ${tryCode}
2927
3121
  ${baseIndent}}`;
@@ -3013,7 +3207,7 @@ function transformRaiseStatement(node, ctx) {
3013
3207
  const children = getChildren(node);
3014
3208
  const exprNode = children.find((c) => c.name !== "raise");
3015
3209
  if (!exprNode) {
3016
- return "throw";
3210
+ return "throw e";
3017
3211
  }
3018
3212
  const expr = transformNode(exprNode, ctx);
3019
3213
  if (exprNode.name === "CallExpression") {
@@ -3068,6 +3262,13 @@ function isExceptionType(name) {
3068
3262
  function transformImportStatement(node, ctx) {
3069
3263
  const children = getChildren(node);
3070
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
+ }
3071
3272
  let importCode;
3072
3273
  if (hasFrom) {
3073
3274
  importCode = transformFromImport(children, ctx);
@@ -3123,6 +3324,92 @@ function transformSimpleImport(children, ctx) {
3123
3324
  return `import * as ${importName} from "${module}"`;
3124
3325
  }).join("\n");
3125
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
+ }
3126
3413
  var TYPING_MODULES = /* @__PURE__ */ new Set([
3127
3414
  "typing",
3128
3415
  "typing_extensions",
@@ -3432,7 +3719,7 @@ function transformFunctionDefinition(node, ctx) {
3432
3719
  if (child.name === "async") {
3433
3720
  isAsync = true;
3434
3721
  } else if (child.name === "VariableName") {
3435
- funcName = getNodeText(child, ctx.source);
3722
+ funcName = escapeReservedKeyword(getNodeText(child, ctx.source));
3436
3723
  } else if (child.name === "ParamList") {
3437
3724
  paramList = child;
3438
3725
  } else if (child.name === "Body") {
@@ -3507,7 +3794,8 @@ function transformClassDefinition(node, ctx) {
3507
3794
  if (firstParent === "Protocol") {
3508
3795
  return transformProtocol(className, parentClasses, body, ctx);
3509
3796
  }
3510
- if (firstParent === "ABC" || parentClasses.includes("ABC")) {
3797
+ const isAbcClass = firstParent === "ABC" || firstParent === "abc.ABC" || parentClasses.includes("ABC") || parentClasses.includes("abc.ABC");
3798
+ if (isAbcClass) {
3511
3799
  return transformAbstractClass(className, parentClasses, body, ctx, jsdoc);
3512
3800
  }
3513
3801
  const genericParams = extractGenericParams(parentClasses);
@@ -3564,6 +3852,10 @@ function transformClassBody(node, ctx, skipFirst = false) {
3564
3852
  function transformClassProperty(node, ctx, indent) {
3565
3853
  const children = getChildren(node);
3566
3854
  if (children.length < 2) return "";
3855
+ const nodeText = getNodeText(node, ctx.source);
3856
+ if (nodeText.includes(".__doc__")) {
3857
+ return "";
3858
+ }
3567
3859
  const assignOpIndex = children.findIndex((c) => c.name === "AssignOp" || c.name === "=");
3568
3860
  const typeDef = children.find((c) => c.name === "TypeDef");
3569
3861
  if (assignOpIndex === -1) {
@@ -3709,6 +4001,8 @@ function transformMethodParamListWithTypes(node, ctx) {
3709
4001
  function transformMethodParamListImpl(node, ctx, includeTypes) {
3710
4002
  const children = getChildren(node);
3711
4003
  const params = [];
4004
+ let restParam = null;
4005
+ let kwargsParam = null;
3712
4006
  let i = 0;
3713
4007
  let isFirstParam = true;
3714
4008
  while (i < children.length) {
@@ -3733,8 +4027,8 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3733
4027
  if (child.name === "*" || getNodeText(child, ctx.source) === "*") {
3734
4028
  const nextChild = children[i + 1];
3735
4029
  if (nextChild?.name === "VariableName") {
3736
- const name = getNodeText(nextChild, ctx.source);
3737
- params.push(`...${name}`);
4030
+ const name = escapeReservedKeyword(getNodeText(nextChild, ctx.source));
4031
+ restParam = `...${name}`;
3738
4032
  i += 2;
3739
4033
  continue;
3740
4034
  }
@@ -3744,8 +4038,8 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3744
4038
  if (child.name === "**" || getNodeText(child, ctx.source) === "**") {
3745
4039
  const nextChild = children[i + 1];
3746
4040
  if (nextChild?.name === "VariableName") {
3747
- const name = getNodeText(nextChild, ctx.source);
3748
- params.push(name);
4041
+ const name = escapeReservedKeyword(getNodeText(nextChild, ctx.source));
4042
+ kwargsParam = `${name}: Record<string, unknown> = {}`;
3749
4043
  i += 2;
3750
4044
  continue;
3751
4045
  }
@@ -3753,7 +4047,7 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3753
4047
  continue;
3754
4048
  }
3755
4049
  if (child.name === "VariableName") {
3756
- const nameCode = getNodeText(child, ctx.source);
4050
+ const nameCode = escapeReservedKeyword(getNodeText(child, ctx.source));
3757
4051
  let typeStr = "";
3758
4052
  let defaultStr = "";
3759
4053
  let consumed = 1;
@@ -3783,6 +4077,12 @@ function transformMethodParamListImpl(node, ctx, includeTypes) {
3783
4077
  }
3784
4078
  i++;
3785
4079
  }
4080
+ if (kwargsParam) {
4081
+ params.push(kwargsParam);
4082
+ }
4083
+ if (restParam) {
4084
+ params.push(restParam);
4085
+ }
3786
4086
  return params.join(", ");
3787
4087
  }
3788
4088
  function transformClassMethodBody(node, ctx, skipFirst = false, predeclaredVars = []) {
@@ -3955,7 +4255,7 @@ function transformDecoratedStatement(node, ctx) {
3955
4255
  let body = null;
3956
4256
  for (const child of funcChildren) {
3957
4257
  if (child.name === "VariableName") {
3958
- funcName = getNodeText(child, ctx.source);
4258
+ funcName = escapeReservedKeyword(getNodeText(child, ctx.source));
3959
4259
  } else if (child.name === "ParamList") {
3960
4260
  paramList = child;
3961
4261
  } else if (child.name === "Body") {
@@ -4407,7 +4707,7 @@ ${members.join("\n")}
4407
4707
  }`;
4408
4708
  }
4409
4709
  function transformAbstractClass(className, parentClasses, body, ctx, jsdoc) {
4410
- const filteredParents = parentClasses.filter((p) => p !== "ABC");
4710
+ const filteredParents = parentClasses.filter((p) => p !== "ABC" && p !== "abc.ABC");
4411
4711
  let classHeader = `abstract class ${className}`;
4412
4712
  const firstParent = filteredParents[0];
4413
4713
  if (firstParent) {
@@ -4759,9 +5059,9 @@ function parseComprehensionClauses(children, ctx) {
4759
5059
  let variable;
4760
5060
  const firstVarNode = varNodes[0];
4761
5061
  if (varNodes.length === 1 && firstVarNode) {
4762
- variable = transformNode(firstVarNode, ctx);
5062
+ variable = transformForLoopVar(firstVarNode, ctx);
4763
5063
  } else {
4764
- variable = "[" + varNodes.map((v) => transformNode(v, ctx)).join(", ") + "]";
5064
+ variable = "[" + varNodes.map((v) => transformForLoopVar(v, ctx)).join(", ") + "]";
4765
5065
  }
4766
5066
  clauses.push({
4767
5067
  type: "for",
@@ -4874,7 +5174,9 @@ function transformDictComprehension(node, ctx) {
4874
5174
  }
4875
5175
  function transformSetExpression(node, ctx) {
4876
5176
  const children = getChildren(node);
4877
- 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
+ );
4878
5180
  ctx.usesRuntime.add("set");
4879
5181
  const elementCodes = elements.map((el) => transformNode(el, ctx));
4880
5182
  return `set([${elementCodes.join(", ")}])`;
@@ -5156,7 +5458,7 @@ var BUILTINS = /* @__PURE__ */ new Set([
5156
5458
  "input",
5157
5459
  "format"
5158
5460
  ]);
5159
- var MODULE_NAMESPACES = /* @__PURE__ */ new Set(["string", "list", "dict", "set"]);
5461
+ var MODULE_NAMESPACES = /* @__PURE__ */ new Set(["string", "list", "dict", "set", "ndarray"]);
5160
5462
  function generate(input, options = {}) {
5161
5463
  const opts = { ...defaultOptions, ...options };
5162
5464
  const result = transform(input);
@@ -5345,7 +5647,6 @@ export {
5345
5647
  /* v8 ignore next -- defensive: checked exprs.length >= 3 above @preserve */
5346
5648
  /* v8 ignore next 4 -- defensive: items from parser are never null @preserve */
5347
5649
  /* v8 ignore next 3 -- defensive: subscript always has brackets @preserve */
5348
- /* v8 ignore next 3 -- defensive: slice detection already checked for colons @preserve */
5349
5650
  /* v8 ignore next 2 -- del obj.attr edge case @preserve */
5350
5651
  /* v8 ignore next -- fallback for complex del targets @preserve */
5351
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-5VZP7MNR.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-5VZP7MNR.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.2",
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",