Repository: paver/paver

Issue:
Define behavior with both @cmdopts and @consume_args set
Latter now overwrites former, which is not what one generally wants.

Parse for cmdopts and remove it from args. 


Comments:

Patch:
diff --git a/docs/source/pavement.rst b/docs/source/pavement.rst
index cf09246..9dae827 100644
--- a/docs/source/pavement.rst
+++ b/docs/source/pavement.rst
@@ -203,6 +203,22 @@ For sharing, following must be fullfilled:
 
 Otherwise, ``PavementError`` is raised.
 
+You can combine both ``@consume_args`` and ``@cmdopts`` together::
+
+    @task
+    @cmdopts([
+        ('username=', 'u', 'Username to use when logging in to the servers')
+    ])
+    @consume_args
+    def exec(options):
+        pass
+
+
+* ``paver exec -u root`` will result in ``options.username = 'root', options.args = []``
+* ``paver exec -u root production`` will result in ``options.username = 'root', options.args = ['production']``
+* ``paver exec production -u root`` will result in ``options.args = ['production', '-u', 'root']`` with no ``options.username`` attribute.
+* ``paver exec -u root production -u other`` will result in ``options.username = 'root', options.args = ['production', '-u', 'other']``
+
 Hiding tasks
 ---------------
 
diff --git a/paver/tasks.py b/paver/tasks.py
index 7300969..09f7d4e 100644
--- a/paver/tasks.py
+++ b/paver/tasks.py
@@ -741,7 +741,8 @@ def _parse_command_line(args):
     if not isinstance(task, Task):
         raise BuildFailure("%s is not a Task" % taskname)
 
-    if task.consume_args != float('inf'):
+    if task.user_options or task.consume_args != float('inf'):
+        # backwards compatibility around mixing of @cmdopts & @consume_args
         args = task.parse_args(args)
     if task.consume_args > 0:
         args = _consume_nargs(task, args)


Test patch:
diff --git a/paver/tests/test_tasks.py b/paver/tests/test_tasks.py
index 7b971f2..1df7c4e 100644
--- a/paver/tests/test_tasks.py
+++ b/paver/tests/test_tasks.py
@@ -404,6 +404,74 @@ def test_consume_args():
     tasks._process_commands("t3 -v 1".split())
     assert t3.called
 
+def test_consume_args_and_options():
+    @tasks.task
+    @tasks.cmdopts([
+        ("foo=", "f", "Help for foo")
+    ])
+    @tasks.consume_args
+    def t1(options):
+        assert options.foo == "1"
+        assert options.t1.foo == "1"
+        assert options.args == ['abc', 'def']
+
+    environment = _set_environment(t1=t1)
+    tasks._process_commands([
+        't1', '--foo', '1', 'abc', 'def',
+    ])
+    assert t1.called
+
+def test_consume_args_and_options_2():
+    @tasks.task
+    @tasks.cmdopts([
+        ("foo=", "f", "Help for foo")
+    ])
+    @tasks.consume_args
+    def t1(options):
+        assert not hasattr(options, 'foo')
+        assert not hasattr(options.t1, 'foo')
+        assert options.args == ['abc', 'def', '--foo', '1']
+
+    environment = _set_environment(t1=t1)
+    tasks._process_commands([
+        't1', 'abc', 'def', '--foo', '1',
+    ])
+    assert t1.called
+
+def test_consume_args_and_options_3():
+    @tasks.task
+    @tasks.cmdopts([
+        ("foo=", "f", "Help for foo")
+    ])
+    @tasks.consume_args
+    def t1(options):
+        assert options.foo == "1"
+        assert options.t1.foo == "1"
+        assert options.args == []
+
+    environment = _set_environment(t1=t1)
+    tasks._process_commands([
+        't1', '--foo', '1',
+    ])
+    assert t1.called
+
+def test_consume_args_and_options_conflict():
+    @tasks.task
+    @tasks.cmdopts([
+        ("foo=", "f", "Help for foo")
+    ])
+    @tasks.consume_args
+    def t1(options):
+        assert options.foo == "1"
+        assert options.t1.foo == "1"
+        assert options.args == ['abc', 'def', '--foo', '2']
+
+    environment = _set_environment(t1=t1)
+    tasks._process_commands([
+        't1', '--foo', '1', 'abc', 'def', '--foo', '2',
+    ])
+    assert t1.called
+
 def test_consume_nargs():
     # consume all args on first task
     @tasks.task