Skip to content

Commit f34af06

Browse files
authored
Merge pull request #458 from XpressAI/fahreza/fix-dyna-args
🐛 Fix Argument Parameters with Dynaports
2 parents be5cdcd + aaff640 commit f34af06

File tree

2 files changed

+77
-31
lines changed

2 files changed

+77
-31
lines changed

xai_components/base.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ def __deepcopy__(self, memo):
7272
memo[id_self] = _copy
7373
return _copy
7474

75+
def _put_at_index(self, idx: int, obj):
76+
v = self._value
77+
if v is None:
78+
v = []
79+
if isinstance(v, tuple):
80+
v = list(v)
81+
if idx >= len(v):
82+
v.extend([None] * (idx + 1 - len(v)))
83+
v[idx] = obj
84+
self._value = v
85+
86+
class _IndexProxy:
87+
def __init__(self, parent, idx):
88+
self._p = parent
89+
self._i = idx
90+
def connect(self, ref):
91+
self._p._put_at_index(self._i, ref)
92+
93+
def __getitem__(self, idx):
94+
# Enables: port[i].connect(other_port)
95+
return InArg._IndexProxy(self, idx)
96+
97+
def __setitem__(self, idx, value):
98+
# Enables: port[i] = <literal or port>
99+
self._put_at_index(idx, value)
75100

76101
class InCompArg(Generic[T]):
77102
def __init__(self, value: T = None, getter: Callable[[T], any] = lambda x: x) -> None:
@@ -265,6 +290,10 @@ def getter(x):
265290
return []
266291
return [item.value if isinstance(item, (InArg, OutArg)) else item for item in x]
267292

293+
@staticmethod
294+
def initial_value():
295+
return []
296+
268297

269298
class dynatuple(tuple):
270299
def __init__(self, *args):
@@ -282,6 +311,11 @@ def resolve(item):
282311
return item
283312
return tuple(resolve(item) for item in x)
284313

314+
@staticmethod
315+
def initial_value():
316+
return tuple()
317+
318+
285319
def parse_bool(value):
286320
if value is None:
287321
return None

xircuits/compiler/generator.py

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -199,42 +199,54 @@ def connect_args(target, source):
199199

200200
# Handle dynamic connections
201201
dynaports = [p for p in node.ports if
202-
p.direction == 'in' and p.type != 'triangle-link' and p.dataType in DYNAMIC_PORTS]
203-
ports_by_varName = {}
204-
205-
RefOrValue = namedtuple('RefOrValue', ['value', 'is_ref']) # Renamed to RefOrValue
202+
p.direction == 'in' and p.type != 'triangle-link' and p.dataType in DYNAMIC_PORTS]
206203

207-
# Group ports by varName
208-
for port in dynaports:
209-
if port.varName not in ports_by_varName:
210-
ports_by_varName[port.varName] = []
211-
ports_by_varName[port.varName].append(port)
204+
# Capture index N from port names like 'parameter-dynalist-dlist-2'
205+
_name_idx_re = re.compile(r'-(\d+)\s*$')
212206

213-
for varName, ports in ports_by_varName.items():
214-
dynaport_values = []
207+
# Map: varName -> {index: port}
208+
ports_by_varName = {}
215209

216-
for port in ports:
217-
if port.source.id not in named_nodes:
218-
value = _get_value_from_literal_port(port)
219-
dynaport_values.append(RefOrValue(value, False))
220-
else:
221-
# Handle named node references
222-
value = "%s.%s" % (named_nodes[port.source.id], port.sourceLabel) # Variable reference
223-
dynaport_values.append(RefOrValue(value, True))
224-
225-
if ports[0].dataType == 'dynatuple':
226-
tuple_elements = [item.value if item.is_ref else repr(item.value) for item in dynaport_values]
227-
if len(tuple_elements) == 1:
228-
assignment_value = '(' + tuple_elements[0] + ',)'
210+
for port in dynaports:
211+
var_name = port.varName
212+
name = port.name
213+
# Extract index from name; default to 0 if no '-N' suffix
214+
m = _name_idx_re.search(name)
215+
idx = int(m.group(1)) if m else 0
216+
217+
ports_by_varName.setdefault(var_name, {})
218+
ports_by_varName[var_name].setdefault(idx, port)
219+
220+
# Emit code per element in numeric order
221+
for var_name, mapping in ports_by_varName.items():
222+
for i in sorted(mapping.keys()):
223+
port = mapping[i]
224+
target_indexed = f"{named_nodes[port.target.id]}.{var_name}[{i}]"
225+
226+
if port.source.id in named_nodes:
227+
# Component reference -> connect
228+
source_ref = f"{named_nodes[port.source.id]}.{port.sourceLabel}"
229+
init_code.append(ast.parse(f"{target_indexed}.connect({source_ref})"))
229230
else:
230-
assignment_value = '(' + ', '.join(tuple_elements) + ')'
231-
else:
232-
list_elements = [item.value if item.is_ref else repr(item.value) for item in dynaport_values]
233-
assignment_value = '[' + ', '.join(list_elements) + ']'
231+
# Regex: matches e.g. 'Argument (string): argsName'
232+
pattern = re.compile(r'^Argument \((.+?)\): (.+)$')
233+
if port.source.file is None and port.source.name.startswith("Argument "):
234+
match = pattern.match(port.source.name)
235+
arg_type = type_mapping.get(match.group(1), 'any')
236+
arg_name = match.group(2)
237+
238+
if arg_name not in existing_args:
239+
args_code.append(ast.parse(f"{arg_name}: InArg[{arg_type}]").body[0])
240+
existing_args.add(arg_name)
241+
242+
init_code.append(ast.parse(f"{target_indexed}.connect(self.{arg_name})"))
243+
else:
244+
# Literal -> `[i] = <python literal>`
245+
lit_value = _get_value_from_literal_port(port)
246+
assign = ast.parse(f"{target_indexed} = 0")
247+
assign.body[0].value = ast.parse(repr(lit_value)).body[0].value
248+
init_code.append(assign)
234249

235-
assignment_target = "%s.%s" % (named_nodes[ports[0].target.id], ports[0].varName)
236-
tpl = set_value(assignment_target, assignment_value)
237-
init_code.append(tpl)
238250

239251
# Handle output connections
240252
for i, port in enumerate(p for p in finish_node.ports if p.dataType == 'dynalist'):

0 commit comments

Comments
 (0)