AttributeError: attribute '__signature__' of 'type' objects is not writable
steog88 opened this issue · comments
I found a problem with mock>2.0.0
.
When I want to patch a class with autospec=True
, if the class itself has already the attribute __signature__
and it is not writable, an AttributeError
is raised when running _check_signature
.
In the real case, the object I want to patch inherits __signature__
from PySide2.QtWidgets.QObject
, so renaming the property is not a viable option.
Although my code is much more complicate, I can reproduce the error with the following code (save to test_mock3.py
and run, I use python
2.7.15, mock
3.0.5, unittest2
1.1.0 and PySide2
5.12.3):
import unittest2 as unittest
from mock import patch
from PySide2.QtCore import QObject
class SampleClass(QObject):
def __init__(self, txt):
self.txt = txt
class TestMock(unittest.TestCase):
def test_autospec(self):
with patch("test_mock3.SampleClass", autospec=True) as _c:
b = SampleClass("b")
if __name__ == "__main__":
unittest.main()
The error is the following:
======================================================================
ERROR: test_autospec (__main__.TestMock)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_mock3.py", line 13, in test_autospec
with patch("test_mock3.SampleClass", return_value=a, autospec=True) as _c:
File "/usr/lib64/python2.7/site-packages/mock/mock.py", line 1476, in __enter__
_name=self.attribute, **kwargs)
File "/usr/lib64/python2.7/site-packages/mock/mock.py", line 2377, in create_autospec
_check_signature(spec, mock, is_type, instance)
File "/usr/lib64/python2.7/site-packages/mock/mock.py", line 198, in _check_signature
type(mock).__signature__ = sig
AttributeError: attribute '__signature__' of 'type' objects is not writable
The same code works with mock==2.0.0
.
Thanks for the report. I guess I added it so that inspect can use __signature__
to give signature of the mock object. Can you reproduce it with normal objects or is this something you see with QObject only? Do you have the same error in Python 3?
Sorry, I read it again now that it's a non-writable property. One solution would be to have catch the AttributeError. I think this would be an upstream bug too.
@tirkarthi - would you prefer @steog88 to re-log this on https://bugs.python.org/ or would you be happy to do so and get a PR for a workaround/fix going there?
I have to admit, I'm confused as to why setting an attribute on a Mock-ish class is failing like this.
That said, _check_signature
has a pretty bad code smell now: it's called _check but sets stuff, it takes a func arg that is often not a func. @tirkarthi - would you be up for sorting that out upstream too?
@tirkarthi no problems with the builtin unittest.mock
in python3
.
It also works if I define
class SampleClass():
def __init__(self, txt):
self.txt = txt
@property
def __signature__(self):
return self.txt
Curious thing, even if SampleClass
does not inherit from QObject
like here, the test stops working if I import QObject
anywhere in the code. I have no idea why.
I am also confused but this is only reproducible with pyside2. type(mock) should actually be setting it to the mocked object but there is some code in NonCallableMock where __new__
that I wish to understand to see this has some metaclass
Opened upstream report : https://bugs.python.org/issue36848
Another report in pytest-qt : pytest-dev/pytest-qt#258
Yes, I don't know how to write a pure python reproducer without pyside2. I just installed pyside2 and grepped for __signature__
. There is some code comment that it triggers signature initialization. I am not sure too :( I will update if I find anything.
$ rg -B 1 '__signature__' foo-venv/
foo-venv/lib/python3.8/site-packages/PySide2/__init__.py
22- # Trigger signature initialization.
23: type.__signature__
foo-venv/lib/python3.8/site-packages/shiboken2/__init__.py
29-# Trigger signature initialization.
30:type.__signature__
foo-venv/lib/python3.8/site-packages/shiboken2/files.dir/shibokensupport/signature/lib/enum_sig.py
117- ret = self.result_type()
118: signature = getattr(func, '__signature__', None)
foo-venv/lib/python3.8/site-packages/shiboken2/files.dir/shibokensupport/signature/layout.py
46-differently formatted versions of signatures. The default
47:layout is known from the "__signature__" attribute.
Closing this in favour of where in appears to need to be fixed: https://bugreports.qt.io/browse/PYSIDE-1004
@steog88 Just to add to this you can pass autospec=False
as a workaround to mock the class where this signature setting code is not executed and you can use the mocked class but with the tradeoff that the calls to mock are signature validated.
Thanks for the report.
Thanks, I will do as suggested then.