blob: aa666b25480ac2147a2edac5511b2adbfe2812b0 [file] [log] [blame]
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Tests for the pylint.checkers.utils module."""
from __future__ import annotations
import astroid
import pytest
from astroid import nodes
from pylint.checkers import utils
from pylint.checkers.base_checker import BaseChecker
@pytest.mark.parametrize(
"name,expected",
[
("min", True),
("__builtins__", True),
("__path__", False),
("__file__", False),
("whatever", False),
("mybuiltin", False),
],
)
def testIsBuiltin(name: str, expected: bool) -> None:
assert utils.is_builtin(name) == expected
@pytest.mark.parametrize(
"fn,kw",
[("foo(3)", {"keyword": "bar"}), ("foo(one=a, two=b, three=c)", {"position": 1})],
)
def testGetArgumentFromCallError(fn: str, kw: dict[str, int] | dict[str, str]) -> None:
with pytest.raises(utils.NoSuchArgumentError):
node = astroid.extract_node(fn)
utils.get_argument_from_call(node, **kw)
@pytest.mark.parametrize(
"fn,kw", [("foo(bar=3)", {"keyword": "bar"}), ("foo(a, b, c)", {"position": 1})]
)
def testGetArgumentFromCallExists(fn: str, kw: dict[str, int] | dict[str, str]) -> None:
node = astroid.extract_node(fn)
assert utils.get_argument_from_call(node, **kw) is not None
def testGetArgumentFromCall() -> None:
node = astroid.extract_node("foo(a, not_this_one=1, this_one=2)")
arg = utils.get_argument_from_call(node, position=2, keyword="this_one")
assert arg.value == 2
node = astroid.extract_node("foo(a)")
with pytest.raises(utils.NoSuchArgumentError):
utils.get_argument_from_call(node, position=1)
with pytest.raises(ValueError):
utils.get_argument_from_call(node, None, None)
name = utils.get_argument_from_call(node, position=0)
assert name.name == "a"
def test_error_of_type() -> None:
code = astroid.extract_node(
"""
try: pass
except AttributeError: #@
pass
try: pass
except Exception: #@
pass
except: #@
pass
"""
)
assert utils.error_of_type(code[0], AttributeError)
assert utils.error_of_type(code[0], (AttributeError,))
assert not utils.error_of_type(code[0], Exception)
assert utils.error_of_type(code[1], Exception)
def test_node_ignores_exception() -> None:
code = astroid.extract_node(
"""
try:
1/0 #@
except ZeroDivisionError:
pass
try:
1/0 #@
except Exception:
pass
try:
2/0 #@
except:
pass
try:
1/0 #@
except ValueError:
pass
"""
)
assert utils.node_ignores_exception(code[0], ZeroDivisionError)
assert not utils.node_ignores_exception(code[1], ZeroDivisionError)
assert not utils.node_ignores_exception(code[2], ZeroDivisionError)
assert not utils.node_ignores_exception(code[3], ZeroDivisionError)
def test_is_subclass_of_node_b_derived_from_node_a() -> None:
code = astroid.extract_node(
"""
class Superclass: #@
pass
class Subclass(Superclass): #@
pass
"""
)
assert utils.is_subclass_of(code[1], code[0])
def test_is_subclass_of_node_b_not_derived_from_node_a() -> None:
code = astroid.extract_node(
"""
class OneClass: #@
pass
class AnotherClass: #@
pass
"""
)
assert not utils.is_subclass_of(code[1], code[0])
def test_is_subclass_of_not_classdefs() -> None:
node = astroid.extract_node(
"""
class OneClass: #@
pass
"""
)
assert not utils.is_subclass_of(None, node)
assert not utils.is_subclass_of(node, None)
assert not utils.is_subclass_of(None, None)
def test_parse_format_method_string() -> None:
samples = [
("{}", 1),
("{}:{}", 2),
("{field}", 1),
("{:5}", 1),
("{:10}", 1),
("{field:10}", 1),
("{field:10}{{}}", 1),
("{:5}{!r:10}", 2),
("{:5}{}{{}}{}", 3),
("{0}{1}{0}", 2),
("Coordinates: {latitude}, {longitude}", 2),
("X: {0[0]}; Y: {0[1]}", 1),
("{:*^30}", 1),
("{!r:}", 1),
]
for fmt, count in samples:
keys, num_args, pos_args = utils.parse_format_method_string(fmt)
keyword_args = len({k for k, _ in keys if not isinstance(k, int)})
assert keyword_args + num_args + pos_args == count
def test_inherit_from_std_ex_recursive_definition() -> None:
node = astroid.extract_node(
"""
import datetime
class First(datetime.datetime):
pass
class Second(datetime.datetime): #@
pass
datetime.datetime = First
datetime.datetime = Second
"""
)
assert not utils.inherit_from_std_ex(node)
def test_get_node_last_lineno_simple() -> None:
node = astroid.extract_node(
"""
pass
"""
)
assert utils.get_node_last_lineno(node) == 2
def test_get_node_last_lineno_if_simple() -> None:
node = astroid.extract_node(
"""
if True:
print(1)
pass
"""
)
assert utils.get_node_last_lineno(node) == 4
def test_get_node_last_lineno_if_elseif_else() -> None:
node = astroid.extract_node(
"""
if True:
print(1)
elif False:
print(2)
else:
print(3)
"""
)
assert utils.get_node_last_lineno(node) == 7
def test_get_node_last_lineno_while() -> None:
node = astroid.extract_node(
"""
while True:
print(1)
"""
)
assert utils.get_node_last_lineno(node) == 3
def test_get_node_last_lineno_while_else() -> None:
node = astroid.extract_node(
"""
while True:
print(1)
else:
print(2)
"""
)
assert utils.get_node_last_lineno(node) == 5
def test_get_node_last_lineno_for() -> None:
node = astroid.extract_node(
"""
for x in range(0, 5):
print(1)
"""
)
assert utils.get_node_last_lineno(node) == 3
def test_get_node_last_lineno_for_else() -> None:
node = astroid.extract_node(
"""
for x in range(0, 5):
print(1)
else:
print(2)
"""
)
assert utils.get_node_last_lineno(node) == 5
def test_get_node_last_lineno_try() -> None:
node = astroid.extract_node(
"""
try:
print(1)
except ValueError:
print(2)
except Exception:
print(3)
"""
)
assert utils.get_node_last_lineno(node) == 7
def test_get_node_last_lineno_try_except_else() -> None:
node = astroid.extract_node(
"""
try:
print(1)
except Exception:
print(2)
print(3)
else:
print(4)
"""
)
assert utils.get_node_last_lineno(node) == 8
def test_get_node_last_lineno_try_except_finally() -> None:
node = astroid.extract_node(
"""
try:
print(1)
except Exception:
print(2)
finally:
print(4)
"""
)
assert utils.get_node_last_lineno(node) == 7
def test_get_node_last_lineno_try_except_else_finally() -> None:
node = astroid.extract_node(
"""
try:
print(1)
except Exception:
print(2)
else:
print(3)
finally:
print(4)
"""
)
assert utils.get_node_last_lineno(node) == 9
def test_get_node_last_lineno_with() -> None:
node = astroid.extract_node(
"""
with x as y:
print(1)
pass
"""
)
assert utils.get_node_last_lineno(node) == 4
def test_get_node_last_lineno_method() -> None:
node = astroid.extract_node(
"""
def x(a, b):
print(a, b)
pass
"""
)
assert utils.get_node_last_lineno(node) == 4
def test_get_node_last_lineno_decorator() -> None:
node = astroid.extract_node(
"""
@decor()
def x(a, b):
print(a, b)
pass
"""
)
assert utils.get_node_last_lineno(node) == 5
def test_get_node_last_lineno_class() -> None:
node = astroid.extract_node(
"""
class C(object):
CONST = True
def x(self, b):
print(b)
def y(self):
pass
pass
"""
)
assert utils.get_node_last_lineno(node) == 10
def test_get_node_last_lineno_combined() -> None:
node = astroid.extract_node(
"""
class C(object):
CONST = True
def y(self):
try:
pass
except:
pass
finally:
pass
"""
)
assert utils.get_node_last_lineno(node) == 11
def test_if_sys_guard() -> None:
code = astroid.extract_node(
"""
import sys
if sys.version_info > (3, 8): #@
pass
if sys.version_info[:2] > (3, 8): #@
pass
if sys.some_other_function > (3, 8): #@
pass
"""
)
assert isinstance(code, list) and len(code) == 3
assert isinstance(code[0], nodes.If)
assert utils.is_sys_guard(code[0]) is True
assert isinstance(code[1], nodes.If)
assert utils.is_sys_guard(code[1]) is True
assert isinstance(code[2], nodes.If)
assert utils.is_sys_guard(code[2]) is False
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_if_typing_guard() -> None:
code = astroid.extract_node(
"""
import typing
import typing as t
from typing import TYPE_CHECKING
if typing.TYPE_CHECKING: #@
pass
if t.TYPE_CHECKING: #@
pass
if TYPE_CHECKING: #@
pass
if typing.SOME_OTHER_CONST: #@
pass
"""
)
assert isinstance(code, list) and len(code) == 4
assert isinstance(code[0], nodes.If)
assert utils.is_typing_guard(code[0]) is True
assert isinstance(code[1], nodes.If)
assert utils.is_typing_guard(code[1]) is True
assert isinstance(code[2], nodes.If)
assert utils.is_typing_guard(code[2]) is True
assert isinstance(code[3], nodes.If)
assert utils.is_typing_guard(code[3]) is False
def test_in_type_checking_block() -> None:
code = astroid.extract_node(
"""
if TYPE_CHECKING: # don't import this!
import math #@
"""
)
assert utils.in_type_checking_block(code) is False
def test_is_empty_literal() -> None:
list_node = astroid.extract_node("a = []")
assert utils.is_base_container(list_node.value)
not_empty_list_node = astroid.extract_node("a = [1,2,3]")
assert not utils.is_base_container(not_empty_list_node.value)
tuple_node = astroid.extract_node("a = ()")
assert utils.is_base_container(tuple_node.value)
not_empty_tuple_node = astroid.extract_node("a = (1,2)")
assert not utils.is_base_container(not_empty_tuple_node.value)
dict_node = astroid.extract_node("a = {}")
assert utils.is_empty_dict_literal(dict_node.value)
not_empty_dict_node = astroid.extract_node("a = {1:1}")
assert not utils.is_empty_dict_literal(not_empty_dict_node.value)
string_node = astroid.extract_node("a = ''")
assert utils.is_empty_str_literal(string_node.value)
not_empty_string_node = astroid.extract_node("a = 'hello'")
assert not utils.is_empty_str_literal(not_empty_string_node.value)
def test_deprecation_is_inside_lambda() -> None:
"""Test that is_inside_lambda is throwing a DeprecationWarning."""
with pytest.warns(DeprecationWarning) as records:
utils.is_inside_lambda(nodes.NodeNG())
assert len(records) == 1
def test_deprecation_check_messages() -> None:
with pytest.warns(DeprecationWarning) as records:
class Checker(BaseChecker): # pylint: disable=unused-variable
@utils.check_messages("my-message")
def visit_assname(self, node: nodes.NodeNG) -> None:
pass
deprecationMessage = (
"utils.check_messages will be removed in "
"favour of calling utils.only_required_for_messages in pylint 3.0"
)
assert len(records) == 1
assert records[0].message.args[0] == deprecationMessage
def test_is_typing_member() -> None:
code = astroid.extract_node(
"""
from typing import Literal as Lit, Set as Literal
import typing as t
Literal #@
Lit #@
t.Literal #@
"""
)
assert not utils.is_typing_member(code[0], ("Literal",))
assert utils.is_typing_member(code[1], ("Literal",))
assert utils.is_typing_member(code[2], ("Literal",))
code = astroid.extract_node(
"""
Literal #@
typing.Literal #@
"""
)
assert not utils.is_typing_member(code[0], ("Literal",))
assert not utils.is_typing_member(code[1], ("Literal",))