-
-
Notifications
You must be signed in to change notification settings - Fork 275
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
Add orelse_lineno
and orelse_col_offset
to nodes.If
#1480
base: main
Are you sure you want to change the base?
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.
LGTM, that would be very useful indeed.
Drafting this. Thought about this some more over the weekend and I think it makes sense to also pick up the if:
...
elif:
...
else:
... The |
Pull Request Test Coverage Report for Build 2307861275
💛 - Coveralls |
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.
Looks good, I think a review by Marc would be nice but if he does not have the time this can go :)
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.
The current solution is quite inefficient. In worse case, the whole token list is generated multiple times for a single node. It would be better to calculate the end_line
beforehand and using the generate_tokens
iterator directly. For _get_position_info
I've used node.end_lineno
(were available) or iterated until the last line. Since it's a generator, we can exit early easily.
Besides the performance issue, I'm not sure a tokenize solution is the right approach. We need to search a whole range of lines which can contain basically any other nodes. For example, what about a nested function with an IfExp
node in its body? There is also the issue of else
which isn't exclusive to if - else
. It can also be used with try
and for
. All in all, you would need to keep track of a lot of different things which I imaging will be pretty complicated.
Thinking about a different solution, is something preventing us to look at the first node in orelse
and using its lineno
and col_offset
?
Some other questions:
- If that works, do we really need to add new node attributes in astroid? (Or can pylint deal with it itself.)
- What about other block nodes
for
withelse
,try
withexcept
,else
, andfinally
? How do we deal with them?
In the end, is a change worth the effort?
if condition:
do_something()
elif other_condition:
do_something_different()
else:
# We don't want to do anything here
# But we don't to disable a message: # pylint: disable=invalid-name
dont_do_anything() The issue with the approach and the code above is that we don't think comments into account. I thought this might become problematic, especially since the use-case for this is the handle disable comments in these blocks better. Doing The issue then became that I had to take quite large range of lines in order not to pass |
@cdce8p I changed the code to use a pretty naive but working solution. I'm not sure what the impact of Let me know what you think! |
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.
Not particularly pretty, but I guess it should work.
Could you add some more test cases with for - else
and try - except - else
in addition to the nested one? This shouldn't be an issue with the current implementation, but those cases are never the less good as regression tests if we ever need to touch this part again.
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
5461a80
to
4608fe4
Compare
@cdce8p After your comments I found out that we were actually missing some nodes which could have access to the same attributes. |
Let's step back for a moment. Just for me to understand, once the PR is merged how would you integrate the new information so that the issue is fixed? If I remember correctly, wouldn't you need to modify the Something else, although I'm not sure it's an issue, with this PR we're only addressing |
@cdce8p Current iteration is with changing The issue is basically the following example: if x:
1
elif y: # pylint: disable=pointless-statement
1
else:
1 Which lines should be covered by the disable? With this proposal it would be 3 and 4. Similar examples can be seen in the tests. The PR would make it so that every keyword creates its own scope "disable-wise". That's not completely backwards compatible I think, but it does seem a little bit more logical and also more in line with other tools such as |
Steps
Description
This helps to identify where the
else
keyword is. It can be very useful, for example, for this issue: pylint-dev/pylint#872.With a line number for the
else
keyword we can separate the different blocks in theif...else
block and handle the disables accordingly.Let me know what you guys think of this approach.
Type of Changes
Related Issue