Repository: pytest-dev/pytest
File: src/_pytest/_code/code.py (Python)

Code before:
    def __init__(
        self,
        rawentry: TracebackType,
        repr_style: Literal["short", "long"] | None = None,
    ) -> None:
        self._rawentry: Final = rawentry
        self._repr_style: Final = repr_style

    def with_repr_style(
        self, repr_style: Literal["short", "long"] | None
    ) -> TracebackEntry:
        return TracebackEntry(self._rawentry, repr_style)

    @property
    def lineno(self) -> int:
        return self._rawentry.tb_lineno - 1

    def get_python_framesummary(self) -> traceback.FrameSummary:
        # Python's built-in traceback module implements all the nitty gritty
        # details to get column numbers of out frames.
        stack_summary = traceback.extract_tb(self._rawentry, limit=1)
        return stack_summary[0]

    @property
    def end_lineno(self) -> int:
        return self.get_python_framesummary().end_lineno - 1

    @property
    def colno(self) -> int | None:
        """Starting byte offset of the expression in the traceback entry."""
        return self.get_python_framesummary().colno

    @property
    def end_colno(self) -> int | None:
        """Ending byte offset of the expression in the traceback entry."""
        return self.get_python_framesummary().end_colno

    @property
    def frame(self) -> Frame:
        return Frame(self._rawentry.tb_frame)

    @property
    def relline(self) -> int:
        return self.lineno - self.frame.code.firstlineno

    def __repr__(self) -> str:
        return f"<TracebackEntry {self.frame.code.path}:{self.lineno+1}>"

    @property
    def statement(self) -> Source:
        """_pytest._code.Source object for the current statement."""

Review comment (suggestion):
Here, and in `end_colno` below, we'll need to handle the case where on earlier Python versions these attributes don't exist.  I'd consider defining the properties differently based on version, so that we can easily remove the backwards-compatibility code once we're ready to drop support for Python 3.10

Code after:
    def __init__(
        self,
        rawentry: TracebackType,
        repr_style: Literal["short", "long"] | None = None,
    ) -> None:
        self._rawentry: Final = rawentry
        self._repr_style: Final = repr_style

    def with_repr_style(
        self, repr_style: Literal["short", "long"] | None
    ) -> TracebackEntry:
        return TracebackEntry(self._rawentry, repr_style)

    @property
    def lineno(self) -> int:
        return self._rawentry.tb_lineno - 1

    def get_python_framesummary(self) -> traceback.FrameSummary:
        # Python's built-in traceback module implements all the nitty gritty
        # details to get column numbers of out frames.
        stack_summary = traceback.extract_tb(self._rawentry, limit=1)
        return stack_summary[0]

    # Column and end line numbers introduced in python 3.11
    if sys.version_info < (3, 11):

        @property
        def end_lineno_relative(self) -> int | None:
            return None

        @property
        def colno(self) -> int | None:
            return None

        @property
        def end_colno(self) -> int | None:
            return None
    else:

        @property
        def end_lineno_relative(self) -> int | None:
            frame_summary = self.get_python_framesummary()
            if frame_summary.end_lineno is None:  # pragma: no cover
                return None
            return frame_summary.end_lineno - 1 - self.frame.code.firstlineno

        @property
        def colno(self) -> int | None:
            """Starting byte offset of the expression in the traceback entry."""
            return self.get_python_framesummary().colno