-
Notifications
You must be signed in to change notification settings - Fork 231
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
this __dict__ descriptor does not support '_DictWrapper' objects \ since version 1.15.0 #231
Comments
All I can speculate at the moment is that it relates to the change:
Basically, incorrect prior behaviour in wrapt resulted in an exception that was raised by a property wrapper during access not being raised properly in effect hiding an error in that wrapper code. This error only occurred in C extension version of wrapt so if something using wrapt had been correctly also tested against pure Python version of wrapt then it would have been detected and the code using wrapt written properly to handle the exception. In other words, something using wrapt here should be dealing with a lower level exception from what wrapt is wrapping or from the wrapper, but it isn't, or the thing being wrapped shouldn't be raising an exception to begin with and is broken., There is a chance because of how Python works that fixing this is subtly changing how hasattr() worked in the failure case. This might have consequences with following code from tensorflow, which was possibly relying on prior wrong behaviour in wrapt without realising it was an issue that should have been reported, or would have been known of if had tested with pure Python version of wrapt.
The change in what hasattr() is reporting could result in it going down the wrong branch of the if statement triggering code that looks like it will not work. To try and understand this all better, need to work out what was being wrapped in the specific case and what exception was being raised by the property, why, and whether that is a bug or not. That all said, I did know about this difference between pure Python and C extension versions for quite a while (years) and I didn't fix it for specific reasons, which when I finally did recently make change after prompting for related issue, I couldn't remember. Seeing this issue it does remind me that the change possibly wasn't made because of problems in properly dealing with hasattr() check when using C extension. I will have to do some testing to try and remember what those original reasons were that caused me to hold off addressing this difference for so long and why I kept thinking it couldn't be fixed. In the mean time, can you rerun your code when setting the process environment variable:
and see if the behaviour changes. Note that this environment variable should be set and exported in parent process environment (shell) before running your program. It cannot be set from in Python code of the program itself, unless you can guarantee it is set before wrapt module is imported. If the wrapt module has already been imported, too late to set it. |
Related change: |
Having thought about it a bit, I think the reason I originally didn't fix it was because CPython lacks a C API equivalent of the hasattr() function available from Python code. For me to emulate what hasattr() did in my own C code was basically going to be impossible to do and support across multiple versions because of changes of C object model over time. What I dumbly only realised more recently was that hasattr() more or less is the same as invoking getattr() and if it raises AttributeError then the attribute doesn't exist. So the C code was fixed so that instead of ignoring all exceptions like it wrongly used it, it would raise them if not AttributeError and if was AttributeError then fall through to next phase of trying to get attribute. Thus I think the code change I made is valid and fixes the wrong behaviour, but looks like stuff may be relying on the wrong behaviour or otherwise not dealing with the exceptions which are being raised like they should have been. I still can't grok what the layers of tf2onnx and tensorflow are doing in wrapping things to understand what may need to change in them to handle the corrected behaviour. |
Okay so if I understand correctly, you suspect wrong usage based on mistakes in prior wrapt versions. |
Oh wait setting the Environment Variable as you told actually solved the issue. |
Not good if setting the environment variable fixes the issue, was expecting it to still fail. If it also fails then does mean I have introduced a difference. Is disappointing that they just pinned the version and moved on without really seeming to be too interested in the underlying cause. I expect this is going to be hard to track down and understand. I am going to need a really simple example I can run which isn't dependent on having some extra data set to work. |
Neither of the two code examples I can find in or linked to the tensorflow issue replicate the issue when using wrapt 1.15.0. :-( |
And nor does the example from StackOverflow fail either. I will have to try with older tensorflow, or maybe even snapshot if that was version issue occurred with. The current public |
Example for replicating issue is:
and use Running as:
doesn't result in a failure, indicating difference in behaviour with C extension and pure Python versions of wrapt. |
So far it looks like the issue is that the change to correct behaviour in wrapt is exposing an object of type
or:
works for that type and so it blows up. |
My first guess at what maybe they should be doing is:
I have node idea how to validate this. Making the changes means it runs, but I don't know how to check that saved data is actually what is required and can be restored. The
Mucking with |
Another possibility is that for that access type it should not return anything but instead should raise an AttributeError. It is quite possible that higher layers are checking for existence and will do something else if
This probably makes more sense. |
To summarise at this point as I don't think I can do anything more. The behaviour of wrapt changed for the C extension when a bug in wrapt was fixed that caused suppression of exceptions when they should have been raised. With wrapt code now behaving correctly, that resulted in tensorflow acting a bit differently likely due to some inner works of how Python At this point I really need someone in tensorflow community of authors to try with the code above which raises an AttributeError for the specific case of accessing |
BTW, forgot to highlight this, but the trigger for this is that the exception that tensorflow raises:
uses If it changed to |
Ping from tensorflow. Thanks for root causing the issue! I guess you mean TF shouldn't call What does the |
Also, the error message So the best TensorFlow can do is to catch it and reraise as an Attribute error, like this:
Or something similar along the lines. I tried that and the test case in #231 (comment) finished without that error. The pattern was used in several child classes of wrapt objects in data_structures.py in TensorFlow. If this change looks good to you. I can patch them all up. Although I am still quite puzzled (and I am in general puzzled) by the getattribute behavior in Python. |
Sorry for the slow reply, have had a lot going on the last few weeks. The Using that try/except, raising it as |
Thanks. It turns out @k-w-w prepared a very simple fix from saved_model exactly aligned with your suggestion a while ago. The patch was cherrypicked to the 2.13 branch and on its way to RC1. If all goes well, tensorflow 2.13 will work with wrapt 1.15. |
Hi! I got here from investigating a bug report over at CPython: The behaviour of raising Python 3.11.2 (tags/v3.11.2:878ead1, Feb 7 2023, 16:38:35) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import inspect, wrapt
>>> import typing_extensions as te
>>> class Foo: pass
...
>>> class Bar(Foo, wrapt.ObjectProxy): pass
...
>>> Bar({}).__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: this __dict__ descriptor does not support 'Bar' objects
>>> inspect.getattr_static(Bar({}), 'baz')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\alexw\AppData\Local\Programs\Python\Python311\Lib\inspect.py", line 1825, in getattr_static
instance_result = _check_instance(obj, attr)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\alexw\AppData\Local\Programs\Python\Python311\Lib\inspect.py", line 1772, in _check_instance
instance_dict = object.__getattribute__(obj, "__dict__")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: this __dict__ descriptor does not support 'Bar' objects
>>> @te.runtime_checkable
... class Proto(te.Protocol):
... def method(self) -> None: pass
...
>>> isinstance(Bar({}), Proto)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\alexw\coding\getattr-static-repro\.venv\Lib\site-packages\typing_extensions.py", line 604, in __instancecheck__
val = inspect.getattr_static(instance, attr)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\alexw\AppData\Local\Programs\Python\Python311\Lib\inspect.py", line 1825, in getattr_static
instance_result = _check_instance(obj, attr)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\alexw\AppData\Local\Programs\Python\Python311\Lib\inspect.py", line 1772, in _check_instance
instance_dict = object.__getattribute__(obj, "__dict__")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: this __dict__ descriptor does not support 'Bar' objects I'm not yet sure whether the possibility that accessing |
I will do some digging, but on first impression I suspect to resolve resolution order when have multiple inheritance like that with |
As example, if
For that get:
The first error mirrors what would occur if attempted to access
And the second error similarly.
I don't have The big question thus is in the real use case what does |
If have issues with type check, then you may also need to counter that
and provide a property to override those in |
A change in the behaviour of wrapt ends up in a unit test failure for TensorFlow. This can be removed once mitigation in TensorFlow is in place. tensorflow#60687 and GrahamDumpleton/wrapt#231 This is an alternative and less intrusive fix to tensorflow#60688 which was inadvertently reverted by a mis-merge in another commit.
For details, see the thread below, particularly starting with the linked comment: GrahamDumpleton/wrapt#231 (comment) PiperOrigin-RevId: 586406063
Using tf2onnx with the following requirements I ran into a to me unexplainable error. The problem came out of no where, as the same code worked only days before.
requirements:
Error Traceback
I pinned the problem down to one unfrozen dependency in tensorflow: wrapt>=1.11.0
While the old unproblematic runs had 1.14.1 installed, the new failed runs installed 1.15.0
By freezing this to wrapt==1.14.1 I was able to solve the issue.
There are other users reporting similar problems on stackoverflow and the tf2onnx issues.
@GrahamDumpleton Maybe this is an issue with the new release?
The text was updated successfully, but these errors were encountered: