pyinvoke / invoke

Pythonic task management & command execution.
http://pyinvoke.org
BSD 2-Clause "Simplified" License
4.32k stars 365 forks source link

Issue with Python 3.11, or type annotations: the inspect module changed #833

Closed HacKanCuBa closed 1 year ago

HacKanCuBa commented 2 years ago

I'm having an issue running invoke (latest) in Python 3.11-dev (3.11.0a3). For this tasks file, this is the trace I get:

root@18bb2bd21aaa:/b2s# poetry run inv tests
Traceback (most recent call last):
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/bin/inv", line 8, in <module>
    sys.exit(program.run())
             ^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/program.py", line 373, in run
    self.parse_collection()
    ^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/program.py", line 465, in parse_collection
    self.load_collection()
    ^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/program.py", line 696, in load_collection
    module, parent = loader.load(coll_name)
                     ^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/loader.py", line 76, in load
    module = imp.load_module(name, fd, path, desc)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/imp.py", line 235, in load_module
    return load_source(name, filename, file)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/imp.py", line 172, in load_source
    module = _load(spec)
             ^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 721, in _load
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 902, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/b2s/tasks.py", line 11, in <module>
    def flake8(ctx):
    ^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/tasks.py", line 325, in task
    return klass(args[0], **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/tasks.py", line 76, in __init__
    self.positional = self.fill_implicit_positionals(positional)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/tasks.py", line 167, in fill_implicit_positionals
    args, spec_dict = self.argspec(self.body)
                      ^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/blake2signer-uv6dtlQx-py3.11/lib/python3.11/site-packages/invoke/tasks.py", line 153, in argspec
    spec = inspect.getargspec(func)
           ^^^^^^^^^^^^^^^^^^
AttributeError: module 'inspect' has no attribute 'getargspec'. Did you mean: 'getargs'?

It works perfectly fine for any other Python version I tried (3.7, 3.8, 3.9, 3.10, PyPy3.7, PyPy3.8, Stackless3.7, Stackless3.8).

The module inspect does indeed not have said attribute, and I see that there's a potential replacement done in invoke.vendor.decorator for some reason.

ktbarrett commented 1 year ago

I'm getting a similar error in Python 3.10 if I use type annotations. inspect.getargspec was deprecated in 3.0. After looking at the Python 2 documentation, it seems it doesn't have the suggested replacement inspect.getfullargspec nor the more flexible inspect.signature. Fixing this is going to involve some version specific code.

kasium commented 1 year ago

It seems like invoke is not python 3.11 ready at all

emcd commented 1 year ago

Broken against the 3.11 release.

Looks like @bitprophet has been working on getting rid of Python 2 support, which would make fixing this issue easier. That said, I agree with the original poster that a potential fix already exists in invoke.vendor.decorator. Seems like the invoke.tasks code could use the replacement getargspec implementation provided in that module and we would have a workaround for the issue.

ian-em commented 1 year ago

Until an update is released this can be worked around temporarily with a hack to monkey patch the inspect module in your tasks.py file. Here is an example working for me on Python 3.11:


import inspect

if not hasattr(inspect, 'getargspec'):
    inspect.getargspec = inspect.getfullargspec

from invoke import task

@task
def say_hello(c):
    print("Hello, World!")
manykarim commented 1 year ago

Both PRs below would solve it, right?

https://github.com/pyinvoke/invoke/pull/458 https://github.com/pyinvoke/invoke/pull/606

emcd commented 1 year ago

I ended up forking this repo, applying this commit to the fork, and then referencing the package as invoke @ git+https://github.com/emcd/invoke.git@fb40a3c79b3fa59cb589a642d0c4b8fa87ca082e, which pip (and presumably other Python package utilities/systems) can use to build/install the package directly from the repo. Doing this fixed/worked around the Python 3.11 compatibility issue for me.

Others could do likewise with their own forks, if they need to be unblocked. Not a great long-term solution, obviously....

janrito commented 1 year ago

This monkeypatch worked for me - adapted from @zelo's in https://github.com/pyinvoke/invoke/pull/606

# type: ignore
from collections import namedtuple
from inspect import getfullargspec
from unittest.mock import patch

import invoke

def fix_annotations():
    """
    Pyinvoke doesn't accept annotations by default, this fix that
    Based on: @zelo's fix in https://github.com/pyinvoke/invoke/pull/606
    Context in: https://github.com/pyinvoke/invoke/issues/357
        Python 3.11 https://github.com/pyinvoke/invoke/issues/833
    """

    ArgSpec = namedtuple("ArgSpec", ["args", "defaults"])

    def patched_inspect_getargspec(func):
        spec = getfullargspec(func)
        return ArgSpec(spec.args, spec.defaults)

    org_task_argspec = invoke.tasks.Task.argspec

    def patched_task_argspec(*args, **kwargs):
        with patch(
            target="inspect.getargspec", new=patched_inspect_getargspec, create=True
        ):
            return org_task_argspec(*args, **kwargs)

    invoke.tasks.Task.argspec = patched_task_argspec
JonZeolla commented 1 year ago

I think this is fixed by https://github.com/pyinvoke/invoke/commit/406a45e854f6e8df4aa0de01e3b731fea2b1f1ec, we just need a release

neozenith commented 1 year ago

Sorry I have been absent from triage duties.. life kind of happened.

If anyone want to beta test branches, patches or commits and provide additional test cases you can use this format:

pip install https://github.com/pyinvoke/invoke/archive/406a45e854f6e8df4aa0de01e3b731fea2b1f1ec.zip

Stackoverflow: How to pip install from a github branch or commit

bitprophet commented 1 year ago

Apologies for the radio silence on here myself! The above fix will get released in 2.0.0 which should be out pretty soon (and which will be backwards compatible besides dropping support for Python <3.6).

voidus commented 1 year ago

My take on the workaround, with a warning once it becomes unneccessary:

# Work around https://github.com/pyinvoke/invoke/issues/833
try:

    @task
    def ignore_me(c):
        """Only defined to detect an invoke issue, ignore me"""
        pass

except AttributeError as e:
    if e.args != ("module 'inspect' has no attribute 'getargspec'",):
        raise
    import invoke.tasks  # type: ignore

    invoke.tasks.inspect.getargspec = invoke.tasks.inspect.getfullargspec  # type: ignore
else:
    import warnings

    warnings.warn(
        "Please remove unneccessary monkeypatch, invoke works with python311 now"
    )
ColibriOS commented 1 year ago

Hasta que se publique una actualización, esto se puede solucionar temporalmente con un truco para parchear el inspectmódulo en su tasks.pyarchivo. Aquí hay un ejemplo que me funciona en Python 3.11:

import inspect

if not hasattr(inspect, 'getargspec'):
    inspect.getargspec = inspect.getfullargspec

from invoke import task

@task
def say_hello(c):
    print("Hello, World!")

it works for me on official Nexus 20, thanks it would be great if it could be put in the next update

HacKanCuBa commented 1 year ago

Hasta que se publique una actualización, esto se puede solucionar temporalmente con un truco para parchear el inspectmódulo en su tasks.pyarchivo. Aquí hay un ejemplo que me funciona en Python 3.11:

import inspect

if not hasattr(inspect, 'getargspec'):
    inspect.getargspec = inspect.getfullargspec

from invoke import task

@task
def say_hello(c):
    print("Hello, World!")

it works for me on official Nexus 20, thanks it would be great if it could be put in the next update

it will be in the next release, whenever there is one :)

