Skip to content

Commit

Permalink
- implemented PyPy 3.10's line table format;
Browse files Browse the repository at this point in the history
- adjusted tests for PyPy 3.10;
  • Loading branch information
jaltmayerpizzorno committed Nov 4, 2024
1 parent 5b7889b commit ffade9d
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 8 deletions.
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ all:
python3 -m pip install -e .

# obtained with e.g. "brew install [email protected]"
HOMEBREW_PYTHON=/opt/homebrew/opt/python@
test:
- rm -f .coverage
@ for V in 3.8 3.9 3.10 3.11 3.12 3.13; do \
P=$$(command -v ${HOMEBREW_PYTHON}$$V/bin/python3 || command -v python$$V); \
@ for V in python3.8 python3.9 pypy3.9 python3.10 pypy3.10 python3.11 python3.12 python3.13; do \
P=$$(command -v $$V); \
if ! [ -z $$P ]; then \
$$P --version; \
$$P -O -m pip uninstall -y slipcover; \
Expand Down
41 changes: 40 additions & 1 deletion src/slipcover/bytecode.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,14 @@ def read_varint_be(it):
return value


def append_pypy_varuint(data: List[int], n: int) -> None:
"""Appends a pypy3.10-style encoded variable length unsigned integer to 'data'"""
while n > 0x7f:
data.append(0x80|(n&0x7f))
n = n >> 7
data.append(n)


class ExceptionTableEntry:
"""Represents an entry from Python 3.11+'s exception table."""

Expand Down Expand Up @@ -282,6 +290,9 @@ def __init__(self, start : int, end : int, number : int):
self.end = end
self.number = number

def __repr__(self):
return f"LineEntry(start={self.start},end={self.end},number={self.number})"

# FIXME tests missing
def adjust(self, insert_offset : int, insert_length : int) -> None:
"""Adjusts this line after a code insertion."""
Expand Down Expand Up @@ -346,7 +357,7 @@ def make_lnotab(firstlineno : int, lines : List[LineEntry]) -> bytes:
if sys.version_info >= (3,9) and sys.version_info < (3,11): # 3.10
@staticmethod
def make_linetable(firstlineno : int, lines : List[LineEntry]) -> bytes:
"""Generates the line number table used by Python 3.10 to map offsets to line numbers."""
"""Generates the line number table used by CPython 3.10 to map offsets to line numbers."""

linetable = []

Expand Down Expand Up @@ -390,6 +401,34 @@ def make_linetable(firstlineno : int, lines : List[LineEntry]) -> bytes:

return bytes(linetable)

if sys.implementation.name == 'pypy' and sys.version_info >= (3,9) and sys.version_info < (3,11): #type: ignore
@staticmethod
def make_linetable(firstlineno: int, lines: List[LineEntry]) -> bytes:
"""Generates the positions table used by PyPy 3.10 map offsets to line numbers."""

linetable = []

prev_end = 0

for l in lines:
while prev_end < l.start:
linetable.append(0)
prev_end += 2

if l.number is None:
while prev_end < l.end:
linetable.append(0)
prev_end += 2
else:
lineno_delta = l.number - firstlineno + 1

while prev_end < l.end:
append_pypy_varuint(linetable, lineno_delta)
linetable.append(0)
prev_end += 2

return bytes(linetable)


if sys.version_info >= (3,11):
@staticmethod
Expand Down
7 changes: 4 additions & 3 deletions tests/test_bytecode.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import sys

PYTHON_IMPL = sys.implementation.name
PYTHON_VERSION = sys.version_info[0:2]

if PYTHON_VERSION >= (3,12):
Expand Down Expand Up @@ -439,7 +440,7 @@ def test_make_lnotab():
0, -30] == unpack_bytes(lnotab)


@pytest.mark.skipif(PYTHON_VERSION != (3,10), reason="N/A: 3.10 specific")
@pytest.mark.skipif(PYTHON_IMPL != 'cpython' or PYTHON_VERSION != (3,10), reason="N/A: cpython 3.10 specific")
def test_make_linetable_310():
lines = [bc.LineEntry(0, 6, 1),
bc.LineEntry(6, 50, 2),
Expand Down Expand Up @@ -525,13 +526,13 @@ def test_make_lines_and_compare(code):
lines = bc.LineEntry.from_code(code)

dis.dis(code)
print(code.co_firstlineno)
print(f"{code.co_firstlineno=}")
print([str(l) for l in lines])

if PYTHON_VERSION < (3,10):
my_lnotab = bc.LineEntry.make_lnotab(code.co_firstlineno, lines)
assert list(code.co_lnotab) == list(my_lnotab)
elif PYTHON_VERSION == (3,10):
elif PYTHON_IMPL == 'cpython' and PYTHON_VERSION == (3,10):
my_linetable = bc.LineEntry.make_linetable(code.co_firstlineno, lines)
assert list(code.co_linetable) == list(my_linetable)
else:
Expand Down
4 changes: 3 additions & 1 deletion tests/test_instrumentation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import sys

PYTHON_IMPL = sys.implementation.name
PYTHON_VERSION = sys.version_info[0:2]

if PYTHON_VERSION >= (3,12):
Expand Down Expand Up @@ -192,7 +193,8 @@ def foo(n): #1
assert X == foo(42)


@pytest.mark.skipif(PYTHON_VERSION != (3,10), reason="N/A: only 3.10 seems to generate code like this")
@pytest.mark.skipif(PYTHON_IMPL != 'cpython' or PYTHON_VERSION != (3,10),
reason="N/A: only cpython 3.10 seems to generate code like this")
def test_instrument_code_before_first_line():
sci = sc.Slipcover()

Expand Down

0 comments on commit ffade9d

Please sign in to comment.