-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add unguarded-next-without-default
#6987
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
Changes from 8 commits
f5f95f7
bb4b1b5
ab2b365
cfee6f4
3019dc1
7c89f1d
2df1c68
93636c0
a86e35f
1b33782
bc6e46f
342972c
6ca4d9e
77b2bd5
8ce1109
a3b1d06
da4074e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
next(i for i in (1, 2) if isinstance(i, str)) # [unguarded-next-without-default] | ||
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1 @@ | ||||||||||||||||||
next((i for i in (1, 2) if isinstance(i, str)), None) | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
- `PEP 479 <https://peps.python.org/pep-0479/>`_ |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -434,6 +434,13 @@ class StdlibChecker(DeprecatedMixin, BaseChecker): | |||||
] | ||||||
}, | ||||||
), | ||||||
"W1519": ( | ||||||
"Using next without explicitly specifying a default value or catching the StopIteration", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There's no implicit default, it's going to raise a StopIteration if you don't set the default right ? |
||||||
"unguarded-next-without-default", | ||||||
"Without a default value calls to next() can raise a StopIteration " | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
"exception. This exception should be caught or a default value can " | ||||||
DanielNoord marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
"be provided.", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
), | ||||||
} | ||||||
|
||||||
def __init__(self, linter: PyLinter) -> None: | ||||||
|
@@ -498,6 +505,7 @@ def _check_shallow_copy_environ(self, node: nodes.Call) -> None: | |||||
"deprecated-class", | ||||||
"unspecified-encoding", | ||||||
"forgotten-debug-statement", | ||||||
"unguarded-next-without-default", | ||||||
) | ||||||
def visit_call(self, node: nodes.Call) -> None: | ||||||
"""Visit a Call node.""" | ||||||
|
@@ -522,6 +530,7 @@ def visit_call(self, node: nodes.Call) -> None: | |||||
self._check_for_preexec_fn_in_popen(node) | ||||||
elif isinstance(inferred, nodes.FunctionDef): | ||||||
name = inferred.qname() | ||||||
self._check_next_call(node, name) | ||||||
if name == COPY_COPY: | ||||||
self._check_shallow_copy_environ(node) | ||||||
elif name in ENV_GETTERS: | ||||||
|
@@ -720,6 +729,24 @@ def _check_env_function(self, node, infer): | |||||
allow_none=True, | ||||||
) | ||||||
|
||||||
def _check_next_call(self, node: nodes.Call, name: str) -> None: | ||||||
if name != "builtins.next": | ||||||
return | ||||||
# We don't care about this call if there are zero arguments | ||||||
if len(node.args) != 1: | ||||||
return | ||||||
if utils.get_exception_handlers(node, StopIteration): | ||||||
return | ||||||
Pierre-Sassoulas marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
# Raising is fine within __next__ | ||||||
func_def = utils.get_node_first_ancestor_of_type(node, nodes.FunctionDef) | ||||||
if func_def and func_def.name == "__next__": | ||||||
return | ||||||
self.add_message( | ||||||
"unguarded-next-without-default", | ||||||
node=node, | ||||||
confidence=interfaces.INFERENCE, | ||||||
) | ||||||
|
||||||
def _check_invalid_envvar_value(self, node, infer, message, call_arg, allow_none): | ||||||
if call_arg in (astroid.Uninferable, None): | ||||||
return | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
cell-var-from-loop:117:27:117:28:bad_case.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:122:20:122:21:bad_case2.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:130:27:130:28:bad_case3.<lambda>:Cell variable j defined in loop:UNDEFINED | ||
cell-var-from-loop:140:19:140:20:bad_case4.nested:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:161:20:161:21:bad_case5.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:169:27:169:28:bad_case6.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:177:12:177:13:bad_case7.<lambda>:Cell variable x defined in loop:UNDEFINED | ||
cell-var-from-loop:178:14:178:15:bad_case7.<lambda>:Cell variable y defined in loop:UNDEFINED | ||
cell-var-from-loop:187:27:187:28:bad_case8.<lambda>:Cell variable j defined in loop:UNDEFINED | ||
cell-var-from-loop:197:27:197:28:bad_case9.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:206:26:206:27:bad_case10.func.func2:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:218:17:218:18:bad_case_issue2846.<lambda>:Cell variable n defined in loop:UNDEFINED | ||
cell-var-from-loop:223:18:223:19:bad_case_issue2846.<lambda>:Cell variable n defined in loop:UNDEFINED | ||
cell-var-from-loop:118:27:118:28:bad_case.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:123:20:123:21:bad_case2.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:131:27:131:28:bad_case3.<lambda>:Cell variable j defined in loop:UNDEFINED | ||
cell-var-from-loop:141:19:141:20:bad_case4.nested:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:162:20:162:21:bad_case5.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:170:27:170:28:bad_case6.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:178:12:178:13:bad_case7.<lambda>:Cell variable x defined in loop:UNDEFINED | ||
cell-var-from-loop:179:14:179:15:bad_case7.<lambda>:Cell variable y defined in loop:UNDEFINED | ||
cell-var-from-loop:188:27:188:28:bad_case8.<lambda>:Cell variable j defined in loop:UNDEFINED | ||
cell-var-from-loop:198:27:198:28:bad_case9.<lambda>:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:207:26:207:27:bad_case10.func.func2:Cell variable i defined in loop:UNDEFINED | ||
cell-var-from-loop:219:17:219:18:bad_case_issue2846.<lambda>:Cell variable n defined in loop:UNDEFINED | ||
cell-var-from-loop:224:18:224:19:bad_case_issue2846.<lambda>:Cell variable n defined in loop:UNDEFINED |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
stop-iteration-return:24:4:24:23:gen_stopiter:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:32:4:32:29:gen_stopiterchild:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:39:14:39:21:gen_next_raises_stopiter:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:59:18:59:25:gen_next_inside_wrong_try_except:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:72:12:72:31:gen_next_inside_wrong_try_except2:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:87:18:87:25:gen_dont_crash_on_no_exception:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:129:10:129:35:invalid_object_passed_to_next:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:25:4:25:23:gen_stopiter:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:33:4:33:29:gen_stopiterchild:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:40:14:40:21:gen_next_raises_stopiter:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:60:18:60:25:gen_next_inside_wrong_try_except:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:73:12:73:31:gen_next_inside_wrong_try_except2:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:88:18:88:25:gen_dont_crash_on_no_exception:Do not raise StopIteration in generator, use return statement instead:UNDEFINED | ||
stop-iteration-return:130:10:130:35:invalid_object_passed_to_next:Do not raise StopIteration in generator, use return statement instead:UNDEFINED |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,46 @@ | ||||||||||||||||||||||||||||||
"""Warnings for using next() without specifying a default value.""" | ||||||||||||||||||||||||||||||
# pylint: disable=missing-class-docstring, too-few-public-methods, missing-function-docstring | ||||||||||||||||||||||||||||||
# pylint: disable=inconsistent-return-statements | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
next((i for i in (1, 2)), None) | ||||||||||||||||||||||||||||||
next(i for i in (1, 2)) # [unguarded-next-without-default] | ||||||||||||||||||||||||||||||
var = next(i for i in (1, 2)) # [unguarded-next-without-default] | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||||
next(i for i in (1, 2)) | ||||||||||||||||||||||||||||||
except StopIteration: | ||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||||
next(i for i in (1, 2)) # [unguarded-next-without-default] | ||||||||||||||||||||||||||||||
except ValueError: | ||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||||
next(i for i in (1, 2)) | ||||||||||||||||||||||||||||||
except (ValueError, StopIteration): | ||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||||
next(i for i in (1, 2)) | ||||||||||||||||||||||||||||||
except ValueError: | ||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||
except StopIteration: | ||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
redefined_next = next | ||||||||||||||||||||||||||||||
redefined_next(i for i in (1, 2)) # [unguarded-next-without-default] | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
class MyClass: | ||||||||||||||||||||||||||||||
def __next__(self): | ||||||||||||||||||||||||||||||
return next(i for i in (1, 2)) | ||||||||||||||||||||||||||||||
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Just a thought. Maybe we don't need to check that it's a proper docstring in numpy style or whatever. If there's StopIteration in the docstring we do not raise. |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Example based on astroid code | ||||||||||||||||||||||||||||||
def func(keywords, context): | ||||||||||||||||||||||||||||||
for value in keywords: | ||||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||||
return next(value.infer(context=context)) | ||||||||||||||||||||||||||||||
except StopIteration: | ||||||||||||||||||||||||||||||
continue |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
unguarded-next-without-default:6:0:6:23::Using next without explicitly specifying a default value or catching the StopIteration:INFERENCE | ||
unguarded-next-without-default:7:6:7:29::Using next without explicitly specifying a default value or catching the StopIteration:INFERENCE | ||
unguarded-next-without-default:15:4:15:27::Using next without explicitly specifying a default value or catching the StopIteration:INFERENCE | ||
unguarded-next-without-default:32:0:32:33::Using next without explicitly specifying a default value or catching the StopIteration:INFERENCE |
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.