Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion python/cpmf_uips_xaml/__version__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Version information for cpmf_uips_xaml package."""

__version__ = "0.1.0"
__version__ = "0.1.2"
__author__ = "Christian Prior-Mamulyan"
__description__ = "Standalone XAML workflow parser for automation projects"
40 changes: 40 additions & 0 deletions python/cpmf_uips_xaml/stages/parsing/extractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
if TYPE_CHECKING:
from .parser import XamlDialect

# Tags that carry VB.NET / C# expressions as an ExpressionText attribute or element text,
# used by the "child-element expression syntax" (Syntax B) introduced in newer UiPath Studio.
_EXPR_ELEMENT_TAGS = frozenset({
"VisualBasicValue",
"VisualBasicReference",
"CSharpValue",
"CSharpReference",
})


class ArgumentExtractor:
"""Extracts workflow arguments from x:Members section."""
Expand Down Expand Up @@ -669,6 +678,25 @@ def _extract_activity_arguments(self, element: ET.Element) -> dict[str, Any]:
clean_name = attr_name.split("}")[-1] if "}" in attr_name else attr_name
arguments[clean_name] = attr_value

# Child-element argument syntax:
# <Activity.Parameters><InArgument><VisualBasicValue ExpressionText="x"/></InArgument></Activity.Parameters>
for child in element:
child_tag = get_local_tag(child)
if "." in child_tag:
for param_child in child:
param_tag = get_local_tag(param_child)
if param_tag in ("InArgument", "OutArgument", "InOutArgument"):
for expr_elem in param_child:
expr_tag = get_local_tag(expr_elem)
if expr_tag in _EXPR_ELEMENT_TAGS:
expr_text = expr_elem.get("ExpressionText")
if expr_text:
existing = arguments.get(child_tag)
if existing is None:
arguments[child_tag] = [expr_text]
elif isinstance(existing, list):
existing.append(expr_text)

return arguments

def _extract_visible_properties(self, element: ET.Element) -> dict[str, Any]:
Expand Down Expand Up @@ -759,6 +787,18 @@ def _extract_business_logic_expressions(self, element: ET.Element) -> list[str]:
extracted_expressions = self.activity_utils.extract_expressions_from_text(element.text)
expressions.extend(extracted_expressions)

# Child-element expression syntax: <VisualBasicValue ExpressionText="..." />
# ExpressionText contains the raw VB.NET/C# expression (no brackets), so add directly.
for descendant in element.iter():
tag = descendant.tag.split("}")[-1] if "}" in descendant.tag else descendant.tag
if tag in _EXPR_ELEMENT_TAGS:
expr_text = descendant.get("ExpressionText")
if expr_text:
expressions.append(expr_text)
# Older serializations may embed the expression as element text
if descendant.text and descendant.text.strip():
expressions.append(descendant.text.strip())

return list(set(expressions)) # Remove duplicates

def _extract_variable_references(
Expand Down
92 changes: 92 additions & 0 deletions python/tests/unit/test_extractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,98 @@ def test_extract_business_logic_expressions(self, xaml_with_expressions):
if len(expressions) > 0:
assert any("[" in expr or "(" in expr for expr in expressions)

def test_extract_expressions_from_visual_basic_value_element(self):
"""VisualBasicValue ExpressionText is extracted as an expression."""
xaml = """<?xml version="1.0"?>
<InvokeMethod
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sd="clr-namespace:System.Data;assembly=System.Data"
MethodName="Foo">
<InvokeMethod.Parameters>
<InArgument x:TypeArguments="sd:DataTable">
<VisualBasicValue x:TypeArguments="sd:DataTable" ExpressionText="lookupData" />
</InArgument>
</InvokeMethod.Parameters>
</InvokeMethod>"""
root = parse_xaml_string(xaml)
extractor = create_activity_extractor()

expressions = extractor._extract_business_logic_expressions(root)

assert "lookupData" in expressions

def test_extract_expressions_from_visual_basic_reference_element(self):
"""VisualBasicReference ExpressionText contributes to expressions."""
xaml = """<?xml version="1.0"?>
<Assign
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sd="clr-namespace:System.Data;assembly=System.Data">
<Assign.To>
<OutArgument x:TypeArguments="sd:DataTable">
<VisualBasicReference x:TypeArguments="sd:DataTable" ExpressionText="myVar" />
</OutArgument>
</Assign.To>
</Assign>"""
root = parse_xaml_string(xaml)
extractor = create_activity_extractor()

expressions = extractor._extract_business_logic_expressions(root)

assert "myVar" in expressions

def test_mixed_bracket_and_element_syntax_same_activity(self):
"""Activities using both [expr] attributes and VisualBasicValue children are both extracted."""
xaml = """<?xml version="1.0"?>
<InvokeMethod
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sd="clr-namespace:System.Data;assembly=System.Data"
MethodName="Validate"
TargetObject="[servicePoint]">
<InvokeMethod.Parameters>
<InArgument x:TypeArguments="sd:DataTable">
<VisualBasicValue x:TypeArguments="sd:DataTable" ExpressionText="lookupData" />
</InArgument>
</InvokeMethod.Parameters>
</InvokeMethod>"""
root = parse_xaml_string(xaml)
extractor = create_activity_extractor()

expressions = extractor._extract_business_logic_expressions(root)

assert "lookupData" in expressions
# Bracket syntax strips delimiters: [servicePoint] → servicePoint
assert "servicePoint" in expressions

def test_extract_arguments_from_child_element_syntax(self):
"""_extract_activity_arguments captures ExpressionText from InArgument children."""
xaml = """<?xml version="1.0"?>
<InvokeMethod
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sd="clr-namespace:System.Data;assembly=System.Data"
MethodName="Foo">
<InvokeMethod.Parameters>
<InArgument x:TypeArguments="sd:DataTable">
<VisualBasicValue x:TypeArguments="sd:DataTable" ExpressionText="in_LookupData" />
</InArgument>
<InArgument x:TypeArguments="x:String">
<VisualBasicValue x:TypeArguments="x:String" ExpressionText="in_RulesString" />
</InArgument>
</InvokeMethod.Parameters>
</InvokeMethod>"""
root = parse_xaml_string(xaml)
extractor = create_activity_extractor()

arguments = extractor._extract_activity_arguments(root)

assert "InvokeMethod.Parameters" in arguments
params = arguments["InvokeMethod.Parameters"]
assert "in_LookupData" in params
assert "in_RulesString" in params


# ============================================================================
# Test ActivityExtractor - Activity Instance Extraction (ADR-009)
Expand Down
Loading