-
-
Notifications
You must be signed in to change notification settings - Fork 255
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
Rename .closed property to avoid recursion #197
Conversation
Argh, I'm sorry to having introduced such regression with my commit... 😬 Thanks for having investigated this and quickly found a successful solution! 😃 I would like to examine this bug too, but I'm unable to reproduce the error. Basic example works fine: from colorama import init, Fore
init(autoreset=True)
print(Fore.RED + 'some red text')
print('automatically back to default color again') I can't figure out how to introduce the offending recursion. 😕 Basically, I would like to reproduce and understand the bug to:
@bbayles Do you have some code snippet I could use to trigger the bug please? |
The problem is that things stack up. If I add a debug statement to the @property
def closed(self):
print('is closed?')
stream = self.__wrapped
return not hasattr(stream, 'closed') or stream.closed Then do something like this will produce this output:
Switching to an older version (or my branch here), you get this:
So there are more checks after 2057f03. Subsequent calls to init (on On an older verison (or my branch here), it's always two checks. |
@bbayles Thanks for the explanations! So, there is no infinite recursion nor method calling itself (just method calling the same method, but the one of the wrapped stream), right? The issue is caused by too much nested calls, occuring while checking To better vizualise what caused these excessive calls after 2057f03, I drew the call stack before and after your fix. Before your fix, accessing @property
def closed(self):
stream = self.__wrapped
return not hasattr(stream, 'closed') or stream.closed But as For example if Before your fix, the call stack would look like this:
As your fix removes the def __getattr__(self, name):
return getattr(self.__wrapped, name) The calls stack looks like this while accessing
Much shorter, and prevent the number of calls to exponentially grows! I think we can have the best of both worlds, that is keep the linear number of calls while accessing What do you think of re-implementing the property like this: @property
def closed(self):
stream = self.__wrapped
try:
return stream.closed
except AttributeError:
return True The resulting call stack would be:
Even simpler! The same fix should be done for |
The @property
def closed(self):
print('is this closed?')
stream = self.__wrapped
try:
return stream.closed
except AttributeError:
return True
|
@bbayles It doesn't seem exponential on my computer, more like You should also tries to add the By the way, this makes me wonder... As the I made my tests by adding a global |
Re: whether it's exponential, I think the number shouldn't be growing at all. Re: printing; we can reproduce the same issue printing to stderr instead of stdout; I don't think it matters. My main thought is that the exponential behavior in the currently released version is significant, and will probably cause problems with downstream projects, at least in test suites. It's probably appropriate to apply my fix here (or revert 2057f03) and release 0.4.1? |
It does. The reason you get this growing number of calls is because you are using "autoreset" and debug-printing from inside the "closed" property. When colorama auto-resets, it checks whether the stream is closed to print the reset sequence. So if every call to "closed" causes a print, it also causes a reset and causes another call to "closed". That would explain the recursion in the first case and the growing prints in after your changes. |
@bbayles The number has to grown, each subsequent call to
I agree with you, the current code on
@wiggin15 I think I understood the issue, as explained in my second message, there is indeed a problem with excessive nested calls, even by omitting erroneous calls to |
Re: issue #196, the exponential calls were causing my hanging problem. Re: a fix, you're welcome to close this PR and go with some other fix. I'm happy as long as PyPI gets updated promptly to remove the exponential behavior. |
Re: issue #196, this PR changes one aspect of #164 that was causing me problems.
When using
autoreset=True
, this check seems to happen over and over. This method is calling itself, sinceStreamWrapper
can be the stream.Adding a print statement here:
So we basically have a name collision. In order to avoid that I've renamed the
StreamWrapper.closed
property to.is_closed
. I'm happy to name it something else, or turn it back into a method instead of a property.