summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2021-07-28 20:18:44 +0200
committerGitHub <noreply@github.com>2021-07-28 20:18:44 +0200
commita8b7dd7bffe343f00bcd6409534edcab530a4fc4 (patch)
tree5f1d6b315ca95634a9328eed70603cfcfbac4e1c
parentc04f92ef68e5ea779a60bfddb91dc677c5470fd0 (diff)
downloadpylint-git-a8b7dd7bffe343f00bcd6409534edcab530a4fc4.tar.gz
Add unspecified-encoding checker #3826 (#4753)
* Add unspecified-encoding checker #3826 This adds an unspecified-encoding checker that adds a warning whenever open() is called without an explicit encoding argument. This closes #3826 * Update tests to conform to unspecified-encoding With addition of the unspecified-encoding checker calls of open() need an encoding argument. Where necessary this argument has been added, or the message has been disabled. This also includes small linting changes to a small number of tests. Their test-data has been updated to reflect new line numbers. * Update scripts to conform to unspecified-encoding With addition of the unspecified-encoding checker calls of open() need an encoding argument. Where necessary this argument has been added. * Update pylint to conform to unspecified-encoding With addition of the unspecified-encoding checker calls of open() need an encoding argument. Where necessary this argument has been added. Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--ChangeLog4
-rw-r--r--doc/whatsnew/2.10.rst3
-rw-r--r--pylint/checkers/similar.py2
-rw-r--r--pylint/checkers/spelling.py2
-rw-r--r--pylint/checkers/stdlib.py54
-rw-r--r--pylint/config/find_default_config_files.py2
-rw-r--r--pylint/config/option_manager_mixin.py2
-rw-r--r--pylint/lint/pylinter.py9
-rw-r--r--pylint/lint/run.py2
-rw-r--r--pylint/pyreverse/utils.py2
-rw-r--r--pylint/pyreverse/writer.py4
-rw-r--r--pylint/testutils/lint_module_test.py4
-rw-r--r--script/bump_changelog.py4
-rw-r--r--script/fix_documentation.py4
-rw-r--r--tests/checkers/unittest_similar.py2
-rw-r--r--tests/functional/b/bad_open_mode_py3.py20
-rw-r--r--tests/functional/c/consider/consider_using_with.py2
-rw-r--r--tests/functional/c/consider/consider_using_with_open.py30
-rw-r--r--tests/functional/c/consider/consider_using_with_open.txt4
-rw-r--r--tests/functional/d/defined_and_used_on_same_line.py2
-rw-r--r--tests/functional/d/disabled_msgid_in_pylintrc.py2
-rw-r--r--tests/functional/n/non/non_iterator_returned.py2
-rw-r--r--tests/functional/r/redefined_argument_from_local.py26
-rw-r--r--tests/functional/r/redefined_argument_from_local.txt14
-rw-r--r--tests/functional/r/regression/regression_4612_crash_pytest_fixture.py2
-rw-r--r--tests/functional/u/unspecified_encoding_py38.py55
-rw-r--r--tests/functional/u/unspecified_encoding_py38.rc2
-rw-r--r--tests/functional/u/unspecified_encoding_py38.txt15
-rw-r--r--tests/functional/w/with_used_before_assign.py7
-rw-r--r--tests/functional/w/with_used_before_assign.txt4
-rw-r--r--tests/lint/unittest_lint.py2
-rw-r--r--tests/test_epylint.py2
-rw-r--r--tests/test_func.py4
-rw-r--r--tests/test_functional.py2
-rw-r--r--tests/test_import_graph.py2
-rw-r--r--tests/test_self.py2
-rw-r--r--tests/unittest_reporting.py2
38 files changed, 221 insertions, 84 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index f1d54dbd5..c0865d0c6 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -521,3 +521,5 @@ contributors:
* Yilei Yang: contributor
* Marcin Kurczewski (rr-): contributor
+
+* Daniel van Noord (DanielNoord): contributor
diff --git a/ChangeLog b/ChangeLog
index 778e86393..52ce722f3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,6 +9,10 @@ Release date: TBA
..
Put new features here and also in 'doc/whatsnew/2.10.rst'
+* Added ``unspecified-encoding``: Emitted when open() is called without specifying an encoding
+
+ Closes #3826
+
What's New in Pylint 2.9.6?
===========================
diff --git a/doc/whatsnew/2.10.rst b/doc/whatsnew/2.10.rst
index 3e17dc781..a2c4e1fbf 100644
--- a/doc/whatsnew/2.10.rst
+++ b/doc/whatsnew/2.10.rst
@@ -12,6 +12,9 @@ Summary -- Release highlights
New checkers
============
+* Added ``unspecified-encoding``: Emitted when open() is called without specifying an encoding
+
+ Closes #3826
Other Changes
diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py
index 8ddbf6857..8d3155c9c 100644
--- a/pylint/checkers/similar.py
+++ b/pylint/checkers/similar.py
@@ -546,7 +546,7 @@ def Run(argv=None):
min_lines, ignore_comments, ignore_docstrings, ignore_imports, ignore_signatures
)
for filename in args:
- with open(filename) as stream:
+ with open(filename, encoding="utf-8") as stream:
sim.append_stream(filename, stream)
sim.run()
sys.exit(0)
diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py
index e2b6df189..1b0a7f653 100644
--- a/pylint/checkers/spelling.py
+++ b/pylint/checkers/spelling.py
@@ -318,7 +318,7 @@ class SpellingChecker(BaseTokenChecker):
dict_name, self.config.spelling_private_dict_file
)
self.private_dict_file = open( # pylint: disable=consider-using-with
- self.config.spelling_private_dict_file, "a"
+ self.config.spelling_private_dict_file, "a", encoding="utf-8"
)
else:
self.spelling_dict = enchant.Dict(dict_name)
diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py
index c0e789ad8..1fba67afd 100644
--- a/pylint/checkers/stdlib.py
+++ b/pylint/checkers/stdlib.py
@@ -30,6 +30,7 @@
# Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
# Copyright (c) 2021 Matus Valo <matusvalo@users.noreply.github.com>
# Copyright (c) 2021 victor <16359131+jiajunsu@users.noreply.github.com>
+# Copyright (c) 2021 Daniel van Noord <13665637+DanielNoord@users.noreply.github.com>
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
@@ -44,12 +45,13 @@ import astroid
from pylint.checkers import BaseChecker, DeprecatedMixin, utils
from pylint.interfaces import IAstroidChecker
-OPEN_FILES = {"open", "file"}
+OPEN_FILES_MODE = ("open", "file")
+OPEN_FILES_ENCODING = ("open",)
UNITTEST_CASE = "unittest.case"
THREADING_THREAD = "threading.Thread"
COPY_COPY = "copy.copy"
OS_ENVIRON = "os._Environ"
-ENV_GETTERS = {"os.getenv"}
+ENV_GETTERS = ("os.getenv",)
SUBPROCESS_POPEN = "subprocess.Popen"
SUBPROCESS_RUN = "subprocess.run"
OPEN_MODULE = "_io"
@@ -425,6 +427,13 @@ class StdlibChecker(DeprecatedMixin, BaseChecker):
"deprecated-decorator",
"The decorator is marked as deprecated and will be removed in the future.",
),
+ "W1514": (
+ "Using open without explicitly specifying an encoding",
+ "unspecified-encoding",
+ "It is better to specify an encoding when opening documents. "
+ "Using the system default implicitly can create problems on other operating systems. "
+ "See https://www.python.org/dev/peps/pep-0597/",
+ ),
}
def __init__(self, linter=None):
@@ -485,6 +494,7 @@ class StdlibChecker(DeprecatedMixin, BaseChecker):
"subprocess-popen-preexec-fn",
"subprocess-run-check",
"deprecated-class",
+ "unspecified-encoding",
)
def visit_call(self, node):
"""Visit a Call node."""
@@ -494,8 +504,18 @@ class StdlibChecker(DeprecatedMixin, BaseChecker):
if inferred is astroid.Uninferable:
continue
if inferred.root().name == OPEN_MODULE:
- if getattr(node.func, "name", None) in OPEN_FILES:
+ if (
+ isinstance(node.func, astroid.Name)
+ and node.func.name in OPEN_FILES_MODE
+ ):
self._check_open_mode(node)
+ if (
+ isinstance(node.func, astroid.Name)
+ and node.func.name in OPEN_FILES_ENCODING
+ or isinstance(node.func, astroid.Attribute)
+ and node.func.attrname in OPEN_FILES_ENCODING
+ ):
+ self._check_open_encoded(node)
elif inferred.root().name == UNITTEST_CASE:
self._check_redundant_assert(node, inferred)
elif isinstance(inferred, astroid.ClassDef):
@@ -573,6 +593,34 @@ class StdlibChecker(DeprecatedMixin, BaseChecker):
):
self.add_message("bad-open-mode", node=node, args=mode_arg.value)
+ def _check_open_encoded(self, node: astroid.Call) -> None:
+ """Check that the encoded argument of an open call is valid."""
+ mode_arg = None
+ try:
+ mode_arg = utils.get_argument_from_call(node, position=1, keyword="mode")
+ except utils.NoSuchArgumentError:
+ pass
+
+ if mode_arg:
+ mode_arg = utils.safe_infer(mode_arg)
+ if not mode_arg or "b" not in mode_arg.value:
+ encoding_arg = None
+ try:
+ encoding_arg = utils.get_argument_from_call(
+ node, position=None, keyword="encoding"
+ )
+ except utils.NoSuchArgumentError:
+ self.add_message("unspecified-encoding", node=node)
+
+ if encoding_arg:
+ encoding_arg = utils.safe_infer(encoding_arg)
+
+ if (
+ isinstance(encoding_arg, astroid.Const)
+ and encoding_arg.value is None
+ ):
+ self.add_message("unspecified-encoding", node=node)
+
def _check_env_function(self, node, infer):
env_name_kwarg = "key"
env_value_kwarg = "default"
diff --git a/pylint/config/find_default_config_files.py b/pylint/config/find_default_config_files.py
index d4faa259e..2a8937e52 100644
--- a/pylint/config/find_default_config_files.py
+++ b/pylint/config/find_default_config_files.py
@@ -9,7 +9,7 @@ from toml.decoder import TomlDecodeError
def _toml_has_config(path):
- with open(path) as toml_handle:
+ with open(path, encoding="utf-8") as toml_handle:
try:
content = toml.load(toml_handle)
except TomlDecodeError as error:
diff --git a/pylint/config/option_manager_mixin.py b/pylint/config/option_manager_mixin.py
index 86785f36f..5aa2323f0 100644
--- a/pylint/config/option_manager_mixin.py
+++ b/pylint/config/option_manager_mixin.py
@@ -267,7 +267,7 @@ class OptionsManagerMixIn:
parser = self.cfgfile_parser
if config_file.endswith(".toml"):
- with open(config_file) as fp:
+ with open(config_file, encoding="utf-8") as fp:
content = toml.load(fp)
try:
diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py
index c2d536211..25ae29c02 100644
--- a/pylint/lint/pylinter.py
+++ b/pylint/lint/pylinter.py
@@ -571,7 +571,9 @@ class PyLinter(
(reporter_output,) = reporter_output
# pylint: disable=consider-using-with
- output_file = stack.enter_context(open(reporter_output, "w"))
+ output_file = stack.enter_context(
+ open(reporter_output, "w", encoding="utf-8")
+ )
reporter.set_output(output_file)
output_files.append(output_file)
@@ -617,8 +619,9 @@ class PyLinter(
except KeyError:
meth = self._bw_options_methods[optname]
warnings.warn(
- "%s is deprecated, replace it by %s"
- % (optname, optname.split("-")[0]),
+ "{} is deprecated, replace it by {}".format(
+ optname, optname.split("-")[0]
+ ),
DeprecationWarning,
)
value = utils._check_csv(value)
diff --git a/pylint/lint/run.py b/pylint/lint/run.py
index 08806e6c8..66c9b3622 100644
--- a/pylint/lint/run.py
+++ b/pylint/lint/run.py
@@ -373,7 +373,7 @@ group are mutually exclusive.",
if self._output:
try:
- with open(self._output, "w") as output:
+ with open(self._output, "w", encoding="utf-8") as output:
linter.reporter.set_output(output)
linter.check(args)
score_value = linter.generate_reports()
diff --git a/pylint/pyreverse/utils.py b/pylint/pyreverse/utils.py
index fea9bda79..06f8d3b7e 100644
--- a/pylint/pyreverse/utils.py
+++ b/pylint/pyreverse/utils.py
@@ -35,7 +35,7 @@ def get_default_options():
if home:
rcfile = os.path.join(home, RCFILE)
try:
- with open(rcfile) as file_handle:
+ with open(rcfile, encoding="utf-8") as file_handle:
options = file_handle.read().split()
except OSError:
pass # ignore if no config file found
diff --git a/pylint/pyreverse/writer.py b/pylint/pyreverse/writer.py
index db54c3f0b..f45783c49 100644
--- a/pylint/pyreverse/writer.py
+++ b/pylint/pyreverse/writer.py
@@ -187,7 +187,9 @@ class VCGWriter(DiagramWriter):
def set_printer(self, file_name, basename):
"""initialize VCGWriter for a UML graph"""
- self.graph_file = open(file_name, "w+") # pylint: disable=consider-using-with
+ self.graph_file = open( # pylint: disable=consider-using-with
+ file_name, "w+", encoding="utf-8"
+ )
self.printer = VCGPrinter(self.graph_file)
self.printer.open_graph(
title=basename,
diff --git a/pylint/testutils/lint_module_test.py b/pylint/testutils/lint_module_test.py
index c88463821..8f24daf86 100644
--- a/pylint/testutils/lint_module_test.py
+++ b/pylint/testutils/lint_module_test.py
@@ -141,14 +141,14 @@ class LintModuleTest:
# pylint: disable=consider-using-with
def _open_expected_file(self):
try:
- return open(self._test_file.expected_output)
+ return open(self._test_file.expected_output, encoding="utf-8")
except FileNotFoundError:
return StringIO("")
# pylint: disable=consider-using-with
def _open_source_file(self):
if self._test_file.base == "invalid_encoded_data":
- return open(self._test_file.source)
+ return open(self._test_file.source, encoding="utf-8")
if "latin1" in self._test_file.base:
return open(self._test_file.source, encoding="latin1")
return open(self._test_file.source, encoding="utf8")
diff --git a/script/bump_changelog.py b/script/bump_changelog.py
index 278b627da..41c8c3c8a 100644
--- a/script/bump_changelog.py
+++ b/script/bump_changelog.py
@@ -33,10 +33,10 @@ def main() -> None:
logging.debug(f"Launching bump_changelog with args: {args}")
if "dev" in args.version:
return
- with open(DEFAULT_CHANGELOG_PATH) as f:
+ with open(DEFAULT_CHANGELOG_PATH, encoding="utf-8") as f:
content = f.read()
content = transform_content(content, args.version)
- with open(DEFAULT_CHANGELOG_PATH, "w") as f:
+ with open(DEFAULT_CHANGELOG_PATH, "w", encoding="utf-8") as f:
f.write(content)
diff --git a/script/fix_documentation.py b/script/fix_documentation.py
index 242225381..b03f70ead 100644
--- a/script/fix_documentation.py
+++ b/script/fix_documentation.py
@@ -82,7 +82,7 @@ def main(argv: Union[List[str], None] = None) -> int:
return_value: int = 0
for file_name in args.filenames:
- with open(file_name) as fp:
+ with open(file_name, encoding="utf-8") as fp:
orignal_content = fp.read()
content = orignal_content
# Modify files
@@ -91,7 +91,7 @@ def main(argv: Union[List[str], None] = None) -> int:
content = changelog_insert_empty_lines(content, args.subtitle_prefix)
# If modified, write changes and eventually return 1
if orignal_content != content:
- with open(file_name, "w") as fp:
+ with open(file_name, "w", encoding="utf-8") as fp:
fp.write(content)
return_value |= 1
return return_value
diff --git a/tests/checkers/unittest_similar.py b/tests/checkers/unittest_similar.py
index 8edc87bca..ca64bdba3 100644
--- a/tests/checkers/unittest_similar.py
+++ b/tests/checkers/unittest_similar.py
@@ -449,7 +449,7 @@ def test_get_map_data():
# Manually perform a 'map' type function
for source_fname in source_streams:
sim = similar.SimilarChecker(linter)
- with open(source_fname) as stream:
+ with open(source_fname, encoding="utf-8") as stream:
sim.append_stream(source_fname, stream)
# The map bit, can you tell? ;)
data.extend(sim.get_map_data())
diff --git a/tests/functional/b/bad_open_mode_py3.py b/tests/functional/b/bad_open_mode_py3.py
index 27aa2e6a2..b68238013 100644
--- a/tests/functional/b/bad_open_mode_py3.py
+++ b/tests/functional/b/bad_open_mode_py3.py
@@ -3,22 +3,22 @@
NAME = "foo.bar"
open(NAME, "wb")
-open(NAME, "w")
+open(NAME, "w", encoding="utf-8")
open(NAME, "rb")
-open(NAME, "x")
+open(NAME, "x", encoding="utf-8")
open(NAME, "br")
-open(NAME, "+r")
+open(NAME, "+r", encoding="utf-8")
open(NAME, "xb")
-open(NAME, "rwx") # [bad-open-mode]
-open(NAME, "rr") # [bad-open-mode]
-open(NAME, "+") # [bad-open-mode]
-open(NAME, "xw") # [bad-open-mode]
+open(NAME, "rwx", encoding="utf-8") # [bad-open-mode]
+open(NAME, "rr", encoding="utf-8") # [bad-open-mode]
+open(NAME, "+", encoding="utf-8") # [bad-open-mode]
+open(NAME, "xw", encoding="utf-8") # [bad-open-mode]
open(NAME, "ab+")
open(NAME, "a+b")
open(NAME, "+ab")
open(NAME, "+rUb")
open(NAME, "x+b")
-open(NAME, "Ua") # [bad-open-mode]
-open(NAME, "Ur++") # [bad-open-mode]
-open(NAME, "Ut")
+open(NAME, "Ua", encoding="utf-8") # [bad-open-mode]
+open(NAME, "Ur++", encoding="utf-8") # [bad-open-mode]
+open(NAME, "Ut", encoding="utf-8")
open(NAME, "Ubr")
diff --git a/tests/functional/c/consider/consider_using_with.py b/tests/functional/c/consider/consider_using_with.py
index fc2804455..d05866aaa 100644
--- a/tests/functional/c/consider/consider_using_with.py
+++ b/tests/functional/c/consider/consider_using_with.py
@@ -151,7 +151,7 @@ def test_suppress_in_exit_stack():
"""Regression test for issue #4654 (false positive)"""
with contextlib.ExitStack() as stack:
_ = stack.enter_context(
- open("/sys/firmware/devicetree/base/hwid,location", "r")
+ open("/sys/firmware/devicetree/base/hwid,location", "r", encoding="utf-8")
) # must not trigger
diff --git a/tests/functional/c/consider/consider_using_with_open.py b/tests/functional/c/consider/consider_using_with_open.py
index e93b619b7..30be92def 100644
--- a/tests/functional/c/consider/consider_using_with_open.py
+++ b/tests/functional/c/consider/consider_using_with_open.py
@@ -1,5 +1,5 @@
# pylint: disable=missing-function-docstring, missing-module-docstring, invalid-name, import-outside-toplevel
-# pylint: disable=missing-class-docstring, too-few-public-methods, unused-variable, multiple-statements
+# pylint: disable=missing-class-docstring, too-few-public-methods, unused-variable, multiple-statements, line-too-long
"""
The functional test for the standard ``open()`` function has to be moved in a separate file,
because PyPy has to be excluded for the tests as the ``open()`` function is uninferable in PyPy.
@@ -8,26 +8,26 @@ PyPy from ALL functional tests.
"""
from contextlib import contextmanager
-
-myfile = open("test.txt") # [consider-using-with]
+myfile = open("test.txt", encoding="utf-8") # [consider-using-with]
def test_open():
- fh = open("test.txt") # [consider-using-with]
+ fh = open("test.txt", encoding="utf-8") # [consider-using-with]
fh.close()
- with open("test.txt") as fh: # must not trigger
+ with open("test.txt", encoding="utf-8") as fh: # must not trigger
fh.read()
def test_open_in_enter():
"""Message must not trigger if the resource is allocated in a context manager."""
+
class MyContextManager:
def __init__(self):
self.file_handle = None
def __enter__(self):
- self.file_handle = open("foo.txt", "w") # must not trigger
+ self.file_handle = open("foo.txt", "w", encoding="utf-8") # must not trigger
def __exit__(self, exc_type, exc_value, traceback):
self.file_handle.close()
@@ -36,31 +36,31 @@ def test_open_in_enter():
@contextmanager
def test_open_in_with_contextlib():
"""Message must not trigger if the resource is allocated in a context manager."""
- file_handle = open("foo.txt", "w") # must not trigger
+ file_handle = open("foo.txt", "w", encoding="utf-8") # must not trigger
yield file_handle
file_handle.close()
def test_open_outside_assignment():
- open("foo").read() # [consider-using-with]
- content = open("foo").read() # [consider-using-with]
+ open("foo", encoding="utf-8").read() # [consider-using-with]
+ content = open("foo", encoding="utf-8").read() # [consider-using-with]
def test_open_inside_with_block():
- with open("foo") as fh:
- open("bar") # [consider-using-with]
+ with open("foo", encoding="utf-8") as fh:
+ open("bar", encoding="utf-8") # [consider-using-with]
def test_ternary_if_in_with_block(file1, file2, which):
"""Regression test for issue #4676 (false positive)"""
- with (open(file1) if which else open(file2)) as input_file: # must not trigger
+ with (open(file1, encoding="utf-8") if which else open(file2, encoding="utf-8")) as input_file: # must not trigger
return input_file.read()
def test_single_line_with(file1):
- with open(file1): return file1.read() # must not trigger
+ with open(file1, encoding="utf-8"): return file1.read() # must not trigger
def test_multiline_with_items(file1, file2, which):
- with (open(file1) if which
- else open(file2)) as input_file: return input_file.read()
+ with (open(file1, encoding="utf-8") if which
+ else open(file2, encoding="utf-8")) as input_file: return input_file.read()
diff --git a/tests/functional/c/consider/consider_using_with_open.txt b/tests/functional/c/consider/consider_using_with_open.txt
index 63cd08b7c..ea3503123 100644
--- a/tests/functional/c/consider/consider_using_with_open.txt
+++ b/tests/functional/c/consider/consider_using_with_open.txt
@@ -1,5 +1,5 @@
-consider-using-with:12:9::Consider using 'with' for resource-allocating operations
-consider-using-with:16:9:test_open:Consider using 'with' for resource-allocating operations
+consider-using-with:11:9::Consider using 'with' for resource-allocating operations
+consider-using-with:15:9:test_open:Consider using 'with' for resource-allocating operations
consider-using-with:45:4:test_open_outside_assignment:Consider using 'with' for resource-allocating operations
consider-using-with:46:14:test_open_outside_assignment:Consider using 'with' for resource-allocating operations
consider-using-with:51:8:test_open_inside_with_block:Consider using 'with' for resource-allocating operations
diff --git a/tests/functional/d/defined_and_used_on_same_line.py b/tests/functional/d/defined_and_used_on_same_line.py
index a9b9eb7d5..d5bdb4db0 100644
--- a/tests/functional/d/defined_and_used_on_same_line.py
+++ b/tests/functional/d/defined_and_used_on_same_line.py
@@ -1,5 +1,5 @@
"""Check for definitions and usage happening on the same line."""
-#pylint: disable=missing-docstring,multiple-statements,no-absolute-import,parameter-unpacking,wrong-import-position,unnecessary-comprehension
+#pylint: disable=missing-docstring,multiple-statements,no-absolute-import,parameter-unpacking,wrong-import-position,unnecessary-comprehension,unspecified-encoding
from __future__ import print_function
print([index
for index in range(10)])
diff --git a/tests/functional/d/disabled_msgid_in_pylintrc.py b/tests/functional/d/disabled_msgid_in_pylintrc.py
index 09ddf1147..91cb155e1 100644
--- a/tests/functional/d/disabled_msgid_in_pylintrc.py
+++ b/tests/functional/d/disabled_msgid_in_pylintrc.py
@@ -1,6 +1,6 @@
"""https://github.com/PyCQA/pylint/issues/4265"""
try:
- f = open('test')
+ f = open('test', encoding="utf-8")
except Exception:
pass
diff --git a/tests/functional/n/non/non_iterator_returned.py b/tests/functional/n/non/non_iterator_returned.py
index 94c3601f5..4d0dd2a39 100644
--- a/tests/functional/n/non/non_iterator_returned.py
+++ b/tests/functional/n/non/non_iterator_returned.py
@@ -67,7 +67,7 @@ class FileBasedIterator(object):
def __iter__(self):
if self.file is not None:
self.file.close()
- self.file = open(self.path)
+ self.file = open(self.path, encoding="utf-8")
# self file has two infered values: None and <instance of 'file'>
# we don't want to emit error in this case
return self.file
diff --git a/tests/functional/r/redefined_argument_from_local.py b/tests/functional/r/redefined_argument_from_local.py
index 820252781..7ccff1d18 100644
--- a/tests/functional/r/redefined_argument_from_local.py
+++ b/tests/functional/r/redefined_argument_from_local.py
@@ -1,31 +1,33 @@
# pylint: disable=missing-docstring, unused-variable, unused-argument
# pylint: disable=redefined-outer-name, invalid-name
+
def test_redefined_in_with(name):
- with open('something') as name: # [redefined-argument-from-local]
+ with open("something", encoding="utf-8") as name: # [redefined-argument-from-local]
pass
- with open('something') as (second, name): # [redefined-argument-from-local]
+ with open("something", encoding="utf-8") as (second, name): # [redefined-argument-from-local]
pass
- with open('something') as (second, (name, third)): # [redefined-argument-from-local]
+ with open("something", encoding="utf-8") as (
+ second,
+ (name, third), # [redefined-argument-from-local]
+ ):
pass
other = None
- with open('something') as other:
+ with open("something", encoding="utf-8") as other:
pass
-
def test_not_redefined_in_with(name):
- with open('something') as test_redefined_in_with:
+ with open("something", encoding="utf-8") as test_redefined_in_with:
pass
-
def test_redefined_in_for(name):
- for name in []: # [redefined-argument-from-local]
+ for name in []: # [redefined-argument-from-local]
pass
- for (name, is_) in []: # [redefined-argument-from-local]
+ for (name, is_) in []: # [redefined-argument-from-local]
pass
- for (is_, (name, _)) in []: # [redefined-argument-from-local]
+ for (is_, (name, _)) in []: # [redefined-argument-from-local]
pass
for _ in []:
pass
@@ -45,7 +47,7 @@ def test_not_redefined_in_for(name):
def test_redefined_in_except_handler(name):
try:
1 / 0
- except ZeroDivisionError as name: # [redefined-argument-from-local]
+ except ZeroDivisionError as name: # [redefined-argument-from-local]
pass
@@ -58,7 +60,7 @@ def test_not_redefined_in_except_handler(name):
def test_not_redefined(name):
if not name:
- name = ''
+ name = ""
def apply_filter(objects, filt=lambda obj: True):
diff --git a/tests/functional/r/redefined_argument_from_local.txt b/tests/functional/r/redefined_argument_from_local.txt
index 28ec50da1..b811971c6 100644
--- a/tests/functional/r/redefined_argument_from_local.txt
+++ b/tests/functional/r/redefined_argument_from_local.txt
@@ -1,7 +1,7 @@
-redefined-argument-from-local:5:30:test_redefined_in_with:Redefining argument with the local name 'name'
-redefined-argument-from-local:7:39:test_redefined_in_with:Redefining argument with the local name 'name'
-redefined-argument-from-local:9:40:test_redefined_in_with:Redefining argument with the local name 'name'
-redefined-argument-from-local:24:8:test_redefined_in_for:Redefining argument with the local name 'name'
-redefined-argument-from-local:26:9:test_redefined_in_for:Redefining argument with the local name 'name'
-redefined-argument-from-local:28:15:test_redefined_in_for:Redefining argument with the local name 'name'
-redefined-argument-from-local:48:4:test_redefined_in_except_handler:Redefining argument with the local name 'name'
+redefined-argument-from-local:6:48:test_redefined_in_with:Redefining argument with the local name 'name'
+redefined-argument-from-local:8:57:test_redefined_in_with:Redefining argument with the local name 'name'
+redefined-argument-from-local:12:9:test_redefined_in_with:Redefining argument with the local name 'name'
+redefined-argument-from-local:26:8:test_redefined_in_for:Redefining argument with the local name 'name'
+redefined-argument-from-local:28:9:test_redefined_in_for:Redefining argument with the local name 'name'
+redefined-argument-from-local:30:15:test_redefined_in_for:Redefining argument with the local name 'name'
+redefined-argument-from-local:50:4:test_redefined_in_except_handler:Redefining argument with the local name 'name'
diff --git a/tests/functional/r/regression/regression_4612_crash_pytest_fixture.py b/tests/functional/r/regression/regression_4612_crash_pytest_fixture.py
index f0aa0f5d1..f7680f1fb 100644
--- a/tests/functional/r/regression/regression_4612_crash_pytest_fixture.py
+++ b/tests/functional/r/regression/regression_4612_crash_pytest_fixture.py
@@ -5,5 +5,5 @@ import pytest
@pytest.fixture
def qm_file():
- qm_file = open("src/test/resources/example_qm_file.csv").read()
+ qm_file = open("src/test/resources/example_qm_file.csv", encoding="utf-8").read()
return qm_file
diff --git a/tests/functional/u/unspecified_encoding_py38.py b/tests/functional/u/unspecified_encoding_py38.py
new file mode 100644
index 000000000..20d2d7be1
--- /dev/null
+++ b/tests/functional/u/unspecified_encoding_py38.py
@@ -0,0 +1,55 @@
+"""Warnings for using open() without specifying an encoding"""
+# pylint: disable=consider-using-with
+import io
+import locale
+
+FILENAME = "foo.bar"
+open(FILENAME, "w", encoding="utf-8")
+open(FILENAME, "wb")
+open(FILENAME, "w+b")
+open(FILENAME) # [unspecified-encoding]
+open(FILENAME, "wt") # [unspecified-encoding]
+open(FILENAME, "w+") # [unspecified-encoding]
+open(FILENAME, "w", encoding=None) # [unspecified-encoding]
+open(FILENAME, "r") # [unspecified-encoding]
+
+with open(FILENAME, encoding="utf8", errors="surrogateescape") as f:
+ pass
+
+LOCALE_ENCODING = locale.getlocale()[1]
+with open(FILENAME, encoding=LOCALE_ENCODING) as f:
+ pass
+
+with open(FILENAME) as f: # [unspecified-encoding]
+ pass
+
+with open(FILENAME, encoding=None) as f: # [unspecified-encoding]
+ pass
+
+LOCALE_ENCODING = None
+with open(FILENAME, encoding=LOCALE_ENCODING) as f: # [unspecified-encoding]
+ pass
+
+io.open(FILENAME, "w+b")
+io.open_code(FILENAME)
+io.open(FILENAME) # [unspecified-encoding]
+io.open(FILENAME, "wt") # [unspecified-encoding]
+io.open(FILENAME, "w+") # [unspecified-encoding]
+io.open(FILENAME, "w", encoding=None) # [unspecified-encoding]
+
+with io.open(FILENAME, encoding="utf8", errors="surrogateescape") as f:
+ pass
+
+LOCALE_ENCODING = locale.getlocale()[1]
+with io.open(FILENAME, encoding=LOCALE_ENCODING) as f:
+ pass
+
+with io.open(FILENAME) as f: # [unspecified-encoding]
+ pass
+
+with io.open(FILENAME, encoding=None) as f: # [unspecified-encoding]
+ pass
+
+LOCALE_ENCODING = None
+with io.open(FILENAME, encoding=LOCALE_ENCODING) as f: # [unspecified-encoding]
+ pass
diff --git a/tests/functional/u/unspecified_encoding_py38.rc b/tests/functional/u/unspecified_encoding_py38.rc
new file mode 100644
index 000000000..85fc502b3
--- /dev/null
+++ b/tests/functional/u/unspecified_encoding_py38.rc
@@ -0,0 +1,2 @@
+[testoptions]
+min_pyver=3.8
diff --git a/tests/functional/u/unspecified_encoding_py38.txt b/tests/functional/u/unspecified_encoding_py38.txt
new file mode 100644
index 000000000..cde7f2238
--- /dev/null
+++ b/tests/functional/u/unspecified_encoding_py38.txt
@@ -0,0 +1,15 @@
+unspecified-encoding:10:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:11:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:12:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:13:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:14:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:23:5::"Using open without explicitly specifying an encoding"
+unspecified-encoding:26:5::"Using open without explicitly specifying an encoding"
+unspecified-encoding:30:5::"Using open without explicitly specifying an encoding"
+unspecified-encoding:35:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:36:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:37:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:38:0::"Using open without explicitly specifying an encoding"
+unspecified-encoding:47:5::"Using open without explicitly specifying an encoding"
+unspecified-encoding:50:5::"Using open without explicitly specifying an encoding"
+unspecified-encoding:54:5::"Using open without explicitly specifying an encoding"
diff --git a/tests/functional/w/with_used_before_assign.py b/tests/functional/w/with_used_before_assign.py
index 64a475af1..ebe7d3093 100644
--- a/tests/functional/w/with_used_before_assign.py
+++ b/tests/functional/w/with_used_before_assign.py
@@ -1,11 +1,12 @@
-'''
+"""
Regression test for
https://bitbucket.org/logilab/pylint/issue/128/attributeerror-when-parsing
-'''
+"""
from __future__ import with_statement
+
def do_nothing():
""" empty """
- with open("") as ctx.obj: # [undefined-variable]
+ with open("", encoding="utf-8") as ctx.obj: # [undefined-variable]
context.do() # [used-before-assignment]
context = None
diff --git a/tests/functional/w/with_used_before_assign.txt b/tests/functional/w/with_used_before_assign.txt
index 4a98cec60..8e1fe1be8 100644
--- a/tests/functional/w/with_used_before_assign.txt
+++ b/tests/functional/w/with_used_before_assign.txt
@@ -1,2 +1,2 @@
-undefined-variable:9:21:do_nothing:Undefined variable 'ctx'
-used-before-assignment:10:8:do_nothing:Using variable 'context' before assignment
+undefined-variable:10:39:do_nothing:Undefined variable 'ctx'
+used-before-assignment:11:8:do_nothing:Using variable 'context' before assignment
diff --git a/tests/lint/unittest_lint.py b/tests/lint/unittest_lint.py
index 984e9759d..004c29281 100644
--- a/tests/lint/unittest_lint.py
+++ b/tests/lint/unittest_lint.py
@@ -156,7 +156,7 @@ def create_files(paths, chroot="."):
if not isdir(dirpath):
os.makedirs(dirpath)
for filepath in files:
- with open(filepath, "w"):
+ with open(filepath, "w", encoding="utf-8"):
pass
diff --git a/tests/test_epylint.py b/tests/test_epylint.py
index a53c2b045..525627166 100644
--- a/tests/test_epylint.py
+++ b/tests/test_epylint.py
@@ -13,7 +13,7 @@ def example_path(tmp_path):
self.hassan()
"""
path = tmp_path / "my_app.py"
- with open(path, "w") as f:
+ with open(path, "w", encoding="utf-8") as f:
f.write(content)
return path
diff --git a/tests/test_func.py b/tests/test_func.py
index e15d3b0f8..8742b42f9 100644
--- a/tests/test_func.py
+++ b/tests/test_func.py
@@ -88,7 +88,7 @@ class LintTestUsingModule:
def _get_expected(self):
if self._has_output() and self.output:
- with open(self.output) as fobj:
+ with open(self.output, encoding="utf-8") as fobj:
return fobj.read().strip() + "\n"
else:
return ""
@@ -103,7 +103,7 @@ class LintTestUpdate(LintTestUsingModule):
except OSError:
expected = ""
if got != expected:
- with open(self.output, "w") as f:
+ with open(self.output, "w", encoding="utf-8") as f:
f.write(got)
diff --git a/tests/test_functional.py b/tests/test_functional.py
index f3959b8e3..4ab761738 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -59,7 +59,7 @@ class LintModuleOutputUpdate(testutils.LintModuleTest):
if os.path.exists(self._test_file.expected_output):
os.remove(self._test_file.expected_output)
return
- with open(self._test_file.expected_output, "w") as f:
+ with open(self._test_file.expected_output, "w", encoding="utf-8") as f:
writer = csv.writer(f, dialect="test")
for line in actual_output:
writer.writerow(line.to_csv())
diff --git a/tests/test_import_graph.py b/tests/test_import_graph.py
index 92ca4a510..07a093759 100644
--- a/tests/test_import_graph.py
+++ b/tests/test_import_graph.py
@@ -46,7 +46,7 @@ POSSIBLE_DOT_FILENAMES = ["foo.dot", "foo.gv", "tests/regrtest_data/foo.dot"]
def test_dependencies_graph(dest):
"""DOC files are correctly generated, and the graphname is the basename"""
imports._dependencies_graph(dest, {"labas": ["hoho", "yep"], "hoho": ["yep"]})
- with open(dest) as stream:
+ with open(dest, encoding="utf-8") as stream:
assert (
stream.read().strip()
== """
diff --git a/tests/test_self.py b/tests/test_self.py
index 937ee0394..de63ebd6f 100644
--- a/tests/test_self.py
+++ b/tests/test_self.py
@@ -553,7 +553,7 @@ class TestRunTC:
# create module under directories which have the same name as reporter.path_strip_prefix
# e.g. /src/some/path/src/test_target.py when reporter.path_strip_prefix = /src/
os.makedirs(fake_path)
- with open(module, "w") as test_target:
+ with open(module, "w", encoding="utf-8") as test_target:
test_target.write("a,b = object()")
self._test_output(
diff --git a/tests/unittest_reporting.py b/tests/unittest_reporting.py
index 6b238b484..3946ec61d 100644
--- a/tests/unittest_reporting.py
+++ b/tests/unittest_reporting.py
@@ -128,7 +128,7 @@ def test_multi_format_output(tmp_path):
linter.reporter.close_output_files()
del linter.reporter
- with open(json) as f:
+ with open(json, encoding="utf-8") as f:
assert (
f.read() == "[\n"
" {\n"