JonZeolla commented 1 year ago

@HacKanCuBa it's already out

https://github.com/pyinvoke/invoke/releases/tag/2.0.0 https://pypi.org/project/invoke/2.0.0/

bitprophet commented 1 year ago

Yup, 2.0.0 is out, so if anyone is still getting this error please let us know!

barismutanpicus commented 1 year ago

I just tried with invoke=2.0.0 and still getting the same error, here is the traceback:

Traceback (most recent call last): File "/Users/barismutan/yeti/yeti.py", line 7, in <module> from core.web import webapp File "/Users/barismutan/yeti/core/web/__init__.py", line 1, in <module> from core.web.webapp import webapp File "/Users/barismutan/yeti/core/web/webapp.py", line 14, in <module> from core.web.api import api File "/Users/barismutan/yeti/core/web/api/__init__.py", line 1, in <module> from core.web.api.api import api File "/Users/barismutan/yeti/core/web/api/api.py", line 52, in <module> Analysis.register(api) File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/flask_classy.py", line 128, in register rule = cls.build_rule("/", value) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/flask_classy.py", line 244, in build_rule args = get_true_argspec(method)[0] ^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/flask_classy.py", line 297, in get_true_argspec argspec = inspect.getargspec(method) ^^^^^^^^^^^^^^^^^^ AttributeError: module 'inspect' has no attribute 'getargspec'. Did you mean: 'getargs'?

kasium commented 1 year ago

@barismutanpicus seems to be related to flask_class not invoke as you can see in the traceback

bitprophet commented 1 year ago

Yup, that's not us this time 😇

HacKanCuBa commented 1 year ago

bookworm sid 12 (ubuntu-debian) /usr/bin/gnome-language-selector Traceback (most recent call last): File "/usr/bin/gnome-language-selector", line 5, in <module> from LanguageSelector.gtk.GtkLanguageSelector import GtkLanguageSelector File "/usr/lib/python3/dist-packages/LanguageSelector/gtk/GtkLanguageSelector.py", line 27, in <module> import aptdaemon.client File "/usr/lib/python3/dist-packages/aptdaemon/client.py", line 54, in <module> class AptTransaction(GObject.Object): File "/usr/lib/python3/dist-packages/aptdaemon/client.py", line 656, in AptTransaction @convert_dbus_exception ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/aptdaemon/errors.py", line 177, in convert_dbus_exception argnames, varargs, kwargs, defaults = inspect.getargspec(func)

There's no mention of invoke in that trace, it doesn't seem to be related to this package :) Try on gnome-language-selector bug tracker, or ubuntu's bug tracker.

BTW, I now feel guilty that this issue's name is attracting a lot of false positives :P I'm gonna change the title