| # Copyright (c) 2015-2018, 2020 Claudiu Popa <[email protected]> |
| # Copyright (c) 2015 Cezar <[email protected]> |
| # Copyright (c) 2016 Derek Gustafson <[email protected]> |
| # Copyright (c) 2017 Martin <[email protected]> |
| # Copyright (c) 2019-2021 Pierre Sassoulas <[email protected]> |
| # Copyright (c) 2019 Ashley Whetter <[email protected]> |
| # Copyright (c) 2020 hippo91 <[email protected]> |
| # Copyright (c) 2021 Marc Mueller <[email protected]> |
| |
| # 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 |
| |
| import contextlib |
| |
| import astroid |
| from astroid import nodes |
| |
| from pylint.checkers import stdlib |
| from pylint.interfaces import UNDEFINED |
| from pylint.testutils import CheckerTestCase, Message |
| |
| |
| @contextlib.contextmanager |
| def _add_transform(manager, node, transform, predicate=None): |
| manager.register_transform(node, transform, predicate) |
| try: |
| yield |
| finally: |
| manager.unregister_transform(node, transform, predicate) |
| |
| |
| class TestStdlibChecker(CheckerTestCase): |
| CHECKER_CLASS = stdlib.StdlibChecker |
| |
| def test_deprecated_no_qname_on_unexpected_nodes(self): |
| # Test that we don't crash on nodes which don't have |
| # a qname method. While this test might seem weird since |
| # it uses a transform, it's actually testing a crash that |
| # happened in production, but there was no way to retrieve |
| # the code for which this occurred (how an AssignAttr |
| # got to be the result of a function inference |
| # beats me..) |
| |
| def infer_func(node, context=None): # pylint: disable=unused-argument |
| new_node = nodes.AssignAttr() |
| new_node.parent = node |
| yield new_node |
| |
| manager = astroid.MANAGER |
| transform = astroid.inference_tip(infer_func) |
| with _add_transform(manager, nodes.Name, transform): |
| node = astroid.extract_node( |
| """ |
| call_something() |
| """ |
| ) |
| with self.assertNoMessages(): |
| self.checker.visit_call(node) |
| |
| def test_copy_environ(self): |
| # shallow copy of os.environ should be reported |
| node = astroid.extract_node( |
| """ |
| import copy, os |
| copy.copy(os.environ) |
| """ |
| ) |
| with self.assertAddsMessages( |
| Message(msg_id="shallow-copy-environ", node=node, confidence=UNDEFINED) |
| ): |
| self.checker.visit_call(node) |
| |
| def test_copy_environ_hidden(self): |
| # shallow copy of os.environ should be reported |
| # hide function names to be sure that checker is not just matching text |
| node = astroid.extract_node( |
| """ |
| from copy import copy as test_cp |
| import os as o |
| test_cp(o.environ) |
| """ |
| ) |
| with self.assertAddsMessages( |
| Message(msg_id="shallow-copy-environ", node=node, confidence=UNDEFINED) |
| ): |
| self.checker.visit_call(node) |
| |
| def test_copy_dict(self): |
| # copy of dict is OK |
| node = astroid.extract_node( |
| """ |
| import copy |
| test_dict = {} |
| copy.copy(test_dict) |
| """ |
| ) |
| with self.assertNoMessages(): |
| self.checker.visit_call(node) |
| |
| def test_copy_uninferable(self): |
| # copy of uninferable object should not raise exception, nor make |
| # the checker crash |
| node = astroid.extract_node( |
| """ |
| import copy |
| from missing_library import MissingObject |
| copy.copy(MissingObject) |
| """ |
| ) |
| with self.assertNoMessages(): |
| self.checker.visit_call(node) |
| |
| def test_deepcopy_environ(self): |
| # deepcopy of os.environ is OK |
| node = astroid.extract_node( |
| """ |
| import copy, os |
| copy.deepcopy(os.environ) |
| """ |
| ) |
| with self.assertNoMessages(): |
| self.checker.visit_call(node) |