From 99abc893e4374e3695ca29a0f474decf4f837f52 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 11 Nov 2017 15:46:14 +0100 Subject: New warning: shallow copy of os.environ (#1733) Shallow copy of os.environ doesn't work as people may expect. os.environ is not a dict object but rather a proxy object, so any changes made on the copy may have unexpected effects on os.environ Instead of copy.copy(os.environ) method os.environ.copy() should be used. Message id is: `shallow-copy-environ` See https://bugs.python.org/issue15373 for details. Resolves: #1301 --- pylint/checkers/stdlib.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'pylint/checkers/stdlib.py') diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index 4c2d504cb..45247c4e6 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -23,6 +23,8 @@ from pylint.checkers import utils OPEN_FILES = {'open', 'file'} UNITTEST_CASE = 'unittest.case' THREADING_THREAD = 'threading.Thread' +COPY_COPY = 'copy.copy' +OS_ENVIRON = 'os._Environ' if sys.version_info >= (3, 0): OPEN_MODULE = '_io' else: @@ -105,6 +107,12 @@ class StdlibChecker(BaseChecker): 'The warning is emitted when a threading.Thread class ' 'is instantiated without the target function being passed. ' 'By default, the first parameter is the group param, not the target param. '), + 'W1507': ('Using copy.copy(os.environ). Use os.environ.copy() ' # TODO: number + 'instead. ', + 'shallow-copy-environ', + 'os.environ is not a dict object but proxy object, so ' + 'shallow copy has still effects on original object. ' + 'See https://bugs.python.org/issue15373 for reference. '), } deprecated = { @@ -186,9 +194,17 @@ class StdlibChecker(BaseChecker): if not node.kwargs and node.args: self.add_message('bad-thread-instantiation', node=node) + def _check_shallow_copy_environ(self, node): + arg = utils.get_argument_from_call(node, position=0) + for inferred in arg.inferred(): + if inferred.qname() == OS_ENVIRON: + self.add_message('shallow-copy-environ', node=node) + break + @utils.check_messages('bad-open-mode', 'redundant-unittest-assert', 'deprecated-method', - 'bad-thread-instantiation') + 'bad-thread-instantiation', + 'shallow-copy-environ') def visit_call(self, node): """Visit a Call node.""" try: @@ -202,6 +218,8 @@ class StdlibChecker(BaseChecker): self._check_redundant_assert(node, inferred) if isinstance(inferred, astroid.ClassDef) and inferred.qname() == THREADING_THREAD: self._check_bad_thread_instantiation(node) + if isinstance(inferred, astroid.FunctionDef) and inferred.qname() == COPY_COPY: + self._check_shallow_copy_environ(node) self._check_deprecated_method(node, inferred) except astroid.InferenceError: return -- cgit v1.2.1