blob: b66cf0e4db9eaf20287d657a6dbc71f615858194 [file]
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import io
import re
import sys
import unittest
import pytest
import astroid
from astroid import MANAGER, builder, nodes, objects, test_utils, util
from astroid.bases import Instance
from astroid.brain.brain_namedtuple_enum import _get_namedtuple_fields
from astroid.const import PY312_PLUS, PY313_PLUS
from astroid.exceptions import (
AttributeInferenceError,
InferenceError,
UseInferenceDefault,
)
def assertEqualMro(klass: nodes.ClassDef, expected_mro: list[str]) -> None:
"""Check mro names."""
assert [member.qname() for member in klass.mro()] == expected_mro
class CollectionsDequeTests(unittest.TestCase):
def _inferred_queue_instance(self) -> Instance:
node = builder.extract_node(
"""
import collections
q = collections.deque([])
q
"""
)
return next(node.infer())
def test_deque(self) -> None:
inferred = self._inferred_queue_instance()
self.assertTrue(inferred.getattr("__len__"))
def test_deque_py35methods(self) -> None:
inferred = self._inferred_queue_instance()
self.assertIn("copy", inferred.locals)
self.assertIn("insert", inferred.locals)
self.assertIn("index", inferred.locals)
def test_deque_py39methods(self):
inferred = self._inferred_queue_instance()
self.assertTrue(inferred.getattr("__class_getitem__"))
class OrderedDictTest(unittest.TestCase):
def _inferred_ordered_dict_instance(self) -> Instance:
node = builder.extract_node(
"""
import collections
d = collections.OrderedDict()
d
"""
)
return next(node.infer())
def test_ordered_dict_py34method(self) -> None:
inferred = self._inferred_ordered_dict_instance()
self.assertIn("move_to_end", inferred.locals)
class DefaultDictTest(unittest.TestCase):
def test_1(self) -> None:
node = builder.extract_node(
"""
from collections import defaultdict
X = defaultdict(int)
X[0]
"""
)
inferred = next(node.infer())
self.assertIs(util.Uninferable, inferred)
class ModuleExtenderTest(unittest.TestCase):
def test_extension_modules(self) -> None:
transformer = MANAGER._transform
for extender, _ in transformer.transforms[nodes.Module]:
n = nodes.Module("__main__")
extender(n)
def streams_are_fine():
"""Check if streams are being overwritten,
for example, by pytest
stream inference will not work if they are overwritten
PY3 only
"""
return all(isinstance(s, io.IOBase) for s in (sys.stdout, sys.stderr, sys.stdin))
class IOBrainTest(unittest.TestCase):
@unittest.skipUnless(
streams_are_fine(),
"Needs Python 3 io model / doesn't work with plain pytest."
"use pytest -s for this test to work",
)
def test_sys_streams(self):
for name in ("stdout", "stderr", "stdin"):
node = astroid.extract_node(
f"""
import sys
sys.{name}
"""
)
inferred = next(node.infer())
buffer_attr = next(inferred.igetattr("buffer"))
self.assertIsInstance(buffer_attr, astroid.Instance)
self.assertEqual(buffer_attr.name, "BufferedWriter")
raw = next(buffer_attr.igetattr("raw"))
self.assertIsInstance(raw, astroid.Instance)
self.assertEqual(raw.name, "FileIO")
class TypeBrain(unittest.TestCase):
def test_type_subscript(self):
"""
Check that type object has the __class_getitem__ method
when it is used as a subscript
"""
src = builder.extract_node(
"""
a: type[int] = int
"""
)
val_inf = src.annotation.value.inferred()[0]
self.assertIsInstance(val_inf, nodes.ClassDef)
self.assertEqual(val_inf.name, "type")
meth_inf = val_inf.getattr("__class_getitem__")[0]
self.assertIsInstance(meth_inf, nodes.FunctionDef)
def test_invalid_type_subscript(self):
"""
Check that a type (str for example) that inherits
from type does not have __class_getitem__ method even
when it is used as a subscript
"""
src = builder.extract_node(
"""
a: str[int] = "abc"
"""
)
val_inf = src.annotation.value.inferred()[0]
self.assertIsInstance(val_inf, nodes.ClassDef)
self.assertEqual(val_inf.name, "str")
with self.assertRaises(AttributeInferenceError):
# pylint: disable=expression-not-assigned
# noinspection PyStatementEffect
val_inf.getattr("__class_getitem__")[0]
def test_builtin_subscriptable(self):
"""Starting with python3.9 builtin types such as list are subscriptable.
Any builtin class such as "enumerate" or "staticmethod" also works."""
for typename in ("tuple", "list", "dict", "set", "frozenset", "enumerate"):
src = f"""
{typename:s}[int]
"""
right_node = builder.extract_node(src)
inferred = next(right_node.infer())
self.assertIsInstance(inferred, nodes.ClassDef)
self.assertIsInstance(inferred.getattr("__iter__")[0], nodes.FunctionDef)
def check_metaclass_is_abc(node: nodes.ClassDef):
if PY312_PLUS and node.name == "ByteString":
# .metaclass() finds the first metaclass in the mro(),
# which, from 3.12, is _DeprecateByteStringMeta (unhelpful)
# until ByteString is removed in 3.17.
# Jump over the first two ByteString classes in the mro().
check_metaclass_is_abc(node.mro()[2])
else:
meta = node.metaclass()
assert isinstance(meta, nodes.ClassDef)
assert meta.name == "ABCMeta"
class CollectionsBrain(unittest.TestCase):
def test_collections_abc_is_importable(self) -> None:
"""
Test that we can import `collections.abc`.
The collections.abc has gone through various formats of being frozen. Therefore, we ensure
that we can still import it (correctly).
"""
import_node = builder.extract_node("import collections.abc")
assert isinstance(import_node, nodes.Import)
imported_module = import_node.do_import_module(import_node.names[0][0])
# Make sure that the file we have imported is actually the submodule of collections and
# not the `abc` module. (Which would happen if you call `importlib.util.find_spec("abc")`
# instead of `importlib.util.find_spec("collections.abc")`)
assert isinstance(imported_module.file, str)
assert "collections" in imported_module.file
def test_collections_object_not_subscriptable(self) -> None:
"""
Test that unsubscriptable types are detected
Hashable is not subscriptable even with python39
"""
wrong_node = builder.extract_node(
"""
import collections.abc
collections.abc.Hashable[int]
"""
)
with self.assertRaises(InferenceError):
next(wrong_node.infer())
right_node = builder.extract_node(
"""
import collections.abc
collections.abc.Hashable
"""
)
inferred = next(right_node.infer())
check_metaclass_is_abc(inferred)
assertEqualMro(
inferred,
[
"_collections_abc.Hashable",
"builtins.object",
],
)
with self.assertRaises(AttributeInferenceError):
inferred.getattr("__class_getitem__")
def test_collections_object_subscriptable(self):
"""Starting with python39 some object of collections module are subscriptable. Test one of them"""
right_node = builder.extract_node(
"""
import collections.abc
collections.abc.MutableSet[int]
"""
)
inferred = next(right_node.infer())
check_metaclass_is_abc(inferred)
assertEqualMro(
inferred,
[
"_collections_abc.MutableSet",
"_collections_abc.Set",
"_collections_abc.Collection",
"_collections_abc.Sized",
"_collections_abc.Iterable",
"_collections_abc.Container",
"builtins.object",
],
)
self.assertIsInstance(
inferred.getattr("__class_getitem__")[0], nodes.FunctionDef
)
def test_collections_object_subscriptable_2(self):
"""Starting with python39 Iterator in the collection.abc module is subscriptable"""
node = builder.extract_node(
"""
import collections.abc
class Derived(collections.abc.Iterator[int]):
pass
"""
)
inferred = next(node.infer())
check_metaclass_is_abc(inferred)
assertEqualMro(
inferred,
[
".Derived",
"_collections_abc.Iterator",
"_collections_abc.Iterable",
"builtins.object",
],
)
def test_collections_object_subscriptable_3(self):
"""With Python 3.9 the ByteString class of the collections module is subscriptable
(but not the same class from typing module)"""
right_node = builder.extract_node(
"""
import collections.abc
collections.abc.ByteString[int]
"""
)
inferred = next(right_node.infer())
check_metaclass_is_abc(inferred)
self.assertIsInstance(
inferred.getattr("__class_getitem__")[0], nodes.FunctionDef
)
def test_collections_object_subscriptable_4(self):
"""Multiple inheritance with subscriptable collection class"""
node = builder.extract_node(
"""
import collections.abc
class Derived(collections.abc.Hashable, collections.abc.Iterator[int]):
pass
"""
)
inferred = next(node.infer())
assertEqualMro(
inferred,
[
".Derived",
"_collections_abc.Hashable",
"_collections_abc.Iterator",
"_collections_abc.Iterable",
"builtins.object",
],
)
def test_statistics_quantiles_from_import(self):
node = builder.extract_node(
"""
from statistics import quantiles
quantiles([1, 2, 3, 4, 5, 6, 7, 8, 9], n=4)
"""
)
inferred = next(node.infer())
self.assertIs(inferred, util.Uninferable)
class TypingBrain(unittest.TestCase):
def test_namedtuple_base(self) -> None:
klass = builder.extract_node(
"""
from typing import NamedTuple
class X(NamedTuple("X", [("a", int), ("b", str), ("c", bytes)])):
pass
"""
)
self.assertEqual(
[anc.name for anc in klass.ancestors()], ["X", "tuple", "object"]
)
for anc in klass.ancestors():
self.assertFalse(anc.parent is None)
def test_namedtuple_can_correctly_access_methods(self) -> None:
klass, called = builder.extract_node(
"""
from typing import NamedTuple
class X(NamedTuple): #@
a: int
b: int
def as_string(self):
return '%s' % self.a
def as_integer(self):
return 2 + 3
X().as_integer() #@
"""
)
self.assertEqual(len(klass.getattr("as_string")), 1)
inferred = next(called.infer())
self.assertIsInstance(inferred, nodes.Const)
self.assertEqual(inferred.value, 5)
def test_namedtuple_inference(self) -> None:
klass = builder.extract_node(
"""
from typing import NamedTuple
class X(NamedTuple("X", [("a", int), ("b", str), ("c", bytes)])):
pass
"""
)
base = next(base for base in klass.ancestors() if base.name == "X")
self.assertSetEqual({"a", "b", "c"}, set(base.instance_attrs))
def test_namedtuple_inference_nonliteral(self) -> None:
# Note: NamedTuples in mypy only work with literals.
klass = builder.extract_node(
"""
from typing import NamedTuple
name = "X"
fields = [("a", int), ("b", str), ("c", bytes)]
NamedTuple(name, fields)
"""
)
inferred = next(klass.infer())
self.assertIsInstance(inferred, astroid.Instance)
self.assertEqual(inferred.qname(), "typing.NamedTuple")
def test_namedtuple_instance_attrs(self) -> None:
result = builder.extract_node(
"""
from typing import NamedTuple
NamedTuple("A", [("a", int), ("b", str), ("c", bytes)])(1, 2, 3) #@
"""
)
inferred = next(result.infer())
for name, attr in inferred.instance_attrs.items():
self.assertEqual(attr[0].attrname, name)
def test_namedtuple_simple(self) -> None:
result = builder.extract_node(
"""
from typing import NamedTuple
NamedTuple("A", [("a", int), ("b", str), ("c", bytes)])
"""
)
inferred = next(result.infer())
self.assertIsInstance(inferred, nodes.ClassDef)
self.assertSetEqual({"a", "b", "c"}, set(inferred.instance_attrs))
def test_namedtuple_few_args(self) -> None:
result = builder.extract_node(
"""
from typing import NamedTuple
NamedTuple("A")
"""
)
inferred = next(result.infer())
self.assertIsInstance(inferred, astroid.Instance)
self.assertEqual(inferred.qname(), "typing.NamedTuple")
def test_namedtuple_few_fields(self) -> None:
result = builder.extract_node(
"""
from typing import NamedTuple
NamedTuple("A", [("a",), ("b", str), ("c", bytes)])
"""
)
inferred = next(result.infer())
self.assertIsInstance(inferred, astroid.Instance)
self.assertEqual(inferred.qname(), "typing.NamedTuple")
def test_namedtuple_class_form(self) -> None:
result = builder.extract_node(
"""
from typing import NamedTuple
class Example(NamedTuple):
CLASS_ATTR = "class_attr"
mything: int
Example(mything=1)
"""
)
inferred = next(result.infer())
self.assertIsInstance(inferred, astroid.Instance)
class_attr = inferred.getattr("CLASS_ATTR")[0]
self.assertIsInstance(class_attr, nodes.AssignName)
const = next(class_attr.infer())
self.assertEqual(const.value, "class_attr")
def test_namedtuple_inferred_as_class(self) -> None:
node = builder.extract_node(
"""
from typing import NamedTuple
NamedTuple
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.ClassDef)
assert inferred.name == "NamedTuple"
def test_namedtuple_bug_pylint_4383(self) -> None:
"""Inference of 'NamedTuple' function shouldn't cause InferenceError.
https://github.com/pylint-dev/pylint/issues/4383
"""
node = builder.extract_node(
"""
if True:
def NamedTuple():
pass
NamedTuple
"""
)
next(node.infer())
def test_namedtuple_uninferable_member(self) -> None:
call = builder.extract_node(
"""
from typing import namedtuple
namedtuple('uninf', {x: x for x in range(0)}) #@"""
)
with pytest.raises(UseInferenceDefault):
_get_namedtuple_fields(call)
call = builder.extract_node(
"""
from typing import namedtuple
uninferable = {x: x for x in range(0)}
namedtuple('uninferable', uninferable) #@
"""
)
with pytest.raises(UseInferenceDefault):
_get_namedtuple_fields(call)
def test_typing_types(self) -> None:
ast_nodes = builder.extract_node(
"""
from typing import TypeVar, Iterable, Tuple, NewType, Dict, Union
TypeVar('MyTypeVar', int, float, complex) #@
Iterable[Tuple[MyTypeVar, MyTypeVar]] #@
TypeVar('AnyStr', str, bytes) #@
NewType('UserId', str) #@
Dict[str, str] #@
Union[int, str] #@
"""
)
for node in ast_nodes:
inferred = next(node.infer())
self.assertIsInstance(inferred, nodes.ClassDef, node.as_string())
def test_typing_type_without_tip(self):
"""Regression test for https://github.com/pylint-dev/pylint/issues/5770"""
node = builder.extract_node(
"""
from typing import NewType
def make_new_type(t):
new_type = NewType(f'IntRange_{t}', t) #@
"""
)
with self.assertRaises(UseInferenceDefault):
astroid.brain.brain_typing.infer_typing_typevar_or_newtype(node.value)
def test_namedtuple_nested_class(self):
result = builder.extract_node(
"""
from typing import NamedTuple
class Example(NamedTuple):
class Foo:
bar = "bar"
Example
"""
)
inferred = next(result.infer())
self.assertIsInstance(inferred, nodes.ClassDef)
class_def_attr = inferred.getattr("Foo")[0]
self.assertIsInstance(class_def_attr, nodes.ClassDef)
attr_def = class_def_attr.getattr("bar")[0]
attr = next(attr_def.infer())
self.assertEqual(attr.value, "bar")
def test_tuple_type(self):
node = builder.extract_node(
"""
from typing import Tuple
Tuple[int, int]
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.ClassDef)
assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef)
assert inferred.qname() == "typing.Tuple"
def test_callable_type(self):
node = builder.extract_node(
"""
from typing import Callable, Any
Callable[..., Any]
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.ClassDef)
assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef)
assert inferred.qname() == "typing.Callable"
def test_typing_generic_subscriptable(self):
"""Test typing.Generic is subscriptable with __class_getitem__ (added in PY37)"""
node = builder.extract_node(
"""
from typing import Generic, TypeVar
T = TypeVar('T')
Generic[T]
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.ClassDef)
assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef)
@test_utils.require_version(minver="3.12")
def test_typing_generic_subscriptable_pep695(self):
"""Test class using type parameters is subscriptable with __class_getitem__ (added in PY312)"""
node = builder.extract_node(
"""
class Foo[T]: ...
class Bar[T](Foo[T]): ...
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.ClassDef)
assert inferred.name == "Bar"
assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef)
ancestors = list(inferred.ancestors())
assert len(ancestors) == 2
assert ancestors[0].name == "Foo"
assert ancestors[1].name == "object"
def test_typing_annotated_subscriptable(self):
"""typing.Annotated is subscriptable with __class_getitem__ below 3.13."""
node = builder.extract_node(
"""
import typing
typing.Annotated[str, "data"]
"""
)
inferred = next(node.infer())
if PY313_PLUS:
assert isinstance(inferred, nodes.FunctionDef)
else:
assert isinstance(inferred, nodes.ClassDef)
assert isinstance(
inferred.getattr("__class_getitem__")[0], nodes.FunctionDef
)
def test_typing_generic_slots(self):
"""Test slots for Generic subclass."""
node = builder.extract_node(
"""
from typing import Generic, TypeVar
T = TypeVar('T')
class A(Generic[T]):
__slots__ = ['value']
def __init__(self, value):
self.value = value
"""
)
inferred = next(node.infer())
slots = inferred.slots()
assert len(slots) == 1
assert isinstance(slots[0], nodes.Const)
assert slots[0].value == "value"
def test_typing_no_duplicates(self):
node = builder.extract_node(
"""
from typing import List
List[int]
"""
)
assert len(node.inferred()) == 1
def test_typing_no_duplicates_2(self):
node = builder.extract_node(
"""
from typing import Optional, Tuple
Tuple[Optional[int], ...]
"""
)
assert len(node.inferred()) == 1
def test_typing_param_spec(self):
node = builder.extract_node(
"""
from typing import ParamSpec
P = ParamSpec("P")
"""
)
inferred = next(node.targets[0].infer())
assert next(inferred.igetattr("args")) is not None
assert next(inferred.igetattr("kwargs")) is not None
def test_collections_generic_alias_slots(self):
"""Test slots for a class which is a subclass of a generic alias type."""
node = builder.extract_node(
"""
import collections
import typing
Type = typing.TypeVar('Type')
class A(collections.abc.AsyncIterator[Type]):
__slots__ = ('_value',)
def __init__(self, value: collections.abc.AsyncIterator[Type]):
self._value = value
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.ClassDef)
slots = inferred.slots()
assert len(slots) == 1
assert isinstance(slots[0], nodes.Const)
assert slots[0].value == "_value"
def test_has_dunder_args(self) -> None:
ast_node = builder.extract_node(
"""
from typing import Union
NumericTypes = Union[int, float]
NumericTypes.__args__ #@
"""
)
inferred = next(ast_node.infer())
assert isinstance(inferred, nodes.Tuple)
def test_typing_namedtuple_dont_crash_on_no_fields(self) -> None:
node = builder.extract_node(
"""
from typing import NamedTuple
Bar = NamedTuple("bar", [])
Bar()
"""
)
inferred = next(node.infer())
self.assertIsInstance(inferred, astroid.Instance)
def test_typed_dict(self):
code = builder.extract_node(
"""
from typing import TypedDict
class CustomTD(TypedDict): #@
var: int
CustomTD(var=1) #@
"""
)
inferred_base = next(code[0].bases[0].infer())
assert isinstance(inferred_base, nodes.ClassDef)
assert inferred_base.qname() == "typing.TypedDict"
typedDict_base = next(inferred_base.bases[0].infer())
assert typedDict_base.qname() == "builtins.dict"
# Test TypedDict has `__call__` method
local_call = inferred_base.locals.get("__call__", None)
assert local_call and len(local_call) == 1
assert isinstance(local_call[0], nodes.Name) and local_call[0].name == "dict"
# Test TypedDict instance is callable
assert next(code[1].infer()).callable() is True
def test_typing_alias_type(self):
"""
Test that the type aliased thanks to typing._alias function are
correctly inferred.
typing_alias function is introduced with python37
"""
node = builder.extract_node(
"""
from typing import TypeVar, MutableSet
T = TypeVar("T")
MutableSet[T]
class Derived1(MutableSet[T]):
pass
"""
)
inferred = next(node.infer())
assertEqualMro(
inferred,
[
".Derived1",
"typing.MutableSet",
"_collections_abc.MutableSet",
"_collections_abc.Set",
"_collections_abc.Collection",
"_collections_abc.Sized",
"_collections_abc.Iterable",
"_collections_abc.Container",
"builtins.object",
],
)
def test_typing_alias_type_2(self):
"""
Test that the type aliased thanks to typing._alias function are
correctly inferred.
typing_alias function is introduced with python37.
OrderedDict in the typing module appears only with python 3.7.2
"""
node = builder.extract_node(
"""
import typing
class Derived2(typing.OrderedDict[int, str]):
pass
"""
)
inferred = next(node.infer())
assertEqualMro(
inferred,
[
".Derived2",
"typing.OrderedDict",
"collections.OrderedDict",
"builtins.dict",
"builtins.object",
],
)
def test_typing_object_not_subscriptable(self):
"""Hashable is not subscriptable"""
wrong_node = builder.extract_node(
"""
import typing
typing.Hashable[int]
"""
)
with self.assertRaises(InferenceError):
next(wrong_node.infer())
right_node = builder.extract_node(
"""
import typing
typing.Hashable
"""
)
inferred = next(right_node.infer())
assertEqualMro(
inferred,
[
"typing.Hashable",
"_collections_abc.Hashable",
"builtins.object",
],
)
with self.assertRaises(AttributeInferenceError):
inferred.getattr("__class_getitem__")
def test_typing_object_subscriptable(self):
"""Test that MutableSet is subscriptable"""
right_node = builder.extract_node(
"""
import typing
typing.MutableSet[int]
"""
)
inferred = next(right_node.infer())
assertEqualMro(
inferred,
[
"typing.MutableSet",
"_collections_abc.MutableSet",
"_collections_abc.Set",
"_collections_abc.Collection",
"_collections_abc.Sized",
"_collections_abc.Iterable",
"_collections_abc.Container",
"builtins.object",
],
)
self.assertIsInstance(
inferred.getattr("__class_getitem__")[0], nodes.FunctionDef
)
def test_typing_object_subscriptable_2(self):
"""Multiple inheritance with subscriptable typing alias"""
node = builder.extract_node(
"""
import typing
class Derived(typing.Hashable, typing.Iterator[int]):
pass
"""
)
inferred = next(node.infer())
assertEqualMro(
inferred,
[
".Derived",
"typing.Hashable",
"_collections_abc.Hashable",
"typing.Iterator",
"_collections_abc.Iterator",
"_collections_abc.Iterable",
"builtins.object",
],
)
def test_typing_object_notsubscriptable_3(self):
"""The ByteString class of the typing module is not
subscriptable (whereas it is in the collections' module)"""
right_node = builder.extract_node(
"""
import typing
typing.ByteString
"""
)
inferred = next(right_node.infer())
check_metaclass_is_abc(inferred)
with self.assertRaises(AttributeInferenceError):
self.assertIsInstance(
inferred.getattr("__class_getitem__")[0], nodes.FunctionDef
)
def test_typing_object_builtin_subscriptable(self):
"""
Test that builtins alias, such as typing.List, are subscriptable
"""
for typename in ("List", "Dict", "Set", "FrozenSet", "Tuple"):
src = f"""
import typing
typing.{typename:s}[int]
"""
right_node = builder.extract_node(src)
inferred = next(right_node.infer())
self.assertIsInstance(inferred, nodes.ClassDef)
self.assertIsInstance(inferred.getattr("__iter__")[0], nodes.FunctionDef)
@staticmethod
def test_typing_type_subscriptable():
node = builder.extract_node(
"""
from typing import Type
Type[int]
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.ClassDef)
assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef)
assert inferred.qname() == "typing.Type"
def test_typing_cast(self) -> None:
node = builder.extract_node(
"""
from typing import cast
class A:
pass
b = 42
cast(A, b)
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.Const)
assert inferred.value == 42
def test_typing_cast_attribute(self) -> None:
node = builder.extract_node(
"""
import typing
class A:
pass
b = 42
typing.cast(A, b)
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.Const)
assert inferred.value == 42
def test_typing_cast_multiple_inference_calls(self) -> None:
"""Inference of an outer function should not store the result for cast."""
ast_nodes = builder.extract_node(
"""
from typing import TypeVar, cast
T = TypeVar("T")
def ident(var: T) -> T:
return cast(T, var)
ident(2) #@
ident("Hello") #@
"""
)
i0 = next(ast_nodes[0].infer())
assert isinstance(i0, nodes.Const)
assert i0.value == 2
i1 = next(ast_nodes[1].infer())
assert isinstance(i1, nodes.Const)
assert i1.value == "Hello"
class ReBrainTest(unittest.TestCase):
def test_regex_flags(self) -> None:
names = [name for name in dir(re) if name.isupper()]
re_ast = MANAGER.ast_from_module_name("re")
for name in names:
self.assertIn(name, re_ast)
self.assertEqual(next(re_ast[name].infer()).value, getattr(re, name))
def test_re_pattern_subscriptable(self):
"""Test re.Pattern and re.Match are subscriptable in PY39+"""
node1 = builder.extract_node(
"""
import re
re.Pattern[str]
"""
)
inferred1 = next(node1.infer())
assert isinstance(inferred1, nodes.ClassDef)
assert isinstance(inferred1.getattr("__class_getitem__")[0], nodes.FunctionDef)
node2 = builder.extract_node(
"""
import re
re.Match[str]
"""
)
inferred2 = next(node2.infer())
assert isinstance(inferred2, nodes.ClassDef)
assert isinstance(inferred2.getattr("__class_getitem__")[0], nodes.FunctionDef)
class BrainNamedtupleAnnAssignTest(unittest.TestCase):
def test_no_crash_on_ann_assign_in_namedtuple(self) -> None:
node = builder.extract_node(
"""
from enum import Enum
from typing import Optional
class A(Enum):
B: str = 'B'
"""
)
inferred = next(node.infer())
self.assertIsInstance(inferred, nodes.ClassDef)
class BrainUUIDTest(unittest.TestCase):
def test_uuid_has_int_member(self) -> None:
node = builder.extract_node(
"""
import uuid
u = uuid.UUID('{12345678-1234-5678-1234-567812345678}')
u.int
"""
)
inferred = next(node.infer())
self.assertIsInstance(inferred, nodes.Const)
class RandomSampleTest(unittest.TestCase):
def test_inferred_successfully(self) -> None:
node = astroid.extract_node(
"""
import random
random.sample([1, 2], 2) #@
"""
)
inferred = next(node.infer())
self.assertIsInstance(inferred, nodes.List)
elems = sorted(elem.value for elem in inferred.elts)
self.assertEqual(elems, [1, 2])
def test_arguments_inferred_successfully(self) -> None:
"""Test inference of `random.sample` when both arguments are of type `nodes.Call`."""
node = astroid.extract_node(
"""
import random
def sequence():
return [1, 2]
random.sample(sequence(), len([1,2])) #@
"""
)
# Check that arguments are of type `nodes.Call`.
sequence, length = node.args
self.assertIsInstance(sequence, nodes.Call)
self.assertIsInstance(length, nodes.Call)
# Check the inference of `random.sample` call.
inferred = next(node.infer())
self.assertIsInstance(inferred, nodes.List)
elems = sorted(elem.value for elem in inferred.elts)
self.assertEqual(elems, [1, 2])
def test_no_crash_on_evaluatedobject(self) -> None:
node = astroid.extract_node(
"""
from random import sample
class A: pass
sample(list({1: A()}.values()), 1)"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.List)
assert len(inferred.elts) == 1
assert isinstance(inferred.elts[0], nodes.Call)
def test_no_crash_on_uninferable_element(self) -> None:
"""Test that random.sample does not crash when elements are Uninferable.
Regression test for https://github.com/pylint-dev/astroid/issues/2518
"""
node = astroid.extract_node(
"""
import random
random.sample(1*[b], 1) #@
"""
)
inferred = next(node.infer())
assert inferred is astroid.Uninferable
def test_no_crash_on_classdef_clone(self) -> None:
"""Test that random.sample does not crash when cloning ClassDef nodes.
Regression test for https://github.com/pylint-dev/astroid/issues/2923
"""
node = astroid.extract_node(
"""
import random
random.sample([dict] * 2, 1) #@
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.List)
assert len(inferred.elts) == 1
assert isinstance(inferred.elts[0], nodes.ClassDef)
assert inferred.elts[0].name == "dict"
def test_no_crash_on_functiondef_clone(self) -> None:
"""Test that random.sample does not crash when cloning FunctionDef nodes.
Regression test for https://github.com/pylint-dev/astroid/issues/2923
"""
node = astroid.extract_node(
"""
import random
random.sample([len] * 2, 1) #@
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.List)
assert len(inferred.elts) == 1
assert isinstance(inferred.elts[0], nodes.FunctionDef)
assert inferred.elts[0].name == "len"
class SubprocessTest(unittest.TestCase):
"""Test subprocess brain"""
def test_subprocess_args(self) -> None:
"""Make sure the args attribute exists for Popen
Test for https://github.com/pylint-dev/pylint/issues/1860"""
name = astroid.extract_node(
"""
import subprocess
p = subprocess.Popen(['ls'])
p #@
"""
)
[inst] = name.inferred()
self.assertIsInstance(next(inst.igetattr("args")), nodes.List)
def test_subprcess_check_output(self) -> None:
code = """
import subprocess
subprocess.check_output(['echo', 'hello']);
"""
node = astroid.extract_node(code)
inferred = next(node.infer())
# Can be either str or bytes
assert isinstance(inferred, nodes.Const)
assert isinstance(inferred.value, (str, bytes))
def test_popen_does_not_have_class_getitem(self):
code = """import subprocess; subprocess.Popen"""
node = astroid.extract_node(code)
inferred = next(node.infer())
assert "__class_getitem__" in inferred
class TestIsinstanceInference:
"""Test isinstance builtin inference"""
def test_type_type(self) -> None:
assert _get_result("isinstance(type, type)") == "True"
def test_object_type(self) -> None:
assert _get_result("isinstance(object, type)") == "True"
def test_type_object(self) -> None:
assert _get_result("isinstance(type, object)") == "True"
def test_isinstance_int_true(self) -> None:
"""Make sure isinstance can check builtin int types"""
assert _get_result("isinstance(1, int)") == "True"
def test_isinstance_int_false(self) -> None:
assert _get_result("isinstance('a', int)") == "False"
def test_isinstance_object_true(self) -> None:
assert (
_get_result(
"""
class Bar(object):
pass
isinstance(Bar(), object)
"""
)
== "True"
)
def test_isinstance_object_true3(self) -> None:
assert (
_get_result(
"""
class Bar(object):
pass
isinstance(Bar(), Bar)
"""
)
== "True"
)
def test_isinstance_class_false(self) -> None:
assert (
_get_result(
"""
class Foo(object):
pass
class Bar(object):
pass
isinstance(Bar(), Foo)
"""
)
== "False"
)
def test_isinstance_type_false(self) -> None:
assert (
_get_result(
"""
class Bar(object):
pass
isinstance(Bar(), type)
"""
)
== "False"
)
def test_isinstance_str_true(self) -> None:
"""Make sure isinstance can check builtin str types"""
assert _get_result("isinstance('a', str)") == "True"
def test_isinstance_str_false(self) -> None:
assert _get_result("isinstance(1, str)") == "False"
def test_isinstance_tuple_argument(self) -> None:
"""obj just has to be an instance of ANY class/type on the right"""
assert _get_result("isinstance(1, (str, int))") == "True"
def test_isinstance_type_false2(self) -> None:
assert (
_get_result(
"""
isinstance(1, type)
"""
)
== "False"
)
def test_isinstance_object_true2(self) -> None:
assert (
_get_result(
"""
class Bar(type):
pass
mainbar = Bar("Bar", tuple(), {})
isinstance(mainbar, object)
"""
)
== "True"
)
def test_isinstance_type_true(self) -> None:
assert (
_get_result(
"""
class Bar(type):
pass
mainbar = Bar("Bar", tuple(), {})
isinstance(mainbar, type)
"""
)
== "True"
)
def test_isinstance_edge_case(self) -> None:
"""isinstance allows bad type short-circuting"""
assert _get_result("isinstance(1, (int, 1))") == "True"
def test_uninferable_bad_type(self) -> None:
"""The second argument must be a class or a tuple of classes"""
with pytest.raises(InferenceError):
_get_result_node("isinstance(int, 1)")
def test_uninferable_keywords(self) -> None:
"""isinstance does not allow keywords"""
with pytest.raises(InferenceError):
_get_result_node("isinstance(1, class_or_tuple=int)")
def test_too_many_args(self) -> None:
"""isinstance must have two arguments"""
with pytest.raises(InferenceError):
_get_result_node("isinstance(1, int, str)")
def test_first_param_is_uninferable(self) -> None:
with pytest.raises(InferenceError):
_get_result_node("isinstance(something, int)")
class TestIssubclassBrain:
"""Test issubclass() builtin inference"""
def test_type_type(self) -> None:
assert _get_result("issubclass(type, type)") == "True"
def test_object_type(self) -> None:
assert _get_result("issubclass(object, type)") == "False"
def test_type_object(self) -> None:
assert _get_result("issubclass(type, object)") == "True"
def test_issubclass_same_class(self) -> None:
assert _get_result("issubclass(int, int)") == "True"
def test_issubclass_not_the_same_class(self) -> None:
assert _get_result("issubclass(str, int)") == "False"
def test_issubclass_object_true(self) -> None:
assert (
_get_result(
"""
class Bar(object):
pass
issubclass(Bar, object)
"""
)
== "True"
)
def test_issubclass_same_user_defined_class(self) -> None:
assert (
_get_result(
"""
class Bar(object):
pass
issubclass(Bar, Bar)
"""
)
== "True"
)
def test_issubclass_different_user_defined_classes(self) -> None:
assert (
_get_result(
"""
class Foo(object):
pass
class Bar(object):
pass
issubclass(Bar, Foo)
"""
)
== "False"
)
def test_issubclass_type_false(self) -> None:
assert (
_get_result(
"""
class Bar(object):
pass
issubclass(Bar, type)
"""
)
== "False"
)
def test_isinstance_tuple_argument(self) -> None:
"""obj just has to be a subclass of ANY class/type on the right"""
assert _get_result("issubclass(int, (str, int))") == "True"
def test_isinstance_object_true2(self) -> None:
assert (
_get_result(
"""
class Bar(type):
pass
issubclass(Bar, object)
"""
)
== "True"
)
def test_issubclass_short_circuit(self) -> None:
"""issubclasss allows bad type short-circuting"""
assert _get_result("issubclass(int, (int, 1))") == "True"
def test_uninferable_bad_type(self) -> None:
"""The second argument must be a class or a tuple of classes"""
# Should I subclass
with pytest.raises(InferenceError):
_get_result_node("issubclass(int, 1)")
def test_uninferable_keywords(self) -> None:
"""issubclass does not allow keywords"""
with pytest.raises(InferenceError):
_get_result_node("issubclass(int, class_or_tuple=int)")
def test_too_many_args(self) -> None:
"""issubclass must have two arguments"""
with pytest.raises(InferenceError):
_get_result_node("issubclass(int, int, str)")
def _get_result_node(code: str) -> nodes.Const:
node = next(astroid.extract_node(code).infer())
return node
def _get_result(code: str) -> str:
return _get_result_node(code).as_string()
class TestLenBuiltinInference:
def test_len_list(self) -> None:
# Uses .elts
node = astroid.extract_node(
"""
len(['a','b','c'])
"""
)
node = next(node.infer())
assert node.as_string() == "3"
assert isinstance(node, nodes.Const)
def test_len_tuple(self) -> None:
node = astroid.extract_node(
"""
len(('a','b','c'))
"""
)
node = next(node.infer())
assert node.as_string() == "3"
def test_len_var(self) -> None:
# Make sure argument is inferred
node = astroid.extract_node(
"""
a = [1,2,'a','b','c']
len(a)
"""
)
node = next(node.infer())
assert node.as_string() == "5"
def test_len_dict(self) -> None:
# Uses .items
node = astroid.extract_node(
"""
a = {'a': 1, 'b': 2}
len(a)
"""
)
node = next(node.infer())
assert node.as_string() == "2"
def test_len_set(self) -> None:
node = astroid.extract_node(
"""
len({'a'})
"""
)
inferred_node = next(node.infer())
assert inferred_node.as_string() == "1"
def test_len_object(self) -> None:
"""Test len with objects that implement the len protocol"""
node = astroid.extract_node(
"""
class A:
def __len__(self):
return 57
len(A())
"""
)
inferred_node = next(node.infer())
assert inferred_node.as_string() == "57"
def test_len_class_with_metaclass(self) -> None:
"""Make sure proper len method is located"""
cls_node, inst_node = astroid.extract_node(
"""
class F2(type):
def __new__(cls, name, bases, attrs):
return super().__new__(cls, name, bases, {})
def __len__(self):
return 57
class F(metaclass=F2):
def __len__(self):
return 4
len(F) #@
len(F()) #@
"""
)
assert next(cls_node.infer()).as_string() == "57"
assert next(inst_node.infer()).as_string() == "4"
def test_len_object_failure(self) -> None:
"""If taking the length of a class, do not use an instance method"""
node = astroid.extract_node(
"""
class F:
def __len__(self):
return 57
len(F)
"""
)
with pytest.raises(InferenceError):
next(node.infer())
def test_len_string(self) -> None:
node = astroid.extract_node(
"""
len("uwu")
"""
)
assert next(node.infer()).as_string() == "3"
def test_len_generator_failure(self) -> None:
node = astroid.extract_node(
"""
def gen():
yield 'a'
yield 'b'
len(gen())
"""
)
with pytest.raises(InferenceError):
next(node.infer())
def test_len_failure_missing_variable(self) -> None:
node = astroid.extract_node(
"""
len(a)
"""
)
with pytest.raises(InferenceError):
next(node.infer())
def test_len_bytes(self) -> None:
node = astroid.extract_node(
"""
len(b'uwu')
"""
)
assert next(node.infer()).as_string() == "3"
def test_int_subclass_result(self) -> None:
"""Check that a subclass of an int can still be inferred
This test does not properly infer the value passed to the
int subclass (5) but still returns a proper integer as we
fake the result of the `len()` call.
"""
node = astroid.extract_node(
"""
class IntSubclass(int):
pass
class F:
def __len__(self):
return IntSubclass(5)
len(F())
"""
)
assert next(node.infer()).as_string() == "0"
@pytest.mark.xfail(reason="Can't use list special astroid fields")
def test_int_subclass_argument(self):
"""I am unable to access the length of an object which
subclasses list"""
node = astroid.extract_node(
"""
class ListSubclass(list):
pass
len(ListSubclass([1,2,3,4,4]))
"""
)
assert next(node.infer()).as_string() == "5"
def test_len_builtin_inference_attribute_error_str(self) -> None:
"""Make sure len builtin doesn't raise an AttributeError
on instances of str or bytes
See https://github.com/pylint-dev/pylint/issues/1942
"""
code = 'len(str("F"))'
try:
next(astroid.extract_node(code).infer())
except InferenceError:
pass
def test_len_builtin_inference_recursion_error_self_referential_attribute(
self,
) -> None:
"""Make sure len calls do not trigger
recursion errors for self referential assignment
See https://github.com/pylint-dev/pylint/issues/2734
"""
code = """
class Data:
def __init__(self):
self.shape = []
data = Data()
data.shape = len(data.shape)
data.shape #@
"""
try:
astroid.extract_node(code).inferred()
except RecursionError:
pytest.fail("Inference call should not trigger a recursion error")
def test_infer_str() -> None:
ast_nodes = astroid.extract_node(
"""
str(s) #@
str('a') #@
str(some_object()) #@
"""
)
for node in ast_nodes:
inferred = next(node.infer())
assert isinstance(inferred, nodes.Const)
node = astroid.extract_node(
"""
str(s='') #@
"""
)
inferred = next(node.infer())
assert isinstance(inferred, astroid.Instance)
assert inferred.qname() == "builtins.str"
def test_infer_int() -> None:
ast_nodes = astroid.extract_node(
"""
int(0) #@
int('1') #@
"""
)
for node in ast_nodes:
inferred = next(node.infer())
assert isinstance(inferred, nodes.Const)
ast_nodes = astroid.extract_node(
"""
int(s='') #@
int('2.5') #@
int('something else') #@
int(unknown) #@
int(b'a') #@
"""
)
for node in ast_nodes:
inferred = next(node.infer())
assert isinstance(inferred, astroid.Instance)
assert inferred.qname() == "builtins.int"
def test_infer_dict_from_keys() -> None:
bad_nodes = astroid.extract_node(
"""
dict.fromkeys() #@
dict.fromkeys(1, 2, 3) #@
dict.fromkeys(a=1) #@
"""
)
for node in bad_nodes:
with pytest.raises(InferenceError):
if isinstance(next(node.infer()), util.UninferableBase):
raise InferenceError
# Test uninferable values
good_nodes = astroid.extract_node(
"""
from unknown import Unknown
dict.fromkeys(some_value) #@
dict.fromkeys(some_other_value) #@
dict.fromkeys([Unknown(), Unknown()]) #@
dict.fromkeys([Unknown(), Unknown()]) #@
"""
)
for node in good_nodes:
inferred = next(node.infer())
assert isinstance(inferred, nodes.Dict)
assert inferred.items == []
# Test inferable values
# from a dictionary's keys
from_dict = astroid.extract_node(
"""
dict.fromkeys({'a':2, 'b': 3, 'c': 3}) #@
"""
)
inferred = next(from_dict.infer())
assert isinstance(inferred, nodes.Dict)
itered = inferred.itered()
assert all(isinstance(elem, nodes.Const) for elem in itered)
actual_values = [elem.value for elem in itered]
assert sorted(actual_values) == ["a", "b", "c"]
# from a string
from_string = astroid.extract_node(
"""
dict.fromkeys('abc')
"""
)
inferred = next(from_string.infer())
assert isinstance(inferred, nodes.Dict)
itered = inferred.itered()
assert all(isinstance(elem, nodes.Const) for elem in itered)
actual_values = [elem.value for elem in itered]
assert sorted(actual_values) == ["a", "b", "c"]
# from bytes
from_bytes = astroid.extract_node(
"""
dict.fromkeys(b'abc')
"""
)
inferred = next(from_bytes.infer())
assert isinstance(inferred, nodes.Dict)
itered = inferred.itered()
assert all(isinstance(elem, nodes.Const) for elem in itered)
actual_values = [elem.value for elem in itered]
assert sorted(actual_values) == [97, 98, 99]
# From list/set/tuple
from_others = astroid.extract_node(
"""
dict.fromkeys(('a', 'b', 'c')) #@
dict.fromkeys(['a', 'b', 'c']) #@
dict.fromkeys({'a', 'b', 'c'}) #@
"""
)
for node in from_others:
inferred = next(node.infer())
assert isinstance(inferred, nodes.Dict)
itered = inferred.itered()
assert all(isinstance(elem, nodes.Const) for elem in itered)
actual_values = [elem.value for elem in itered]
assert sorted(actual_values) == ["a", "b", "c"]
class TestFunctoolsPartial:
@staticmethod
def test_infer_partial() -> None:
ast_node = astroid.extract_node(
"""
from functools import partial
def test(a, b):
'''Docstring'''
return a + b
partial(test, 1)(3) #@
"""
)
assert isinstance(ast_node.func, nodes.Call)
inferred = ast_node.func.inferred()
assert len(inferred) == 1
partial = inferred[0]
assert isinstance(partial, objects.PartialFunction)
assert isinstance(partial.as_string(), str)
assert isinstance(partial.doc_node, nodes.Const)
assert partial.doc_node.value == "Docstring"
assert partial.lineno == 3
assert partial.col_offset == 0
def test_invalid_functools_partial_calls(self) -> None:
ast_nodes = astroid.extract_node(
"""
from functools import partial
from unknown import Unknown
def test(a, b, c):
return a + b + c
partial() #@
partial(test) #@
partial(func=test) #@
partial(some_func, a=1) #@
partial(Unknown, a=1) #@
partial(2, a=1) #@
partial(test, unknown=1) #@
"""
)
for node in ast_nodes:
inferred = next(node.infer())
assert isinstance(inferred, (nodes.FunctionDef, astroid.Instance))
assert inferred.qname() in {
"functools.partial",
"functools.partial.newfunc",
}
def test_inferred_partial_function_calls(self) -> None:
ast_nodes = astroid.extract_node(
"""
from functools import partial
def test(a, b):
return a + b
partial(test, 1)(3) #@
partial(test, b=4)(3) #@
partial(test, b=4)(a=3) #@
def other_test(a, b, *, c=1):
return (a + b) * c
partial(other_test, 1, 2)() #@
partial(other_test, 1, 2)(c=4) #@
partial(other_test, c=4)(1, 3) #@
partial(other_test, 4, c=4)(4) #@
partial(other_test, 4, c=4)(b=5) #@
test(1, 2) #@
partial(other_test, 1, 2)(c=3) #@
partial(test, b=4)(a=3) #@
"""
)
expected_values = [4, 7, 7, 3, 12, 16, 32, 36, 3, 9, 7]
for node, expected_value in zip(ast_nodes, expected_values):
inferred = next(node.infer())
assert isinstance(inferred, nodes.Const)
assert inferred.value == expected_value
def test_partial_assignment(self) -> None:
"""Make sure partials are not assigned to original scope."""
ast_nodes = astroid.extract_node(
"""
from functools import partial
def test(a, b): #@
return a + b
test2 = partial(test, 1)
test2 #@
def test3_scope(a):
test3 = partial(test, a)
test3 #@
"""
)
func1, func2, func3 = ast_nodes
assert func1.parent.scope() == func2.parent.scope()
assert func1.parent.scope() != func3.parent.scope()
partial_func3 = next(func3.infer())
# use scope of parent, so that it doesn't just refer to self
scope = partial_func3.parent.scope()
assert scope.name == "test3_scope", "parented by closure"
def test_partial_does_not_affect_scope(self) -> None:
"""Make sure partials are not automatically assigned."""
ast_nodes = astroid.extract_node(
"""
from functools import partial
def test(a, b):
return a + b
def scope():
test2 = partial(test, 1)
test2 #@
"""
)
test2 = next(ast_nodes.infer())
mod_scope = test2.root()
scope = test2.parent.scope()
assert set(mod_scope) == {"test", "scope", "partial"}
assert set(scope) == {"test2"}
def test_multiple_partial_args(self) -> None:
"Make sure partials remember locked-in args."
ast_node = astroid.extract_node(
"""
from functools import partial
def test(a, b, c, d, e=5):
return a + b + c + d + e
test1 = partial(test, 1)
test2 = partial(test1, 2)
test3 = partial(test2, 3)
test3(4, e=6) #@
"""
)
expected_args = [1, 2, 3, 4]
expected_keywords = {"e": 6}
call_site = astroid.arguments.CallSite.from_call(ast_node)
called_func = next(ast_node.func.infer())
called_args = called_func.filled_args + call_site.positional_arguments
called_keywords = {**called_func.filled_keywords, **call_site.keyword_arguments}
assert len(called_args) == len(expected_args)
assert [arg.value for arg in called_args] == expected_args
assert len(called_keywords) == len(expected_keywords)
for keyword, value in expected_keywords.items():
assert keyword in called_keywords
assert called_keywords[keyword].value == value
def test_http_client_brain() -> None:
node = astroid.extract_node(
"""
from http.client import OK
OK
"""
)
inferred = next(node.infer())
assert isinstance(inferred, astroid.Instance)
def test_http_status_brain() -> None:
node = astroid.extract_node(
"""
import http
http.HTTPStatus.CONTINUE.phrase
"""
)
inferred = next(node.infer())
# Cannot infer the exact value but the field is there.
assert inferred.value == ""
node = astroid.extract_node(
"""
import http
http.HTTPStatus(200).phrase
"""
)
inferred = next(node.infer())
assert isinstance(inferred, nodes.Const)
def test_http_status_brain_iterable() -> None:
"""Astroid inference of `http.HTTPStatus` is an iterable subclass of `enum.IntEnum`"""
node = astroid.extract_node(
"""
import http
http.HTTPStatus
"""
)
inferred = next(node.infer())
assert "enum.IntEnum" in [ancestor.qname() for ancestor in inferred.ancestors()]
assert inferred.getattr("__iter__")
def test_oserror_model() -> None:
node = astroid.extract_node(
"""
try:
1/0
except OSError as exc:
exc #@
"""
)
inferred = next(node.infer())
strerror = next(inferred.igetattr("strerror"))
assert isinstance(strerror, nodes.Const)
assert strerror.value == ""
@pytest.mark.skipif(PY313_PLUS, reason="Python >= 3.13 no longer has a crypt module")
def test_crypt_brain() -> None:
module = MANAGER.ast_from_module_name("crypt")
dynamic_attrs = [
"METHOD_SHA512",
"METHOD_SHA256",
"METHOD_BLOWFISH",
"METHOD_MD5",
"METHOD_CRYPT",
]
for attr in dynamic_attrs:
assert attr in module
@pytest.mark.parametrize(
"code,expected_class,expected_value",
[
("'hey'.encode()", nodes.Const, b""),
("b'hey'.decode()", nodes.Const, ""),
("'hey'.encode().decode()", nodes.Const, ""),
],
)
def test_str_and_bytes(code, expected_class, expected_value):
node = astroid.extract_node(code)
inferred = next(node.infer())
assert isinstance(inferred, expected_class)
assert inferred.value == expected_value
def test_no_recursionerror_on_self_referential_length_check() -> None:
"""
Regression test for https://github.com/pylint-dev/astroid/issues/777
This test should only raise an InferenceError and no RecursionError.
"""
with pytest.raises(InferenceError):
node = astroid.extract_node(
"""
class Crash:
def __len__(self) -> int:
return len(self)
len(Crash()) #@
"""
)
assert isinstance(node, nodes.NodeNG)
node.inferred()
def test_inference_on_outer_referential_length_check() -> None:
"""
Regression test for https://github.com/pylint-dev/pylint/issues/5244
See also https://github.com/pylint-dev/astroid/pull/1234
This test should succeed without any error.
"""
node = astroid.extract_node(
"""
class A:
def __len__(self) -> int:
return 42
class Crash:
def __len__(self) -> int:
a = A()
return len(a)
len(Crash()) #@
"""
)
inferred = node.inferred()
assert len(inferred) == 1
assert isinstance(inferred[0], nodes.Const)
assert inferred[0].value == 42
def test_no_attributeerror_on_self_referential_length_check() -> None:
"""
Regression test for https://github.com/pylint-dev/pylint/issues/5244
See also https://github.com/pylint-dev/astroid/pull/1234
This test should only raise an InferenceError and no AttributeError.
"""
with pytest.raises(InferenceError):
node = astroid.extract_node(
"""
class MyClass:
def some_func(self):
return lambda: 42
def __len__(self):
return len(self.some_func())
len(MyClass()) #@
"""
)
assert isinstance(node, nodes.NodeNG)
node.inferred()