Skip to content

Commit

Permalink
Add format-string-without-interpolation checker (#4794)
Browse files Browse the repository at this point in the history
* Add ``format-string-without-interpolation`` checker
This adds a checker that checks strings with '%' or format() applied to them.
If no variables to be replaced are found the warning is emitted.
Closes #4042

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Aug 4, 2021
1 parent 02e5289 commit ea7f39e
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 16 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Expand Up @@ -75,6 +75,10 @@ Release date: TBA
Closes #3608
Closes #4346

* Added ``format-string-without-interpolation`` checker: Emitted when formatting is applied to a string without any variables to be replaced

Closes #4042


* Refactor of ``--list-msgs`` & ``--list-msgs-enabled``: both options now show whether messages are emittable with the current interpreter.

Expand Down
4 changes: 4 additions & 0 deletions doc/whatsnew/2.10.rst
Expand Up @@ -70,3 +70,7 @@ Other Changes
in an exception handler, but used outside of the handler.

Closes #626

* Added ``format-string-without-interpolation`` checker: Emitted when formatting is applied to a string without any variables to be replaced

Closes #4042
13 changes: 13 additions & 0 deletions pylint/checkers/strings.py
Expand Up @@ -204,6 +204,12 @@
"Used when we detect an f-string that does not use any interpolation variables, "
"in which case it can be either a normal string or a bug in the code.",
),
"W1310": (
"Using formatting for a string that does not have any interpolated variables",
"format-string-without-interpolation",
"Used when we detect a string that does not have any interpolation variables, "
"in which case it can be either a normal string without formatting or a bug in the code.",
),
}

OTHER_NODES = (
Expand Down Expand Up @@ -273,6 +279,7 @@ class StringFormatChecker(BaseChecker):
"too-many-format-args",
"too-few-format-args",
"bad-string-format-type",
"format-string-without-interpolation",
)
def visit_binop(self, node):
if node.op != "%":
Expand Down Expand Up @@ -301,6 +308,9 @@ def visit_binop(self, node):
except utils.IncompleteFormatString:
self.add_message("truncated-format-string", node=node)
return
if not required_keys and not required_num_args:
self.add_message("format-string-without-interpolation", node=node)
return
if required_keys and required_num_args:
# The format string uses both named and unnamed format
# specifiers.
Expand Down Expand Up @@ -518,6 +528,9 @@ def _check_new_format(self, node, func):
if check_args:
# num_args can be 0 if manual_pos is not.
num_args = num_args or manual_pos
if not num_args:
self.add_message("format-string-without-interpolation", node=node)
return
if len(positional_arguments) > num_args:
self.add_message("too-many-format-args", node=node)
elif len(positional_arguments) < num_args:
Expand Down
5 changes: 1 addition & 4 deletions pylint/config/__init__.py
Expand Up @@ -115,8 +115,7 @@ def find_pylintrc():

PYLINTRC = find_pylintrc()

ENV_HELP = (
"""
ENV_HELP = """
The following environment variables are used:
* PYLINTHOME
Path to the directory where persistent data for the run will be stored. If
Expand All @@ -126,5 +125,3 @@ def find_pylintrc():
Path to the configuration file. See the documentation for the method used
to search for configuration file.
"""
% globals() # type: ignore
)
6 changes: 5 additions & 1 deletion tests/functional/s/string/string_formatting.py
Expand Up @@ -65,7 +65,7 @@ def print_good():

def pprint_bad():
"""Test string format """
"{{}}".format(1) # [too-many-format-args]
"{{}}".format(1) # [format-string-without-interpolation]
"{} {".format() # [bad-format-string]
"{} }".format() # [bad-format-string]
"{0} {}".format(1, 2) # [format-combined-specification]
Expand Down Expand Up @@ -98,6 +98,10 @@ def pprint_bad():
"{[0]} {}".format([4], 5, 6) # [too-many-format-args]
logging.debug("%s %s", 42) # [logging-too-few-args]
logging.debug("%s", 42, 43) # [logging-too-many-args]
"String".format(1) # [format-string-without-interpolation]
"String".format(()) # [format-string-without-interpolation]
"String".format([]) # [format-string-without-interpolation]
"String".format(None) # [format-string-without-interpolation]


def good_issue288(*args, **kwargs):
Expand Down
26 changes: 15 additions & 11 deletions tests/functional/s/string/string_formatting.txt
@@ -1,4 +1,4 @@
too-many-format-args:68:4:pprint_bad:Too many arguments for format string
format-string-without-interpolation:68:4:pprint_bad:Using formatting for a string that does not have any interpolated variables
bad-format-string:69:4:pprint_bad:Invalid format string
bad-format-string:70:4:pprint_bad:Invalid format string
format-combined-specification:71:4:pprint_bad:Format string contains both automatic field numbering and manual field specification
Expand Down Expand Up @@ -31,13 +31,17 @@ too-few-format-args:97:4:pprint_bad:Not enough arguments for format string
too-many-format-args:98:4:pprint_bad:Too many arguments for format string
logging-too-few-args:99:4:pprint_bad:Not enough arguments for logging format string
logging-too-many-args:100:4:pprint_bad:Too many arguments for logging format string
too-few-format-args:128:4:nested_issue294:Not enough arguments for format string
too-many-format-args:129:4:nested_issue294:Too many arguments for format string
missing-format-argument-key:130:4:nested_issue294:Missing keyword argument 'a' for format string
missing-format-attribute:131:4:nested_issue294:Missing format attribute 'x' in format specifier 'a.x'
too-few-format-args:137:4:issue310:Not enough arguments for format string
too-many-format-args:145:4:issue322:Too many arguments for format string
too-few-format-args:146:4:issue322:Not enough arguments for format string
too-few-format-args:171:4:issue351:Not enough arguments for format string
too-many-format-args:173:4:issue351:Too many arguments for format string
bad-format-string:209:11:avoid_empty_attribute:Invalid format string
format-string-without-interpolation:101:4:pprint_bad:Using formatting for a string that does not have any interpolated variables
format-string-without-interpolation:102:4:pprint_bad:Using formatting for a string that does not have any interpolated variables
format-string-without-interpolation:103:4:pprint_bad:Using formatting for a string that does not have any interpolated variables
format-string-without-interpolation:104:4:pprint_bad:Using formatting for a string that does not have any interpolated variables
too-few-format-args:132:4:nested_issue294:Not enough arguments for format string
too-many-format-args:133:4:nested_issue294:Too many arguments for format string
missing-format-argument-key:134:4:nested_issue294:Missing keyword argument 'a' for format string
missing-format-attribute:135:4:nested_issue294:Missing format attribute 'x' in format specifier 'a.x'
too-few-format-args:141:4:issue310:Not enough arguments for format string
too-many-format-args:149:4:issue322:Too many arguments for format string
too-few-format-args:150:4:issue322:Not enough arguments for format string
too-few-format-args:175:4:issue351:Not enough arguments for format string
too-many-format-args:177:4:issue351:Too many arguments for format string
bad-format-string:213:11:avoid_empty_attribute:Invalid format string
4 changes: 4 additions & 0 deletions tests/functional/s/string/string_formatting_error.py
Expand Up @@ -18,3 +18,7 @@ def pprint():
print("%2z" % PARG_1) # [bad-format-character]
print("strange format %2" % PARG_2) # [truncated-format-string]
print("works in 3 %a" % 1)
print("String" % PARG_1) # [format-string-without-interpolation]
print("String" % ()) # [format-string-without-interpolation]
print("String" % []) # [format-string-without-interpolation]
print("String" % None) # [format-string-without-interpolation]
4 changes: 4 additions & 0 deletions tests/functional/s/string/string_formatting_error.txt
Expand Up @@ -9,3 +9,7 @@ format-needs-mapping:16:10:pprint:Expected mapping for format string, not Tuple
format-needs-mapping:17:10:pprint:Expected mapping for format string, not List
bad-format-character:18:10:pprint:Unsupported format character 'z' (0x7a) at index 2
truncated-format-string:19:10:pprint:Format string ends in middle of conversion specifier
format-string-without-interpolation:21:10:pprint:Using formatting for a string that does not have any interpolated variables
format-string-without-interpolation:22:10:pprint:Using formatting for a string that does not have any interpolated variables
format-string-without-interpolation:23:10:pprint:Using formatting for a string that does not have any interpolated variables
format-string-without-interpolation:24:10:pprint:Using formatting for a string that does not have any interpolated variables

0 comments on commit ea7f39e

Please sign in to comment.