summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rwxr-xr-xdoc/exts/pylint_extensions.py2
-rw-r--r--doc/whatsnew/2.11.rst4
-rw-r--r--pylint/checkers/__init__.py4
-rw-r--r--pylint/checkers/base.py18
-rw-r--r--pylint/checkers/base_checker.py20
-rw-r--r--pylint/checkers/classes.py3
-rw-r--r--pylint/checkers/exceptions.py17
-rw-r--r--pylint/checkers/imports.py22
-rw-r--r--pylint/checkers/misc.py2
-rw-r--r--pylint/checkers/raw_metrics.py4
-rw-r--r--pylint/checkers/refactoring/not_checker.py6
-rw-r--r--pylint/checkers/refactoring/refactoring_checker.py6
-rw-r--r--pylint/checkers/similar.py8
-rw-r--r--pylint/checkers/spelling.py6
-rw-r--r--pylint/checkers/strings.py79
-rw-r--r--pylint/checkers/typecheck.py2
-rw-r--r--pylint/checkers/variables.py11
-rw-r--r--pylint/config/__init__.py2
-rw-r--r--pylint/config/man_help_formatter.py7
-rw-r--r--pylint/config/option.py5
-rw-r--r--pylint/config/option_manager_mixin.py2
-rwxr-xr-xpylint/epylint.py4
-rw-r--r--pylint/extensions/_check_docs_utils.py144
-rw-r--r--pylint/extensions/broad_try_clause.py4
-rw-r--r--pylint/extensions/mccabe.py8
-rw-r--r--pylint/extensions/overlapping_exceptions.py6
-rw-r--r--pylint/graph.py24
-rw-r--r--pylint/lint/pylinter.py13
-rw-r--r--pylint/lint/report_functions.py2
-rw-r--r--pylint/lint/run.py5
-rw-r--r--pylint/lint/utils.py4
-rw-r--r--pylint/message/message_definition.py12
-rw-r--r--pylint/message/message_handler_mix_in.py14
-rw-r--r--pylint/pyreverse/diadefslib.py4
-rw-r--r--pylint/pyreverse/diagrams.py4
-rw-r--r--pylint/pyreverse/inspector.py4
-rw-r--r--pylint/pyreverse/utils.py8
-rw-r--r--pylint/pyreverse/vcg_printer.py2
-rw-r--r--pylint/reporters/base_reporter.py2
-rw-r--r--pylint/reporters/reports_handler_mix_in.py2
-rw-r--r--pylint/reporters/text.py9
-rw-r--r--pylint/reporters/ureports/nodes.py4
-rw-r--r--pylint/reporters/ureports/text_writer.py2
-rw-r--r--pylint/testutils/constants.py6
-rw-r--r--pylint/testutils/lint_module_test.py16
-rw-r--r--pylint/testutils/reporter_for_tests.py2
-rw-r--r--pylint/utils/utils.py18
-rw-r--r--script/bump_changelog.py10
-rw-r--r--tests/benchmark/test_baseline_benchmarks.py40
-rw-r--r--tests/checkers/unittest_python3.py4
-rw-r--r--tests/checkers/unittest_similar.py68
-rw-r--r--tests/functional/a/arguments.py2
-rw-r--r--tests/functional/c/consider/consider_using_f_string.py107
-rw-r--r--tests/functional/c/consider/consider_using_f_string.txt30
-rw-r--r--tests/functional/d/docstrings.py2
-rw-r--r--tests/functional/d/duplicate_string_formatting_argument.py2
-rw-r--r--tests/functional/l/logging_format_interpolation.py2
-rw-r--r--tests/functional/l/logging_not_lazy.py3
-rw-r--r--tests/functional/l/logging_not_lazy_with_logger.py1
-rw-r--r--tests/functional/l/logging_not_lazy_with_logger.txt6
-rw-r--r--tests/functional/m/misplaced_format_function.py2
-rw-r--r--tests/functional/n/new_style_class_py_30.py5
-rw-r--r--tests/functional/n/new_style_class_py_30.txt6
-rw-r--r--tests/functional/r/raise/raising_format_tuple.py2
-rw-r--r--tests/functional/r/renamed_import_logging_not_lazy.py2
-rw-r--r--tests/functional/s/slots_checks.py2
-rw-r--r--tests/functional/s/string/string_formatting.py2
-rw-r--r--tests/functional/s/string/string_formatting_error.py2
-rw-r--r--tests/functional/s/string/string_formatting_failed_inference.py2
-rw-r--r--tests/functional/s/string/string_formatting_failed_inference_py35.py2
-rw-r--r--tests/functional/s/string/string_formatting_py3.py2
-rw-r--r--tests/functional/t/too/too_many_return_statements.py2
-rw-r--r--tests/functional/u/undefined/undefined_loop_variable.py2
-rw-r--r--tests/functional/u/unused/unused_argument.py2
-rw-r--r--tests/functional/u/use/used_before_assignement.py2
-rw-r--r--tests/functional/u/use/used_before_assignment_issue853.py2
-rw-r--r--tests/input/func_w0401_package/thing2.py2
-rw-r--r--tests/profile/test_profile_against_externals.py7
-rw-r--r--tests/pyreverse/test_utils.py2
-rw-r--r--tests/test_check_parallel.py5
-rw-r--r--tests/test_epylint.py2
-rw-r--r--tests/test_func.py5
-rw-r--r--tests/test_self.py12
84 files changed, 525 insertions, 384 deletions
diff --git a/ChangeLog b/ChangeLog
index d39a0248d..26c82cf1e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,6 +10,10 @@ Release date: TBA
..
Put new features here and also in 'doc/whatsnew/2.11.rst'
+* Added ``consider-using-f-string``: Emitted when .format() or '%' is being used to format a string.
+
+ Closes #3592
+
What's New in Pylint 2.10.3?
============================
@@ -193,7 +197,6 @@ Release date: 2021-08-20
* Allow ``true`` and ``false`` values in ``pylintrc`` for better compatibility with ``toml`` config.
-
* Class methods' signatures are ignored the same way as functions' with similarities "ignore-signatures" option enabled
Closes #4653
diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py
index 02e9be5bc..4dc6f184f 100755
--- a/doc/exts/pylint_extensions.py
+++ b/doc/exts/pylint_extensions.py
@@ -37,7 +37,7 @@ def builder_inited(app):
if name[0] == "_" or name in DEPRECATED_MODULES:
continue
if ext == ".py":
- modules.append("pylint.extensions.%s" % name)
+ modules.append(f"pylint.extensions.{name}")
elif ext == ".rst":
doc_files["pylint.extensions." + name] = os.path.join(ext_path, filename)
modules.sort()
diff --git a/doc/whatsnew/2.11.rst b/doc/whatsnew/2.11.rst
index fc370ecd4..e9c05b909 100644
--- a/doc/whatsnew/2.11.rst
+++ b/doc/whatsnew/2.11.rst
@@ -12,6 +12,10 @@ Summary -- Release highlights
New checkers
============
+* Added ``consider-using-f-string``: Emitted when .format() or '%' is being used to format a string.
+
+ Closes #3592
+
Extensions
==========
diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py
index 4bc586a1c..ffc940223 100644
--- a/pylint/checkers/__init__.py
+++ b/pylint/checkers/__init__.py
@@ -65,8 +65,8 @@ def table_lines_from_stats(stats, old_stats, columns):
diff_str = diff_string(old, new)
else:
old, diff_str = "NC", "NC"
- new = "%.3f" % new if isinstance(new, float) else str(new)
- old = "%.3f" % old if isinstance(old, float) else str(old)
+ new = f"{new:.3f}" if isinstance(new, float) else str(new)
+ old = f"{old:.3f}" if isinstance(old, float) else str(old)
lines += (m_type.replace("_", " "), new, old, diff_str)
return lines
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py
index 4422ab336..6aba62d79 100644
--- a/pylint/checkers/base.py
+++ b/pylint/checkers/base.py
@@ -193,7 +193,7 @@ DEFAULT_ARGUMENT_SYMBOLS = dict(
["set()", "{}", "[]"],
),
**{
- x: "%s()" % x
+ x: f"{x}()"
for x in (
"collections.deque",
"collections.ChainMap",
@@ -404,12 +404,12 @@ def report_by_type_stats(sect, stats, old_stats):
try:
documented = total - stats["undocumented_" + node_type]
percent = (documented * 100.0) / total
- nice_stats[node_type]["percent_documented"] = "%.2f" % percent
+ nice_stats[node_type]["percent_documented"] = f"{percent:.2f}"
except KeyError:
nice_stats[node_type]["percent_documented"] = "NC"
try:
percent = (stats["badname_" + node_type] * 100.0) / total
- nice_stats[node_type]["percent_badname"] = "%.2f" % percent
+ nice_stats[node_type]["percent_badname"] = f"{percent:.2f}"
except KeyError:
nice_stats[node_type]["percent_badname"] = "NC"
lines = ("type", "number", "old number", "difference", "%documented", "%badname")
@@ -1703,8 +1703,7 @@ def _create_naming_options():
"type": "choice",
"choices": list(NAMING_STYLES.keys()),
"metavar": "<style>",
- "help": "Naming style matching correct %s names."
- % (human_readable_name,),
+ "help": f"Naming style matching correct {human_readable_name} names.",
},
)
)
@@ -1715,8 +1714,7 @@ def _create_naming_options():
"default": None,
"type": "regexp",
"metavar": "<regexp>",
- "help": "Regular expression matching correct %s names. Overrides %s-naming-style."
- % (human_readable_name, name_type),
+ "help": f"Regular expression matching correct {human_readable_name} names. Overrides {name_type}-naming-style.",
},
)
)
@@ -1888,9 +1886,9 @@ class NameChecker(_BasicChecker):
regexps[name_type] = custom_regex
if custom_regex is not None:
- hints[name_type] = "%r pattern" % custom_regex.pattern
+ hints[name_type] = f"{custom_regex.pattern!r} pattern"
else:
- hints[name_type] = "%s naming style" % naming_style_name
+ hints[name_type] = f"{naming_style_name} naming style"
return regexps, hints
@@ -2023,7 +2021,7 @@ class NameChecker(_BasicChecker):
type_label = HUMAN_READABLE_TYPES[node_type]
hint = self._name_hints[node_type]
if self.config.include_naming_hint:
- hint += " (%r pattern)" % self._name_regexps[node_type].pattern
+ hint += f" ({self._name_regexps[node_type].pattern!r} pattern)"
args = (
(type_label.capitalize(), name, hint)
if warning == "invalid-name"
diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py
index 4b220f28d..5dfd7ea78 100644
--- a/pylint/checkers/base_checker.py
+++ b/pylint/checkers/base_checker.py
@@ -70,35 +70,37 @@ class BaseChecker(OptionsProviderMixIn):
def get_full_documentation(self, msgs, options, reports, doc=None, module=None):
result = ""
- checker_title = "%s checker" % (self.name.replace("_", " ").title())
+ checker_title = f"{self.name.replace('_', ' ').title()} checker"
if module:
# Provide anchor to link against
- result += ".. _%s:\n\n" % module
- result += "%s\n" % get_rst_title(checker_title, "~")
+ result += f".. _{module}:\n\n"
+ result += f"{get_rst_title(checker_title, '~')}\n"
if module:
- result += "This checker is provided by ``%s``.\n" % module
- result += "Verbatim name of the checker is ``%s``.\n\n" % self.name
+ result += f"This checker is provided by ``{module}``.\n"
+ result += f"Verbatim name of the checker is ``{self.name}``.\n\n"
if doc:
# Provide anchor to link against
result += get_rst_title(f"{checker_title} Documentation", "^")
- result += "%s\n\n" % cleandoc(doc)
+ result += f"{cleandoc(doc)}\n\n"
# options might be an empty generator and not be False when casted to boolean
options = list(options)
if options:
result += get_rst_title(f"{checker_title} Options", "^")
- result += "%s\n" % get_rst_section(None, options)
+ result += f"{get_rst_section(None, options)}\n"
if msgs:
result += get_rst_title(f"{checker_title} Messages", "^")
for msgid, msg in sorted(
msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])
):
msg = self.create_message_definition_from_tuple(msgid, msg)
- result += "%s\n" % msg.format_help(checkerref=False)
+ result += f"{msg.format_help(checkerref=False)}\n"
result += "\n"
if reports:
result += get_rst_title(f"{checker_title} Reports", "^")
for report in reports:
- result += ":%s: %s\n" % report[:2]
+ result += (
+ ":%s: %s\n" % report[:2] # pylint: disable=consider-using-f-string
+ )
result += "\n"
result += "\n"
return result
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py
index 92395114b..3b18b7e00 100644
--- a/pylint/checkers/classes.py
+++ b/pylint/checkers/classes.py
@@ -2039,7 +2039,7 @@ class SpecialMethodsChecker(BaseChecker):
"__iter__ returns non-iterator",
"non-iterator-returned",
"Used when an __iter__ method returns something which is not an "
- "iterable (i.e. has no `%s` method)" % NEXT_METHOD,
+ f"iterable (i.e. has no `{NEXT_METHOD}` method)",
{
"old_names": [
("W0234", "old-non-iterator-returned-1"),
@@ -2189,6 +2189,7 @@ class SpecialMethodsChecker(BaseChecker):
# tuple, although the user should implement the method
# to take all of them in consideration.
emit = mandatory not in expected_params
+ # pylint: disable-next=consider-using-f-string
expected_params = "between %d or %d" % expected_params
else:
# If the number of mandatory parameters doesn't
diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py
index 7c173a5ab..fd6c4d482 100644
--- a/pylint/checkers/exceptions.py
+++ b/pylint/checkers/exceptions.py
@@ -271,7 +271,7 @@ class ExceptionsChecker(checkers.BaseChecker):
"default": OVERGENERAL_EXCEPTIONS,
"type": "csv",
"metavar": "<comma-separated class names>",
- "help": "Exceptions that will emit a warning "
+ "help": "Exceptions that will emit a warning " # pylint: disable=consider-using-f-string
'when being caught. Defaults to "%s".'
% (", ".join(OVERGENERAL_EXCEPTIONS),),
},
@@ -488,20 +488,14 @@ class ExceptionsChecker(checkers.BaseChecker):
def visit_binop(self, node):
if isinstance(node.parent, nodes.ExceptHandler):
# except (V | A)
- suggestion = "Did you mean '({}, {})' instead?".format(
- node.left.as_string(),
- node.right.as_string(),
- )
+ suggestion = f"Did you mean '({node.left.as_string()}, {node.right.as_string()})' instead?"
self.add_message("wrong-exception-operation", node=node, args=(suggestion,))
@utils.check_messages("wrong-exception-operation")
def visit_compare(self, node):
if isinstance(node.parent, nodes.ExceptHandler):
# except (V < A)
- suggestion = "Did you mean '({}, {})' instead?".format(
- node.left.as_string(),
- ", ".join(operand.as_string() for _, operand in node.ops),
- )
+ suggestion = f"Did you mean '({node.left.as_string()}, {', '.join(operand.as_string() for _, operand in node.ops)})' instead?"
self.add_message("wrong-exception-operation", node=node, args=(suggestion,))
@utils.check_messages(
@@ -561,10 +555,7 @@ class ExceptionsChecker(checkers.BaseChecker):
for previous_exc in exceptions_classes:
if previous_exc in exc_ancestors:
- msg = "{} is an ancestor class of {}".format(
- previous_exc.name,
- exc.name,
- )
+ msg = f"{previous_exc.name} is an ancestor class of {exc.name}"
self.add_message(
"bad-except-order", node=handler.type, args=msg
)
diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py
index e67456722..d4d813812 100644
--- a/pylint/checkers/imports.py
+++ b/pylint/checkers/imports.py
@@ -151,16 +151,16 @@ def _repr_tree_defs(data, indent_str=None):
lines = []
nodes_items = data.items()
for i, (mod, (sub, files)) in enumerate(sorted(nodes_items, key=lambda x: x[0])):
- files = "" if not files else "(%s)" % ",".join(sorted(files))
+ files = "" if not files else f"({','.join(sorted(files))})"
if indent_str is None:
lines.append(f"{mod} {files}")
sub_indent_str = " "
else:
lines.append(fr"{indent_str}\-{mod} {files}")
if i == len(nodes_items) - 1:
- sub_indent_str = "%s " % indent_str
+ sub_indent_str = f"{indent_str} "
else:
- sub_indent_str = "%s| " % indent_str
+ sub_indent_str = f"{indent_str}| "
if sub:
lines.append(_repr_tree_defs(sub, sub_indent_str))
return "\n".join(lines)
@@ -739,8 +739,8 @@ class ImportsChecker(DeprecatedMixin, BaseChecker):
"wrong-import-order",
node=node,
args=(
- 'standard import "%s"' % node.as_string(),
- '"%s"' % wrong_import[0][0].as_string(),
+ f'standard import "{node.as_string()}"',
+ f'"{wrong_import[0][0].as_string()}"',
),
)
elif import_category == "THIRDPARTY":
@@ -754,8 +754,8 @@ class ImportsChecker(DeprecatedMixin, BaseChecker):
"wrong-import-order",
node=node,
args=(
- 'third party import "%s"' % node.as_string(),
- '"%s"' % wrong_import[0][0].as_string(),
+ f'third party import "{node.as_string()}"',
+ f'"{wrong_import[0][0].as_string()}"',
),
)
elif import_category == "FIRSTPARTY":
@@ -769,8 +769,8 @@ class ImportsChecker(DeprecatedMixin, BaseChecker):
"wrong-import-order",
node=node,
args=(
- 'first party import "%s"' % node.as_string(),
- '"%s"' % wrong_import[0][0].as_string(),
+ f'first party import "{node.as_string()}"',
+ f'"{wrong_import[0][0].as_string()}"',
),
)
elif import_category == "LOCALFOLDER":
@@ -787,9 +787,7 @@ class ImportsChecker(DeprecatedMixin, BaseChecker):
return None
self.add_message("relative-beyond-top-level", node=importnode)
except astroid.AstroidSyntaxError as exc:
- message = "Cannot import {!r} due to syntax error {!r}".format(
- modname, str(exc.error) # pylint: disable=no-member; false positive
- )
+ message = f"Cannot import {modname!r} due to syntax error {str(exc.error)!r}" # pylint: disable=no-member; false positive
self.add_message("syntax-error", line=importnode.lineno, args=message)
except astroid.AstroidBuildingException:
diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py
index 4fd9ffd9c..e53935adf 100644
--- a/pylint/checkers/misc.py
+++ b/pylint/checkers/misc.py
@@ -110,7 +110,7 @@ class EncodingChecker(BaseChecker):
if self.config.notes_rgx:
regex_string = fr"#\s*({notes}|{self.config.notes_rgx})\b"
else:
- regex_string = r"#\s*(%s)\b" % (notes)
+ regex_string = fr"#\s*({notes})\b"
self._fixme_pattern = re.compile(regex_string, re.I)
diff --git a/pylint/checkers/raw_metrics.py b/pylint/checkers/raw_metrics.py
index 22bd096cb..eb3f717ff 100644
--- a/pylint/checkers/raw_metrics.py
+++ b/pylint/checkers/raw_metrics.py
@@ -29,7 +29,7 @@ def report_raw_stats(sect, stats, old_stats):
total_lines = stats["total_lines"]
if not total_lines:
raise EmptyReportError()
- sect.description = "%s lines have been analyzed" % total_lines
+ sect.description = f"{total_lines} lines have been analyzed"
lines = ("type", "number", "%", "previous", "difference")
for node_type in ("code", "docstring", "comment", "empty"):
key = node_type + "_lines"
@@ -40,7 +40,7 @@ def report_raw_stats(sect, stats, old_stats):
diff_str = diff_string(old, total)
else:
old, diff_str = "NC", "NC"
- lines += (node_type, str(total), "%.2f" % percent, str(old), diff_str)
+ lines += (node_type, str(total), f"{percent:.2f}", str(old), diff_str)
sect.append(Table(children=lines, cols=5, rheaders=1))
diff --git a/pylint/checkers/refactoring/not_checker.py b/pylint/checkers/refactoring/not_checker.py
index a14818ff3..ebe2c0ebe 100644
--- a/pylint/checkers/refactoring/not_checker.py
+++ b/pylint/checkers/refactoring/not_checker.py
@@ -75,10 +75,8 @@ class NotChecker(checkers.BaseChecker):
and _type.qname() in self.skipped_classnames
):
return
- suggestion = "{} {} {}".format(
- left.as_string(),
- self.reverse_op[operator],
- right.as_string(),
+ suggestion = (
+ f"{left.as_string()} {self.reverse_op[operator]} {right.as_string()}"
)
self.add_message(
"unneeded-not", node=node, args=(node.as_string(), suggestion)
diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py
index 773839961..7a3048ab4 100644
--- a/pylint/checkers/refactoring/refactoring_checker.py
+++ b/pylint/checkers/refactoring/refactoring_checker.py
@@ -1406,11 +1406,7 @@ class RefactoringChecker(checkers.BaseTokenChecker):
suggestion = false_value.as_string()
else:
message = "consider-using-ternary"
- suggestion = "{truth} if {cond} else {false}".format(
- truth=truth_value.as_string(),
- cond=cond.as_string(),
- false=false_value.as_string(),
- )
+ suggestion = f"{truth_value.as_string()} if {cond.as_string()} else {false_value.as_string()}"
self.add_message(message, node=node, args=(suggestion,))
def _append_context_managers_to_stack(self, node: nodes.Assign) -> None:
diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py
index 215b816b2..c3c4ed080 100644
--- a/pylint/checkers/similar.py
+++ b/pylint/checkers/similar.py
@@ -457,11 +457,7 @@ class Similar:
report += f" {line.rstrip()}\n" if line.rstrip() else "\n"
duplicated_line_number += number * (len(couples_l) - 1)
total_line_number: int = sum(len(lineset) for lineset in self.linesets)
- report += "TOTAL lines={} duplicates={} percent={:.2f}\n".format(
- total_line_number,
- duplicated_line_number,
- duplicated_line_number * 100.0 / total_line_number,
- )
+ report += f"TOTAL lines={total_line_number} duplicates={duplicated_line_number} percent={duplicated_line_number * 100.0 / total_line_number:.2f}\n"
return report
def _find_common(
@@ -676,7 +672,7 @@ class LineSet:
)
def __str__(self):
- return "<Lineset for %s>" % self.name
+ return f"<Lineset for {self.name}>"
def __len__(self):
return len(self._real_lines)
diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py
index 16685272f..d5ab5c1ca 100644
--- a/pylint/checkers/spelling.py
+++ b/pylint/checkers/spelling.py
@@ -233,7 +233,7 @@ class SpellingChecker(BaseTokenChecker):
"metavar": "<dict name>",
"choices": dict_choices,
"help": "Spelling dictionary name. "
- "Available dictionaries: %s.%s" % (dicts, instr),
+ f"Available dictionaries: {dicts}.{instr}",
},
),
(
@@ -400,14 +400,14 @@ class SpellingChecker(BaseTokenChecker):
# Store word to private dict or raise a message.
if self.config.spelling_store_unknown_words:
if lower_cased_word not in self.unknown_words:
- self.private_dict_file.write("%s\n" % lower_cased_word)
+ self.private_dict_file.write(f"{lower_cased_word}\n")
self.unknown_words.add(lower_cased_word)
else:
# Present up to N suggestions.
suggestions = self.spelling_dict.suggest(word)
del suggestions[self.config.max_spelling_suggestions :]
line_segment = line[word_start_at:]
- match = re.search(r"(\W|^)(%s)(\W|$)" % word, line_segment)
+ match = re.search(fr"(\W|^)({word})(\W|$)", line_segment)
if match:
# Start position of second group in regex.
col = match.regs[2][0]
diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py
index 666ffb3c6..b45b7bfdc 100644
--- a/pylint/checkers/strings.py
+++ b/pylint/checkers/strings.py
@@ -80,9 +80,9 @@ _PREFIXES = {
"Rb",
"RB",
}
-SINGLE_QUOTED_REGEX = re.compile("(%s)?'''" % "|".join(_PREFIXES))
-DOUBLE_QUOTED_REGEX = re.compile('(%s)?"""' % "|".join(_PREFIXES))
-QUOTE_DELIMITER_REGEX = re.compile("(%s)?(\"|')" % "|".join(_PREFIXES), re.DOTALL)
+SINGLE_QUOTED_REGEX = re.compile(f"({'|'.join(_PREFIXES)})?'''")
+DOUBLE_QUOTED_REGEX = re.compile(f"({'|'.join(_PREFIXES)})?\"\"\"")
+QUOTE_DELIMITER_REGEX = re.compile(f"({'|'.join(_PREFIXES)})?(\"|')", re.DOTALL)
MSGS = { # pylint: disable=consider-using-namedtuple-or-dataclass
"E1300": (
@@ -673,6 +673,12 @@ class StringConstantChecker(BaseTokenChecker):
"in Python 2 to indicate a string was Unicode, but since Python 3.0 strings "
"are Unicode by default.",
),
+ "C1407": (
+ "Formatting a regular string which could be a f-string",
+ "consider-using-f-string",
+ "Used when we detect a string that is being formatted with format() or % "
+ "which could potentially be a f-string. The use of f-strings is preferred.",
+ ),
}
options = (
(
@@ -910,11 +916,13 @@ class StringConstantChecker(BaseTokenChecker):
index += 2
@check_messages("redundant-u-string-prefix")
+ @check_messages("consider-using-f-string")
def visit_const(self, node: nodes.Const):
if node.pytype() == "builtins.str" and not isinstance(
node.parent, nodes.JoinedStr
):
self._detect_u_string_prefix(node)
+ self._detect_replacable_format_call(node)
def _detect_u_string_prefix(self, node: nodes.Const):
"""Check whether strings include a 'u' prefix like u'String'"""
@@ -925,6 +933,69 @@ class StringConstantChecker(BaseTokenChecker):
col_offset=node.col_offset,
)
+ def _detect_replacable_format_call(self, node: nodes.Const) -> None:
+ """Check whether a string is used in a call to format() or '%' and whether it
+ can be replaced by a f-string"""
+ if (
+ isinstance(node.parent, nodes.Attribute)
+ and node.parent.attrname == "format"
+ ):
+ # Allow assigning .format to a variable
+ if isinstance(node.parent.parent, nodes.Assign):
+ return
+
+ if node.parent.parent.args:
+ for arg in node.parent.parent.args:
+ # If star expressions with more than 1 element are being used
+ if isinstance(arg, nodes.Starred):
+ inferred = utils.safe_infer(arg.value)
+ if (
+ isinstance(inferred, astroid.List)
+ and len(inferred.elts) > 1
+ ):
+ return
+
+ elif node.parent.parent.keywords:
+ keyword_args = [
+ i[0] for i in utils.parse_format_method_string(node.value)[0]
+ ]
+ for keyword in node.parent.parent.keywords:
+ # If keyword is used multiple times
+ if keyword_args.count(keyword.arg) > 1:
+ return
+
+ keyword = utils.safe_infer(keyword.value)
+
+ # If lists of more than one element are being unpacked
+ if isinstance(keyword, nodes.Dict):
+ if len(keyword.items) > 1 and len(keyword_args) > 1:
+ return
+
+ # If all tests pass, then raise message
+ self.add_message(
+ "consider-using-f-string",
+ line=node.lineno,
+ col_offset=node.col_offset,
+ )
+
+ elif isinstance(node.parent, nodes.BinOp) and node.parent.op == "%":
+ inferred_right = utils.safe_infer(node.parent.right)
+
+ # If dicts or lists of length > 1 are used
+ if isinstance(inferred_right, nodes.Dict):
+ if len(inferred_right.items) > 1:
+ return
+ elif isinstance(inferred_right, nodes.List):
+ if len(inferred_right.elts) > 1:
+ return
+
+ # If all tests pass, then raise message
+ self.add_message(
+ "consider-using-f-string",
+ line=node.lineno,
+ col_offset=node.col_offset,
+ )
+
def register(linter):
"""required method to auto register this checker"""
@@ -989,7 +1060,7 @@ def _get_quote_delimiter(string_token: str) -> str:
"""
match = QUOTE_DELIMITER_REGEX.match(string_token)
if not match:
- raise ValueError("string token %s is not a well-formed string" % string_token)
+ raise ValueError(f"string token {string_token} is not a well-formed string")
return match.group(2)
diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py
index 749428376..54a47057a 100644
--- a/pylint/checkers/typecheck.py
+++ b/pylint/checkers/typecheck.py
@@ -248,7 +248,7 @@ def _missing_member_hint(owner, attrname, distance_threshold, max_choices):
if len(names) == 1:
names = ", ".join(names)
else:
- names = "one of {} or {}".format(", ".join(names[:-1]), names[-1])
+ names = f"one of {', '.join(names[:-1])} or {names[-1]}"
return f"; maybe {names}?"
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index ab5d04d7c..922cc0370 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -185,9 +185,9 @@ def _get_unpacking_extra_info(node, inferred):
inferred_module = inferred.root().name
if node.root().name == inferred_module:
if node.lineno == inferred.lineno:
- more = " %s" % inferred.as_string()
+ more = f" {inferred.as_string()}"
elif inferred.lineno:
- more = " defined at line %s" % inferred.lineno
+ more = f" defined at line {inferred.lineno}"
elif inferred.lineno:
more = f" defined at line {inferred.lineno} of {inferred_module}"
return more
@@ -1711,7 +1711,7 @@ class VariablesChecker(BaseChecker):
if asname is not None:
msg = f"{qname} imported as {asname}"
else:
- msg = "import %s" % name
+ msg = f"import {name}"
self.add_message("unused-import", args=msg, node=stmt)
return
if isinstance(stmt, nodes.ImportFrom):
@@ -2102,7 +2102,7 @@ class VariablesChecker(BaseChecker):
if as_name == "_":
continue
if as_name is None:
- msg = "import %s" % imported_name
+ msg = f"import {imported_name}"
else:
msg = f"{imported_name} imported as {as_name}"
if not _is_type_checking_import(stmt):
@@ -2128,8 +2128,7 @@ class VariablesChecker(BaseChecker):
if as_name is None:
msg = f"{imported_name} imported from {stmt.modname}"
else:
- fields = (imported_name, stmt.modname, as_name)
- msg = "%s imported from %s as %s" % fields
+ msg = f"{imported_name} imported from {stmt.modname} as {as_name}"
if not _is_type_checking_import(stmt):
self.add_message("unused-import", args=msg, node=stmt)
del self._to_consume
diff --git a/pylint/config/__init__.py b/pylint/config/__init__.py
index 8c85cc7fa..a3e538124 100644
--- a/pylint/config/__init__.py
+++ b/pylint/config/__init__.py
@@ -127,7 +127,7 @@ def save_results(results, base):
try:
os.mkdir(PYLINT_HOME)
except OSError:
- print("Unable to create directory %s" % PYLINT_HOME, file=sys.stderr)
+ print(f"Unable to create directory {PYLINT_HOME}", file=sys.stderr)
data_file = _get_pdata_path(base, 1)
try:
with open(data_file, "wb") as stream:
diff --git a/pylint/config/man_help_formatter.py b/pylint/config/man_help_formatter.py
index d2c6feca2..edef771a4 100644
--- a/pylint/config/man_help_formatter.py
+++ b/pylint/config/man_help_formatter.py
@@ -16,7 +16,7 @@ class _ManHelpFormatter(optparse.HelpFormatter):
)
def format_heading(self, heading):
- return ".SH %s\n" % heading.upper()
+ return f".SH {heading.upper()}\n"
def format_description(self, description):
return description
@@ -54,7 +54,10 @@ class _ManHelpFormatter(optparse.HelpFormatter):
@staticmethod
def format_title(pgm, section):
- date = "%d-%02d-%02d" % time.localtime()[:3]
+ date = (
+ "%d-%02d-%02d" # pylint: disable=consider-using-f-string
+ % time.localtime()[:3]
+ )
return f'.TH {pgm} {section} "{date}" {pgm}'
@staticmethod
diff --git a/pylint/config/option.py b/pylint/config/option.py
index 911287ec6..eb5890896 100644
--- a/pylint/config/option.py
+++ b/pylint/config/option.py
@@ -91,7 +91,7 @@ VALIDATORS = {
def _call_validator(opttype, optdict, option, value):
if opttype not in VALIDATORS:
- raise Exception('Unsupported type "%s"' % opttype)
+ raise Exception(f'Unsupported type "{opttype}"')
try:
return VALIDATORS[opttype](optdict, option, value)
except TypeError:
@@ -149,13 +149,14 @@ class Option(optparse.Option):
)
if not isinstance(self.choices, (tuple, list)):
raise optparse.OptionError(
+ # pylint: disable-next=consider-using-f-string
"choices must be a list of strings ('%s' supplied)"
% str(type(self.choices)).split("'")[1],
self,
)
elif self.choices is not None:
raise optparse.OptionError(
- "must not supply choices for type %r" % self.type, self
+ f"must not supply choices for type {self.type!r}", self
)
# pylint: disable=unsupported-assignment-operation
diff --git a/pylint/config/option_manager_mixin.py b/pylint/config/option_manager_mixin.py
index 97aaea773..106ec47cc 100644
--- a/pylint/config/option_manager_mixin.py
+++ b/pylint/config/option_manager_mixin.py
@@ -245,7 +245,7 @@ class OptionsManagerMixIn:
if opt in self._all_options:
break # already processed
help_function = functools.partial(self.helpfunc, level=help_level)
- help_msg = "%s verbose help." % " ".join(["more"] * help_level)
+ help_msg = f"{' '.join(['more'] * help_level)} verbose help."
optdict = {
"action": "callback",
"callback": help_function,
diff --git a/pylint/epylint.py b/pylint/epylint.py
index 59028864f..e3e8b73d1 100755
--- a/pylint/epylint.py
+++ b/pylint/epylint.py
@@ -186,10 +186,10 @@ def py_run(command_options="", return_std=False, stdout=None, stderr=None):
def Run():
if len(sys.argv) == 1:
- print("Usage: %s <filename> [options]" % sys.argv[0])
+ print(f"Usage: {sys.argv[0]} <filename> [options]")
sys.exit(1)
elif not os.path.exists(sys.argv[1]):
- print("%s does not exist" % sys.argv[1])
+ print(f"{sys.argv[1]} does not exist")
sys.exit(1)
else:
sys.exit(lint(sys.argv[1], sys.argv[2:]))
diff --git a/pylint/extensions/_check_docs_utils.py b/pylint/extensions/_check_docs_utils.py
index 659ef675b..3e29e487f 100644
--- a/pylint/extensions/_check_docs_utils.py
+++ b/pylint/extensions/_check_docs_utils.py
@@ -247,12 +247,10 @@ class SphinxDocstring(Docstring):
\w(?:\w|\.[^\.])* # Valid python name
"""
- re_simple_container_type = r"""
- {type} # a container type
+ re_simple_container_type = fr"""
+ {re_type} # a container type
[\(\[] [^\n\s]+ [\)\]] # with the contents of the container
- """.format(
- type=re_type
- )
+ """
re_multiple_simple_type = r"""
(?:{container_type}|{type})
@@ -261,14 +259,12 @@ class SphinxDocstring(Docstring):
type=re_type, container_type=re_simple_container_type
)
- re_xref = r"""
+ re_xref = fr"""
(?::\w+:)? # optional tag
- `{}` # what to reference
- """.format(
- re_type
- )
+ `{re_type}` # what to reference
+ """
- re_param_raw = r"""
+ re_param_raw = fr"""
: # initial colon
(?: # Sphinx keywords
param|parameter|
@@ -278,51 +274,43 @@ class SphinxDocstring(Docstring):
\s+ # whitespace
(?: # optional type declaration
- ({type}|{container_type})
+ ({re_type}|{re_simple_container_type})
\s+
)?
(\w+) # Parameter name
\s* # whitespace
: # final colon
- """.format(
- type=re_type, container_type=re_simple_container_type
- )
+ """
re_param_in_docstring = re.compile(re_param_raw, re.X | re.S)
- re_type_raw = r"""
- :type # Sphinx keyword
- \s+ # whitespace
- ({type}) # Parameter name
- \s* # whitespace
- : # final colon
- """.format(
- type=re_multiple_simple_type
- )
+ re_type_raw = fr"""
+ :type # Sphinx keyword
+ \s+ # whitespace
+ ({re_multiple_simple_type}) # Parameter name
+ \s* # whitespace
+ : # final colon
+ """
re_type_in_docstring = re.compile(re_type_raw, re.X | re.S)
- re_property_type_raw = r"""
- :type: # Sphinx keyword
- \s+ # whitespace
- {type} # type declaration
- """.format(
- type=re_multiple_simple_type
- )
+ re_property_type_raw = fr"""
+ :type: # Sphinx keyword
+ \s+ # whitespace
+ {re_multiple_simple_type} # type declaration
+ """
re_property_type_in_docstring = re.compile(re_property_type_raw, re.X | re.S)
- re_raise_raw = r"""
- : # initial colon
- (?: # Sphinx keyword
+ re_raise_raw = fr"""
+ : # initial colon
+ (?: # Sphinx keyword
raises?|
except|exception
)
- \s+ # whitespace
- ({type}) # exception type
- \s* # whitespace
- : # final colon
- """.format(
- type=re_multiple_simple_type
- )
+ \s+ # whitespace
+ ({re_multiple_simple_type}) # exception type
+ \s* # whitespace
+ : # final colon
+ """
re_raise_in_docstring = re.compile(re_raise_raw, re.X | re.S)
re_rtype_in_docstring = re.compile(r":rtype:")
@@ -454,12 +442,10 @@ class GoogleDocstring(Docstring):
re_xref = SphinxDocstring.re_xref
- re_container_type = r"""
- (?:{type}|{xref}) # a container type
+ re_container_type = fr"""
+ (?:{re_type}|{re_xref}) # a container type
[\(\[] [^\n]+ [\)\]] # with the contents of the container
- """.format(
- type=re_type, xref=re_xref
- )
+ """
re_multiple_type = r"""
(?:{container_type}|{type}|{xref})
@@ -484,16 +470,14 @@ class GoogleDocstring(Docstring):
)
re_param_line = re.compile(
- r"""
+ fr"""
\s* \*{{0,2}}(\w+) # identifier potentially with asterisks
\s* ( [(]
- {type}
+ {re_multiple_type}
(?:,\s+optional)?
[)] )? \s* : # optional type declaration
\s* (.*) # beginning of optional description
- """.format(
- type=re_multiple_type
- ),
+ """,
re.X | re.S | re.M,
)
@@ -502,12 +486,10 @@ class GoogleDocstring(Docstring):
)
re_raise_line = re.compile(
- r"""
- \s* ({type}) \s* : # identifier
+ fr"""
+ \s* ({re_multiple_type}) \s* : # identifier
\s* (.*) # beginning of optional description
- """.format(
- type=re_multiple_type
- ),
+ """,
re.X | re.S | re.M,
)
@@ -516,22 +498,18 @@ class GoogleDocstring(Docstring):
)
re_returns_line = re.compile(
- r"""
- \s* ({type}:)? # identifier
+ fr"""
+ \s* ({re_multiple_type}:)? # identifier
\s* (.*) # beginning of description
- """.format(
- type=re_multiple_type
- ),
+ """,
re.X | re.S | re.M,
)
re_property_returns_line = re.compile(
- r"""
- ^{type}: # indentifier
+ fr"""
+ ^{re_multiple_type}: # indentifier
\s* (.*) # Summary line / description
- """.format(
- type=re_multiple_type
- ),
+ """,
re.X | re.S | re.M,
)
@@ -743,15 +721,13 @@ class NumpyDocstring(GoogleDocstring):
)
re_param_line = re.compile(
- r"""
- \s* (\w+) # identifier
+ fr"""
+ \s* (\w+) # identifier
\s* :
- \s* (?:({type})(?:,\s+optional)?)? # optional type declaration
- \n # description starts on a new line
- \s* (.*) # description
- """.format(
- type=GoogleDocstring.re_multiple_type
- ),
+ \s* (?:({GoogleDocstring.re_multiple_type})(?:,\s+optional)?)? # optional type declaration
+ \n # description starts on a new line
+ \s* (.*) # description
+ """,
re.X | re.S,
)
@@ -760,12 +736,10 @@ class NumpyDocstring(GoogleDocstring):
)
re_raise_line = re.compile(
- r"""
- \s* ({type})$ # type declaration
- \s* (.*) # optional description
- """.format(
- type=GoogleDocstring.re_type
- ),
+ fr"""
+ \s* ({GoogleDocstring.re_type})$ # type declaration
+ \s* (.*) # optional description
+ """,
re.X | re.S | re.M,
)
@@ -774,13 +748,11 @@ class NumpyDocstring(GoogleDocstring):
)
re_returns_line = re.compile(
- r"""
+ fr"""
\s* (?:\w+\s+:\s+)? # optional name
- ({type})$ # type declaration
- \s* (.*) # optional description
- """.format(
- type=GoogleDocstring.re_multiple_type
- ),
+ ({GoogleDocstring.re_multiple_type})$ # type declaration
+ \s* (.*) # optional description
+ """,
re.X | re.S | re.M,
)
diff --git a/pylint/extensions/broad_try_clause.py b/pylint/extensions/broad_try_clause.py
index dc38a1c89..b7d2fc974 100644
--- a/pylint/extensions/broad_try_clause.py
+++ b/pylint/extensions/broad_try_clause.py
@@ -61,9 +61,7 @@ class BroadTryClauseChecker(checkers.BaseChecker):
def visit_tryexcept(self, node):
try_clause_statements = self._count_statements(node)
if try_clause_statements > self.config.max_try_statements:
- msg = "try clause contains {} statements, expected at most {}".format(
- try_clause_statements, self.config.max_try_statements
- )
+ msg = f"try clause contains {try_clause_statements} statements, expected at most {self.config.max_try_statements}"
self.add_message(
"too-many-try-statements", node.lineno, node=node, args=msg
)
diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py
index 87e9b8375..c85588ca2 100644
--- a/pylint/extensions/mccabe.py
+++ b/pylint/extensions/mccabe.py
@@ -50,7 +50,7 @@ class PathGraphingAstVisitor(Mccabe_PathGraphingAstVisitor):
pathnode = self._append_node(node)
self.tail = pathnode
self.dispatch_list(node.body)
- bottom = "%s" % self._bottom_counter
+ bottom = f"{self._bottom_counter}"
self._bottom_counter += 1
self.graph.connect(self.tail, bottom)
self.graph.connect(node, bottom)
@@ -135,7 +135,7 @@ class PathGraphingAstVisitor(Mccabe_PathGraphingAstVisitor):
else:
loose_ends.append(node)
if node:
- bottom = "%s" % self._bottom_counter
+ bottom = f"{self._bottom_counter}"
self._bottom_counter += 1
for end in loose_ends:
self.graph.connect(end, bottom)
@@ -181,9 +181,9 @@ class McCabeMethodChecker(checkers.BaseChecker):
complexity = graph.complexity()
node = graph.root
if hasattr(node, "name"):
- node_name = "'%s'" % node.name
+ node_name = f"'{node.name}'"
else:
- node_name = "This '%s'" % node.__class__.__name__.lower()
+ node_name = f"This '{node.__class__.__name__.lower()}'"
if complexity <= self.config.max_complexity:
continue
self.add_message(
diff --git a/pylint/extensions/overlapping_exceptions.py b/pylint/extensions/overlapping_exceptions.py
index aba70b559..891dde59f 100644
--- a/pylint/extensions/overlapping_exceptions.py
+++ b/pylint/extensions/overlapping_exceptions.py
@@ -66,8 +66,7 @@ class OverlappingExceptionsChecker(checkers.BaseChecker):
self.add_message(
"overlapping-except",
node=handler.type,
- args="%s and %s are the same"
- % (prev_part.as_string(), part.as_string()),
+ args=f"{prev_part.as_string()} and {part.as_string()} are the same",
)
elif prev_exc in exc_ancestors or exc in prev_exc_ancestors:
ancestor = part if exc in prev_exc_ancestors else prev_part
@@ -75,8 +74,7 @@ class OverlappingExceptionsChecker(checkers.BaseChecker):
self.add_message(
"overlapping-except",
node=handler.type,
- args="%s is an ancestor class of %s"
- % (ancestor.as_string(), descendant.as_string()),
+ args=f"{ancestor.as_string()} is an ancestor class of {descendant.as_string()}",
)
handled_in_clause += [(part, exc)]
diff --git a/pylint/graph.py b/pylint/graph.py
index 7cb357c2d..927fac47e 100644
--- a/pylint/graph.py
+++ b/pylint/graph.py
@@ -54,18 +54,20 @@ class DotBackend:
self.renderer = renderer
self.lines = []
self._source = None
- self.emit("digraph %s {" % normalize_node_id(graphname))
+ self.emit(f"digraph {normalize_node_id(graphname)} {{")
if rankdir:
- self.emit("rankdir=%s" % rankdir)
+ self.emit(f"rankdir={rankdir}")
if ratio:
- self.emit("ratio=%s" % ratio)
+ self.emit(f"ratio={ratio}")
if size:
- self.emit('size="%s"' % size)
+ self.emit(f'size="{size}"')
if charset:
- assert charset.lower() in ("utf-8", "iso-8859-1", "latin1"), (
- "unsupported charset %s" % charset
- )
- self.emit('charset="%s"' % charset)
+ assert charset.lower() in (
+ "utf-8",
+ "iso-8859-1",
+ "latin1",
+ ), f"unsupported charset {charset}"
+ self.emit(f'charset="{charset}"')
for param in additional_param.items():
self.emit("=".join(param))
@@ -150,19 +152,19 @@ class DotBackend:
"""
attrs = [f'{prop}="{value}"' for prop, value in props.items()]
n_from, n_to = normalize_node_id(name1), normalize_node_id(name2)
- self.emit("{} -> {} [{}];".format(n_from, n_to, ", ".join(sorted(attrs))))
+ self.emit(f"{n_from} -> {n_to} [{', '.join(sorted(attrs))}];")
def emit_node(self, name, **props):
"""emit a node with given properties.
node properties: see https://www.graphviz.org/doc/info/attrs.html
"""
attrs = [f'{prop}="{value}"' for prop, value in props.items()]
- self.emit("{} [{}];".format(normalize_node_id(name), ", ".join(sorted(attrs))))
+ self.emit(f"{normalize_node_id(name)} [{', '.join(sorted(attrs))}];")
def normalize_node_id(nid):
"""Returns a suitable DOT node id for `nid`."""
- return '"%s"' % nid
+ return f'"{nid}"'
def get_cycles(graph_dict, vertices=None):
diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py
index e4ed47683..d09f4e39e 100644
--- a/pylint/lint/pylinter.py
+++ b/pylint/lint/pylinter.py
@@ -315,8 +315,7 @@ class PyLinter(
"choices": [c.name for c in interfaces.CONFIDENCE_LEVELS],
"group": "Messages control",
"help": "Only show warnings with the listed confidence levels."
- " Leave empty to show all. Valid levels: %s."
- % (", ".join(c.name for c in interfaces.CONFIDENCE_LEVELS),),
+ f" Leave empty to show all. Valid levels: {', '.join(c.name for c in interfaces.CONFIDENCE_LEVELS)}.",
},
),
(
@@ -630,9 +629,7 @@ class PyLinter(
except KeyError:
meth = self._bw_options_methods[optname]
warnings.warn(
- "{} is deprecated, replace it by {}".format(
- optname, optname.split("-")[0]
- ),
+ f"{optname} is deprecated, replace it by {optname.split('-')[0]}",
DeprecationWarning,
)
value = utils._check_csv(value)
@@ -652,7 +649,7 @@ class PyLinter(
try:
checkers.BaseTokenChecker.set_option(self, optname, value, action, optdict)
except config.UnsupportedAction:
- print("option %s can't be read from config file" % optname, file=sys.stderr)
+ print(f"option {optname} can't be read from config file", file=sys.stderr)
def register_reporter(self, reporter_class):
self._reporters[reporter_class.name] = reporter_class
@@ -1302,10 +1299,10 @@ class PyLinter(
try:
note = eval(evaluation, {}, self.stats) # pylint: disable=eval-used
except Exception as ex: # pylint: disable=broad-except
- msg = "An exception occurred while rating: %s" % ex
+ msg = f"An exception occurred while rating: {ex}"
else:
self.stats["global_note"] = note
- msg = "Your code has been rated at %.2f/10" % note
+ msg = f"Your code has been rated at {note:.2f}/10"
pnote = previous_stats.get("global_note")
if pnote is not None:
msg += f" (previous run: {pnote:.2f}/10, {note - pnote:+.2f})"
diff --git a/pylint/lint/report_functions.py b/pylint/lint/report_functions.py
index 15ca05d62..fd316c611 100644
--- a/pylint/lint/report_functions.py
+++ b/pylint/lint/report_functions.py
@@ -65,7 +65,7 @@ def report_messages_by_module_stats(sect, stats, _):
continue
lines.append(line[-1])
for val in line[:-1]:
- lines.append("%.2f" % val)
+ lines.append(f"{val:.2f}")
if len(lines) == 5:
raise exceptions.EmptyReportError()
sect.append(report_nodes.Table(children=lines, cols=5, rheaders=1))
diff --git a/pylint/lint/run.py b/pylint/lint/run.py
index c84567cc2..ac4f586f4 100644
--- a/pylint/lint/run.py
+++ b/pylint/lint/run.py
@@ -40,7 +40,7 @@ def cb_list_extensions(option, optname, value, parser):
def cb_list_confidence_levels(option, optname, value, parser):
for level in interfaces.CONFIDENCE_LEVELS:
- print("%-18s: %s" % level)
+ print(f"%-18s: {level}")
sys.exit(0)
@@ -350,8 +350,7 @@ group are mutually exclusive.",
if linter.config.jobs < 0:
print(
- "Jobs number (%d) should be greater than or equal to 0"
- % linter.config.jobs,
+ f"Jobs number ({linter.config.jobs}) should be greater than or equal to 0",
file=sys.stderr,
)
sys.exit(32)
diff --git a/pylint/lint/utils.py b/pylint/lint/utils.py
index 38784a9bc..b1f8edfff 100644
--- a/pylint/lint/utils.py
+++ b/pylint/lint/utils.py
@@ -96,12 +96,12 @@ def preprocess_options(args, search_for):
del args[i]
if takearg and val is None:
if i >= len(args) or args[i].startswith("-"):
- msg = "Option %s expects a value" % option
+ msg = f"Option {option} expects a value"
raise ArgumentPreprocessingError(msg)
val = args[i]
del args[i]
elif not takearg and val is not None:
- msg = "Option %s doesn't expects a value" % option
+ msg = f"Option {option} doesn't expects a value"
raise ArgumentPreprocessingError(msg)
cb(option, val)
diff --git a/pylint/message/message_definition.py b/pylint/message/message_definition.py
index a48526e9b..f7f680590 100644
--- a/pylint/message/message_definition.py
+++ b/pylint/message/message_definition.py
@@ -64,24 +64,24 @@ class MessageDefinition:
"""return the help string for the given message id"""
desc = self.description
if checkerref:
- desc += " This message belongs to the %s checker." % self.checker_name
+ desc += f" This message belongs to the {self.checker_name} checker."
title = self.msg
if self.minversion or self.maxversion:
restr = []
if self.minversion:
- restr.append("< %s" % ".".join(str(n) for n in self.minversion))
+ restr.append(f"< {'.'.join(str(n) for n in self.minversion)}")
if self.maxversion:
- restr.append(">= %s" % ".".join(str(n) for n in self.maxversion))
+ restr.append(f">= {'.'.join(str(n) for n in self.maxversion)}")
restriction = " or ".join(restr)
if checkerref:
- desc += " It can't be emitted when using Python %s." % restriction
+ desc += f" It can't be emitted when using Python {restriction}."
else:
desc += (
- " This message can't be emitted when using Python %s." % restriction
+ f" This message can't be emitted when using Python {restriction}."
)
msg_help = normalize_text(" ".join(desc.split()), indent=" ")
message_id = f"{self.symbol} ({self.msgid})"
if title != "%s":
title = title.splitlines()[0]
- return ":{}: *{}*\n{}".format(message_id, title.rstrip(" "), msg_help)
+ return f":{message_id}: *{title.rstrip(' ')}*\n{msg_help}"
return f":{message_id}:\n{msg_help}"
diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py
index 495599757..c2dd4d0cb 100644
--- a/pylint/message/message_handler_mix_in.py
+++ b/pylint/message/message_handler_mix_in.py
@@ -255,20 +255,18 @@ class MessagesHandlerMixIn:
if message_definition.scope == WarningScope.LINE:
if line is None:
raise InvalidMessageError(
- "Message %s must provide line, got None"
- % message_definition.msgid
+ f"Message {message_definition.msgid} must provide line, got None"
)
if node is not None:
raise InvalidMessageError(
- "Message %s must only provide line, "
- "got line=%s, node=%s" % (message_definition.msgid, line, node)
+ f"Message {message_definition.msgid} must only provide line, "
+ f"got line={line}, node={node}"
)
elif message_definition.scope == WarningScope.NODE:
# Node-based warnings may provide an override line.
if node is None:
raise InvalidMessageError(
- "Message %s must provide Node, got None"
- % message_definition.msgid
+ f"Message {message_definition.msgid} must provide Node, got None"
)
def add_one_message(
@@ -372,9 +370,9 @@ Pylint provides global options and switches.
if section is None:
title = "General options"
else:
- title = "%s options" % section.capitalize()
+ title = f"{section.capitalize()} options"
result += get_rst_title(title, "~")
- result += "%s\n" % get_rst_section(None, options)
+ result += f"{get_rst_section(None, options)}\n"
result += get_rst_title("Pylint checkers' options and switches", "-")
result += """\
diff --git a/pylint/pyreverse/diadefslib.py b/pylint/pyreverse/diadefslib.py
index b6f6110e8..df9a7f6ad 100644
--- a/pylint/pyreverse/diadefslib.py
+++ b/pylint/pyreverse/diadefslib.py
@@ -139,10 +139,10 @@ class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator):
"""
mode = self.config.mode
if len(node.modules) > 1:
- self.pkgdiagram = PackageDiagram("packages %s" % node.name, mode)
+ self.pkgdiagram = PackageDiagram(f"packages {node.name}", mode)
else:
self.pkgdiagram = None
- self.classdiagram = ClassDiagram("classes %s" % node.name, mode)
+ self.classdiagram = ClassDiagram(f"classes {node.name}", mode)
def leave_project(self, node): # pylint: disable=unused-argument
"""leave the pyreverse.utils.Project node
diff --git a/pylint/pyreverse/diagrams.py b/pylint/pyreverse/diagrams.py
index 6160fb48c..abb456ade 100644
--- a/pylint/pyreverse/diagrams.py
+++ b/pylint/pyreverse/diagrams.py
@@ -110,7 +110,7 @@ class ClassDiagram(Figure, FilterMixIn):
continue
names = self.class_names(associated_nodes)
if names:
- node_name = "{} : {}".format(node_name, ", ".join(names))
+ node_name = f"{node_name} : {', '.join(names)}"
attrs.append(node_name)
return sorted(attrs)
@@ -240,7 +240,7 @@ class PackageDiagram(ClassDiagram):
package = node.root().name
if mod_name == f"{package}.{name}":
return mod
- if mod_name == "{}.{}".format(package.rsplit(".", 1)[0], name):
+ if mod_name == f"{package.rsplit('.', 1)[0]}.{name}":
return mod
raise KeyError(name)
diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py
index 7a0749243..6a97982df 100644
--- a/pylint/pyreverse/inspector.py
+++ b/pylint/pyreverse/inspector.py
@@ -33,7 +33,7 @@ def _iface_hdlr(_):
def _astroid_wrapper(func, modname):
- print("parsing %s..." % modname)
+ print(f"parsing {modname}...")
try:
return func(modname)
except astroid.exceptions.AstroidBuildingException as exc:
@@ -282,7 +282,7 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
module = node.root()
context_name = module.name
if relative:
- mod_path = "{}.{}".format(".".join(context_name.split(".")[:-1]), mod_path)
+ mod_path = f"{'.'.join(context_name.split('.')[:-1])}.{mod_path}"
if self.compute_module(context_name, mod_path):
# handle dependencies
if not hasattr(module, "depends"):
diff --git a/pylint/pyreverse/utils.py b/pylint/pyreverse/utils.py
index b68073a26..71702febf 100644
--- a/pylint/pyreverse/utils.py
+++ b/pylint/pyreverse/utils.py
@@ -131,7 +131,7 @@ class FilterMixIn:
try:
__mode += MODES[nummod]
except KeyError as ex:
- print("Unknown filter mode %s" % ex, file=sys.stderr)
+ print(f"Unknown filter mode {ex}", file=sys.stderr)
self.__mode = __mode
def show_attr(self, node):
@@ -176,10 +176,10 @@ class ASTWalker:
handler = self.handler
kid = klass.__name__.lower()
e_method = getattr(
- handler, "visit_%s" % kid, getattr(handler, "visit_default", None)
+ handler, f"visit_{kid}", getattr(handler, "visit_default", None)
)
l_method = getattr(
- handler, "leave_%s" % kid, getattr(handler, "leave_default", None)
+ handler, f"leave_{kid}", getattr(handler, "leave_default", None)
)
self._cache[klass] = (e_method, l_method)
else:
@@ -257,7 +257,7 @@ def get_annotation(
label = get_annotation_label(ann)
if ann:
label = (
- rf"Optional[{label}]"
+ fr"Optional[{label}]"
if getattr(default, "value", "value") is None
and not label.startswith("Optional")
else label
diff --git a/pylint/pyreverse/vcg_printer.py b/pylint/pyreverse/vcg_printer.py
index ca8bf6d72..238bfb8bb 100644
--- a/pylint/pyreverse/vcg_printer.py
+++ b/pylint/pyreverse/vcg_printer.py
@@ -227,7 +227,7 @@ class VCGPrinter(Printer):
@staticmethod
def _build_label_for_node(properties: NodeProperties) -> str:
fontcolor = "\f09" if properties.fontcolor == "red" else ""
- label = rf"\fb{fontcolor}{properties.label}\fn"
+ label = fr"\fb{fontcolor}{properties.label}\fn"
if properties.attrs is None and properties.methods is None:
# return a compact form which only displays the classname in a box
return label
diff --git a/pylint/reporters/base_reporter.py b/pylint/reporters/base_reporter.py
index 067a32c77..fda99f4ef 100644
--- a/pylint/reporters/base_reporter.py
+++ b/pylint/reporters/base_reporter.py
@@ -42,7 +42,7 @@ class BaseReporter:
"""display results encapsulated in the layout tree"""
self.section = 0
if hasattr(layout, "report_id"):
- layout.children[0].children[0].data += " (%s)" % layout.report_id
+ layout.children[0].children[0].data += f" ({layout.report_id})"
self._display(layout)
def _display(self, layout):
diff --git a/pylint/reporters/reports_handler_mix_in.py b/pylint/reporters/reports_handler_mix_in.py
index f42824559..914556ef4 100644
--- a/pylint/reporters/reports_handler_mix_in.py
+++ b/pylint/reporters/reports_handler_mix_in.py
@@ -51,7 +51,7 @@ class ReportsHandlerMixIn:
def make_reports(self, stats, old_stats):
"""render registered reports"""
- sect = Section("Report", "%s statements analysed." % (self.stats["statement"]))
+ sect = Section("Report", f"{self.stats['statement']} statements analysed.")
for checker in self.report_order():
for reportid, r_title, r_cb in self._reports[checker]:
if not self.report_is_enabled(reportid):
diff --git a/pylint/reporters/text.py b/pylint/reporters/text.py
index 865b3c84d..6d9a05f78 100644
--- a/pylint/reporters/text.py
+++ b/pylint/reporters/text.py
@@ -147,7 +147,7 @@ class TextReporter(BaseReporter):
"""manage message of different type and in the context of path"""
if msg.module not in self._modules:
if msg.module:
- self.writeln("************* Module %s" % msg.module)
+ self.writeln(f"************* Module {msg.module}")
self._modules.add(msg.module)
else:
self.writeln("************* ")
@@ -171,8 +171,7 @@ class ParseableTextReporter(TextReporter):
def __init__(self, output=None):
warnings.warn(
- "%s output format is deprecated. This is equivalent "
- "to --msg-template=%s" % (self.name, self.line_format),
+ f"{self.name} output format is deprecated. This is equivalent to --msg-template={self.line_format}",
DeprecationWarning,
)
TextReporter.__init__(self, output)
@@ -227,10 +226,10 @@ class ColorizedTextReporter(TextReporter):
color, style = self._get_decoration("S")
if msg.module:
modsep = colorize_ansi(
- "************* Module %s" % msg.module, color, style
+ f"************* Module {msg.module}", color, style
)
else:
- modsep = colorize_ansi("************* %s" % msg.module, color, style)
+ modsep = colorize_ansi(f"************* {msg.module}", color, style)
self.writeln(modsep)
self._modules.add(msg.module)
color, style = self._get_decoration(msg.C)
diff --git a/pylint/reporters/ureports/nodes.py b/pylint/reporters/ureports/nodes.py
index 76d75959c..d359093b8 100644
--- a/pylint/reporters/ureports/nodes.py
+++ b/pylint/reporters/ureports/nodes.py
@@ -50,11 +50,11 @@ class VNode:
return self.__class__.__name__.lower()
def accept(self, visitor, *args, **kwargs):
- func = getattr(visitor, "visit_%s" % self._get_visit_name())
+ func = getattr(visitor, f"visit_{self._get_visit_name()}")
return func(self, *args, **kwargs)
def leave(self, visitor, *args, **kwargs):
- func = getattr(visitor, "leave_%s" % self._get_visit_name())
+ func = getattr(visitor, f"leave_{self._get_visit_name()}")
return func(self, *args, **kwargs)
diff --git a/pylint/reporters/ureports/text_writer.py b/pylint/reporters/ureports/text_writer.py
index 42b03f0ae..a48d73aac 100644
--- a/pylint/reporters/ureports/text_writer.py
+++ b/pylint/reporters/ureports/text_writer.py
@@ -94,4 +94,4 @@ class TextWriter(BaseWriter):
def visit_text(self, layout):
"""add some text"""
- self.write("%s" % layout.data)
+ self.write(f"{layout.data}")
diff --git a/pylint/testutils/constants.py b/pylint/testutils/constants.py
index fa70d3b57..fc04d927d 100644
--- a/pylint/testutils/constants.py
+++ b/pylint/testutils/constants.py
@@ -7,7 +7,9 @@ import sys
from os.path import abspath, dirname
from pathlib import Path
-SYS_VERS_STR = "%d%d%d" % sys.version_info[:3]
+SYS_VERS_STR = (
+ "%d%d%d" % sys.version_info[:3] # pylint: disable=consider-using-f-string
+)
TITLE_UNDERLINES = ["", "=", "-", "."]
PREFIX = abspath(dirname(__file__))
UPDATE_OPTION = "--update-functional-output"
@@ -20,7 +22,7 @@ _MESSAGE = {"msg": r"[a-z][a-z\-]+"}
# - followed by a list of bracketed message symbols.
# Used to extract expected messages from testdata files.
_EXPECTED_RE = re.compile(
- r"\s*#\s*(?:(?P<line>[+-]?[0-9]+):)?"
+ r"\s*#\s*(?:(?P<line>[+-]?[0-9]+):)?" # pylint: disable=consider-using-f-string
r"(?:(?P<op>[><=]+) *(?P<version>[0-9.]+):)?"
r"\s*\[(?P<msgs>%(msg)s(?:,\s*%(msg)s)*)]" % _MESSAGE
)
diff --git a/pylint/testutils/lint_module_test.py b/pylint/testutils/lint_module_test.py
index 8f24daf86..2b5ad2dcb 100644
--- a/pylint/testutils/lint_module_test.py
+++ b/pylint/testutils/lint_module_test.py
@@ -53,8 +53,7 @@ class LintModuleTest:
def setUp(self):
if self._should_be_skipped_due_to_version():
pytest.skip(
- "Test cannot run with Python %s."
- % sys.version.split(" ", maxsplit=1)[0]
+ f"Test cannot run with Python {sys.version.split(' ', maxsplit=1)[0]}."
)
missing = []
for requirement in self._test_file.options["requires"]:
@@ -63,7 +62,7 @@ class LintModuleTest:
except ImportError:
missing.append(requirement)
if missing:
- pytest.skip("Requires %s to be present." % ",".join(missing))
+ pytest.skip(f"Requires {','.join(missing)} to be present.")
except_implementations = self._test_file.options["except_implementations"]
if except_implementations:
implementations = [i.strip() for i in except_implementations.split(",")]
@@ -74,7 +73,7 @@ class LintModuleTest:
if excluded_platforms:
platforms = [p.strip() for p in excluded_platforms.split(",")]
if sys.platform.lower() in platforms:
- pytest.skip("Test cannot run on platform %r" % sys.platform)
+ pytest.skip(f"Test cannot run on platform {sys.platform!r}")
def runTest(self):
self._runTest()
@@ -193,16 +192,19 @@ class LintModuleTest:
def error_msg_for_unequal_messages(
self, actual_messages, expected_messages, actual_output: List[OutputLine]
):
- msg = ['Wrong results for file "%s":' % (self._test_file.base)]
+ msg = [f'Wrong results for file "{self._test_file.base}":']
missing, unexpected = self.multiset_difference(
expected_messages, actual_messages
)
if missing:
msg.append("\nExpected in testdata:")
- msg.extend(" %3d: %s" % msg for msg in sorted(missing))
+ msg.extend(
+ " %3d: %s" % msg # pylint: disable=consider-using-f-string
+ for msg in sorted(missing)
+ )
if unexpected:
msg.append("\nUnexpected in testdata:")
- msg.extend(" %3d: %s" % msg for msg in sorted(unexpected)) # type: ignore
+ msg.extend(" %3d: %s" % msg for msg in sorted(unexpected)) # type: ignore #pylint: disable=consider-using-f-string
error_msg = "\n".join(msg)
if self._config and self._config.getoption("verbose") > 0:
error_msg += "\n\nActual pylint output for this file:\n"
diff --git a/pylint/testutils/reporter_for_tests.py b/pylint/testutils/reporter_for_tests.py
index 45aa63577..b1b7af7e4 100644
--- a/pylint/testutils/reporter_for_tests.py
+++ b/pylint/testutils/reporter_for_tests.py
@@ -32,7 +32,7 @@ class GenericTestReporter(BaseReporter):
str_message: str = msg.msg
self.message_ids[msg_id] = 1
if obj:
- obj = ":%s" % obj
+ obj = f":{obj}"
sigle = msg_id[0]
if linesep != "\n":
# 2to3 writes os.linesep instead of using
diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py
index 1a8f7957d..e0241ce0b 100644
--- a/pylint/utils/utils.py
+++ b/pylint/utils/utils.py
@@ -47,7 +47,7 @@ def diff_string(old, new):
difference
"""
diff = abs(old - new)
- diff_str = "{}{}".format(CMPS[cmp(old, new)], diff and ("%.2f" % diff) or "")
+ diff_str = f"{CMPS[cmp(old, new)]}{diff and f'{diff:.2f}' or ''}"
return diff_str
@@ -80,16 +80,16 @@ def get_rst_section(section, options, doc=None):
result += get_rst_title(section, "'")
if doc:
formatted_doc = normalize_text(doc)
- result += "%s\n\n" % formatted_doc
+ result += f"{formatted_doc}\n\n"
for optname, optdict, value in options:
help_opt = optdict.get("help")
- result += ":%s:\n" % optname
+ result += f":{optname}:\n"
if help_opt:
formatted_help = normalize_text(help_opt, indent=" ")
- result += "%s\n" % formatted_help
+ result += f"{formatted_help}\n"
if value:
value = str(_format_option_value(optdict, value))
- result += "\n Default: ``%s``\n" % value.replace("`` ", "```` ``")
+ result += f"\n Default: ``{value.replace('`` ', '```` ``')}``\n"
return result
@@ -240,7 +240,7 @@ def _check_csv(value):
def _comment(string):
"""return string as a comment"""
lines = [line.strip() for line in string.splitlines()]
- return "# " + ("%s# " % os.linesep).join(lines)
+ return "# " + f"{os.linesep}# ".join(lines)
def _format_option_value(optdict, value):
@@ -257,7 +257,7 @@ def _format_option_value(optdict, value):
elif optdict.get("type") == "yn":
value = "yes" if value else "no"
elif isinstance(value, str) and value.isspace():
- value = "'%s'" % value
+ value = f"'{value}'"
return value
@@ -265,7 +265,7 @@ def format_section(stream, section, options, doc=None):
"""format an options section using the INI format"""
if doc:
print(_comment(doc), file=stream)
- print("[%s]" % section, file=stream)
+ print(f"[{section}]", file=stream)
_ini_format(stream, options)
@@ -281,7 +281,7 @@ def _ini_format(stream, options):
else:
print(file=stream)
if value is None:
- print("#%s=" % optname, file=stream)
+ print(f"#{optname}=", file=stream)
else:
value = str(value).strip()
if re.match(r"^([\w-]+,)+[\w-]+$", str(value)):
diff --git a/script/bump_changelog.py b/script/bump_changelog.py
index 8d8225379..6b8e4abbf 100644
--- a/script/bump_changelog.py
+++ b/script/bump_changelog.py
@@ -18,7 +18,7 @@ RELEASE_DATE_TEXT = "Release date: TBA"
WHATS_NEW_TEXT = "What's New in Pylint"
TODAY = datetime.now()
FULL_WHATS_NEW_TEXT = WHATS_NEW_TEXT + " {version}?"
-NEW_RELEASE_DATE_MESSAGE = "Release date: {}".format(TODAY.strftime("%Y-%m-%d"))
+NEW_RELEASE_DATE_MESSAGE = f"Release date: {TODAY.strftime('%Y-%m-%d')}"
def main() -> None:
@@ -128,10 +128,14 @@ def transform_content(content: str, version: str) -> str:
def do_checks(content, next_version, version, version_type):
err = "in the changelog, fix that first!"
NEW_VERSION_ERROR_MSG = (
- "The text for this version '{version}' did not exists %s" % err
+ # pylint: disable-next=consider-using-f-string
+ "The text for this version '{version}' did not exists %s"
+ % err
)
NEXT_VERSION_ERROR_MSG = (
- "The text for the next version '{version}' already exists %s" % err
+ # pylint: disable-next=consider-using-f-string
+ "The text for the next version '{version}' already exists %s"
+ % err
)
wn_next_version = get_whats_new(next_version)
wn_this_version = get_whats_new(version)
diff --git a/tests/benchmark/test_baseline_benchmarks.py b/tests/benchmark/test_baseline_benchmarks.py
index e7713daff..693fe4d68 100644
--- a/tests/benchmark/test_baseline_benchmarks.py
+++ b/tests/benchmark/test_baseline_benchmarks.py
@@ -131,9 +131,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_benchmark_j10(self, benchmark):
"""Establish a baseline of pylint performance with no work across threads
@@ -155,9 +153,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_benchmark_check_parallel_j10(self, benchmark):
"""Should demonstrate times very close to `test_baseline_benchmark_j10`"""
@@ -170,9 +166,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(check_parallel, linter, jobs=10, files=fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_lots_of_files_j1(self, benchmark):
"""Establish a baseline with only 'master' checker being run in -j1
@@ -190,9 +184,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_lots_of_files_j10(self, benchmark):
"""Establish a baseline with only 'master' checker being run in -j10
@@ -211,9 +203,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_lots_of_files_j1_empty_checker(self, benchmark):
"""Baselines pylint for a single extra checker being run in -j1, for N-files
@@ -232,9 +222,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_lots_of_files_j10_empty_checker(self, benchmark):
"""Baselines pylint for a single extra checker being run in -j10, for N-files
@@ -253,9 +241,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_benchmark_j1_single_working_checker(self, benchmark):
"""Establish a baseline of single-worker performance for PyLinter
@@ -280,9 +266,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_benchmark_j10_single_working_checker(self, benchmark):
"""Establishes baseline of multi-worker performance for PyLinter/check_parallel
@@ -308,9 +292,7 @@ class TestEstablishBaselineBenchmarks:
benchmark(linter.check, fileinfos)
assert (
linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(linter.reporter.messages)}"
def test_baseline_benchmark_j1_all_checks_single_file(self, benchmark):
"""Runs a single file, with -j1, against all plug-ins
@@ -327,9 +309,7 @@ class TestEstablishBaselineBenchmarks:
assert (
runner.linter.msg_status == 0
- ), "Expected no errors to be thrown: %s" % pprint.pformat(
- runner.linter.reporter.messages
- )
+ ), f"Expected no errors to be thrown: {pprint.pformat(runner.linter.reporter.messages)}"
def test_baseline_benchmark_j1_all_checks_lots_of_files(self, benchmark):
"""Runs lots of files, with -j1, against all plug-ins
diff --git a/tests/checkers/unittest_python3.py b/tests/checkers/unittest_python3.py
index d8f87d14f..6dd78cd25 100644
--- a/tests/checkers/unittest_python3.py
+++ b/tests/checkers/unittest_python3.py
@@ -500,7 +500,7 @@ class TestPython3Checker(testutils.CheckerTestCase):
def test_dict_iter_method(self):
for meth in ("keys", "values", "items"):
- node = astroid.extract_node("x.iter%s() #@" % meth)
+ node = astroid.extract_node(f"x.iter{meth}() #@")
message = testutils.Message("dict-iter-method", node=node)
with self.assertAddsMessages(message):
self.checker.visit_call(node)
@@ -537,7 +537,7 @@ class TestPython3Checker(testutils.CheckerTestCase):
def test_dict_view_method(self):
for meth in ("keys", "values", "items"):
- node = astroid.extract_node("x.view%s() #@" % meth)
+ node = astroid.extract_node(f"x.view{meth}() #@")
message = testutils.Message("dict-view-method", node=node)
with self.assertAddsMessages(message):
self.checker.visit_call(node)
diff --git a/tests/checkers/unittest_similar.py b/tests/checkers/unittest_similar.py
index 467033e62..f27df9272 100644
--- a/tests/checkers/unittest_similar.py
+++ b/tests/checkers/unittest_similar.py
@@ -53,10 +53,10 @@ def test_ignore_comments():
assert (
output.getvalue().strip()
== (
- """
+ f"""
10 similar lines in 2 files
-==%s:[0:11]
-==%s:[0:11]
+=={SIMILAR1}:[0:11]
+=={SIMILAR2}:[0:11]
import one
from two import two
three
@@ -70,7 +70,6 @@ def test_ignore_comments():
''' ten
TOTAL lines=62 duplicates=10 percent=16.13
"""
- % (SIMILAR1, SIMILAR2)
).strip()
)
@@ -83,10 +82,10 @@ def test_ignore_docstrings():
assert (
output.getvalue().strip()
== (
- """
+ f"""
5 similar lines in 2 files
-==%s:[7:15]
-==%s:[7:15]
+=={SIMILAR1}:[7:15]
+=={SIMILAR2}:[7:15]
seven
eight
nine
@@ -97,8 +96,8 @@ def test_ignore_docstrings():
fourteen
5 similar lines in 2 files
-==%s:[0:5]
-==%s:[0:5]
+=={SIMILAR1}:[0:5]
+=={SIMILAR2}:[0:5]
import one
from two import two
three
@@ -106,7 +105,6 @@ def test_ignore_docstrings():
five
TOTAL lines=62 duplicates=10 percent=16.13
"""
- % ((SIMILAR1, SIMILAR2) * 2)
).strip()
)
@@ -132,10 +130,10 @@ def test_multiline_imports():
assert (
output.getvalue().strip()
== (
- """
+ f"""
8 similar lines in 2 files
-==%s:[0:8]
-==%s:[0:8]
+=={MULTILINE}:[0:8]
+=={MULTILINE}:[0:8]
from foo import (
bar,
baz,
@@ -146,7 +144,6 @@ def test_multiline_imports():
)
TOTAL lines=16 duplicates=8 percent=50.00
"""
- % (MULTILINE, MULTILINE)
).strip()
)
@@ -172,10 +169,10 @@ def test_ignore_signatures_fail():
assert (
output.getvalue().strip()
== (
- '''
+ f'''
9 similar lines in 2 files
-==%s:[7:17]
-==%s:[8:18]
+=={SIMILAR5}:[7:17]
+=={SIMILAR6}:[8:18]
arg1: int = 3,
arg2: Class1 = val1,
arg3: Class2 = func3(val2),
@@ -188,8 +185,8 @@ def test_ignore_signatures_fail():
"""Valid function definition with docstring only."""
6 similar lines in 2 files
-==%s:[0:6]
-==%s:[1:7]
+=={SIMILAR5}:[0:6]
+=={SIMILAR6}:[1:7]
@deco1(dval1)
@deco2(dval2)
@deco3(
@@ -198,7 +195,6 @@ def test_ignore_signatures_fail():
)
TOTAL lines=35 duplicates=15 percent=42.86
'''
- % (SIMILAR5, SIMILAR6, SIMILAR5, SIMILAR6)
).strip()
)
@@ -224,10 +220,10 @@ def test_ignore_signatures_class_methods_fail():
assert (
output.getvalue().strip()
== (
- '''
+ f'''
15 similar lines in 2 files
-==%s:[1:18]
-==%s:[1:18]
+=={SIMILAR_CLS_A}:[1:18]
+=={SIMILAR_CLS_B}:[1:18]
def parent_method(
self,
*,
@@ -247,8 +243,8 @@ def test_ignore_signatures_class_methods_fail():
7 similar lines in 2 files
-==%s:[20:27]
-==%s:[20:27]
+=={SIMILAR_CLS_A}:[20:27]
+=={SIMILAR_CLS_B}:[20:27]
self,
*,
a=None,
@@ -258,7 +254,6 @@ def test_ignore_signatures_class_methods_fail():
pass
TOTAL lines=54 duplicates=22 percent=40.74
'''
- % (SIMILAR_CLS_A, SIMILAR_CLS_B, SIMILAR_CLS_A, SIMILAR_CLS_B)
).strip()
)
@@ -284,10 +279,10 @@ def test_ignore_signatures_empty_functions_fail():
assert (
output.getvalue().strip()
== (
- '''
+ f'''
6 similar lines in 2 files
-==%s:[1:7]
-==%s:[1:7]
+=={EMPTY_FUNCTION_1}:[1:7]
+=={EMPTY_FUNCTION_2}:[1:7]
arg1: int = 1,
arg2: str = "2",
arg3: int = 3,
@@ -296,7 +291,6 @@ def test_ignore_signatures_empty_functions_fail():
"""Valid function definition with docstring only."""
TOTAL lines=14 duplicates=6 percent=42.86
'''
- % (EMPTY_FUNCTION_1, EMPTY_FUNCTION_2)
).strip()
)
@@ -330,10 +324,10 @@ def test_ignore_nothing():
assert (
output.getvalue().strip()
== (
- """
+ f"""
5 similar lines in 2 files
-==%s:[0:5]
-==%s:[0:5]
+=={SIMILAR1}:[0:5]
+=={SIMILAR2}:[0:5]
import one
from two import two
three
@@ -341,7 +335,6 @@ def test_ignore_nothing():
five
TOTAL lines=62 duplicates=5 percent=8.06
"""
- % (SIMILAR1, SIMILAR2)
).strip()
)
@@ -354,10 +347,10 @@ def test_lines_without_meaningful_content_do_not_trigger_similarity():
assert (
output.getvalue().strip()
== (
- """
+ f"""
14 similar lines in 2 files
-==%s:[11:25]
-==%s:[11:25]
+=={SIMILAR3}:[11:25]
+=={SIMILAR4}:[11:25]
b = (
(
[
@@ -374,7 +367,6 @@ def test_lines_without_meaningful_content_do_not_trigger_similarity():
)
TOTAL lines=50 duplicates=14 percent=28.00
"""
- % (SIMILAR3, SIMILAR4)
).strip()
)
diff --git a/tests/functional/a/arguments.py b/tests/functional/a/arguments.py
index 7d0c74f83..e1a8005aa 100644
--- a/tests/functional/a/arguments.py
+++ b/tests/functional/a/arguments.py
@@ -1,5 +1,5 @@
# pylint: disable=too-few-public-methods, no-absolute-import,missing-docstring,import-error,wrong-import-position
-# pylint: disable=wrong-import-order, useless-object-inheritance,unnecessary-lambda
+# pylint: disable=wrong-import-order, useless-object-inheritance,unnecessary-lambda, consider-using-f-string
def decorator(fun):
"""Decorator"""
diff --git a/tests/functional/c/consider/consider_using_f_string.py b/tests/functional/c/consider/consider_using_f_string.py
new file mode 100644
index 000000000..825f3517c
--- /dev/null
+++ b/tests/functional/c/consider/consider_using_f_string.py
@@ -0,0 +1,107 @@
+"""Test to see if a f-string would be possible and consider-using-f-string should be raised"""
+# pylint: disable=unused-variable, invalid-name, missing-function-docstring, pointless-statement
+# pylint: disable=expression-not-assigned, repeated-keyword
+
+PARAM_1 = PARAM_2 = PARAM_3 = 1
+PARAM_LIST = [PARAM_1, PARAM_2, PARAM_3]
+PARAM_LIST_SINGLE = [PARAM_1]
+PARAM_DICT = {"Param_1": PARAM_1, "Param_2": PARAM_2, "Param_3": PARAM_3}
+PARAM_DICT_SINGLE = {"Param_1": PARAM_1}
+
+
+def return_parameter():
+ return PARAM_1
+
+
+def return_list():
+ return PARAM_LIST
+
+
+def return_dict():
+ return PARAM_DICT
+
+
+def print_good():
+ print("String {}, {} or {}".format(*PARAM_LIST))
+ print("String {}, {}, {} or {}".format(*PARAM_LIST_SINGLE, *PARAM_LIST))
+ print("String {Param}, {}, {} or {}".format(Param=PARAM_1, *PARAM_LIST))
+ print("String {Param} {Param}".format(Param=PARAM_1))
+ print("{Param_1} {Param_2}".format(**PARAM_DICT))
+ print("{Param_1} {Param_2} {Param_3}".format(**PARAM_DICT_SINGLE, **PARAM_DICT))
+ print("{Param_1} {Param_2} {Param_3}".format(Param_1=PARAM_1, **PARAM_DICT))
+ print("{Param_1} {Param_2}".format(**PARAM_DICT))
+ print("{Param_1} {Param_2}".format(**return_dict()))
+ print("%(Param_1)s %(Param_2)s" % PARAM_LIST)
+ print("%(Param_1)s %(Param_2)s" % PARAM_DICT)
+ print("%(Param_1)s %(Param_2)s" % return_dict())
+ print("{a[Param_1]}{a[Param_2]}".format(a=PARAM_DICT))
+
+def print_bad():
+ print("String %f" % PARAM_1) # [consider-using-f-string]
+ print("String {}".format(PARAM_1)) # [consider-using-f-string]
+ print("String {Param_1}".format(Param_1=PARAM_1)) # [consider-using-f-string]
+ print("{} {}".format(PARAM_1, PARAM_2)) # [consider-using-f-string]
+ print("{Par_1}{Par_2}".format(Par_1=PARAM_1, Par_2=PARAM_2)) # [consider-using-f-string]
+ print("{Param_1}".format(*PARAM_LIST_SINGLE)) # [consider-using-f-string]
+ print("{Param_1}".format(**PARAM_DICT_SINGLE)) # [consider-using-f-string]
+ print("String %s" % (PARAM_1)) # [consider-using-f-string]
+ print("String %s %s" % (PARAM_1, PARAM_2)) # [consider-using-f-string]
+ print("String %s" % (PARAM_LIST_SINGLE)) # [consider-using-f-string]
+
+
+def statement_good():
+ "String {}, {} or {}".format(*PARAM_LIST)
+ "String {}, {}, {} or {}".format(*PARAM_LIST_SINGLE, *PARAM_LIST)
+ "String {Param}, {}, {} or {}".format(Param=PARAM_1, *PARAM_LIST)
+ "String {Param} {Param}".format(Param=PARAM_1)
+ "{Param_1} {Param_2}".format(**PARAM_DICT)
+ "{Param_1} {Param_2} {Param_3}".format(**PARAM_DICT_SINGLE, **PARAM_DICT)
+ "{Param_1} {Param_2} {Param_3}".format(Param_1=PARAM_1, **PARAM_DICT)
+ "{Param_1} {Param_2}".format(**PARAM_DICT)
+ "{Param_1} {Param_2}".format(**return_dict())
+ "%(Param_1)s %(Param_2)s" % PARAM_LIST
+ "%(Param_1)s %(Param_2)s" % PARAM_DICT
+ "%(Param_1)s %(Param_2)s" % return_dict()
+ "{a[Param_1]}{a[Param_2]}".format(a=PARAM_DICT)
+
+def statement_bad():
+ "String %f" % PARAM_1 # [consider-using-f-string]
+ "String {}".format(PARAM_1) # [consider-using-f-string]
+ "String {Param_1}".format(Param_1=PARAM_1) # [consider-using-f-string]
+ "{} {}".format(PARAM_1, PARAM_2) # [consider-using-f-string]
+ "{Par_1}{Par_2}".format(Par_1=PARAM_1, Par_2=PARAM_2) # [consider-using-f-string]
+ "{Param_1}".format(*PARAM_LIST_SINGLE) # [consider-using-f-string]
+ "{Param_1}".format(**PARAM_DICT_SINGLE) # [consider-using-f-string]
+ "String %s" % (PARAM_1) # [consider-using-f-string]
+ "String %s %s" % (PARAM_1, PARAM_2) # [consider-using-f-string]
+ "String %s" % (PARAM_LIST_SINGLE) # [consider-using-f-string]
+
+
+def assignment_good():
+ A = "String {}, {} or {}".format(*PARAM_LIST)
+ B = "String {}, {}, {} or {}".format(*PARAM_LIST_SINGLE, *PARAM_LIST)
+ C = "String {Param}, {}, {} or {}".format(Param=PARAM_1, *PARAM_LIST)
+ D = "String {Param} {Param}".format(Param=PARAM_1)
+ E = "{Param_1} {Param_2}".format(**PARAM_DICT)
+ F = "{Param_1} {Param_2} {Param_3}".format(**PARAM_DICT_SINGLE, **PARAM_DICT)
+ G = "{Param_1} {Param_2} {Param_3}".format(Param_1=PARAM_1, **PARAM_DICT)
+ H = "{Param_1} {Param_2}".format(**PARAM_DICT)
+ I = "{Param_1} {Param_2}".format(**return_dict())
+ J = "%(Param_1)s %(Param_2)s" % PARAM_LIST
+ K = "%(Param_1)s %(Param_2)s" % PARAM_DICT
+ L = "%(Param_1)s %(Param_2)s" % return_dict()
+ M = "{a[Param_1]}{a[Param_2]}".format(a=PARAM_DICT)
+ N = "{Param}".format
+
+
+def assignment_bad():
+ a = "String %f" % PARAM_1 # [consider-using-f-string]
+ b = "String {}".format(PARAM_1) # [consider-using-f-string]
+ c = "String {Param_1}".format(Param_1=PARAM_1) # [consider-using-f-string]
+ d = "{} {}".format(PARAM_1, PARAM_2) # [consider-using-f-string]
+ e = "{Par_1}{Par_2}".format(Par_1=PARAM_1, Par_2=PARAM_2) # [consider-using-f-string]
+ f = "{Param_1}".format(*PARAM_LIST_SINGLE) # [consider-using-f-string]
+ g = "{Param_1}".format(**PARAM_DICT_SINGLE) # [consider-using-f-string]
+ h = "String %s" % (PARAM_1) # [consider-using-f-string]
+ i = "String %s %s" % (PARAM_1, PARAM_2) # [consider-using-f-string]
+ j = "String %s" % (PARAM_LIST_SINGLE) # [consider-using-f-string]
diff --git a/tests/functional/c/consider/consider_using_f_string.txt b/tests/functional/c/consider/consider_using_f_string.txt
new file mode 100644
index 000000000..05288cde0
--- /dev/null
+++ b/tests/functional/c/consider/consider_using_f_string.txt
@@ -0,0 +1,30 @@
+consider-using-f-string:40:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:41:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:42:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:43:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:44:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:45:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:46:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:47:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:48:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:49:10::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:68:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:69:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:70:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:71:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:72:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:73:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:74:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:75:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:76:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:77:4::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:98:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:99:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:100:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:101:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:102:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:103:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:104:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:105:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:106:8::"Formatting a regular string which could be a f-string":HIGH
+consider-using-f-string:107:8::"Formatting a regular string which could be a f-string":HIGH
diff --git a/tests/functional/d/docstrings.py b/tests/functional/d/docstrings.py
index e466a000f..e9d137a7f 100644
--- a/tests/functional/d/docstrings.py
+++ b/tests/functional/d/docstrings.py
@@ -1,4 +1,4 @@
-# pylint: disable=no-self-use, useless-object-inheritance, unnecessary-pass
+# pylint: disable=no-self-use, useless-object-inheritance, unnecessary-pass, consider-using-f-string
# -1: [missing-module-docstring]
from __future__ import print_function
diff --git a/tests/functional/d/duplicate_string_formatting_argument.py b/tests/functional/d/duplicate_string_formatting_argument.py
index b012f98e0..572f20eca 100644
--- a/tests/functional/d/duplicate_string_formatting_argument.py
+++ b/tests/functional/d/duplicate_string_formatting_argument.py
@@ -1,4 +1,4 @@
-# pylint: disable=missing-docstring
+# pylint: disable=missing-docstring, consider-using-f-string
NAME = 42
OTHER_NAME = 24
diff --git a/tests/functional/l/logging_format_interpolation.py b/tests/functional/l/logging_format_interpolation.py
index 8defa940b..782e3834b 100644
--- a/tests/functional/l/logging_format_interpolation.py
+++ b/tests/functional/l/logging_format_interpolation.py
@@ -1,5 +1,5 @@
# pylint: disable=no-member, no-absolute-import, import-error,line-too-long
-# pylint: disable=invalid-name,missing-docstring,wrong-import-order,wrong-import-position
+# pylint: disable=invalid-name,missing-docstring,wrong-import-order,wrong-import-position, consider-using-f-string
try:
import __builtin__ as builtins
except ImportError:
diff --git a/tests/functional/l/logging_not_lazy.py b/tests/functional/l/logging_not_lazy.py
index c0634327f..28aa3b783 100644
--- a/tests/functional/l/logging_not_lazy.py
+++ b/tests/functional/l/logging_not_lazy.py
@@ -1,4 +1,4 @@
-# pylint: disable=missing-docstring,no-member,deprecated-method,invalid-name
+# pylint: disable=missing-docstring,no-member,deprecated-method,invalid-name, consider-using-f-string
# Muck up the names in an effort to confuse...
import logging as renamed_logging
@@ -14,7 +14,6 @@ renamed_logging.log(renamed_logging.INFO, "Var: " + var) # [logging-not-lazy]
renamed_logging.warn('%s' + ' the rest of a single string') # [logging-not-lazy]
renamed_logging.log(renamed_logging.INFO, var_name + var) # [logging-not-lazy]
-var_name = 'Var:'
# Statements that should not be flagged:
renamed_logging.warn('%s, %s', 4, 5)
renamed_logging.log(renamed_logging.INFO, 'msg: %s', 'Run!')
diff --git a/tests/functional/l/logging_not_lazy_with_logger.py b/tests/functional/l/logging_not_lazy_with_logger.py
index ef2221f23..69d0e9bd4 100644
--- a/tests/functional/l/logging_not_lazy_with_logger.py
+++ b/tests/functional/l/logging_not_lazy_with_logger.py
@@ -1,4 +1,5 @@
"""Logging warnings using a logger class."""
+# pylint: disable=consider-using-f-string
from __future__ import absolute_import
import logging
diff --git a/tests/functional/l/logging_not_lazy_with_logger.txt b/tests/functional/l/logging_not_lazy_with_logger.txt
index 31fe45c6d..43d9c1620 100644
--- a/tests/functional/l/logging_not_lazy_with_logger.txt
+++ b/tests/functional/l/logging_not_lazy_with_logger.txt
@@ -1,4 +1,4 @@
-logging-not-lazy:8:0::Use lazy % formatting in logging functions
logging-not-lazy:9:0::Use lazy % formatting in logging functions
-logging-not-lazy:11:0::Use lazy % formatting in logging functions
-logging-not-lazy:13:0::Use lazy % formatting in logging functions
+logging-not-lazy:10:0::Use lazy % formatting in logging functions
+logging-not-lazy:12:0::Use lazy % formatting in logging functions
+logging-not-lazy:14:0::Use lazy % formatting in logging functions
diff --git a/tests/functional/m/misplaced_format_function.py b/tests/functional/m/misplaced_format_function.py
index 7bcabfc03..679f59858 100644
--- a/tests/functional/m/misplaced_format_function.py
+++ b/tests/functional/m/misplaced_format_function.py
@@ -1,7 +1,7 @@
"""Test that format function is used only with string."""
# pylint: disable=invalid-name, pointless-string-statement, line-too-long, no-member, disallowed-name, undefined-variable, missing-docstring, too-few-public-methods
-
+# pylint: disable=consider-using-f-string
print('value: {}').format(123) # [misplaced-format-function]
print("value: {}").format(123) # [misplaced-format-function]
print('value: {}'.format(123))
diff --git a/tests/functional/n/new_style_class_py_30.py b/tests/functional/n/new_style_class_py_30.py
index fd78c5590..7a2a59a17 100644
--- a/tests/functional/n/new_style_class_py_30.py
+++ b/tests/functional/n/new_style_class_py_30.py
@@ -14,8 +14,7 @@ class File(file): # pylint: disable=file-builtin,undefined-variable
self.verbose = verbose
super(File, self).__init__(name, mode, buffering) # [super-with-arguments]
if self.verbose:
- print("File %s is opened. The mode is: %s" % (self.name,
- self.mode))
+ print(f"File {self.name} is opened. The mode is: {self.mode}")
def write(self, a_string):
""" Write a string to the file."""
@@ -30,6 +29,6 @@ class File(file): # pylint: disable=file-builtin,undefined-variable
def close(self):
"""Close the file."""
if self.verbose:
- print("Closing file %s" % self.name)
+ print(f"Closing file {self.name}")
super(File, self).close() # [super-with-arguments]
self.was_modified = False
diff --git a/tests/functional/n/new_style_class_py_30.txt b/tests/functional/n/new_style_class_py_30.txt
index 1f3434378..b86cc2b54 100644
--- a/tests/functional/n/new_style_class_py_30.txt
+++ b/tests/functional/n/new_style_class_py_30.txt
@@ -1,4 +1,4 @@
super-with-arguments:15:8:File.__init__:Consider using Python 3 style super() without arguments
-super-with-arguments:22:8:File.write:Consider using Python 3 style super() without arguments
-super-with-arguments:27:8:File.writelines:Consider using Python 3 style super() without arguments
-super-with-arguments:34:8:File.close:Consider using Python 3 style super() without arguments
+super-with-arguments:21:8:File.write:Consider using Python 3 style super() without arguments
+super-with-arguments:26:8:File.writelines:Consider using Python 3 style super() without arguments
+super-with-arguments:33:8:File.close:Consider using Python 3 style super() without arguments
diff --git a/tests/functional/r/raise/raising_format_tuple.py b/tests/functional/r/raise/raising_format_tuple.py
index 12e763d47..268d834f4 100644
--- a/tests/functional/r/raise/raising_format_tuple.py
+++ b/tests/functional/r/raise/raising_format_tuple.py
@@ -4,7 +4,7 @@ contains a percent sign, thus suggesting a % string formatting was intended
instead. The same holds for a string containing {...} suggesting str.format()
was intended.
'''
-# pylint: disable=redundant-u-string-prefix
+# pylint: disable=redundant-u-string-prefix, consider-using-f-string
def bad_percent(arg):
'''Raising a percent-formatted string and an argument'''
diff --git a/tests/functional/r/renamed_import_logging_not_lazy.py b/tests/functional/r/renamed_import_logging_not_lazy.py
index e4ca1c338..5eb23bf95 100644
--- a/tests/functional/r/renamed_import_logging_not_lazy.py
+++ b/tests/functional/r/renamed_import_logging_not_lazy.py
@@ -1,4 +1,4 @@
-# pylint: disable=missing-docstring, too-few-public-methods, no-member
+# pylint: disable=missing-docstring, too-few-public-methods, no-member, consider-using-f-string
from __future__ import absolute_import
# Muck up the names in an effort to confuse...
diff --git a/tests/functional/s/slots_checks.py b/tests/functional/s/slots_checks.py
index 9cdcf97d1..c8cfd04e0 100644
--- a/tests/functional/s/slots_checks.py
+++ b/tests/functional/s/slots_checks.py
@@ -25,7 +25,7 @@ class ThirdGood(object):
__slots__ = ['a']
class FourthGood(object):
- __slots__ = ('a%s' % i for i in range(10))
+ __slots__ = (f'a{i}' for i in range(10))
class FifthGood(object):
__slots__ = deque(["a", "b", "c"])
diff --git a/tests/functional/s/string/string_formatting.py b/tests/functional/s/string/string_formatting.py
index 23067601a..6d2b18665 100644
--- a/tests/functional/s/string/string_formatting.py
+++ b/tests/functional/s/string/string_formatting.py
@@ -1,7 +1,7 @@
"""Test for Python 3 string formatting error"""
# pylint: disable=too-few-public-methods, import-error, unused-argument, line-too-long, no-absolute-import,
-# pylint: disable=useless-object-inheritance
+# pylint: disable=useless-object-inheritance, consider-using-f-string
import os
import sys
import logging
diff --git a/tests/functional/s/string/string_formatting_error.py b/tests/functional/s/string/string_formatting_error.py
index 1f9347a15..d48b47d3f 100644
--- a/tests/functional/s/string/string_formatting_error.py
+++ b/tests/functional/s/string/string_formatting_error.py
@@ -1,5 +1,5 @@
"""test string format error"""
-# pylint: disable=print-statement,unsupported-binary-operation,line-too-long
+# pylint: disable=print-statement,unsupported-binary-operation,line-too-long, consider-using-f-string
from __future__ import print_function
PARG_1 = PARG_2 = PARG_3 = 1
diff --git a/tests/functional/s/string/string_formatting_failed_inference.py b/tests/functional/s/string/string_formatting_failed_inference.py
index c5add58c8..e47ca5baa 100644
--- a/tests/functional/s/string/string_formatting_failed_inference.py
+++ b/tests/functional/s/string/string_formatting_failed_inference.py
@@ -1,4 +1,4 @@
""" Testing string format with a failed inference. This should not crash. """
-# pylint: disable=using-constant-test
+# pylint: disable=using-constant-test, consider-using-f-string
import collections
"{dict[0]}".format(dict=collections.defaultdict(int))
diff --git a/tests/functional/s/string/string_formatting_failed_inference_py35.py b/tests/functional/s/string/string_formatting_failed_inference_py35.py
index a511ff80e..f4d3ef34e 100644
--- a/tests/functional/s/string/string_formatting_failed_inference_py35.py
+++ b/tests/functional/s/string/string_formatting_failed_inference_py35.py
@@ -1,5 +1,5 @@
""" Testing string format with a failed inference. This should not crash. """
-# pylint: disable=using-constant-test
+# pylint: disable=using-constant-test, consider-using-f-string
import collections
"{dict[0]}".format(dict=collections.defaultdict(int))
diff --git a/tests/functional/s/string/string_formatting_py3.py b/tests/functional/s/string/string_formatting_py3.py
index 3cdd60eb2..6ab4d8c91 100644
--- a/tests/functional/s/string/string_formatting_py3.py
+++ b/tests/functional/s/string/string_formatting_py3.py
@@ -1,4 +1,4 @@
-# pylint: disable=missing-docstring,import-error
+# pylint: disable=missing-docstring,import-error, consider-using-f-string
def issue_957_good():
diff --git a/tests/functional/t/too/too_many_return_statements.py b/tests/functional/t/too/too_many_return_statements.py
index 66a85da40..b9f35a2ad 100644
--- a/tests/functional/t/too/too_many_return_statements.py
+++ b/tests/functional/t/too/too_many_return_statements.py
@@ -27,7 +27,7 @@ def stupid_function(arg): # [too-many-return-statements]
def many_yield(text):
"""Not a problem"""
if text:
- yield " line 1: %s\n" % text
+ yield f" line 1: {text}\n"
yield " line 2\n"
yield " line 3\n"
yield " line 4\n"
diff --git a/tests/functional/u/undefined/undefined_loop_variable.py b/tests/functional/u/undefined/undefined_loop_variable.py
index 3df17f7d1..956773e31 100644
--- a/tests/functional/u/undefined/undefined_loop_variable.py
+++ b/tests/functional/u/undefined/undefined_loop_variable.py
@@ -1,4 +1,4 @@
-# pylint: disable=missing-docstring,redefined-builtin
+# pylint: disable=missing-docstring,redefined-builtin, consider-using-f-string
def do_stuff(some_random_list):
for var in some_random_list:
diff --git a/tests/functional/u/unused/unused_argument.py b/tests/functional/u/unused/unused_argument.py
index 707eb25af..c5f534259 100644
--- a/tests/functional/u/unused/unused_argument.py
+++ b/tests/functional/u/unused/unused_argument.py
@@ -72,7 +72,7 @@ class AAAA(object):
def using_inner_function(self, etype, size=1):
"""return a fake result set for a particular entity type"""
- rset = AAAA([('A',)]*size, '%s X' % etype,
+ rset = AAAA([('A',)]*size, f'{etype} X',
description=[(etype,)]*size)
def inner(row, col=0, etype=etype, req=self, rset=rset):
"""inner using all its argument"""
diff --git a/tests/functional/u/use/used_before_assignement.py b/tests/functional/u/use/used_before_assignement.py
index c975b1dc2..285f3d180 100644
--- a/tests/functional/u/use/used_before_assignement.py
+++ b/tests/functional/u/use/used_before_assignement.py
@@ -1,5 +1,5 @@
"""pylint doesn't see the NameError in this module"""
-
+#pylint: disable=consider-using-f-string
__revision__ = None
MSG = "hello %s" % MSG # [used-before-assignment]
diff --git a/tests/functional/u/use/used_before_assignment_issue853.py b/tests/functional/u/use/used_before_assignment_issue853.py
index f8b412252..7da9fdd50 100644
--- a/tests/functional/u/use/used_before_assignment_issue853.py
+++ b/tests/functional/u/use/used_before_assignment_issue853.py
@@ -1,4 +1,4 @@
-# pylint: disable=missing-docstring,bare-except,pointless-statement,superfluous-parens
+# pylint: disable=missing-docstring,bare-except,pointless-statement,superfluous-parens, consider-using-f-string
def strangeproblem():
try:
for _ in range(0, 4):
diff --git a/tests/input/func_w0401_package/thing2.py b/tests/input/func_w0401_package/thing2.py
index 66d331677..80bec1dd8 100644
--- a/tests/input/func_w0401_package/thing2.py
+++ b/tests/input/func_w0401_package/thing2.py
@@ -4,4 +4,4 @@ from .all_the_things import THING1
__revision__ = None
THING2 = "I am thing2"
-THING1_PLUS_THING2 = "%s, plus %s" % (THING1, THING2)
+THING1_PLUS_THING2 = f"{THING1}, plus {THING2}"
diff --git a/tests/profile/test_profile_against_externals.py b/tests/profile/test_profile_against_externals.py
index dd3e72708..20fa55d4a 100644
--- a/tests/profile/test_profile_against_externals.py
+++ b/tests/profile/test_profile_against_externals.py
@@ -18,7 +18,7 @@ from pylint.testutils import GenericTestReporter as Reporter
def _get_py_files(scanpath):
- assert os.path.exists(scanpath), "Dir not found %s" % scanpath
+ assert os.path.exists(scanpath), f"Dir not found {scanpath}"
filepaths = []
for dirpath, dirnames, filenames in os.walk(scanpath):
@@ -46,12 +46,11 @@ def test_run(tmp_path, name, git_repo):
checkoutdir.mkdir()
os.system(f"git clone --depth=1 {git_repo} {checkoutdir}")
filepaths = _get_py_files(scanpath=str(checkoutdir))
- print("Have %d files" % len(filepaths))
+ print(f"Have {len(filepaths)} files")
runner = Run(filepaths, reporter=Reporter(), do_exit=False)
print(
- "Had %d files with %d messages"
- % (len(filepaths), len(runner.linter.reporter.messages))
+ f"Had {len(filepaths)} files with {len(runner.linter.reporter.messages)} messages"
)
pprint.pprint(runner.linter.reporter.messages)
diff --git a/tests/pyreverse/test_utils.py b/tests/pyreverse/test_utils.py
index 67db132b9..e11002035 100644
--- a/tests/pyreverse/test_utils.py
+++ b/tests/pyreverse/test_utils.py
@@ -66,7 +66,7 @@ def test_get_annotation_annassign(assign, label):
)
def test_get_annotation_assignattr(init_method, label):
"""AssignAttr"""
- assign = rf"""
+ assign = fr"""
class A:
{init_method}
"""
diff --git a/tests/test_check_parallel.py b/tests/test_check_parallel.py
index 79e373ee0..6e94ed74c 100644
--- a/tests/test_check_parallel.py
+++ b/tests/test_check_parallel.py
@@ -30,9 +30,9 @@ def _gen_file_data(idx=0):
os.path.join(os.path.dirname(__file__), "input", "similar1")
)
file_data = (
- "--test-file_data-name-%d--" % idx,
+ f"--test-file_data-name-{idx}--",
filepath,
- "--test-file_data-modname-%d--" % idx,
+ f"--test-file_data-modname-{idx}--",
)
return file_data
@@ -419,6 +419,7 @@ class TestCheckParallel:
# with the number of files.
expected_stats = {
"by_module": {
+ # pylint: disable-next=consider-using-f-string
"--test-file_data-name-%d--"
% idx: {
"convention": 0,
diff --git a/tests/test_epylint.py b/tests/test_epylint.py
index 525627166..796b1ca87 100644
--- a/tests/test_epylint.py
+++ b/tests/test_epylint.py
@@ -20,6 +20,7 @@ def example_path(tmp_path):
def test_epylint_good_command(example_path):
out, err = lint.py_run(
+ # pylint: disable-next=consider-using-f-string
"%s -E --disable=E1111 --msg-template '{category} {module} {obj} {line} {column} {msg}'"
% example_path,
return_std=True,
@@ -37,6 +38,7 @@ def test_epylint_good_command(example_path):
def test_epylint_strange_command(example_path):
out, err = lint.py_run(
+ # pylint: disable-next=consider-using-f-string
"%s -E --disable=E1111 --msg-template={category} {module} {obj} {line} {column} {msg}"
% example_path,
return_std=True,
diff --git a/tests/test_func.py b/tests/test_func.py
index 0e093458a..b57dc7d18 100644
--- a/tests/test_func.py
+++ b/tests/test_func.py
@@ -38,7 +38,7 @@ INFO_TEST_RGX = re.compile(r"^func_i\d\d\d\d$")
def exception_str(self, ex): # pylint: disable=unused-argument
"""function used to replace default __str__ method of exception instances"""
- return "in {}\n:: {}".format(ex.file, ", ".join(ex.args))
+ return f"in {ex.file}\n:: {', '.join(ex.args)}"
class LintTestUsingModule:
@@ -55,8 +55,7 @@ class LintTestUsingModule:
# pylint: disable=not-an-iterable; can't handle boolean checks for now
if self.depends:
tocheck += [
- self.package + ".%s" % name.replace(".py", "")
- for name, _ in self.depends
+ self.package + f".{name.replace('.py', '')}" for name, _ in self.depends
]
self._test(tocheck)
diff --git a/tests/test_self.py b/tests/test_self.py
index a8877d6d3..519899021 100644
--- a/tests/test_self.py
+++ b/tests/test_self.py
@@ -234,7 +234,7 @@ class TestRunTC:
output = out.getvalue()
# Get rid of the pesky messages that pylint emits if the
# configuration file is not found.
- pattern = rf"\[{MAIN_CHECKER_NAME.upper()}"
+ pattern = fr"\[{MAIN_CHECKER_NAME.upper()}"
master = re.search(pattern, output)
assert master is not None, f"{pattern} not found in {output}"
out = StringIO(output[master.start() :])
@@ -298,7 +298,7 @@ class TestRunTC:
[
"--py3k",
"--disable=no-absolute-import",
- "-j %d" % jobs,
+ f"-j {int(jobs)}",
join(HERE, "input", "no_absolute_import.py"),
],
code=expected_ret_code,
@@ -484,7 +484,7 @@ class TestRunTC:
config_path = join(HERE, "regrtest_data", ".pylintrc")
expected = "Your code has been rated at 10.00/10"
self._test_output(
- [path, "--rcfile=%s" % config_path, "-rn"], expected_output=expected
+ [path, f"--rcfile={config_path}", "-rn"], expected_output=expected
)
def test_pylintrc_plugin_duplicate_options(self):
@@ -500,7 +500,7 @@ class TestRunTC:
)
self._test_output(
[
- "--rcfile=%s" % config_path,
+ f"--rcfile={config_path}",
"--help-msg=dummy-message-01,dummy-message-02",
],
expected_output=expected,
@@ -510,7 +510,7 @@ class TestRunTC:
"# Dummy option 2\ndummy_option_2=dummy value 2"
)
self._test_output(
- ["--rcfile=%s" % config_path, "--generate-rcfile"], expected_output=expected
+ [f"--rcfile={config_path}", "--generate-rcfile"], expected_output=expected
)
sys.path.remove(dummy_plugin_path)
@@ -526,7 +526,7 @@ class TestRunTC:
"""
)
self._test_output(
- [path, "--rcfile=%s" % config_path, "-rn"], expected_output=expected
+ [path, f"--rcfile={config_path}", "-rn"], expected_output=expected
)
def test_no_crash_with_formatting_regex_defaults(self):