Permalink
Browse files

Don't crash on tasks with annotations (#170)

We've been adding type hints to our Python code for a while now, we'd
also like to annotate functions in pavement files but we can't currently.

Let's have a sample pavement file:

    from paver.easy import task

    @task
    def say_hello() -> None:
        print('hello')

Before this patch running "paver say_hello" would result in:

    % paver say_hello
    Traceback (most recent call last):
      (...)
      File "/Volumes/smarkets/paver/paver/__main__.py", line 2, in <module>
        paver.tasks.main()
      File "/Volumes/smarkets/paver/paver/tasks.py", line 886, in main
        _launch_pavement(args)
      File "/Volumes/smarkets/paver/paver/tasks.py", line 866, in _launch_pavement
        _process_commands(args, auto_pending=auto_pending)
      File "/Volumes/smarkets/paver/paver/tasks.py", line 817, in _process_commands
        task()
      File "/Volumes/smarkets/paver/paver/tasks.py", line 329, in __call__
        retval = environment._run_task(self.name, self.needs, self.func)
      File "/Volumes/smarkets/paver/paver/tasks.py", line 159, in _run_task
        (funcargs, varargs, varkw, defaults) = inspect.getargspec(func)
      File "/usr/local/Cellar/python3/3.5.2/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py", line 1045, in getargspec
        raise ValueError("Function has keyword-only arguments or annotations"
    ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them
  • Loading branch information...
1 parent ecb793a commit 133969e025f4cf21681fd215442e85c35dbe75f2 @jstasiak jstasiak committed with Almad Aug 14, 2016
Showing with 28 additions and 2 deletions.
  1. +6 −1 paver/tasks.py
  2. +22 −1 paver/tests/test_tasks.py
View
7 paver/tasks.py
@@ -156,7 +156,12 @@ def call_task(self, task_name, args=None, options=None):
task()
def _run_task(self, task_name, needs, func):
- (funcargs, varargs, varkw, defaults) = inspect.getargspec(func)
+ try:
+ getfullargspec = inspect.getfullargspec
+ except AttributeError:
+ (funcargs, varargs, varkw, defaults) = inspect.getargspec(func)
+ else:
+ (funcargs, varargs, varkw, defaults, _, _, _) = getfullargspec(func)
kw = dict()
for i in xrange(0, len(funcargs)):
arg = funcargs[i]
View
23 paver/tests/test_tasks.py
@@ -2,7 +2,7 @@
import os
from pprint import pprint
-from paver.deps.six import print_
+from paver.deps.six import exec_, PY2, print_
from paver import setuputils, misctasks, tasks, options
@@ -934,3 +934,24 @@ def t2(options):
assert t1.called
assert t2.called
+
+
+if not PY2:
+ def test_paver_doesnt_crash_on_task_function_with_annotations():
+ local_scope = {}
+ # exec()ing so that it doesn't crash when this test file is run
+ # under Python 2 which doesn't support this syntax
+ exec_(
+ """
+@tasks.task
+def fun() -> None:
+ pass""",
+ globals(), local_scope,
+ )
+ fun = local_scope['fun']
+ environment = _set_environment(fun=fun)
+
+ # This call would fail with:
+ # ValueError: Function has keyword-only arguments or annotations,
+ # use getfullargspec() API which can support them
+ fun()

0 comments on commit 133969e

Please sign in to comment.