Skip to content
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

Not preserving low level signature metadada #148

Open
prusse-martin opened this issue Nov 18, 2022 · 1 comment
Open

Not preserving low level signature metadada #148

prusse-martin opened this issue Nov 18, 2022 · 1 comment

Comments

@prusse-martin
Copy link

prusse-martin commented Nov 18, 2022

(Sorry if the title is somewhat misleading or I did get some jargon wrong)

In version 5.1.1 methods decorated by decorate created with the decorator lib stopped working with qt(pyqt?) signals that inspect the signature of the connected slot (also tested with 4.0.2 with a slight different error).

Failing snippet:

@decorator
def dummy_decorator(f, *args, **kwargs):
    return f(*args, **kwargs)

 class Foo:
    def __init__(self):
        self.action = QAction()
        self.action.triggered.connect(self.ActionCallbackNoArg)
    @dummy_decorator
    def ActionCallbackNoArg(self):
        pass

foo = Foo()
foo.action.trigger()

This was not an issue in 4.4.2.
While porting our code base to python 3.10 the decorator version also got upgraded, decorator version prior to this was 4.4.2.
We will probably be able to downgrade back for now.

I created a test to reproduce the error.
add_qt_test_patch.txt

Create env and install decorator in editable mode:

conda create -n decorator python=3.6 pyqt
conda activate decorator
pip install --editable .

Test with 4.4.2 (pass):

W:\my\Projects\decorator (master -> origin)
(decorator) λ git checkout -- src/tests/test.py

W:\my\Projects\decorator (master -> origin)
(decorator) λ git checkout 4.4.2
Note: switching to '4.4.2'.
...
HEAD is now at 55a68b5 Updated setup.py [skip CI]

W:\my\Projects\decorator (HEAD detached at 55a68b5)
(decorator) λ patch -i add_qt_test_patch.txt -p 1
patching file src/tests/test.py
Hunk #1 succeeded at 18 with fuzz 1 (offset 6 lines).

W:\my\Projects\decorator (HEAD detached at 55a68b5)
(decorator) λ python src/tests/test.py -v QtActionTestCase.test_qt_decorator_signature_preserving_interaction_methods
test_qt_decorator_signature_preserving_interaction_methods (__main__.QtActionTestCase) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Test with 5.1.1 (fail, at the time of this writing origin/master will also fail):

W:\my\Projects\decorator (HEAD detached at 55a68b5)
(decorator) λ git checkout -- src/tests/test.py

W:\my\Projects\decorator (HEAD detached at 55a68b5)
(decorator) λ git checkout 5.1.1
Previous HEAD position was 55a68b5 Updated setup.py [skip CI]
HEAD is now at ad013a2 Updated changelog and bumped version to 5.1.1

W:\my\Projects\decorator (HEAD detached at ad013a2)
(decorator) λ patch -i add_qt_test_patch.txt -p 1
patching file src/tests/test.py

W:\my\Projects\decorator (HEAD detached at ad013a2)
(decorator) λ python src/tests/test.py -v QtActionTestCase.test_qt_decorator_signature_preserving_interaction_methods
test_qt_decorator_signature_preserving_interaction_methods (__main__.QtActionTestCase) ... Traceback (most recent call last):
  File "w:\my\projects\decorator\src\decorator.py", line 231, in fun
    args, kw = fix(args, kw, sig)
  File "w:\my\projects\decorator\src\decorator.py", line 203, in fix
    ba = sig.bind(*args, **kwargs)
  File "W:\my\envs\decorator\lib\inspect.py", line 2997, in bind
    return args[0]._bind(args[1:], kwargs)
  File "W:\my\envs\decorator\lib\inspect.py", line 2918, in _bind
    raise TypeError('too many positional arguments') from None
TypeError: too many positional arguments

I also was somewhat curious.
"Plain" functions:

>>> plain.ActionCallbackNoArg.__func__.__code__.co_argcount
1
>>> plain.ActionCallbackNoArg.__func__.__code__.co_varnames
('self',)
>>> plain.ActionCallbackAnyArgs.__func__.__code__.co_argcount
1
>>> plain.ActionCallbackAnyArgs.__func__.__code__.co_varnames
('self', 'args', 'kwargs')

Decorated with 4.4.2:

>>> decorated.ActionCallbackNoArg.__func__.__code__.co_argcount
1
>>> decorated.ActionCallbackNoArg.__func__.__code__.co_varnames
('self',)
>>> decorated.ActionCallbackAnyArgs.__func__.__code__.co_argcount
1
>>> decorated.ActionCallbackAnyArgs.__func__.__code__.co_varnames
('self', 'args', 'kwargs')

Decorated with 5.1.1:

>>> decorated.ActionCallbackNoArg.__func__.__code__.co_argcount
0
>>> decorated.ActionCallbackNoArg.__func__.__code__.co_varnames
('args', 'kw')
>>> decorated.ActionCallbackAnyArgs.__func__.__code__.co_argcount
0
>>> decorated.ActionCallbackAnyArgs.__func__.__code__.co_varnames
('args', 'kw')
@micheles
Copy link
Owner

micheles commented Nov 19, 2022

Decorator 5 (notice the change in the major version number) changed its implementation to not use exec anymore, so it is subtly different from the old versions. There is a way to get the old behavior, but I will be back on this another day, when I will have time ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants