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
Unsynchronized tree: __repr__ recursion fix for #119, #121 #122
Conversation
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.
First of all thank you for such a detailled investigation. LGTM overall, just a few changes need to be made before merging.
Can you add a line in the CHANGELOG explaining the fix (one short line is enough).
Do you know any cases where this solution could raise an AttributeError because
self
doesn't have attributenode_list
?
Me neither. We can always add test cases later on when the problem arises.
Hmm, Should I add my short explanation into the block for 0.6.2(unreleased)? |
If you think that this is going to be too long you can simply refer to this PR :) |
@Psycojoker I just wonder where I should write it ;) |
EDIT: In fact, can you update your branch and merge in the latest master? The 0.6.2 branch has been released already. |
@ibizaman ok, |
Hmm, I am confused a little, these changes would be included into 0.6.2 or I should add it into 0.6.3? |
0.6.3 here :) |
done, check it |
@rojaster Thanks! |
Fix released https://pypi.python.org/pypi/redbaron/0.6.3 |
Possible fix #119 and fix #121 .
(Copied from my email version to @Psycojoker )
After debugging, I found some solutions or hacks:
Let me explain on excerpts from the code.
In master branch problem does not appear, but the bug is not solved. It's not appearing because last changes have double-negative(problem's not in lazy or unlazy logging function):
__repr__
method of Node class conditionif in_a_shell()
is True and dataflow is going down into__str__()
.redbaron.DEBUG
parameter is False. And repr function is not called. String representation of the object wouldn't be called.Look carefully at this code in 0.6.1 and in master branch: the difference is in one comma in call of log function. And that's why in 0.6.1 python tries to get repr of the object before a call of a log function.
The cost of this bug is in one comma you think=) but it is actually still deeper, check.
This problem occurs in 0.6.1 because in https://github.com/PyCQA/redbaron/blob/0.6.1/redbaron/base_nodes.py#L1591
python tries to get a string before it calls a log function. It calls a
__repr__
function implicitly. In__repr__
functionin_a_shell()
returns False for Pycharm or something else that's not a python shell or ipython shell.Next, it's trying to get a path :
https://github.com/PyCQA/redbaron/blob/0.6.1/redbaron/base_nodes.py#L951,
and after a few intermediate calls stops here: https://github.com/PyCQA/redbaron/blob/master/redbaron/base_nodes.py#L121 , to get an index of the node in the parent, but a tree has not synchronized yet with last changes: https://github.com/PyCQA/redbaron/blob/0.6.1/redbaron/base_nodes.py#L1333.
Look, here we have already done insertion of the code.
Node.data
includes insertion, butnode.node_list
does not. Parent points tonode_list
, butnode_list
doesn't have a new insertion. Index method raises the exception -ValueError
(https://docs.python.org/2/tutorial/datastructures.html), because there is no such item.Excerpt from https://github.com/PyCQA/redbaron/blob/master/redbaron/base_nodes.py#L121:
python pos = parent.index(node.node_list if isinstance(node, ProxyList) else node)
forparent.index(node)
raises a ValueError, message of this error calls implicitly__repr__
again to get a representation of the object, I mean, this call is being called again and would try to get path and index again. Because the Tree is not being synchronized before a log call. Yeap,Pycharm(py.test)
tells about it =)write something like this into https://github.com/PyCQA/redbaron/blob/master/redbaron/utils.py#L33:
I tested this code and that's why it looks how it looks to avoid exceptions and misunderstanding.
But it is a hack. And maybe is not useful.
add
try, except
block into https://github.com/PyCQA/redbaron/blob/master/redbaron/base_nodes.py#L121:In this part, we catch exception for ValueError in UserList or another unknown exception(UserList.index raises ValueError)
This is the hack too and code is not clean and clear(IMHO). And this ain't cool.
Delete all
log
calls in https://github.com/PyCQA/redbaron/blob/master/redbaron/base_nodes.py#L1642. But error occurs in another place.or rewrite log function...
But it has already done because (you)@Psycojoker changed a log function call that's satisfied declaration of this method. I described it above.
I've tested it with master and 0.6.1, all is good. but I am not sure that it is a right and idiomatic way.
It doesn't matter that in master I've changed this call.
Do you know any cases where this solution could raise an AttributeError because
self
doesn't have attributenode_list
? I couldn't find.