summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniël van Noord <13665637+DanielNoord@users.noreply.github.com>2021-08-05 06:59:00 +0200
committerGitHub <noreply@github.com>2021-08-05 06:59:00 +0200
commit7d84a322afb129045b5c82790bad56b7d7f4fbc3 (patch)
treeea159d966f239f763bdae15bdafa16ad15fcdd0f
parent8ea16d4077412f1ce7c4ebee551ff8fc0947f35a (diff)
downloadpylint-git-7d84a322afb129045b5c82790bad56b7d7f4fbc3.tar.gz
Add ``disable-next`` option (#4797)
* Add ``disable-next`` option Adding `# pylint: disable-next=msgid` to your file will disable the message for the next line. This closes #1682 * Add documentation, rorganize the FAQ for disable/enable and add ref to the full doc Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
-rw-r--r--ChangeLog4
-rw-r--r--doc/faq.rst16
-rw-r--r--doc/user_guide/message-control.rst15
-rw-r--r--doc/whatsnew/2.10.rst4
-rw-r--r--pylint/exceptions.py4
-rw-r--r--pylint/lint/pylinter.py8
-rw-r--r--pylint/message/message_handler_mix_in.py26
-rw-r--r--pylint/utils/pragma_parser.py4
-rw-r--r--tests/functional/d/disable_msg_next_line.py20
-rw-r--r--tests/functional/d/disable_msg_next_line.txt5
10 files changed, 97 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index e03a9ed33..4e8252b8c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -89,6 +89,10 @@ Release date: TBA
Closes #626
+* Add ``disable-next`` option: allows using `# pylint: disable-next=msgid` to disable a message for the following line
+
+ Closes #1682
+
What's New in Pylint 2.9.6?
===========================
diff --git a/doc/faq.rst b/doc/faq.rst
index d679bc286..02e252457 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -123,12 +123,20 @@ Much probably. Read :ref:`ide-integration`
4.1 How to disable a particular message?
-----------------------------------------------------------
-Add "#pylint: disable=some-message,another-one" at the desired block level
-or at the end of the desired line of code.
-
-A block is either a scope (say a function, a module), or a multiline statement (Try, Finally, if statements, for loops).
+For a single line : Add ``#pylint: disable=some-message,another-one`` at the
+end of the desired line of code. Since Pylint 2.10 you can also use
+``#pylint: disable-next=...`` on the line just above the problem.
+``...`` in the following example is a short hand for the list of
+messages you want to disable.
+
+For larger disable : You can add ``#pylint: disable=...`` at the block level to
+disable for the block. It's possible to enable for the reminder of the block
+with ``#pylint: enable=...`` A block is either a scope (say a function, a module),
+or a multiline statement (try, finally, if statements, for loops).
`It's currently impossible to disable inside an else block`_
+Read :ref:`message-control` for details and examples.
+
.. _`It's currently impossible to disable inside an else block`: https://github.com/PyCQA/pylint/issues/872
4.2 Is there a way to disable a message for a particular module only?
diff --git a/doc/user_guide/message-control.rst b/doc/user_guide/message-control.rst
index 217b1b556..dafbe2af4 100644
--- a/doc/user_guide/message-control.rst
+++ b/doc/user_guide/message-control.rst
@@ -39,6 +39,13 @@ The pragma controls can disable / enable:
a, b = ... # pylint: disable=unbalanced-tuple-unpacking
+* All the violations on the following line
+
+ .. sourcecode:: python
+
+ # pylint: disable-next=unbalanced-tuple-unpacking
+ a, b = ...
+
* All the violations in a single scope
.. sourcecode:: python
@@ -179,6 +186,14 @@ Here's an example with all these rules in a single place:
print(self.bla)
print(self.blop)
+ def meth9(self):
+ """test next line disabling"""
+ # no error
+ # pylint: disable-next=no-member
+ print(self.bla)
+ # error
+ print(self.blop)
+
Detecting useless disables
--------------------------
diff --git a/doc/whatsnew/2.10.rst b/doc/whatsnew/2.10.rst
index de209d5d2..6622c20fa 100644
--- a/doc/whatsnew/2.10.rst
+++ b/doc/whatsnew/2.10.rst
@@ -71,6 +71,10 @@ Other Changes
Closes #626
+* Add ``disable-next`` option: allows using `# pylint: disable-next=msgid` to disable a message for the following line
+
+ Closes #1682
+
* Added ``format-string-without-interpolation`` checker: Emitted when formatting is applied to a string without any variables to be replaced
Closes #4042
diff --git a/pylint/exceptions.py b/pylint/exceptions.py
index 416183abc..68d03b269 100644
--- a/pylint/exceptions.py
+++ b/pylint/exceptions.py
@@ -31,3 +31,7 @@ class InvalidReporterError(Exception):
class InvalidArgsError(ValueError):
"""raised when passed arguments are invalid, e.g., have the wrong length"""
+
+
+class NoLineSuppliedError(Exception):
+ """raised when trying to disable a message on a next line without supplying a line number"""
diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py
index 3c816cbff..c34594fb0 100644
--- a/pylint/lint/pylinter.py
+++ b/pylint/lint/pylinter.py
@@ -490,7 +490,11 @@ class PyLinter(
self._external_opts = options
self.options = options + PyLinter.make_options()
self.option_groups = option_groups + PyLinter.option_groups
- self._options_methods = {"enable": self.enable, "disable": self.disable}
+ self._options_methods = {
+ "enable": self.enable,
+ "disable": self.disable,
+ "disable-next": self.disable_next,
+ }
self._bw_options_methods = {
"disable-msg": self._options_methods["disable"],
"enable-msg": self._options_methods["enable"],
@@ -800,7 +804,7 @@ class PyLinter(
def process_tokens(self, tokens):
"""Process tokens from the current module to search for module/block level
options."""
- control_pragmas = {"disable", "enable"}
+ control_pragmas = {"disable", "disable-next", "enable"}
prev_line = None
saw_newline = True
seen_newline = True
diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py
index b28be128d..495599757 100644
--- a/pylint/message/message_handler_mix_in.py
+++ b/pylint/message/message_handler_mix_in.py
@@ -2,7 +2,7 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
import sys
-from typing import List, Tuple
+from typing import List, Tuple, Union
from pylint.constants import (
_SCOPE_EXEMPT,
@@ -15,7 +15,11 @@ from pylint.constants import (
MSG_TYPES_STATUS,
WarningScope,
)
-from pylint.exceptions import InvalidMessageError, UnknownMessageError
+from pylint.exceptions import (
+ InvalidMessageError,
+ NoLineSuppliedError,
+ UnknownMessageError,
+)
from pylint.interfaces import UNDEFINED
from pylint.message.message import Message
from pylint.utils import get_module_and_frameid, get_rst_section, get_rst_title
@@ -59,6 +63,24 @@ class MessagesHandlerMixIn:
)
self._register_by_id_managed_msg(msgid, line)
+ def disable_next(
+ self,
+ msgid: str,
+ scope: str = "package",
+ line: Union[bool, int] = None,
+ ignore_unknown: bool = False,
+ ):
+ if not line:
+ raise NoLineSuppliedError
+ self._set_msg_status(
+ msgid,
+ enable=False,
+ scope=scope,
+ line=line + 1,
+ ignore_unknown=ignore_unknown,
+ )
+ self._register_by_id_managed_msg(msgid, line + 1)
+
def enable(self, msgid, scope="package", line=None, ignore_unknown=False):
self._set_msg_status(
msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown
diff --git a/pylint/utils/pragma_parser.py b/pylint/utils/pragma_parser.py
index 488b4626c..d57d6a1de 100644
--- a/pylint/utils/pragma_parser.py
+++ b/pylint/utils/pragma_parser.py
@@ -27,7 +27,9 @@ PragmaRepresenter = namedtuple("PragmaRepresenter", "action messages")
ATOMIC_KEYWORDS = frozenset(("disable-all", "skip-file"))
-MESSAGE_KEYWORDS = frozenset(("disable-msg", "enable-msg", "disable", "enable"))
+MESSAGE_KEYWORDS = frozenset(
+ ("disable-next", "disable-msg", "enable-msg", "disable", "enable")
+)
# sorted is necessary because sets are unordered collections and ALL_KEYWORDS
#  string should not vary between executions
#  reverse is necessary in order to have the longest keywords first, so that, for example,
diff --git a/tests/functional/d/disable_msg_next_line.py b/tests/functional/d/disable_msg_next_line.py
new file mode 100644
index 000000000..f500feb1e
--- /dev/null
+++ b/tests/functional/d/disable_msg_next_line.py
@@ -0,0 +1,20 @@
+"""Test if disable-next only disables messages for the next line"""
+# pylint: disable=missing-function-docstring
+# pylint: disable-next=unused-argument, invalid-name
+def function_A(arg1, arg2):
+ return arg1
+
+
+# pylint: disable-next=unused-argument,invalid-name
+def function_B(arg1, arg2):
+ return arg1
+
+
+# pylint: disable-next=invalid-name, f-string-without-interpolation
+def function_C():
+ x = "string" # [unused-variable, invalid-name]
+ return f"This should be a normal string" # [f-string-without-interpolation]
+
+
+def function_D(arg1, arg2): # [unused-argument, invalid-name]
+ return arg1
diff --git a/tests/functional/d/disable_msg_next_line.txt b/tests/functional/d/disable_msg_next_line.txt
new file mode 100644
index 000000000..bfd0adeae
--- /dev/null
+++ b/tests/functional/d/disable_msg_next_line.txt
@@ -0,0 +1,5 @@
+invalid-name:15:4:function_C:"Variable name ""x"" doesn't conform to snake_case naming style":HIGH
+unused-variable:15:4:function_C:"Unused variable 'x'":HIGH
+f-string-without-interpolation:16:11:function_C:"Using an f-string that does not have any interpolated variables":HIGH
+invalid-name:19:0:function_D:"Function name ""function_D"" doesn't conform to snake_case naming style":HIGH
+unused-argument:19:21:function_D:"Unused argument 'arg2'":HIGH