py2dag 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl
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/parser.py +101 -2
- {py2dag-0.2.2.dist-info → py2dag-0.2.4.dist-info}/METADATA +1 -1
- {py2dag-0.2.2.dist-info → py2dag-0.2.4.dist-info}/RECORD +6 -6
- {py2dag-0.2.2.dist-info → py2dag-0.2.4.dist-info}/LICENSE +0 -0
- {py2dag-0.2.2.dist-info → py2dag-0.2.4.dist-info}/WHEEL +0 -0
- {py2dag-0.2.2.dist-info → py2dag-0.2.4.dist-info}/entry_points.txt +0 -0
py2dag/parser.py
CHANGED
@@ -173,6 +173,10 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
173
173
|
ops.append({"id": ssa, "op": op_name, "deps": deps, "args": kwargs})
|
174
174
|
return ssa
|
175
175
|
|
176
|
+
def _emit_expr_call(call: ast.Call) -> str:
|
177
|
+
"""Emit a node for a bare expression call (no assignment)."""
|
178
|
+
return _emit_assign_from_call("call", call)
|
179
|
+
|
176
180
|
def _emit_assign_from_fstring(var_name: str, fstr: ast.JoinedStr) -> str:
|
177
181
|
deps: List[str] = []
|
178
182
|
parts: List[str] = []
|
@@ -222,6 +226,45 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
222
226
|
"args": {},
|
223
227
|
})
|
224
228
|
return ssa
|
229
|
+
if isinstance(value, ast.Dict):
|
230
|
+
# Support dict with values from names/calls or literals by synthesizing nodes
|
231
|
+
keys: List[str] = []
|
232
|
+
deps: List[str] = []
|
233
|
+
for k_node, v_node in zip(value.keys, value.values):
|
234
|
+
k_str = _literal(k_node)
|
235
|
+
if not isinstance(k_str, (str, int, float, bool)):
|
236
|
+
k_str = str(k_str)
|
237
|
+
keys.append(str(k_str))
|
238
|
+
if isinstance(v_node, ast.Name):
|
239
|
+
deps.append(_ssa_get(v_node.id))
|
240
|
+
elif isinstance(v_node, ast.Await):
|
241
|
+
inner = v_node.value
|
242
|
+
if not isinstance(inner, ast.Call):
|
243
|
+
raise DSLParseError("await must wrap a call in dict value")
|
244
|
+
tmp_id = _emit_assign_from_call(f"{var_name}_field", inner)
|
245
|
+
deps.append(tmp_id)
|
246
|
+
elif isinstance(v_node, ast.Call):
|
247
|
+
tmp_id = _emit_assign_from_call(f"{var_name}_field", v_node)
|
248
|
+
deps.append(tmp_id)
|
249
|
+
else:
|
250
|
+
# Synthesize const for literal value
|
251
|
+
lit_val = _literal(v_node)
|
252
|
+
tmp = _ssa_new(f"{var_name}_lit")
|
253
|
+
ops.append({
|
254
|
+
"id": tmp,
|
255
|
+
"op": "CONST.value",
|
256
|
+
"deps": [],
|
257
|
+
"args": {"value": lit_val},
|
258
|
+
})
|
259
|
+
deps.append(tmp)
|
260
|
+
ssa = _ssa_new(var_name)
|
261
|
+
ops.append({
|
262
|
+
"id": ssa,
|
263
|
+
"op": "PACK.dict",
|
264
|
+
"deps": deps,
|
265
|
+
"args": {"keys": keys},
|
266
|
+
})
|
267
|
+
return ssa
|
225
268
|
raise
|
226
269
|
|
227
270
|
def _emit_assign_from_comp(var_name: str, node: ast.AST) -> str:
|
@@ -245,6 +288,26 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
245
288
|
})
|
246
289
|
return ssa
|
247
290
|
|
291
|
+
def _emit_assign_from_subscript(var_name: str, node: ast.Subscript) -> str:
|
292
|
+
# Support name[key] where key is a JSON-serialisable literal
|
293
|
+
base = node.value
|
294
|
+
if not isinstance(base, ast.Name):
|
295
|
+
raise DSLParseError("Subscript base must be a variable name")
|
296
|
+
# Extract slice expression across Python versions
|
297
|
+
sl = getattr(node, 'slice', None)
|
298
|
+
# In Python >=3.9, slice is the actual node; before it may be ast.Index
|
299
|
+
if hasattr(ast, 'Index') and isinstance(sl, getattr(ast, 'Index')): # type: ignore[attr-defined]
|
300
|
+
sl = sl.value # type: ignore[assignment]
|
301
|
+
key = _literal(sl) # may raise if not literal
|
302
|
+
ssa = _ssa_new(var_name)
|
303
|
+
ops.append({
|
304
|
+
"id": ssa,
|
305
|
+
"op": "GET.item",
|
306
|
+
"deps": [_ssa_get(base.id)],
|
307
|
+
"args": {"key": key},
|
308
|
+
})
|
309
|
+
return ssa
|
310
|
+
|
248
311
|
def _emit_cond(node: ast.AST, kind: str = "if") -> str:
|
249
312
|
expr = _stringify(node)
|
250
313
|
deps = [_ssa_get(n) for n in _collect_value_deps(node)]
|
@@ -279,6 +342,8 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
279
342
|
return _emit_assign_from_literal_or_pack(var_name, value)
|
280
343
|
elif isinstance(value, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)):
|
281
344
|
return _emit_assign_from_comp(var_name, value)
|
345
|
+
elif isinstance(value, ast.Subscript):
|
346
|
+
return _emit_assign_from_subscript(var_name, value)
|
282
347
|
else:
|
283
348
|
raise DSLParseError("Right hand side must be a call or f-string")
|
284
349
|
elif isinstance(stmt, ast.Expr):
|
@@ -310,7 +375,8 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
310
375
|
raise DSLParseError("output requires as=\"filename\"")
|
311
376
|
outputs.append({"from": ssa_from, "as": filename})
|
312
377
|
else:
|
313
|
-
|
378
|
+
# General expression call: represent as an op node too
|
379
|
+
_emit_expr_call(call)
|
314
380
|
return None
|
315
381
|
elif isinstance(stmt, ast.Return):
|
316
382
|
if isinstance(stmt.value, ast.Name):
|
@@ -411,6 +477,29 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
411
477
|
ctx_counts["loop"] += 1
|
412
478
|
context_suffix = f"loop{ctx_counts['loop']}"
|
413
479
|
versions, latest = versions_body, latest_body
|
480
|
+
# Predefine loop target variables as items from iterator for dependency resolution
|
481
|
+
def _bind_loop_target(target: ast.AST):
|
482
|
+
if isinstance(target, ast.Name):
|
483
|
+
ssa_item = _ssa_new(target.id)
|
484
|
+
ops.append({
|
485
|
+
"id": ssa_item,
|
486
|
+
"op": "ITER.item",
|
487
|
+
"deps": [iter_id],
|
488
|
+
"args": {"target": target.id},
|
489
|
+
})
|
490
|
+
elif isinstance(target, ast.Tuple):
|
491
|
+
for elt in target.elts:
|
492
|
+
if isinstance(elt, ast.Name):
|
493
|
+
ssa_item = _ssa_new(elt.id)
|
494
|
+
ops.append({
|
495
|
+
"id": ssa_item,
|
496
|
+
"op": "ITER.item",
|
497
|
+
"deps": [iter_id],
|
498
|
+
"args": {"target": elt.id},
|
499
|
+
})
|
500
|
+
# Other patterns are ignored for now
|
501
|
+
|
502
|
+
_bind_loop_target(stmt.target)
|
414
503
|
for inner in stmt.body:
|
415
504
|
_parse_stmt(inner)
|
416
505
|
versions_body, latest_body = versions, latest
|
@@ -419,6 +508,16 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
419
508
|
# Add iter dep to first op in body
|
420
509
|
if len(ops) > body_ops_start:
|
421
510
|
ops[body_ops_start]["deps"] = [*ops[body_ops_start].get("deps", []), iter_id]
|
511
|
+
# Emit a summary foreach comp node depending on iterable value deps
|
512
|
+
iter_name_deps = _collect_value_deps(stmt.iter)
|
513
|
+
foreach_deps = [_ssa_get(n) for n in iter_name_deps]
|
514
|
+
ssa_foreach = _ssa_new("foreach")
|
515
|
+
ops.append({
|
516
|
+
"id": ssa_foreach,
|
517
|
+
"op": "COMP.foreach",
|
518
|
+
"deps": foreach_deps,
|
519
|
+
"args": {"target": t_label or ""},
|
520
|
+
})
|
422
521
|
# Loop-carried vars: only those existing pre-loop and reassigned in body
|
423
522
|
changed = {k for k in latest_body if pre_latest.get(k) != latest_body.get(k)}
|
424
523
|
carried = [k for k in changed if k in pre_latest]
|
@@ -461,7 +560,7 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
461
560
|
"args": {"var": var},
|
462
561
|
})
|
463
562
|
return None
|
464
|
-
elif isinstance(stmt, (ast.Pass,)):
|
563
|
+
elif isinstance(stmt, (ast.Pass, ast.Continue, ast.Break)):
|
465
564
|
return None
|
466
565
|
else:
|
467
566
|
raise DSLParseError("Only assignments, control flow, settings/output calls, and return are allowed in function body")
|
@@ -2,10 +2,10 @@ py2dag/__init__.py,sha256=i8VB44JCVcRJAcvnQtbH8YVRUz5j7dE355iRbikXPGQ,250
|
|
2
2
|
py2dag/cli.py,sha256=BCBi5mNxOqeEN8uEMt_hiDx0iSt7ZE3y74cGXREzZ2I,1296
|
3
3
|
py2dag/export_dagre.py,sha256=lScDQCIh5nE3RI_ac4Ye8AsLnn3MIn-fm1WlqP9Ei4U,4608
|
4
4
|
py2dag/export_svg.py,sha256=tKqh16hCrCE1QJ9-MTue1ELRxmXOb2akokpSz_a5eE8,2383
|
5
|
-
py2dag/parser.py,sha256=
|
5
|
+
py2dag/parser.py,sha256=mJX_rjjSnO7G0-3thh5bBpPlG8N0IWAUqnp4Yn7Vd3A,28711
|
6
6
|
py2dag/pseudo.py,sha256=NJK61slyFLtSjhj8gJDJneUInEpBN57_41g8IfHNPWI,922
|
7
|
-
py2dag-0.2.
|
8
|
-
py2dag-0.2.
|
9
|
-
py2dag-0.2.
|
10
|
-
py2dag-0.2.
|
11
|
-
py2dag-0.2.
|
7
|
+
py2dag-0.2.4.dist-info/LICENSE,sha256=3Qee1EPwej_nusovTbyIQ8LvD2rXHdM0c6LNwk_D8Kc,1067
|
8
|
+
py2dag-0.2.4.dist-info/METADATA,sha256=yNQ2eR_WmySOQGRyq0uzvuYeKzV5_AxH2FTlSfPIvd0,3549
|
9
|
+
py2dag-0.2.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
10
|
+
py2dag-0.2.4.dist-info/entry_points.txt,sha256=Q0SHexJJ0z1te4AYL1xTZogx5FrxCCE1ZJ5qntkFMZs,42
|
11
|
+
py2dag-0.2.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|