-
Notifications
You must be signed in to change notification settings - Fork 233
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
pydevd error when debugging with wrapt #257
Comments
The debugger is trying to access attributes of the wrapt ProxyObject instance before it is initialized properly. The exception is due to that access when it is in an uninitialized state. All you can really do is step over the initialization of the decorator and not into it, or see if it you can run to exit of the function in hope that the debugger isn't still trying to introspect the object instance while it is in the uninitialized state. |
Hi Graham, Do you think the problem is with the debugger? kind regards |
It is the nature of debuggers that they may provide a live view of objects on the stack or otherwise somehow in scope. If that feature is enabled, possibly simply by having a tracing view in the debugger visible, it may try to aggressively introspect objects, which could be an issue when objects can be in intermediate initialised states. That said, it should ignore any exceptions when doing introspection and not fail, so am not sure, especially not on the limited information you have given. If you could record a video and post it on YouTube or somewhere else where I could watch how you use the debugger and what you are doing, plus where the error shows, that would be helpful as I don't use Python debuggers myself so don't know what they might actually be doing. |
Hi @GrahamDumpleton, I'm having the same issue. Here's a video and traceback. Additional details:
|
Can you confirm that if you tell the debugger to continue from that point that it then will run until the next break point or exception? Also, is there a way to tell the debugger to ignore exceptions which occur at certain points in the code when it is doing its introspection and continue running anyway? As alluded to before, the issue is that the debugger is inserting a tracing function which then tries to introspect objects for every Python opcode (??) execution. This means it could easily trigger exceptions when objects are in an intermediary initialised state. Right now am not sure what options I have to avoid this occurring. One might be to raise an AttributeError instead of ValueError exception. The AttributeError is often treated differently by things and causes stuff to fallback to some other action and do something differently or just ignore things. If you wanted to see if changing the exception type would help, you would need to get down a copy of wrapt source code and change all places which raise ValueError with message "wrapper has not been initialized" to AttributeError instead. Install from that modified source code into your local virtual environment for testing and try again. |
If I tell it to continue, the debugger just terminates with that traceback in the console (plus "Process finished with exit code 1 at the bottom). There is no way of continuing to debug. I'll give changing the exception type a try. |
I do see the same problem, that I could reduce to the following minimal case:
Running this (useless) code works (as in, it does nothing but does not raise anything) but if I run it through the intellij/pycharm debugger, the same exception as seen by the OP stops the process (no resume if I ask the debugger to continue). Here is the stack trace and context (paths shortened):
The "solution" I have for now is to go back to python3.11, the issue does not exist with it. Any help will be greatly appreciated. As it is, it makes anything using wrapt unusable with jetbrains' debugger (like in my case, |
Do you get the same problem if you set and export the environment variable:
This will clarify whether it is only specific to C extension variant of wrapt, or also the pure Python version. If know the pure Python version is affected, then I can try and create a small test case (independent of wrapt), which I believe may replicate the specific code I think is giving the debugger a problem. If that shows the issue, then the exception type change as already explained above can be tried. If can narrow it done that way, then can provide the details to debugger authors and they can look at the issue of why they fail on the particular code. |
@GrahamDumpleton I get the same problem even with that environment variable set. This problem could be related to implementation of PEP 669 in Python 3.12. I meant to check this, but it slipped my mind until I saw @hartym's comment. I can confirm that I do not have this problem in a Python 3.10 or 3.11 virtual environment. Looking at the pydev issue tracker, I don't see anything about this issue definitively, but there are a couple of other issues occurring in 3.12 that may have the same origin, including that pydev's own tests are failing. @hartym May I ask what OS you're using? I can see that the original OP is using Windows, as am I. Given the tests were failing for Windows and Mac in the last pydev release, but not Linux, I'm wondering if this problem is OS-specific. |
@cerrussell I'm using MacOS 12.6 @GrahamDumpleton I tried running again my "test case" using python 3.12 and with |
@hartym @endmarsfr @GrahamDumpleton Found a workaround on the PyCharm issues tracker. In PyCharm, go to Help > Find Action > Registry and uncheck the box for python.debug.low.impact.monitoring.api.
|
Hi @cerrussell |
Can someone try and run this little test program through the debugger to see if it fails in same was as debugger has so far.
If it doesn't I will try and tweak the example further as right now it doesn't mirror exactly what pure Python version of wrapt code does. |
@GrahamDumpleton It didn't fail for me. |
I just modified it so there would be code run before the |
I have made another change to the code to try. This time added a Fudging up a simple debugger which tries to introspect an object, one would see:
So try with latest code from comment #257 (comment) |
@GrahamDumpleton No errors still. |
Try again with this one. We just need to try and work out which special method might be tripping it up.
|
@GrahamDumpleton That one got the error! |
Do you get a nice stack traceback so we know which access is the problem? |
Here ya go. I think the repr exception is from PyCharm trying to render the variables after the initial exception. |
Seems to be the class Object: pass
class Wrapper:
def __init__(self, force_error=False):
if force_error:
print(self.__wrapped__)
self.func()
object.__setattr__(self, "__wrapped__", Object())
def __getattr__(self, name):
if name == '__wrapped__':
raise ValueError('wrapper has not been initialised')
return getattr(self.__wrapped__, name)
@property
def __class__(self):
return self.__wrapped__.__class__
@__class__.setter
def __class__(self, value):
self.__wrapped__.__class__ = value
def func(self):
pass
wrapper = Wrapper() |
I use pytest and this fixture temporarily fixes the problem until a permanent fix can be made. It can easily be adapted to unittest or another framework. @pytest.fixture(scope="session")
def patch_wrapt_for_pycharm():
from wrapt import decorators, FunctionWrapper
from wrapt.decorators import AdapterWrapper, _AdapterFunctionSurrogate
class _PatchedAdapterFunctionSurrogate(_AdapterFunctionSurrogate):
@property
def __class__(self):
try:
return super().__class__
except ValueError:
return type(self)
class PatchedAdapterWrapper(AdapterWrapper):
def __init__(self, *args, **kwargs):
adapter = kwargs.pop("adapter")
FunctionWrapper.__init__(self, *args, **kwargs)
self._self_surrogate = _PatchedAdapterFunctionSurrogate(self.__wrapped__, adapter)
self._self_adapter = adapter
@property
def __class__(self):
try:
return super().__class__
except ValueError:
return type(self)
with pytest.MonkeyPatch.context() as patch:
patch.setattr(decorators, "AdapterWrapper", PatchedAdapterWrapper)
yield It's important to note this patching needs to run before This test will not raise a PyCharm debugging error def test_wrapt(patch_wrapt_for_pycharm):
@wrapt.decorator
def pass_through(wrapped, instance, args, kwargs):
return wrapped(*args, **kwargs)
@pass_through
def function():
print("Hello world")
function() This test will @wrapt.decorator
def pass_through(wrapped, instance, args, kwargs):
return wrapped(*args, **kwargs)
def test_wrapt(patch_wrapt_for_pycharm):
@pass_through
def function():
print("Hello world")
function() |
@GrahamDumpleton I've narrowed it down further. It appears PyCharm does not like the from wrapt.wrappers import ObjectProxy
class ObjectProxyWithAttributeError(ObjectProxy):
def __getattr__(self, name):
if name == '__wrapped__':
raise AttributeError('wrapper has not been initialised')
return getattr(self.__wrapped__, name)
passes = ObjectProxyWithAttributeError("test") # This works
fails = ObjectProxy("test") # This raises an error from the PyCharm debugger Something like this also works and would be more backwards compatible class WrapperNotInitalisedError(AttributeError, ValueError):
def __init__(self, msg:str = 'wrapper has not been initialised'):
super().__init__(msg)
class ObjectProxyWithAttributeError(ObjectProxy):
def __getattr__(self, name):
if name == '__wrapped__':
raise WrapperNotInitalisedError()
return getattr(self.__wrapped__, name) I hope this helps! |
@zyoung-rc I confirm your fixture works for me, the small adjustments I made were to use scope="session" (should the patch be applied once per module ? I believe for now that it can be set for the whole testing session) and autouse=True (so I don't have to explicitely request it in each test). |
Thanks for the confirmation that raising As to monkey patching a temporary fix into existing code, I suspect that wrapt could be used to do that and have it monkey patch itself. I will need to play with that idea as a temporary fix. |
@hartym I edited my comment to reflect the usage of @GrahamDumpleton Glad to help! I would have written a PR, but, sadly, my C skills are non-existent. |
switch python to 3.10 can use |
Hi all, EDIT: sorry I missed the MR mentioned above, on which version is it? thanks in advance |
Was actually hoping to catch up on this issue and look at it again in the coming week as finally have some time available. |
Hi there,
I tested with python 3.12 & python 3.12.1 (x64) on windows 10 22H2.
When I debug with pycharm 2023.3 and wrapt-1.16.0, I get this error:
File "C:\Users\endmarsfr\AppData\Local\Programs\Python\Lib\site-packages\wrapt\decorators.py", line 239, in _build return AdapterWrapper(wrapped=wrapped, wrapper=wrapper, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "_pydevd_bundle/pydevd_pep_669_tracing_cython.pyx", line 504, in _pydevd_bundle.pydevd_pep_669_tracing_cython.PyRaiseCallback.__call__ frame = self.frame File "_pydevd_bundle/pydevd_pep_669_tracing_cython.pyx", line 47, in _pydevd_bundle.pydevd_pep_669_tracing_cython.PEP669CallbackBase.frame while frame and isinstance(frame.f_locals.get('self'), PEP669CallbackBase): ValueError: wrapper has not been initialized python-BaseException
Kind regards
The text was updated successfully, but these errors were encountered: