diff options
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | astroid/bases.py | 2 | ||||
-rw-r--r-- | astroid/brain/brain_argparse.py | 9 | ||||
-rw-r--r-- | astroid/brain/brain_namedtuple_enum.py | 9 | ||||
-rw-r--r-- | astroid/brain/brain_re.py | 2 | ||||
-rw-r--r-- | astroid/brain/brain_regex.py | 2 | ||||
-rw-r--r-- | astroid/brain/brain_typing.py | 8 | ||||
-rw-r--r-- | astroid/nodes/scoped_nodes/mixin.py | 2 | ||||
-rw-r--r-- | astroid/nodes/scoped_nodes/scoped_nodes.py | 124 | ||||
-rw-r--r-- | astroid/raw_building.py | 45 | ||||
-rw-r--r-- | tests/test_inference.py | 2 | ||||
-rw-r--r-- | tests/test_manager.py | 21 | ||||
-rw-r--r-- | tests/test_scoped_nodes.py | 20 |
13 files changed, 149 insertions, 98 deletions
@@ -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 |