Repository: ros/ros_comm

Issue:
roslaunch: _parse_subcontext() does not handle if/unless parsing on args properly
I'm a bit unsure about the issue title, but it basically comes down to:

``` python
def _parse_arg(tag, context):
    name = tag.attributes['name'].value
    if tag.attributes.has_key('if'):
        val = resolve_args(tag.attributes['if'].value, context)
        if val == '1' or val == 'true':
            return (name, _get_arg_value(tag, context))
    [..]
```

In the case of an `if=..` attribute on an `arg` tag, [_parse_arg(..)](https://github.com/ros/ros_comm/blob/indigo-devel/tools/roslaunch/src/roslaunch/depends.py#L99-L102) only returns a value if `val == '1' or val == 'true'`, otherwise, it doesn't return anything (for an `unless`, the behaviour is inverted).

``` python
def _parse_subcontext(tags, context):
    [..]
    for tag in [t for t in tags if t.nodeType == DomNode.ELEMENT_NODE]:
        if tag.tagName == 'arg':
            (name, val) = _parse_arg(tag, context)
    [..]
```

[_parse_subcontext(..)](https://github.com/ros/ros_comm/blob/indigo-devel/tools/roslaunch/src/roslaunch/depends.py#L116-L119) would then receive a `None`, which it tries to unpack and fails with a stacktrace.

Explicitly checking for the return value of `_parse_arg(..)` not being `None` seems to solve/work around this, but I'm not sure of what the intended behaviour is.



Discussion:
This issue prevents `roslaunch_add_file_check(..)` from working on the generated launch files in MoveIt configuration packages, for instance.

Possible patch:

``` patch
diff --git a/tools/roslaunch/src/roslaunch/depends.py b/tools/roslaunch/src/roslaunch/depends.py
index 321465c..b4032b9 100644
--- a/tools/roslaunch/src/roslaunch/depends.py
+++ b/tools/roslaunch/src/roslaunch/depends.py
@@ -115,8 +115,11 @@ def _parse_subcontext(tags, context):

     for tag in [t for t in tags if t.nodeType == DomNode.ELEMENT_NODE]:
         if tag.tagName == 'arg':
-            (name, val) = _parse_arg(tag, context)
-            subcontext['arg'][name] = val
+            # nothing is returned for args with if/unless that evaluate to false
+            ret = _parse_arg(tag, context)
+            if ret is not None:
+                (name, val) = ret
+                subcontext['arg'][name] = val
     return subcontext

 def _parse_launch(tags, launch_file, file_deps, verbose, context):
```

I would also add an explicit `return None` at the end of the function, as to mark clearly that the argument should be ignored:

``` patch
--- a/tools/roslaunch/src/roslaunch/depends.py
+++ b/tools/roslaunch/src/roslaunch/depends.py
@@ -106,6 +106,8 @@ def _parse_arg(tag, context):
             return (name, _get_arg_value(tag, context))
     else:
         return (name, _get_arg_value(tag, context))
+    # If this point is reached, the <arg> tag should be ignored
+    return None

 def _parse_subcontext(tags, context):
     subcontext = {'arg': {}}
```

@po1: certainly. But as I wrote, I wasn't quite sure of what the intended behaviour was of `_parse_arg(..)`. It could well have been that the original author did not include the explicit `return None` on purpose.

If I submit a PR, would this be merged?

That sounds quite strange to me. If there is no `return` statement, then as you have noticed python will implicitly return `None`.

Not returning anything on purpose means only one thing to me: _the return value of the function should not be used_, which is  obviously not the case here.

@gavanderhoorn whether it gets merged or not, a PR is always a good thing. There is obviously a problem, and your fix will give food for thought to anybody wanting to contribute anyway. Please, open a PR :)


Resolving patch:
diff --git a/tools/roslaunch/src/roslaunch/depends.py b/tools/roslaunch/src/roslaunch/depends.py
--- a/tools/roslaunch/src/roslaunch/depends.py
+++ b/tools/roslaunch/src/roslaunch/depends.py
@@ -106,6 +106,8 @@ def _parse_arg(tag, context):
             return (name, _get_arg_value(tag, context))
     else:
         return (name, _get_arg_value(tag, context))
+    # nothing to return (no value, or conditional wasn't satisfied)
+    return None
 
 def _parse_subcontext(tags, context):
     subcontext = {'arg': {}}
@@ -115,8 +117,11 @@ def _parse_subcontext(tags, context):
     
     for tag in [t for t in tags if t.nodeType == DomNode.ELEMENT_NODE]:
         if tag.tagName == 'arg':
-            (name, val) = _parse_arg(tag, context)
-            subcontext['arg'][name] = val
+            # None is returned for args with if/unless that evaluate to false
+            ret = _parse_arg(tag, context)
+            if ret is not None:
+                (name, val) = ret
+                subcontext['arg'][name] = val
     return subcontext
 
 def _parse_launch(tags, launch_file, file_deps, verbose, context):


Test patch: