summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2023-04-22 21:03:04 +0200
committerGitHub <noreply@github.com>2023-04-22 21:03:04 +0200
commitd4529f5617e1b952c3fc167dd714efb090d72081 (patch)
treeb09df00cd83e5186d8f607017f02274e27f0950c
parent8805036d679f41ee54efd08294849dbe12db487d (diff)
downloadastroid-git-d4529f5617e1b952c3fc167dd714efb090d72081.tar.gz
Fix constructors of ``ClassDef`` (#2130)
-rw-r--r--ChangeLog1
-rw-r--r--astroid/bases.py2
-rw-r--r--astroid/brain/brain_argparse.py9
-rw-r--r--astroid/brain/brain_namedtuple_enum.py9
-rw-r--r--astroid/brain/brain_re.py2
-rw-r--r--astroid/brain/brain_regex.py2
-rw-r--r--astroid/brain/brain_typing.py8
-rw-r--r--astroid/nodes/scoped_nodes/mixin.py2
-rw-r--r--astroid/nodes/scoped_nodes/scoped_nodes.py124
-rw-r--r--astroid/raw_building.py45
-rw-r--r--tests/test_inference.py2
-rw-r--r--tests/test_manager.py21
-rw-r--r--tests/test_scoped_nodes.py20
13 files changed, 149 insertions, 98 deletions
diff --git a/ChangeLog b/ChangeLog
index c1488f0a..913a6be6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -28,6 +28,7 @@ Release date: TBA
- ``nodes.Await``
- ``nodes.BinOp``
- ``nodes.Call``
+ - ``nodes.ClassDef``
- ``nodes.Compare``
- ``nodes.Comprehension``
- ``nodes.Decorators``
diff --git a/astroid/bases.py b/astroid/bases.py
index 83e01a19..0b51d44c 100644
--- a/astroid/bases.py
+++ b/astroid/bases.py
@@ -603,6 +603,8 @@ class BoundMethod(UnboundMethod):
lineno=caller.lineno,
col_offset=caller.col_offset,
parent=caller,
+ end_lineno=caller.end_lineno,
+ end_col_offset=caller.end_col_offset,
)
empty = Pass()
cls.postinit(
diff --git a/astroid/brain/brain_argparse.py b/astroid/brain/brain_argparse.py
index 1e0a6e84..da6d5d20 100644
--- a/astroid/brain/brain_argparse.py
+++ b/astroid/brain/brain_argparse.py
@@ -16,7 +16,14 @@ def infer_namespace(node, context: InferenceContext | None = None):
# Cannot make sense of it.
raise UseInferenceDefault()
- class_node = nodes.ClassDef("Namespace")
+ class_node = nodes.ClassDef(
+ "Namespace",
+ lineno=node.lineno,
+ col_offset=node.col_offset,
+ parent=nodes.Unknown(),
+ end_lineno=node.end_lineno,
+ end_col_offset=node.end_col_offset,
+ )
# Set parent manually until ClassDef constructor fixed:
# https://github.com/pylint-dev/astroid/issues/1490
class_node.parent = node.parent
diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py
index 5fc50907..f5d5f595 100644
--- a/astroid/brain/brain_namedtuple_enum.py
+++ b/astroid/brain/brain_namedtuple_enum.py
@@ -155,7 +155,14 @@ def infer_func_form(
# we know it is a namedtuple anyway.
name = name or "Uninferable"
# we want to return a Class node instance with proper attributes set
- class_node = nodes.ClassDef(name)
+ class_node = nodes.ClassDef(
+ name,
+ lineno=node.lineno,
+ col_offset=node.col_offset,
+ end_lineno=node.end_lineno,
+ end_col_offset=node.end_col_offset,
+ parent=nodes.Unknown(),
+ )
# A typical ClassDef automatically adds its name to the parent scope,
# but doing so causes problems, so defer setting parent until after init
# see: https://github.com/pylint-dev/pylint/issues/5982
diff --git a/astroid/brain/brain_re.py b/astroid/brain/brain_re.py
index 1096e6d4..6214865d 100644
--- a/astroid/brain/brain_re.py
+++ b/astroid/brain/brain_re.py
@@ -84,6 +84,8 @@ def infer_pattern_match(node: nodes.Call, ctx: context.InferenceContext | None =
lineno=node.lineno,
col_offset=node.col_offset,
parent=node.parent,
+ end_lineno=node.end_lineno,
+ end_col_offset=node.end_col_offset,
)
if PY39_PLUS:
func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
diff --git a/astroid/brain/brain_regex.py b/astroid/brain/brain_regex.py
index 23fe804f..a3cca65b 100644
--- a/astroid/brain/brain_regex.py
+++ b/astroid/brain/brain_regex.py
@@ -83,6 +83,8 @@ def infer_pattern_match(node: nodes.Call, ctx: context.InferenceContext | None =
lineno=node.lineno,
col_offset=node.col_offset,
parent=node.parent,
+ end_lineno=node.end_lineno,
+ end_col_offset=node.end_col_offset,
)
if PY39_PLUS:
func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
diff --git a/astroid/brain/brain_typing.py b/astroid/brain/brain_typing.py
index 0dc4afae..b50211db 100644
--- a/astroid/brain/brain_typing.py
+++ b/astroid/brain/brain_typing.py
@@ -215,6 +215,8 @@ def infer_typedDict( # pylint: disable=invalid-name
lineno=node.lineno,
col_offset=node.col_offset,
parent=node.parent,
+ end_lineno=node.end_lineno,
+ end_col_offset=node.end_col_offset,
)
class_def.postinit(bases=[extract_node("dict")], body=[], decorators=None)
func_to_add = _extract_single_node("dict")
@@ -295,6 +297,8 @@ def infer_typing_alias(
lineno=assign_name.lineno,
col_offset=assign_name.col_offset,
parent=node.parent,
+ end_lineno=assign_name.end_lineno,
+ end_col_offset=assign_name.end_col_offset,
)
if isinstance(res, ClassDef):
# Only add `res` as base if it's a `ClassDef`
@@ -372,6 +376,10 @@ def infer_special_alias(
class_def = ClassDef(
name=assign_name.name,
parent=node.parent,
+ lineno=assign_name.lineno,
+ col_offset=assign_name.col_offset,
+ end_lineno=assign_name.end_lineno,
+ end_col_offset=assign_name.end_col_offset,
)
class_def.postinit(bases=[res], body=[], decorators=None)
func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
diff --git a/astroid/nodes/scoped_nodes/mixin.py b/astroid/nodes/scoped_nodes/mixin.py
index 57a66f8d..f9f2220b 100644
--- a/astroid/nodes/scoped_nodes/mixin.py
+++ b/astroid/nodes/scoped_nodes/mixin.py
@@ -39,7 +39,7 @@ class LocalsDictNodeNG(node_classes.LookupMixIn):
:rtype: str
"""
# pylint: disable=no-member; github.com/pylint-dev/astroid/issues/278
- if self.parent is None:
+ if self.parent is None or isinstance(self.parent, node_classes.Unknown):
return self.name
return f"{self.parent.frame(future=True).qname()}.{self.name}"
diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py
index e5a1523e..cf72e1e0 100644
--- a/astroid/nodes/scoped_nodes/scoped_nodes.py
+++ b/astroid/nodes/scoped_nodes/scoped_nodes.py
@@ -1735,9 +1735,15 @@ class FunctionDef(
]
except StopIteration as e:
raise InferenceError(node=caller.args[1:], context=context) from e
- new_class = ClassDef(name="temporary_class")
+ new_class = ClassDef(
+ name="temporary_class",
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=self,
+ )
new_class.hide = True
- new_class.parent = self
new_class.postinit(
bases=[
base
@@ -1988,72 +1994,42 @@ class ClassDef(
)
_other_fields = ("name", "doc", "is_dataclass", "position")
_other_other_fields = ("locals", "_newstyle")
- _newstyle = None
+ _newstyle: bool | None = None
- @decorators_mod.deprecate_arguments(doc="Use the postinit arg 'doc_node' instead")
def __init__(
self,
- name=None,
- doc: str | None = None,
- lineno=None,
- col_offset=None,
- parent=None,
+ name: str,
+ lineno: int,
+ col_offset: int,
+ parent: NodeNG,
*,
- end_lineno=None,
- end_col_offset=None,
- ):
- """
- :param name: The name of the class.
- :type name: str or None
-
- :param doc: The class docstring.
-
- :param lineno: The line that this node appears on in the source code.
- :type lineno: int or None
-
- :param col_offset: The column that this node appears on in the
- source code.
- :type col_offset: int or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
-
- :param end_lineno: The last line this node appears on in the source code.
- :type end_lineno: Optional[int]
-
- :param end_col_offset: The end column this node appears on in the
- source code. Note: This is after the last symbol.
- :type end_col_offset: Optional[int]
- """
+ end_lineno: int | None,
+ end_col_offset: int | None,
+ ) -> None:
self.instance_attrs = {}
self.locals = {}
"""A map of the name of a local variable to the node defining it."""
- self.keywords = []
+ self.keywords: list[node_classes.Keyword] = []
"""The keywords given to the class definition.
This is usually for :pep:`3115` style metaclass declaration.
-
- :type: list(Keyword) or None
"""
self.bases: list[NodeNG] = []
"""What the class inherits from."""
- self.body = []
- """The contents of the class body.
-
- :type: list(NodeNG)
- """
+ self.body: list[NodeNG] = []
+ """The contents of the class body."""
self.name = name
- """The name of the class.
+ """The name of the class."""
- :type name: str or None
- """
+ self.decorators: node_classes.Decorators | None = None
+ """The decorators that are applied to this class."""
- self._doc = doc
- """The class docstring."""
+ self._doc = None
+ """DEPRECATED: The class docstring."""
self.doc_node: Const | None = None
"""The doc node associated with this node."""
@@ -2068,7 +2044,7 @@ class ClassDef(
end_col_offset=end_col_offset,
parent=parent,
)
- if parent is not None:
+ if parent and not isinstance(parent, Unknown):
parent.frame(future=True).set_local(name, self)
for local_name, node in self.implicit_locals():
@@ -2114,48 +2090,23 @@ class ClassDef(
# pylint: disable=redefined-outer-name
def postinit(
self,
- bases,
- body,
- decorators,
- newstyle=None,
+ bases: list[NodeNG],
+ body: list[NodeNG],
+ decorators: node_classes.Decorators | None,
+ newstyle: bool | None = None,
metaclass: NodeNG | None = None,
- keywords=None,
+ keywords: list[node_classes.Keyword] | None = None,
*,
position: Position | None = None,
doc_node: Const | None = None,
- ):
- """Do some setup after initialisation.
-
- :param bases: What the class inherits from.
- :type bases: list(NodeNG)
-
- :param body: The contents of the class body.
- :type body: list(NodeNG)
-
- :param decorators: The decorators that are applied to this class.
- :type decorators: Decorators or None
-
- :param newstyle: Whether this is a new style class or not.
- :type newstyle: bool or None
-
- :param metaclass: The metaclass of this class.
-
- :param keywords: The keywords given to the class definition.
- :type keywords: list(Keyword) or None
-
- :param position: Position of class keyword and name.
-
- :param doc_node: The doc node associated with this node.
- """
+ ) -> None:
if keywords is not None:
self.keywords = keywords
self.bases = bases
self.body = body
self.decorators = decorators
- if newstyle is not None:
- self._newstyle = newstyle
- if metaclass is not None:
- self._metaclass = metaclass
+ self._newstyle = newstyle
+ self._metaclass = metaclass
self.position = position
self.doc_node = doc_node
if doc_node:
@@ -2274,7 +2225,14 @@ class ClassDef(
else:
return util.Uninferable
- result = ClassDef(name)
+ result = ClassDef(
+ name,
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=Unknown(),
+ )
# Get the bases of the class.
try:
diff --git a/astroid/raw_building.py b/astroid/raw_building.py
index f0c4a319..45eeb10b 100644
--- a/astroid/raw_building.py
+++ b/astroid/raw_building.py
@@ -100,7 +100,14 @@ def build_class(
name: str, basenames: Iterable[str] = (), doc: str | None = None
) -> nodes.ClassDef:
"""Create and initialize an astroid ClassDef node."""
- node = nodes.ClassDef(name)
+ node = nodes.ClassDef(
+ name,
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=nodes.Unknown(),
+ )
node.postinit(
bases=[
nodes.Name(
@@ -615,7 +622,14 @@ def _astroid_bootstrapping() -> None:
# Set the builtin module as parent for some builtins.
nodes.Const._proxied = property(_set_proxied)
- _GeneratorType = nodes.ClassDef(types.GeneratorType.__name__)
+ _GeneratorType = nodes.ClassDef(
+ types.GeneratorType.__name__,
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=nodes.Unknown(),
+ )
_GeneratorType.parent = astroid_builtin
generator_doc_node = (
nodes.Const(value=types.GeneratorType.__doc__)
@@ -632,7 +646,14 @@ def _astroid_bootstrapping() -> None:
builder.object_build(bases.Generator._proxied, types.GeneratorType)
if hasattr(types, "AsyncGeneratorType"):
- _AsyncGeneratorType = nodes.ClassDef(types.AsyncGeneratorType.__name__)
+ _AsyncGeneratorType = nodes.ClassDef(
+ types.AsyncGeneratorType.__name__,
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=nodes.Unknown(),
+ )
_AsyncGeneratorType.parent = astroid_builtin
async_generator_doc_node = (
nodes.Const(value=types.AsyncGeneratorType.__doc__)
@@ -649,7 +670,14 @@ def _astroid_bootstrapping() -> None:
builder.object_build(bases.AsyncGenerator._proxied, types.AsyncGeneratorType)
if hasattr(types, "UnionType"):
- _UnionTypeType = nodes.ClassDef(types.UnionType.__name__)
+ _UnionTypeType = nodes.ClassDef(
+ types.UnionType.__name__,
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=nodes.Unknown(),
+ )
_UnionTypeType.parent = astroid_builtin
union_type_doc_node = (
nodes.Const(value=types.UnionType.__doc__)
@@ -679,7 +707,14 @@ def _astroid_bootstrapping() -> None:
)
for _type in builtin_types:
if _type.__name__ not in astroid_builtin:
- klass = nodes.ClassDef(_type.__name__)
+ klass = nodes.ClassDef(
+ _type.__name__,
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=nodes.Unknown(),
+ )
klass.parent = astroid_builtin
klass.postinit(
bases=[],
diff --git a/tests/test_inference.py b/tests/test_inference.py
index 7fcbb10c..b81a3e17 100644
--- a/tests/test_inference.py
+++ b/tests/test_inference.py
@@ -1264,7 +1264,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
assert i0.pytype() == "types.UnionType"
assert i0.display_type() == "UnionType"
assert str(i0) == "UnionType(UnionType)"
- assert repr(i0) == f"<UnionType(UnionType) l.None at 0x{id(i0)}>"
+ assert repr(i0) == f"<UnionType(UnionType) l.0 at 0x{id(i0)}>"
i1 = ast_nodes[1].inferred()[0]
assert isinstance(i1, UnionType)
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 4f76d09c..ef0c0699 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -25,7 +25,7 @@ from astroid.exceptions import (
from astroid.interpreter._import import util
from astroid.modutils import EXT_LIB_DIRS, module_in_path
from astroid.nodes import Const
-from astroid.nodes.scoped_nodes import ClassDef
+from astroid.nodes.scoped_nodes import ClassDef, Module
from . import resources
@@ -420,12 +420,27 @@ class ClearCacheTest(unittest.TestCase):
baseline_cache_infos = [lru.cache_info() for lru in lrus]
# Generate some hits and misses
- ClassDef().lookup("garbage")
+ module = Module("", file="", path=[], package=False)
+ ClassDef(
+ "",
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=module,
+ ).lookup("garbage")
module_in_path("unittest", "garbage_path")
util.is_namespace("unittest")
astroid.interpreter.objectmodel.ObjectModel().attributes()
with pytest.raises(AttributeInferenceError):
- ClassDef().getattr("garbage")
+ ClassDef(
+ "",
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=module,
+ ).getattr("garbage")
# Did the hits or misses actually happen?
incremented_cache_infos = [lru.cache_info() for lru in lrus]
diff --git a/tests/test_scoped_nodes.py b/tests/test_scoped_nodes.py
index 3795df56..3e8256dd 100644
--- a/tests/test_scoped_nodes.py
+++ b/tests/test_scoped_nodes.py
@@ -2912,7 +2912,14 @@ def test_deprecation_of_doc_attribute() -> None:
node_module = nodes.Module(name="MyModule")
node_module.postinit(body=[], doc_node=doc_node)
assert node_module.doc_node == doc_node
- node_class = nodes.ClassDef(name="MyClass")
+ node_class = nodes.ClassDef(
+ name="MyClass",
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=nodes.Unknown(),
+ )
node_class.postinit(bases=[], body=[], decorators=[], doc_node=doc_node)
assert node_class.doc_node == doc_node
node_func = nodes.FunctionDef(
@@ -2946,7 +2953,14 @@ def test_deprecation_of_doc_attribute() -> None:
doc_node = nodes.Const("Docstring")
with pytest.warns(DeprecationWarning) as records:
node_module = nodes.Module(name="MyModule", doc="Docstring")
- node_class = nodes.ClassDef(name="MyClass", doc="Docstring")
+ node_class = nodes.ClassDef(
+ name="MyClass",
+ lineno=0,
+ col_offset=0,
+ end_lineno=0,
+ end_col_offset=0,
+ parent=nodes.Unknown(),
+ )
node_func = nodes.FunctionDef(
name="MyFunction",
lineno=0,
@@ -2955,4 +2969,4 @@ def test_deprecation_of_doc_attribute() -> None:
end_lineno=0,
end_col_offset=0,
)
- assert len(records) == 2
+ assert len(records) == 1