Skip to content

Commit

Permalink
python-helper: update deprecated code
Browse files Browse the repository at this point in the history
Currently python-helper.py imports the imp module, but this module has
been deprecated since version 3.4 and removed in version 3.12.

Refactorize the code to handle Python versioning by moving the code at
the start of the file.

Use one block for python>=3.0, declaring the report_trace_real function.
Use a second block for python>=3.5, declaring the new_module function.

Add tests for python2 support.

Fix incorrect code using comparison to `None` instead of `cond is None`.
Reported by ruff check.

Format the code with ruff format, with line-length = 100.
  • Loading branch information
perillo committed Mar 9, 2024
1 parent 1d036f7 commit 011ad7f
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 28 deletions.
74 changes: 46 additions & 28 deletions src/engines/python-helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,52 @@
# Based on http://pymotw.com/2/sys/tracing.html, "Tracing a program as it runs"
# and http://hg.python.org/cpython/file/2.7/Lib/trace.py

import imp
import sys
import os
import struct

fifo_file = None
report_trace_real = None

try:
# In Py 2.x, the builtins were in __builtin__
BUILTINS = sys.modules['__builtin__']
except KeyError:
if sys.version_info >= (3, 0):
# In Py 3.x, they're in builtins
BUILTINS = sys.modules['builtins']
BUILTINS = sys.modules["builtins"]

def report_trace_real(file, line):
size = len(file) + 1 + 8 + 4 + 4
data = struct.pack(
">QLL%dsb" % len(file),
0x6D6574616C6C6775,
size,
int(line),
bytes(file, "utf-8"),
0,
)

fifo_file.write(data)
else:
# In Py 2.x, the builtins were in __builtin__
BUILTINS = sys.modules["__builtin__"]

def report_trace_real(file, line):
size = len(file) + 1 + 8 + 4 + 4
data = struct.pack(">QLL%dsb" % len(file), 0x6D6574616C6C6775, size, int(line), file, 0)

fifo_file.write(data)

def report_trace3(file, line):
size = len(file) + 1 + 8 + 4 + 4
data = struct.pack(">QLL%dsb" % len(file), 0x6d6574616c6c6775, size, int(line), bytes(file, 'utf-8'), 0)

fifo_file.write(data)
if sys.version_info >= (3, 5):
# The imp module has been deprecated since version 3.4 and removed in
# version 3.12.
import types

def report_trace2(file, line):
size = len(file) + 1 + 8 + 4 + 4
data = struct.pack(">QLL%dsb" % len(file), 0x6d6574616c6c6775, size, int(line), file, 0)
def new_module(name):
return types.ModuleType(name)
else:
import imp

def new_module(name):
return imp.new_module(name)

fifo_file.write(data)

def report_trace(file, line):
try:
Expand All @@ -40,17 +59,19 @@ def report_trace(file, line):
# Ignore errors
pass


def trace_lines(frame, event, arg):
if event != 'line':
if event != "line":
return
co = frame.f_code
func_name = co.co_name
line_no = frame.f_lineno
filename = co.co_filename
report_trace(filename, line_no)


def trace_calls(frame, event, arg):
if event != 'call':
if event != "call":
return
co = frame.f_code
func_name = co.co_name
Expand All @@ -59,27 +80,24 @@ def trace_calls(frame, event, arg):
report_trace(filename, line_no)
return trace_lines


def runctx(cmd, globals):
sys.settrace(trace_calls)
try:
exec(cmd, globals)
finally:
sys.settrace(None)

if __name__ == "__main__":
if sys.version_info >= (3, 0):
report_trace_real = report_trace3
else:
report_trace_real = report_trace2

if __name__ == "__main__":
prog_argv = sys.argv[1:]

sys.argv = prog_argv
progname = prog_argv[0]
sys.path[0] = os.path.split(progname)[0]

fifo_path = os.getenv("KCOV_PYTHON_PIPE_PATH")
if fifo_path == None:
if fifo_path is None:
sys.stderr.write("the KCOV_PYTHON_PIPE_PATH environment variable is not set")
sys.exit(127)
try:
Expand All @@ -88,9 +106,9 @@ def runctx(cmd, globals):
sys.stderr.write("Can't open fifo file")
sys.exit(127)

main_mod = imp.new_module('__main__')
old_main_mod = sys.modules['__main__']
sys.modules['__main__'] = main_mod
main_mod = new_module("__main__")
old_main_mod = sys.modules["__main__"]
sys.modules["__main__"] = main_mod
main_mod.__file__ = progname
main_mod.__package__ = None
main_mod.__builtins__ = BUILTINS
Expand All @@ -99,11 +117,11 @@ def runctx(cmd, globals):
with open(progname) as fp:
# try to emulate __main__ namespace as much as possible

code = compile(fp.read(), progname, 'exec')
code = compile(fp.read(), progname, "exec")

runctx(code, main_mod.__dict__)
except IOError:
sys.stderr.write("Cannot run file %r" % (sys.argv[0]))
sys.exit(127)
finally:
sys.modules['__main__'] = old_main_mod
sys.modules["__main__"] = old_main_mod
11 changes: 11 additions & 0 deletions tests/tools/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def runTest(self):

assert b"Cannot find Python parser 'python3'" not in o

class python2_can_set_legal_parser(testbase.KcovTestCase):
def runTest(self):
self.setUp()
rv,o = self.do(testbase.kcov + " --python-parser=python2 " + testbase.outbase + "/kcov " + testbase.sources + "/tests/python/main 5")

assert b"Cannot find Python parser 'python2'" not in o

class python_issue_368_can_handle_symlink_target(testbase.KcovTestCase):
def runTest(self):
self.setUp()
Expand Down Expand Up @@ -84,6 +91,10 @@ class python3_coverage(PythonBase):
def runTest(self):
self.doTest("--python-parser=python3")

class python2_coverage(PythonBase):
def runTest(self):
self.doTest("--python-parser=python2")

class python_tricky_single_line_string_assignment(testbase.KcovTestCase):
def runTest(self):
self.setUp()
Expand Down

0 comments on commit 011ad7f

Please sign in to comment.