py2dag 0.3.2__tar.gz → 0.3.4__tar.gz
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.
- {py2dag-0.3.2 → py2dag-0.3.4}/PKG-INFO +1 -1
- {py2dag-0.3.2 → py2dag-0.3.4}/py2dag/parser.py +100 -21
- {py2dag-0.3.2 → py2dag-0.3.4}/pyproject.toml +1 -1
- {py2dag-0.3.2 → py2dag-0.3.4}/LICENSE +0 -0
- {py2dag-0.3.2 → py2dag-0.3.4}/README.md +0 -0
- {py2dag-0.3.2 → py2dag-0.3.4}/py2dag/__init__.py +0 -0
- {py2dag-0.3.2 → py2dag-0.3.4}/py2dag/cli.py +0 -0
- {py2dag-0.3.2 → py2dag-0.3.4}/py2dag/colors.py +0 -0
- {py2dag-0.3.2 → py2dag-0.3.4}/py2dag/export_dagre.py +0 -0
- {py2dag-0.3.2 → py2dag-0.3.4}/py2dag/export_svg.py +0 -0
- {py2dag-0.3.2 → py2dag-0.3.4}/py2dag/pseudo.py +0 -0
@@ -62,7 +62,8 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
62
62
|
versions: Dict[str, int] = {}
|
63
63
|
latest: Dict[str, str] = {}
|
64
64
|
context_suffix: str = ""
|
65
|
-
ctx_counts: Dict[str, int] = {"if": 0, "loop": 0, "while": 0}
|
65
|
+
ctx_counts: Dict[str, int] = {"if": 0, "loop": 0, "while": 0, "except": 0}
|
66
|
+
loop_depth = 0
|
66
67
|
|
67
68
|
def _ssa_new(name: str) -> str:
|
68
69
|
if not VALID_NAME_RE.match(name):
|
@@ -331,6 +332,48 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
331
332
|
})
|
332
333
|
return ssa
|
333
334
|
|
335
|
+
def _emit_assign_to_subscript(target: ast.Subscript, value: ast.AST) -> str:
|
336
|
+
"""Emit a SET.item node for an assignment like ``name[key] = value``."""
|
337
|
+
base = target.value
|
338
|
+
if not isinstance(base, ast.Name):
|
339
|
+
raise DSLParseError("Subscript base must be a variable name")
|
340
|
+
# Extract slice expression across Python versions
|
341
|
+
sl = getattr(target, "slice", None)
|
342
|
+
if hasattr(ast, "Index") and isinstance(sl, getattr(ast, "Index")): # type: ignore[attr-defined]
|
343
|
+
sl = sl.value # type: ignore[assignment]
|
344
|
+
key = _literal(sl)
|
345
|
+
|
346
|
+
awaited = False
|
347
|
+
if isinstance(value, ast.Await):
|
348
|
+
value = value.value
|
349
|
+
awaited = True
|
350
|
+
|
351
|
+
# Determine SSA id for RHS value
|
352
|
+
if isinstance(value, ast.Call):
|
353
|
+
val_id = _emit_assign_from_call(f"{base.id}_item", value, awaited)
|
354
|
+
elif isinstance(value, ast.JoinedStr):
|
355
|
+
val_id = _emit_assign_from_fstring(f"{base.id}_item", value)
|
356
|
+
elif isinstance(value, (ast.Constant, ast.List, ast.Tuple, ast.Dict)):
|
357
|
+
val_id = _emit_assign_from_literal_or_pack(f"{base.id}_item", value)
|
358
|
+
elif isinstance(value, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)):
|
359
|
+
val_id = _emit_assign_from_comp(f"{base.id}_item", value)
|
360
|
+
elif isinstance(value, ast.Subscript):
|
361
|
+
val_id = _emit_assign_from_subscript(f"{base.id}_item", value)
|
362
|
+
elif isinstance(value, ast.Name):
|
363
|
+
val_id = _ssa_get(value.id)
|
364
|
+
else:
|
365
|
+
raise DSLParseError("Right hand side must be a call or f-string")
|
366
|
+
|
367
|
+
base_id = _ssa_get(base.id)
|
368
|
+
ssa = _ssa_new(base.id)
|
369
|
+
ops.append({
|
370
|
+
"id": ssa,
|
371
|
+
"op": "SET.item",
|
372
|
+
"deps": [base_id, val_id],
|
373
|
+
"args": {"key": key},
|
374
|
+
})
|
375
|
+
return ssa
|
376
|
+
|
334
377
|
def _emit_cond(node: ast.AST, kind: str = "if") -> str:
|
335
378
|
expr = _stringify(node)
|
336
379
|
deps = [_ssa_get(n) for n in _collect_value_deps(node)]
|
@@ -349,28 +392,34 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
349
392
|
return ssa
|
350
393
|
|
351
394
|
def _parse_stmt(stmt: ast.stmt) -> Optional[str]:
|
352
|
-
nonlocal returned_var, versions, latest, context_suffix
|
395
|
+
nonlocal returned_var, versions, latest, context_suffix, loop_depth
|
353
396
|
if isinstance(stmt, ast.Assign):
|
354
|
-
if len(stmt.targets) != 1
|
355
|
-
raise DSLParseError("Assignment
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
397
|
+
if len(stmt.targets) != 1:
|
398
|
+
raise DSLParseError("Assignment must have exactly one target")
|
399
|
+
target = stmt.targets[0]
|
400
|
+
if isinstance(target, ast.Name):
|
401
|
+
var_name = target.id
|
402
|
+
value = stmt.value
|
403
|
+
awaited = False
|
404
|
+
if isinstance(value, ast.Await):
|
405
|
+
value = value.value
|
406
|
+
awaited = True
|
407
|
+
if isinstance(value, ast.Call):
|
408
|
+
return _emit_assign_from_call(var_name, value, awaited)
|
409
|
+
elif isinstance(value, ast.JoinedStr):
|
410
|
+
return _emit_assign_from_fstring(var_name, value)
|
411
|
+
elif isinstance(value, (ast.Constant, ast.List, ast.Tuple, ast.Dict)):
|
412
|
+
return _emit_assign_from_literal_or_pack(var_name, value)
|
413
|
+
elif isinstance(value, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)):
|
414
|
+
return _emit_assign_from_comp(var_name, value)
|
415
|
+
elif isinstance(value, ast.Subscript):
|
416
|
+
return _emit_assign_from_subscript(var_name, value)
|
417
|
+
else:
|
418
|
+
raise DSLParseError("Right hand side must be a call or f-string")
|
419
|
+
elif isinstance(target, ast.Subscript):
|
420
|
+
return _emit_assign_to_subscript(target, stmt.value)
|
372
421
|
else:
|
373
|
-
raise DSLParseError("
|
422
|
+
raise DSLParseError("Assignment targets must be simple names")
|
374
423
|
elif isinstance(stmt, ast.Expr):
|
375
424
|
call = stmt.value
|
376
425
|
awaited = False
|
@@ -406,6 +455,9 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
406
455
|
_emit_expr_call(call, awaited)
|
407
456
|
return None
|
408
457
|
elif isinstance(stmt, ast.Return):
|
458
|
+
if loop_depth > 0:
|
459
|
+
ssa = _ssa_new("break")
|
460
|
+
ops.append({"id": ssa, "op": "CTRL.break", "deps": [], "args": {}})
|
409
461
|
if isinstance(stmt.value, ast.Name):
|
410
462
|
returned_var = _ssa_get(stmt.value.id)
|
411
463
|
elif isinstance(stmt.value, (ast.Constant, ast.List, ast.Tuple, ast.Dict)):
|
@@ -503,6 +555,7 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
503
555
|
saved_ctx = context_suffix
|
504
556
|
ctx_counts["loop"] += 1
|
505
557
|
context_suffix = f"loop{ctx_counts['loop']}"
|
558
|
+
loop_depth += 1
|
506
559
|
versions, latest = versions_body, latest_body
|
507
560
|
# Predefine loop target variables as items from iterator for dependency resolution
|
508
561
|
def _bind_loop_target(target: ast.AST):
|
@@ -529,6 +582,7 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
529
582
|
_bind_loop_target(stmt.target)
|
530
583
|
for inner in stmt.body:
|
531
584
|
_parse_stmt(inner)
|
585
|
+
loop_depth -= 1
|
532
586
|
versions_body, latest_body = versions, latest
|
533
587
|
versions, latest = saved_versions, saved_latest
|
534
588
|
context_suffix = saved_ctx
|
@@ -571,9 +625,11 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
571
625
|
saved_ctx = context_suffix
|
572
626
|
ctx_counts["while"] += 1
|
573
627
|
context_suffix = f"while{ctx_counts['while']}"
|
628
|
+
loop_depth += 1
|
574
629
|
versions, latest = versions_body, latest_body
|
575
630
|
for inner in stmt.body:
|
576
631
|
_parse_stmt(inner)
|
632
|
+
loop_depth -= 1
|
577
633
|
versions_body, latest_body = versions, latest
|
578
634
|
versions, latest = saved_versions, saved_latest
|
579
635
|
context_suffix = saved_ctx
|
@@ -593,6 +649,29 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
593
649
|
"args": {"var": var},
|
594
650
|
})
|
595
651
|
return None
|
652
|
+
elif isinstance(stmt, ast.Try):
|
653
|
+
pre_versions = dict(versions)
|
654
|
+
pre_latest = dict(latest)
|
655
|
+
for inner in stmt.body:
|
656
|
+
_parse_stmt(inner)
|
657
|
+
# else block executes only if no exception
|
658
|
+
for inner in getattr(stmt, "orelse", []):
|
659
|
+
_parse_stmt(inner)
|
660
|
+
post_versions = versions
|
661
|
+
post_latest = latest
|
662
|
+
for handler in getattr(stmt, "handlers", []):
|
663
|
+
saved_ctx = context_suffix
|
664
|
+
ctx_counts["except"] = ctx_counts.get("except", 0) + 1
|
665
|
+
context_suffix = f"except{ctx_counts['except']}"
|
666
|
+
saved_versions, saved_latest = versions, latest
|
667
|
+
versions, latest = dict(pre_versions), dict(pre_latest)
|
668
|
+
for inner in handler.body:
|
669
|
+
_parse_stmt(inner)
|
670
|
+
versions, latest = post_versions, post_latest
|
671
|
+
context_suffix = saved_ctx
|
672
|
+
for inner in getattr(stmt, "finalbody", []):
|
673
|
+
_parse_stmt(inner)
|
674
|
+
return None
|
596
675
|
elif isinstance(stmt, ast.Break):
|
597
676
|
ssa = _ssa_new("break")
|
598
677
|
ops.append({"id": ssa, "op": "CTRL.break", "deps": [], "args": {}})
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|