- 
          
- 
                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 all 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 | 
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| Added ``unguarded-next-without-default`` checker which warns about calls to ``next()`` | ||
| without a default value or handling of possible ``StopIteration`` in the same scope. | ||
|  | ||
| Closes #4725 | 
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|  | @@ -463,6 +463,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 should " | ||||||
| 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
       
 | ||||||
| "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: | ||||||
|  | @@ -533,6 +540,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.""" | ||||||
|  | @@ -557,6 +565,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: | ||||||
|  | @@ -794,6 +803,24 @@ def _check_env_function(self, node: nodes.Call, infer: nodes.FunctionDef) -> Non | |||||
| 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: nodes.Call, | ||||||
|  | ||||||
| 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 | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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.