-
Notifications
You must be signed in to change notification settings - Fork 371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using python3 keyword-only arguments or annotated arguments causes a ValueError from inspect #357
Using python3 keyword-only arguments or annotated arguments causes a ValueError from inspect #357
Comments
Use inspect.signature instead of inspect.getargspect when using Python 3 to allow function annotations to be used in tasks. Also adds TaskContextError and fix missing context in Collection test. Fixes pyinvoke#357
Use inspect.signature instead of inspect.getargspect when using Python 3 to allow function annotations to be used in tasks. Also fix missing context in Collection test. Fixes pyinvoke#357
Use inspect.signature instead of inspect.getargspect when using Python 3 to allow function annotations to be used in tasks. Also fix missing context in Collection test. Fixes pyinvoke#357 Make Travis CI flake8 exit with 0 Revert adding custom exception
Use inspect.signature instead of inspect.getargspect when using Python 3 to allow function annotations to be used in tasks. Also fix missing context in Collection test. Fixes pyinvoke#357 Make Travis CI flake8 exit with 0 Revert adding custom exception
Any word on this? I use Python 3 with |
Thanks for the bump, I'll take a look at #373 soon which seems to be the main patch around this. @FranklinChen If you can check out that PR's branch and confirm it works against latest master & solves your particular flavor of the issue, that'd be a big help! |
@bitprophet Yes, I am actually using the fork at #373 and it works for me. |
Hi! I'm still seeing this with version
I am not sure what might need to be done to diagnose or resolve it from my end. Is anyone else still running into this? |
@nathanielford Just run into this too. As it says in the exception itself, replaced |
@SantjagoCorkez I may not be understanding you correctly, but I don't think it'll work for us to manually fork the invoke library code. If I get a chance, though, I'll see if I can put a PR together to submit here, unless the maintainers already have one ready? I'm a little unclear on where they believe the state of the bug to be, given the merged PR. |
@nathanielford Well, the bug is more than 2 years old at the moment. Developers still didn't apply a patch that is proposed by the exception message itself. So, they don't care. You're on your own. |
@bitprophet Hello? How's it going? |
As mentioned in pyinvoke#357 - using `getfullargspec` as recommended in the error itself.
@bitprophet - submitted a patch ^ Seems to be failing on 2.7 and pypy - I can work on adding some logic to fix that tomorrow. |
passing now. |
Hi, |
@italomaia and I encountered this using Fabric 2 with Python 3 (3.7.3 and 3.7.4 on Linux and macOS). Type hints in a Fabric task triggers Several seemingly-easy solutions presented themselves over the years (since 2016?!) in PRs mentioning this issue. To recap:
|
@eruvanos We experimented with Typer as well, but missed the ability to run simple commands like |
Typer is awesome, it leverages the type system and reduces the decorators you'd use with click. Although I agree with @lsorber about the simplicity Inovke provides, specially if your tasks can only depend on Python 3. In my experience, having something close to Inovke in Click/Typer would require to use https://github.com/amoffat/sh, and for some tasks can be tricky, for example, calling pytest with the flags you're using in CI. |
First invoke task: uses pip-compile to update the versions of all the pip packages in requirements.txt and requirements-dev.txt. It also post-processes the output file and removes any comments that have a "-r" reference in them, since those currently cause Salt to break (and are kind of redundant anyway). Unfortunately, as part of writing this I discovered that invoke can't handle type annotations in the definitions of its task functions, so I had to exclude tasks.py from being checked by mypy. That makes me a little nervous about whether invoke is still being maintained. Relevant issue (over 4 years old): pyinvoke/invoke#357
If I declare the variable 'c' as Context, I wish set the type. But, this problem prevent me to type-hinting... |
As a workaround for code completion to work in PyCharm, I have to add type comments: from invoke import context, task
@task
def hello(ctx, name):
c = ctx # type: context.Context
c.run(f"echo 'Hello {name}'") or sometimes I specify the type in docstrings: from invoke import context, task
@task
def hello(c, name):
"""
My docstring here.
:type c: context.Context
"""
c.run(f"echo 'Hello {name}'") |
Heya, it looks like this still isn't fixed? Any idea what the blockers are? It's been years |
Python 2 doesn't support type hinting, So removing all the type hinting code will fix the issue. |
@karan-webkul Python 2 is at EOL, and while everyone using this package already is avoiding using type hints because it does not support it, this issue is around the fact that we'd like to both use the package and type hints, like other modern Python 3 apps. |
@zelo 's workaround works pretty well, any update on this to avoid this workaround? |
I do something similar to what @dralshehri showed, but for the function. from invoke import Context, task
@task
def run(c):
# type: (Context) -> None
c.run("python src/main.py") |
I'll revisit this after dropping Python 2, which is one of the next things on the roadmap. Should make it much easier to apply/fix overall. |
@exhuma I decided to do something like this, just automatically. I created a wrapper around import inspect
import invoke
import makefun
from typing import Optional
def task(*taskargs, **taskkwargs):
def decorator(fun):
"""
Define invoke.task, while removing annotations from the visible signature in order to work around invoke bug pyinvoke/invoke/357
"""
# Copy over `fun` signature to `wrapper`
# without annotations to work around https://github.com/pyinvoke/invoke/issues/357
func_sig = inspect.Signature(
[
p.replace(annotation=inspect.Parameter.empty)
for p in inspect.signature(fun).parameters.values()
]
)
@invoke.task(*taskargs, **taskkwargs)
@makefun.wraps(fun, new_sig=func_sig)
def wrapper(ctx, *args, **kwargs):
return fun(ctx, *args, **kwargs)
return wrapper
if not taskkwargs and len(taskargs) == 1 and callable(taskargs[0]):
# The decorator was used without any arguments
fun = taskargs[0]
taskargs = []
return decorator(fun)
return decorator
@task
def hello(ctx, name: str, surname: Optional[str] = None) -> None:
"""Greets you by your name and (optionally) surname"""
if surname is None:
print(f"Hello {name}!")
else:
print(f"Hello {name} {surname}!")
@task(default=True)
def hi(ctx) -> None:
"""Says Hi you :)"""
print("Hi you") Usage: $ inv -l
Available tasks:
hello Greets you by your name and (optionally) surname
hi Says Hi you :)
Default task: hi
$ inv
Hi you
$ inv --help hello
Usage: inv[oke] [--core-opts] hello [--options] [other tasks here ...]
Docstring:
Greets you by your name and (optionally) surname
Options:
-n STRING, --name=STRING
-s STRING, --surname=STRING
$ inv hello John
Hello John!
$ inv hello John --surname=Doe
Hello John Doe! |
@bitprophet - have you folks started working on this support? I'd be interested in contributing this support if not! |
@bitprophet - I would also like to contribute. I have previously worked on In particular I am coming from a few years over in Typescript where projects like
Especially as a consultant I can provide packages of prebaked tasks similar to the If there is a gitter channel hit me up or DM me on LinkedIn or twitter. |
For those playing along at home I am now working on issue and PR triage to help catch up on the backlog that has accumulated. https://bitprophet.org/projects/#roadmap
Also But I am doing my best over the coming weeks and months to help close the items that will have the greatest impact for our limited cognitive bandwidth and volunteer time. |
another workaround here. monkey patch #373 import inspect
import six
import types
from invoke.tasks import Task, NO_DEFAULT
if six.PY3:
from itertools import zip_longest
else:
from itertools import izip_longest as zip_longest
def patched_argspec(self, body):
"""
Returns two-tuple:
* First item is list of arg names, in order defined.
* I.e. we *cannot* simply use a dict's ``keys()`` method here.
* Second item is dict mapping arg names to default values or
`.NO_DEFAULT` (an 'empty' value distinct from None, since None
is a valid value on its own).
"""
# Handle callable-but-not-function objects
# TODO: __call__ exhibits the 'self' arg; do we manually nix 1st result
# in argspec, or is there a way to get the "really callable" spec?
func = body if isinstance(body, types.FunctionType) else body.__call__
if six.PY3:
sig = inspect.signature(func)
arg_names = [k for k, v in sig.parameters.items()]
spec_dict = {}
for k, v in sig.parameters.items():
value = v.default if not v.default == sig.empty else NO_DEFAULT
spec_dict.update({k: value})
else:
spec = inspect.getargspec(func)
arg_names = spec.args[:]
matched_args = [
reversed(x) for x in [spec.args, spec.defaults or []]]
spec_dict = dict(zip_longest(*matched_args, fillvalue=NO_DEFAULT))
# Pop context argument
try:
context_arg = arg_names.pop(0)
except IndexError:
# TODO: see TODO under __call__, this should be same type
raise TypeError("Tasks must have an initial Context argument!")
del spec_dict[context_arg]
return arg_names, spec_dict
Task.argspec = types.MethodType(patched_argspec, Task) save above code to import hotfix
from invoke import task,Context
@task
def hello(c: Context):
c.run('echo Hello world!') |
|
2.0.0 will be out soon & is now using |
Defining a task that uses the new Python3 keyword-only or annotation argument syntax and invoking causes a
ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them
and halts.Per the docstring for Python3 inspect, getfullargspec() could be used, but that function is itself deprecated in favor of inspect.signature() Unfortunately, neither of those functions exists in Python 2.6, so some level of cascading will be necessary.
For example, neither of these can be invoked:
The text was updated successfully, but these errors were encountered: