py2dag 0.1.12__tar.gz → 0.1.14__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.1.12 → py2dag-0.1.14}/PKG-INFO +1 -1
- {py2dag-0.1.12 → py2dag-0.1.14}/py2dag/parser.py +117 -19
- {py2dag-0.1.12 → py2dag-0.1.14}/pyproject.toml +1 -1
- {py2dag-0.1.12 → py2dag-0.1.14}/LICENSE +0 -0
- {py2dag-0.1.12 → py2dag-0.1.14}/README.md +0 -0
- {py2dag-0.1.12 → py2dag-0.1.14}/py2dag/__init__.py +0 -0
- {py2dag-0.1.12 → py2dag-0.1.14}/py2dag/cli.py +0 -0
- {py2dag-0.1.12 → py2dag-0.1.14}/py2dag/export_dagre.py +0 -0
- {py2dag-0.1.12 → py2dag-0.1.14}/py2dag/export_svg.py +0 -0
- {py2dag-0.1.12 → py2dag-0.1.14}/py2dag/pseudo.py +0 -0
@@ -47,6 +47,20 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
47
47
|
settings: Dict[str, Any] = {}
|
48
48
|
|
49
49
|
returned_var: Optional[str] = None
|
50
|
+
# Treat function parameters as pre-defined names
|
51
|
+
try:
|
52
|
+
for arg in getattr(fn, "args").args: # type: ignore[attr-defined]
|
53
|
+
defined.add(arg.arg)
|
54
|
+
except Exception:
|
55
|
+
pass
|
56
|
+
|
57
|
+
def _collect_name_deps(node: ast.AST) -> List[str]:
|
58
|
+
names: List[str] = []
|
59
|
+
for n in ast.walk(node):
|
60
|
+
if isinstance(n, ast.Name) and isinstance(n.ctx, ast.Load):
|
61
|
+
if n.id not in names:
|
62
|
+
names.append(n.id)
|
63
|
+
return names
|
50
64
|
# type: ignore[attr-defined]
|
51
65
|
for i, stmt in enumerate(fn.body): # type: ignore[attr-defined]
|
52
66
|
if isinstance(stmt, ast.Assign):
|
@@ -65,25 +79,70 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
65
79
|
op_name = _get_call_name(value.func)
|
66
80
|
|
67
81
|
deps: List[str] = []
|
82
|
+
|
83
|
+
def _expand_star_name(varname: str) -> List[str]:
|
84
|
+
# Try to expand a previously packed list/tuple variable into its element deps
|
85
|
+
for prev in reversed(ops):
|
86
|
+
if prev.get("id") == varname:
|
87
|
+
if prev.get("op") in {"PACK.list", "PACK.tuple"}:
|
88
|
+
return list(prev.get("deps", []))
|
89
|
+
break
|
90
|
+
return [varname]
|
68
91
|
for arg in value.args:
|
69
|
-
if
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
92
|
+
if isinstance(arg, ast.Starred):
|
93
|
+
star_val = arg.value
|
94
|
+
if isinstance(star_val, ast.Name):
|
95
|
+
if star_val.id not in defined:
|
96
|
+
raise DSLParseError(f"Undefined dependency: {star_val.id}")
|
97
|
+
deps.extend(_expand_star_name(star_val.id))
|
98
|
+
elif isinstance(star_val, (ast.List, ast.Tuple)):
|
99
|
+
for elt in star_val.elts:
|
100
|
+
if not isinstance(elt, ast.Name):
|
101
|
+
raise DSLParseError("Starred list/tuple elements must be names")
|
102
|
+
if elt.id not in defined:
|
103
|
+
raise DSLParseError(f"Undefined dependency: {elt.id}")
|
104
|
+
deps.append(elt.id)
|
105
|
+
else:
|
106
|
+
raise DSLParseError("*args must be a name or list/tuple of names")
|
107
|
+
elif isinstance(arg, ast.Name):
|
108
|
+
if arg.id not in defined:
|
109
|
+
raise DSLParseError(f"Undefined dependency: {arg.id}")
|
110
|
+
deps.append(arg.id)
|
111
|
+
elif isinstance(arg, (ast.List, ast.Tuple)):
|
112
|
+
for elt in arg.elts:
|
113
|
+
if not isinstance(elt, ast.Name):
|
114
|
+
raise DSLParseError("List/Tuple positional args must be variable names")
|
115
|
+
if elt.id not in defined:
|
116
|
+
raise DSLParseError(f"Undefined dependency: {elt.id}")
|
117
|
+
deps.append(elt.id)
|
118
|
+
else:
|
119
|
+
raise DSLParseError("Positional args must be variable names or lists/tuples of names")
|
74
120
|
|
75
121
|
kwargs: Dict[str, Any] = {}
|
76
122
|
for kw in value.keywords:
|
77
123
|
if kw.arg is None:
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
124
|
+
# **kwargs support: allow dict literal merge, or variable name as dep
|
125
|
+
v = kw.value
|
126
|
+
if isinstance(v, ast.Dict):
|
127
|
+
# Merge literal kwargs
|
128
|
+
lit = _literal(v)
|
129
|
+
for k, val in lit.items():
|
130
|
+
kwargs[str(k)] = val
|
131
|
+
elif isinstance(v, ast.Name):
|
132
|
+
if v.id not in defined:
|
133
|
+
raise DSLParseError(f"Undefined dependency: {v.id}")
|
134
|
+
deps.append(v.id)
|
135
|
+
else:
|
136
|
+
raise DSLParseError("**kwargs must be a dict literal or a variable name")
|
85
137
|
else:
|
86
|
-
|
138
|
+
# Support variable-name keyword args as dependencies; literals remain in args
|
139
|
+
if isinstance(kw.value, ast.Name):
|
140
|
+
name = kw.value.id
|
141
|
+
if name not in defined:
|
142
|
+
raise DSLParseError(f"Undefined dependency: {name}")
|
143
|
+
deps.append(name)
|
144
|
+
else:
|
145
|
+
kwargs[kw.arg] = _literal(kw.value)
|
87
146
|
|
88
147
|
ops.append({"id": var_name, "op": op_name, "deps": deps, "args": kwargs})
|
89
148
|
elif isinstance(value, ast.JoinedStr):
|
@@ -109,13 +168,52 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
109
168
|
"args": {"template": template},
|
110
169
|
})
|
111
170
|
elif isinstance(value, (ast.Constant, ast.List, ast.Tuple, ast.Dict)):
|
112
|
-
# Allow assigning
|
113
|
-
|
171
|
+
# Allow assigning literals; also support packing lists/tuples of names
|
172
|
+
try:
|
173
|
+
lit = _literal(value)
|
174
|
+
ops.append({
|
175
|
+
"id": var_name,
|
176
|
+
"op": "CONST.value",
|
177
|
+
"deps": [],
|
178
|
+
"args": {"value": lit},
|
179
|
+
})
|
180
|
+
except DSLParseError:
|
181
|
+
if isinstance(value, (ast.List, ast.Tuple)):
|
182
|
+
elts = value.elts
|
183
|
+
names: List[str] = []
|
184
|
+
for elt in elts:
|
185
|
+
if not isinstance(elt, ast.Name):
|
186
|
+
raise DSLParseError("Only names allowed in non-literal list/tuple assignment")
|
187
|
+
if elt.id not in defined:
|
188
|
+
raise DSLParseError(f"Undefined dependency: {elt.id}")
|
189
|
+
names.append(elt.id)
|
190
|
+
kind = "list" if isinstance(value, ast.List) else "tuple"
|
191
|
+
ops.append({
|
192
|
+
"id": var_name,
|
193
|
+
"op": f"PACK.{kind}",
|
194
|
+
"deps": names,
|
195
|
+
"args": {},
|
196
|
+
})
|
197
|
+
else:
|
198
|
+
raise
|
199
|
+
elif isinstance(value, (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)):
|
200
|
+
# Basic comprehension support: collect name deps and emit a generic comp op
|
201
|
+
name_deps = [n for n in _collect_name_deps(value) if n in defined]
|
202
|
+
# Ensure no undefined names used
|
203
|
+
for n in name_deps:
|
204
|
+
if n not in defined:
|
205
|
+
raise DSLParseError(f"Undefined dependency: {n}")
|
206
|
+
kind = (
|
207
|
+
"listcomp" if isinstance(value, ast.ListComp) else
|
208
|
+
"setcomp" if isinstance(value, ast.SetComp) else
|
209
|
+
"dictcomp" if isinstance(value, ast.DictComp) else
|
210
|
+
"genexpr"
|
211
|
+
)
|
114
212
|
ops.append({
|
115
213
|
"id": var_name,
|
116
|
-
"op": "
|
117
|
-
"deps":
|
118
|
-
"args": {
|
214
|
+
"op": f"COMP.{kind}",
|
215
|
+
"deps": name_deps,
|
216
|
+
"args": {},
|
119
217
|
})
|
120
218
|
else:
|
121
219
|
raise DSLParseError("Right hand side must be a call or f-string")
|
@@ -178,7 +276,7 @@ def parse(source: str, function_name: Optional[str] = None) -> Dict[str, Any]:
|
|
178
276
|
returned_var = const_id
|
179
277
|
else:
|
180
278
|
raise DSLParseError("return must return a variable name or literal")
|
181
|
-
elif isinstance(stmt, (ast.For, ast.AsyncFor, ast.While, ast.If)):
|
279
|
+
elif isinstance(stmt, (ast.For, ast.AsyncFor, ast.While, ast.If, ast.Match)):
|
182
280
|
# Ignore control flow blocks; only top-level linear statements are modeled
|
183
281
|
continue
|
184
282
|
elif isinstance(stmt, (ast.Pass,)):
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|