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
avoid infinite regress in __getattr__ #41
Conversation
Without the patch, the new test fails like so:
|
This is really confusing to me... I feel like maybe we're missing something here. My guess is it's somehow getting into a never ending loop when the responses Is the munged name actually guaranteed to be stable across versions and Python implementations? |
What happens if instead you do: class CallbackFileWrapper(object):
# ...
def __del__(self):
del self.__calback
del self.__fp
del self.__buf does that work? |
Was my concern as well. |
The exact name-mangling behavior is documented in the language reference: https://docs.python.org/2/reference/expressions.html#atom-identifiers I would very much expect it to be the same cross-implementation. We can also un-mangle this name if it makes everyone happier. |
A printout from faulthandler of the same scenario:
|
fdf7518
to
aeb40ea
Compare
This patch looks reasonable to me -- I don't know if the name manglign is guarnteed, but like 500000 things rely on it, so I wouldn't worry in the least. |
aeb40ea
to
fa0debe
Compare
# The vaguaries of garbage collection means that self.__fp is not always set. | ||
# This strange style lets us throw AttributeError in that case, rather than | ||
# recursing infinitely | ||
fp = object.__getattribute__(self, '_CallbackFileWrapper__fp') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said in IRC, I think checking explicitly for the attribute name in the __getattr__
is cleaner, although if this style is kept, does self.__getattribute__("_CallbackFileWrapper__fp")
not work?
If @alex thinks relying on the name mangling is safe then it's probably safe. |
fa0debe
to
7534ba4
Compare
This looks reasonable enough to me! |
The name mangling still irks me but if it's safe, that's cool. LGTM |
I believe we have consensus. |
Consensus does not a pull request merge. |
@bukzor I'm happy to merge this, but would it be too much to ask to get a little more clarification in the comment? Specifically it would be helpful to get short summary of what happened, why this fixes it and why the I hope this doesn't come across as "nit picky". My real reason is that I'm not terribly sure I totally understand the fix, which means if I look at it again in a few weeks my lack of understanding will undoubtedly multiplied a few times over! Feel free to leave a comment here if that is easier and I'll update the comment myself. Sorry about difficult time debugging this and thanks for the PR! |
avoid infinite regress in __getattr__
@bukzor Nevermind! I found where I was confused. Thanks again! |
@ionrock if I were you, I'd add comments in case future you becomes confused again. |
@iorock 👍 |
FYI, this has been released in 0.10.4. |
After a large amount of debugging a deadlock in pip's test suite, I found an infinite recursion in the below
__getattr__
method. I've fixed it and regression-tested it as best I could.It's not clear to me how an attribute of this class is referenced after its __fp is deleted, but it did happen.
For historicity's sake, this is the stack that caused the inifinite recursion:
I believe that gc is activating during Mapping.update, and jumping to response.closed. Why it would touch a property, I don't know. Further, this is happening after something (the gc I assume) has deleted
__fp
from the filewrapper. Because filewrapper referencesself.__fp
in__getattr__
, and__fp
isn't present, it recurses there. Why I don't get a StackOverflowError rather than a busy deadlock, I also don't know.But, this fixes it :D