Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions astroid/brain/brain_attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
Without this hook pylint reports unsupported-assignment-operation
for attrs classes
"""
from astroid import nodes
from astroid.brain.helpers import is_class_var
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import AnnAssign, Assign, AssignName, Call, Unknown
from astroid.nodes.scoped_nodes import ClassDef
from astroid.util import safe_infer

ATTRIB_NAMES = frozenset(
Expand Down Expand Up @@ -51,7 +50,7 @@ def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES) -> bool:
if not node.decorators:
return False
for decorator_attribute in node.decorators.nodes:
if isinstance(decorator_attribute, Call): # decorator with arguments
if isinstance(decorator_attribute, nodes.Call): # decorator with arguments
decorator_attribute = decorator_attribute.func
if decorator_attribute.as_string() in decorator_names:
return True
Expand All @@ -62,26 +61,26 @@ def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES) -> bool:
return False


def attr_attributes_transform(node: ClassDef) -> None:
def attr_attributes_transform(node: nodes.ClassDef) -> None:
"""Given that the ClassNode has an attr decorator,
rewrite class attributes as instance attributes
"""
# Astroid can't infer this attribute properly
# Prevents https://github.com/pylint-dev/pylint/issues/1884
node.locals["__attrs_attrs__"] = [Unknown(parent=node)]
node.locals["__attrs_attrs__"] = [nodes.Unknown(parent=node)]

use_bare_annotations = is_decorated_with_attrs(node, NEW_ATTRS_NAMES)
for cdef_body_node in node.body:
if not isinstance(cdef_body_node, (Assign, AnnAssign)):
if not isinstance(cdef_body_node, (nodes.Assign, nodes.AnnAssign)):
continue
if isinstance(cdef_body_node.value, Call):
if isinstance(cdef_body_node.value, nodes.Call):
if cdef_body_node.value.func.as_string() not in ATTRIB_NAMES:
continue
elif not use_bare_annotations:
continue

# Skip attributes that are explicitly annotated as class variables
if isinstance(cdef_body_node, AnnAssign) and is_class_var(
if isinstance(cdef_body_node, nodes.AnnAssign) and is_class_var(
cdef_body_node.annotation
):
continue
Expand All @@ -92,12 +91,12 @@ def attr_attributes_transform(node: ClassDef) -> None:
else [cdef_body_node.target]
)
for target in targets:
rhs_node = Unknown(
rhs_node = nodes.Unknown(
lineno=cdef_body_node.lineno,
col_offset=cdef_body_node.col_offset,
parent=cdef_body_node,
)
if isinstance(target, AssignName):
if isinstance(target, nodes.AssignName):
# Could be a subscript if the code analysed is
# i = Optional[str] = ""
# See https://github.com/pylint-dev/pylint/issues/4439
Expand All @@ -107,5 +106,5 @@ def attr_attributes_transform(node: ClassDef) -> None:

def register(manager: AstroidManager) -> None:
manager.register_transform(
ClassDef, attr_attributes_transform, is_decorated_with_attrs
nodes.ClassDef, attr_attributes_transform, is_decorated_with_attrs
)
28 changes: 16 additions & 12 deletions astroid/brain/brain_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
from astroid.inference_tip import inference_tip
from astroid.interpreter import objectmodel
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import AssignName, Attribute, Call, Name
from astroid.nodes.scoped_nodes import FunctionDef
from astroid.typing import InferenceResult, SuccessfulInferenceResult
from astroid.util import UninferableBase, safe_infer

Expand Down Expand Up @@ -92,7 +90,7 @@ def _functools_partial_inference(
raise UseInferenceDefault from exc
if isinstance(inferred_wrapped_function, UninferableBase):
raise UseInferenceDefault("Cannot infer the wrapped function")
if not isinstance(inferred_wrapped_function, FunctionDef):
if not isinstance(inferred_wrapped_function, nodes.FunctionDef):
raise UseInferenceDefault("The wrapped function is not a function")

# Determine if the passed keywords into the callsite are supported
Expand All @@ -106,7 +104,9 @@ def _functools_partial_inference(
inferred_wrapped_function.args.kwonlyargs or (),
)
parameter_names = {
param.name for param in function_parameters if isinstance(param, AssignName)
param.name
for param in function_parameters
if isinstance(param, nodes.AssignName)
}
if set(call.keyword_arguments) - parameter_names:
raise UseInferenceDefault("wrapped function received unknown parameters")
Expand Down Expand Up @@ -135,23 +135,25 @@ def _looks_like_lru_cache(node) -> bool:
if not node.decorators:
return False
for decorator in node.decorators.nodes:
if not isinstance(decorator, (Attribute, Call)):
if not isinstance(decorator, (nodes.Attribute, nodes.Call)):
continue
if _looks_like_functools_member(decorator, "lru_cache"):
return True
return False


def _looks_like_functools_member(node: Attribute | Call, member: str) -> bool:
def _looks_like_functools_member(
node: nodes.Attribute | nodes.Call, member: str
) -> bool:
"""Check if the given Call node is the wanted member of functools."""
if isinstance(node, Attribute):
if isinstance(node, nodes.Attribute):
return node.attrname == member
if isinstance(node.func, Name):
if isinstance(node.func, nodes.Name):
return node.func.name == member
if isinstance(node.func, Attribute):
if isinstance(node.func, nodes.Attribute):
return (
node.func.attrname == member
and isinstance(node.func.expr, Name)
and isinstance(node.func.expr, nodes.Name)
and node.func.expr.name == "functools"
)
return False
Expand All @@ -161,10 +163,12 @@ def _looks_like_functools_member(node: Attribute | Call, member: str) -> bool:


def register(manager: AstroidManager) -> None:
manager.register_transform(FunctionDef, _transform_lru_cache, _looks_like_lru_cache)
manager.register_transform(
nodes.FunctionDef, _transform_lru_cache, _looks_like_lru_cache
)

manager.register_transform(
Call,
nodes.Call,
inference_tip(_functools_partial_inference),
_looks_like_partial,
)
3 changes: 1 addition & 2 deletions astroid/brain/brain_namedtuple_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from textwrap import dedent
from typing import Final

import astroid
from astroid import arguments, bases, nodes, util
from astroid.builder import AstroidBuilder, _extract_single_node, extract_node
from astroid.context import InferenceContext
Expand Down Expand Up @@ -651,7 +650,7 @@ def _get_namedtuple_fields(node: nodes.Call) -> str:
return field_names


def _is_enum_subclass(cls: astroid.ClassDef) -> bool:
def _is_enum_subclass(cls: nodes.ClassDef) -> bool:
"""Return whether cls is a subclass of an Enum."""
return cls.is_subtype_of("enum.Enum")

Expand Down
4 changes: 2 additions & 2 deletions astroid/brain/brain_numpy_core_function_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

import functools

from astroid import nodes
from astroid.brain.brain_numpy_utils import (
attribute_name_looks_like_numpy_member,
infer_numpy_attribute,
)
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute

METHODS_TO_BE_INFERRED = {
"linspace": """def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0):
Expand All @@ -26,7 +26,7 @@

def register(manager: AstroidManager) -> None:
manager.register_transform(
Attribute,
nodes.Attribute,
inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)),
functools.partial(
attribute_name_looks_like_numpy_member,
Expand Down
5 changes: 2 additions & 3 deletions astroid/brain/brain_numpy_core_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from astroid.builder import parse
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute, Name


def numpy_core_multiarray_transform() -> nodes.Module:
Expand Down Expand Up @@ -96,12 +95,12 @@ def register(manager: AstroidManager) -> None:
method_names = frozenset(METHODS_TO_BE_INFERRED.keys())

manager.register_transform(
Attribute,
nodes.Attribute,
inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)),
functools.partial(attribute_name_looks_like_numpy_member, method_names),
)
manager.register_transform(
Name,
nodes.Name,
inference_tip(functools.partial(infer_numpy_name, METHODS_TO_BE_INFERRED)),
functools.partial(member_name_looks_like_numpy_member, method_names),
)
3 changes: 1 addition & 2 deletions astroid/brain/brain_numpy_core_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from astroid.builder import parse
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute


def numpy_core_numeric_transform() -> nodes.Module:
Expand All @@ -42,7 +41,7 @@ def register(manager: AstroidManager) -> None:
)

manager.register_transform(
Attribute,
nodes.Attribute,
inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)),
functools.partial(
attribute_name_looks_like_numpy_member,
Expand Down
6 changes: 3 additions & 3 deletions astroid/brain/brain_numpy_ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"""Astroid hooks for numpy ndarray class."""
from __future__ import annotations

from astroid import nodes
from astroid.brain.brain_numpy_utils import numpy_supports_type_hints
from astroid.builder import extract_node
from astroid.context import InferenceContext
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute


def infer_numpy_ndarray(node, context: InferenceContext | None = None):
Expand Down Expand Up @@ -151,13 +151,13 @@ def __class_getitem__(cls, value):
return node.infer(context=context)


def _looks_like_numpy_ndarray(node: Attribute) -> bool:
def _looks_like_numpy_ndarray(node: nodes.Attribute) -> bool:
return node.attrname == "ndarray"


def register(manager: AstroidManager) -> None:
manager.register_transform(
Attribute,
nodes.Attribute,
inference_tip(infer_numpy_ndarray),
_looks_like_numpy_ndarray,
)
18 changes: 10 additions & 8 deletions astroid/brain/brain_numpy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

from __future__ import annotations

from astroid import nodes
from astroid.builder import extract_node
from astroid.context import InferenceContext
from astroid.nodes.node_classes import Attribute, Import, Name

# Class subscript is available in numpy starting with version 1.20.0
NUMPY_VERSION_TYPE_HINTS_SUPPORT = ("1", "20", "0")
Expand All @@ -35,20 +35,22 @@ def _get_numpy_version() -> tuple[str, str, str]:


def infer_numpy_name(
sources: dict[str, str], node: Name, context: InferenceContext | None = None
sources: dict[str, str], node: nodes.Name, context: InferenceContext | None = None
):
extracted_node = extract_node(sources[node.name])
return extracted_node.infer(context=context)


def infer_numpy_attribute(
sources: dict[str, str], node: Attribute, context: InferenceContext | None = None
sources: dict[str, str],
node: nodes.Attribute,
context: InferenceContext | None = None,
):
extracted_node = extract_node(sources[node.attrname])
return extracted_node.infer(context=context)


def _is_a_numpy_module(node: Name) -> bool:
def _is_a_numpy_module(node: nodes.Name) -> bool:
"""
Returns True if the node is a representation of a numpy module.

Expand All @@ -62,7 +64,7 @@ def _is_a_numpy_module(node: Name) -> bool:
"""
module_nickname = node.name
potential_import_target = [
x for x in node.lookup(module_nickname)[1] if isinstance(x, Import)
x for x in node.lookup(module_nickname)[1] if isinstance(x, nodes.Import)
]
return any(
("numpy", module_nickname) in target.names or ("numpy", None) in target.names
Expand All @@ -71,7 +73,7 @@ def _is_a_numpy_module(node: Name) -> bool:


def member_name_looks_like_numpy_member(
member_names: frozenset[str], node: Name
member_names: frozenset[str], node: nodes.Name
) -> bool:
"""
Returns True if the Name node's name matches a member name from numpy
Expand All @@ -80,13 +82,13 @@ def member_name_looks_like_numpy_member(


def attribute_name_looks_like_numpy_member(
member_names: frozenset[str], node: Attribute
member_names: frozenset[str], node: nodes.Attribute
) -> bool:
"""
Returns True if the Attribute node's name matches a member name from numpy
"""
return (
node.attrname in member_names
and isinstance(node.expr, Name)
and isinstance(node.expr, nodes.Name)
and _is_a_numpy_module(node.expr)
)
25 changes: 8 additions & 17 deletions astroid/brain/brain_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,18 @@

import random

from astroid import nodes
from astroid.context import InferenceContext
from astroid.exceptions import UseInferenceDefault
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import (
Attribute,
Call,
Const,
EvaluatedObject,
List,
Name,
Set,
Tuple,
)
from astroid.util import safe_infer

ACCEPTED_ITERABLES_FOR_SAMPLE = (List, Set, Tuple)
ACCEPTED_ITERABLES_FOR_SAMPLE = (nodes.List, nodes.Set, nodes.Tuple)


def _clone_node_with_lineno(node, parent, lineno):
if isinstance(node, EvaluatedObject):
if isinstance(node, nodes.EvaluatedObject):
node = node.original
cls = node.__class__
other_fields = node._other_fields
Expand All @@ -52,7 +43,7 @@ def infer_random_sample(node, context: InferenceContext | None = None):
raise UseInferenceDefault

inferred_length = safe_infer(node.args[1], context=context)
if not isinstance(inferred_length, Const):
if not isinstance(inferred_length, nodes.Const):
raise UseInferenceDefault
if not isinstance(inferred_length.value, int):
raise UseInferenceDefault
Expand All @@ -73,7 +64,7 @@ def infer_random_sample(node, context: InferenceContext | None = None):
except ValueError as exc:
raise UseInferenceDefault from exc

new_node = List(
new_node = nodes.List(
lineno=node.lineno,
col_offset=node.col_offset,
parent=node.scope(),
Expand All @@ -90,14 +81,14 @@ def infer_random_sample(node, context: InferenceContext | None = None):

def _looks_like_random_sample(node) -> bool:
func = node.func
if isinstance(func, Attribute):
if isinstance(func, nodes.Attribute):
return func.attrname == "sample"
if isinstance(func, Name):
if isinstance(func, nodes.Name):
return func.name == "sample"
return False


def register(manager: AstroidManager) -> None:
manager.register_transform(
Call, inference_tip(infer_random_sample), _looks_like_random_sample
nodes.Call, inference_tip(infer_random_sample), _looks_like_random_sample
)
Loading
Loading