py2dag 0.3.4__tar.gz → 0.3.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: py2dag
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Convert Python function plans to DAG (JSON, pseudo, optional SVG).
5
5
  License: MIT
6
6
  Author: rvergis
@@ -131,7 +131,7 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
131
131
  return list(prev.get("deps", []))
132
132
  return [ssa_var]
133
133
 
134
- for arg in call.args:
134
+ for idx, arg in enumerate(call.args):
135
135
  if isinstance(arg, ast.Starred):
136
136
  star_val = arg.value
137
137
  if isinstance(star_val, ast.Name):
@@ -156,7 +156,20 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
156
156
  deps.append(_ssa_get(elt.id))
157
157
  dep_labels.append("")
158
158
  else:
159
- raise DSLParseError("Positional args must be variable names or lists/tuples of names")
159
+ # Allow literal positional arguments by emitting a CONST node
160
+ try:
161
+ lit = _literal(arg)
162
+ except DSLParseError:
163
+ raise DSLParseError("Positional args must be variable names or lists/tuples of names or literals")
164
+ const_id = _ssa_new(f"{var_name}_arg{idx}")
165
+ ops.append({
166
+ "id": const_id,
167
+ "op": "CONST.value",
168
+ "deps": [],
169
+ "args": {"value": lit},
170
+ })
171
+ deps.append(const_id)
172
+ dep_labels.append("")
160
173
 
161
174
  kwargs: Dict[str, Any] = {}
162
175
  for kw in call.keywords:
@@ -343,26 +356,7 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
343
356
  sl = sl.value # type: ignore[assignment]
344
357
  key = _literal(sl)
345
358
 
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")
359
+ val_id = _emit_value(f"{base.id}_item", value)
366
360
 
367
361
  base_id = _ssa_get(base.id)
368
362
  ssa = _ssa_new(base.id)
@@ -381,6 +375,51 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
381
375
  ops.append({"id": ssa, "op": "COND.eval", "deps": deps, "args": {"expr": expr, "kind": kind}})
382
376
  return ssa
383
377
 
378
+ def _emit_assign_from_ifexp(var_name: str, node: ast.IfExp) -> str:
379
+ """Emit operations for an inline ``a if cond else b`` expression."""
380
+ cond_id = _emit_cond(node.test, kind="ifexp")
381
+
382
+ then_start = len(ops)
383
+ then_id = _emit_value(f"{var_name}_then", node.body)
384
+ if len(ops) > then_start:
385
+ first = ops[then_start]
386
+ deps0 = first.get("deps", []) or []
387
+ if cond_id not in deps0:
388
+ first["deps"] = [*deps0, cond_id]
389
+
390
+ else_start = len(ops)
391
+ else_id = _emit_value(f"{var_name}_else", node.orelse)
392
+ if len(ops) > else_start:
393
+ first = ops[else_start]
394
+ deps0 = first.get("deps", []) or []
395
+ if cond_id not in deps0:
396
+ first["deps"] = [*deps0, cond_id]
397
+
398
+ ssa = _ssa_new(var_name)
399
+ ops.append({"id": ssa, "op": "PHI", "deps": [then_id, else_id], "args": {"var": var_name}})
400
+ return ssa
401
+
402
+ def _emit_value(var_name: str, value: ast.AST) -> str:
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
+ if isinstance(value, ast.JoinedStr):
410
+ return _emit_assign_from_fstring(var_name, value)
411
+ if isinstance(value, (ast.Constant, ast.List, ast.Tuple, ast.Dict)):
412
+ return _emit_assign_from_literal_or_pack(var_name, value)
413
+ if isinstance(value, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)):
414
+ return _emit_assign_from_comp(var_name, value)
415
+ if isinstance(value, ast.Subscript):
416
+ return _emit_assign_from_subscript(var_name, value)
417
+ if isinstance(value, ast.IfExp):
418
+ return _emit_assign_from_ifexp(var_name, value)
419
+ if isinstance(value, ast.Name):
420
+ return _ssa_get(value.id)
421
+ raise DSLParseError("Right hand side must be a call or f-string")
422
+
384
423
  def _emit_iter(node: ast.AST, target_label: Optional[str] = None) -> str:
385
424
  expr = _stringify(node)
386
425
  deps = [_ssa_get(n) for n in _collect_value_deps(node)]
@@ -399,23 +438,7 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
399
438
  target = stmt.targets[0]
400
439
  if isinstance(target, ast.Name):
401
440
  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")
441
+ return _emit_value(var_name, stmt.value)
419
442
  elif isinstance(target, ast.Subscript):
420
443
  return _emit_assign_to_subscript(target, stmt.value)
421
444
  else:
@@ -459,7 +482,18 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
459
482
  ssa = _ssa_new("break")
460
483
  ops.append({"id": ssa, "op": "CTRL.break", "deps": [], "args": {}})
461
484
  if isinstance(stmt.value, ast.Name):
462
- returned_var = _ssa_get(stmt.value.id)
485
+ name = stmt.value.id
486
+ if name in latest:
487
+ returned_var = _ssa_get(name)
488
+ else:
489
+ const_id = _ssa_new("return_value")
490
+ ops.append({
491
+ "id": const_id,
492
+ "op": "CONST.value",
493
+ "deps": [],
494
+ "args": {"value": None},
495
+ })
496
+ returned_var = const_id
463
497
  elif isinstance(stmt.value, (ast.Constant, ast.List, ast.Tuple, ast.Dict)):
464
498
  lit = _literal(stmt.value)
465
499
  const_id = _ssa_new("return_value")
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "py2dag"
7
- version = "0.3.4"
7
+ version = "0.3.6"
8
8
  description = "Convert Python function plans to DAG (JSON, pseudo, optional SVG)."
9
9
  authors = ["rvergis"]
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes