diff --git a/ChangeLog b/ChangeLog index 9e80772bd3..d7c430e130 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,8 @@ Release date: TBA * Fix issues with ``typing_extensions.TypeVar``. +* Fix ``ClassDef.fromlino`` for PyPy 3.8 (v7.3.11) if class is wrapped by a decorator. + What's New in astroid 2.13.3? ============================= diff --git a/astroid/const.py b/astroid/const.py index cf91ab737e..52360cf970 100644 --- a/astroid/const.py +++ b/astroid/const.py @@ -18,6 +18,9 @@ IS_PYPY = sys.implementation.name == "pypy" IS_JYTHON = sys.implementation.name == "jython" +# pylint: disable-next=no-member +PYPY_7_3_11_PLUS = IS_PYPY and sys.pypy_version_info >= (7, 3, 11) # type: ignore[attr-defined] + class Context(enum.Enum): Load = 1 diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index 42cf64377d..411fe8b6f1 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -22,7 +22,7 @@ from astroid import bases from astroid import decorators as decorators_mod from astroid import util -from astroid.const import IS_PYPY, PY38, PY38_PLUS, PY39_PLUS +from astroid.const import IS_PYPY, PY38, PY38_PLUS, PY39_PLUS, PYPY_7_3_11_PLUS from astroid.context import ( CallContext, InferenceContext, @@ -2139,9 +2139,10 @@ def _newstyle_impl(self, context: InferenceContext | None = None): @cached_property def fromlineno(self) -> int | None: """The first line that this node appears on in the source code.""" - if not PY38_PLUS or PY38 and IS_PYPY: + if not PY38_PLUS or IS_PYPY and PY38 and not PYPY_7_3_11_PLUS: # For Python < 3.8 the lineno is the line number of the first decorator. # We want the class statement lineno. Similar to 'FunctionDef.fromlineno' + # PyPy (3.8): Fixed with version v7.3.11 lineno = self.lineno if self.decorators is not None: lineno += sum( diff --git a/tests/unittest_builder.py b/tests/unittest_builder.py index cd1659668a..6c0c5fa49e 100644 --- a/tests/unittest_builder.py +++ b/tests/unittest_builder.py @@ -19,7 +19,7 @@ import pytest from astroid import Instance, builder, nodes, test_utils, util -from astroid.const import IS_PYPY, PY38, PY38_PLUS, PY39_PLUS +from astroid.const import IS_PYPY, PY38, PY38_PLUS, PY39_PLUS, PYPY_7_3_11_PLUS from astroid.exceptions import ( AstroidBuildingError, AstroidSyntaxError, @@ -130,18 +130,18 @@ def function( def test_decorated_class_lineno() -> None: code = textwrap.dedent( """ - class A: + class A: # L2 ... @decorator - class B: + class B: # L6 ... @deco1 @deco2( var=42 ) - class C: + class C: # L13 ... """ ) @@ -155,23 +155,15 @@ class C: b = ast_module.body[1] assert isinstance(b, nodes.ClassDef) - if PY38 and IS_PYPY: - # Not perfect, but best we can do for PyPy 3.8 - assert b.fromlineno == 7 - else: - assert b.fromlineno == 6 + assert b.fromlineno == 6 assert b.tolineno == 7 c = ast_module.body[2] assert isinstance(c, nodes.ClassDef) - if not PY38_PLUS: - # Not perfect, but best we can do for Python 3.7 + if not PY38_PLUS or IS_PYPY and PY38 and not PYPY_7_3_11_PLUS: + # Not perfect, but best we can do for Python 3.7 and PyPy 3.8 (< v7.3.11). # Can't detect closing bracket on new line. assert c.fromlineno == 12 - elif PY38 and IS_PYPY: - # Not perfect, but best we can do for PyPy 3.8 - # Can't detect closing bracket on new line. - assert c.fromlineno == 16 else: assert c.fromlineno == 13 assert c.tolineno == 14 diff --git a/tests/unittest_nodes_position.py b/tests/unittest_nodes_position.py index d49fe9fa58..9a637657b6 100644 --- a/tests/unittest_nodes_position.py +++ b/tests/unittest_nodes_position.py @@ -7,7 +7,6 @@ import textwrap from astroid import builder, nodes -from astroid.const import IS_PYPY, PY38 class TestNodePosition: @@ -65,11 +64,9 @@ class F: #@ assert isinstance(e, nodes.ClassDef) assert e.position == (13, 0, 13, 7) - if not PY38 or not IS_PYPY: - # The new (2022-12) version of pypy 3.8 broke this - f = ast_nodes[5] - assert isinstance(f, nodes.ClassDef) - assert f.position == (18, 0, 18, 7) + f = ast_nodes[5] + assert isinstance(f, nodes.ClassDef) + assert f.position == (18, 0, 18, 7) @staticmethod def test_position_function() -> None: