Run gdb stubtests (#11644)

The gdb package is only available inside gdb and cannot be installed externally through e.g. pip.
Run the stubtest inside gdb.
This commit is contained in:
peace-maker
2024-03-26 10:57:35 +01:00
committed by GitHub
parent 9965725935
commit d78ce5906b
3 changed files with 213 additions and 3 deletions

View File

@@ -0,0 +1,119 @@
# TODO: Update types in stub
# This list includes everything to allow initial stubtests to run in gdb's environment
gdb.BP_NONE
gdb.Breakpoint.stop
gdb.BreakpointEvent
gdb.ClearObjFilesEvent
gdb.Command.complete
gdb.Command.invoke
gdb.ConnectionEvent
gdb.ContinueEvent
gdb.DUMMY_FRAME
gdb.Event
gdb.EventRegistry
gdb.ExitedEvent
gdb.FRAME_UNWIND_FIRST_ERROR
gdb.FinishBreakpoint.out_of_scope
gdb.Function.invoke
gdb.GdbExitingEvent
gdb.GdbSetPythonDirectory
gdb.HOST_CONFIG
gdb.Inferior.connection
gdb.Inferior.thread_from_thread_handle
gdb.InferiorCallPostEvent
gdb.InferiorCallPreEvent
gdb.InferiorDeletedEvent
gdb.InferiorThread.details
gdb.Instruction
gdb.LazyString
gdb.LineTable.__next__
gdb.LineTable.is_valid
gdb.LineTable.source_lines
gdb.LineTable.source_lnes
gdb.LineTableIterator
gdb.MICommand.invoke
gdb.Membuf
gdb.MemoryChangedEvent
gdb.NewInferiorEvent
gdb.NewObjFileEvent
gdb.NewThreadEvent
gdb.Objfile.frame_unwinders
gdb.Objfile.lookup_static_method
gdb.Objfile.lookup_static_symbol
gdb.Objfile.xmethods
gdb.Parameter.get_set_string
gdb.Parameter.get_show_string
gdb.Progspace.frame_unwinders
gdb.Progspace.xmethods
gdb.Record
gdb.RecordFunctionSegment
gdb.RecordGap
gdb.RecordInstruction
gdb.RegisterChangedEvent
gdb.RegisterDescriptor
gdb.RegisterDescriptorIterator
gdb.RegisterGroup
gdb.RegisterGroupsIterator
gdb.RemoteTargetConnection
gdb.SYMBOL_FUNCTIONS_DOMAIN
gdb.SYMBOL_TYPES_DOMAIN
gdb.SYMBOL_VARIABLES_DOMAIN
gdb.SignalEvent
gdb.StopEvent
gdb.TARGET_CONFIG
gdb.ThreadEvent
gdb.TuiWindow
gdb.Type.__contains__
gdb.Type.get
gdb.Type.has_key
gdb.Type.is_scalar
gdb.Type.is_signed
gdb.Type.items
gdb.Type.iteritems
gdb.Type.iterkeys
gdb.Type.itervalues
gdb.Type.keys
gdb.Type.values
gdb.TypeIterator
gdb.Unwinder
gdb.Value.rvalue_reference_value
gdb.frame_filters
gdb.frame_unwinders
gdb.packages
gdb.prompt_hook
gdb.type_printers
gdb.xmethods
gdb.events
gdb.printing.RegexpCollectionPrettyPrinter.RegexpSubprinter
gdb.printing.add_builtin_pretty_printer
gdb.prompt.prompt_help
gdb.prompt.prompt_substitutions
gdb.types.TypePrinter.instantiate
gdb.unwinder.Unwinder
gdb.xmethod.SimpleXMethodMatcher
gdb.xmethod.XMethodMatcher.match
gdb.xmethod.XMethodWorker.__call__
gdb.xmethod.XMethodWorker.get_arg_types
gdb.xmethod.XMethodWorker.get_result_type
gdb.FrameDecorator
gdb.FrameIterator
gdb.command
gdb.command.explore
gdb.command.frame_filters
gdb.command.pretty_printers
gdb.command.prompt
gdb.command.type_printers
gdb.command.unwinders
gdb.command.xmethods
gdb.frames
gdb.function
gdb.function.as_string
gdb.function.caller_is
gdb.function.strfns
gdb.printer
gdb.printer.bound_registers
gdb.printing.basestring
gdb.printing.long
gdb.xmethod.basestring
gdb.xmethod.long
gdb.styling

View File

@@ -11,6 +11,5 @@ extra_description = """\
"""
[tool.stubtest]
# Since the "gdb" Python package is available only inside GDB, it is not
# possible to install it through pip, so stub tests cannot install it.
skip = true
platforms = ["linux"]
apt_dependencies = ["gdb"]

View File

@@ -73,6 +73,12 @@ def run_stubtest(
# TODO: Maybe find a way to cache these in CI
dists_to_install = [dist_req, get_mypy_req()]
dists_to_install.extend(requirements.external_pkgs) # Internal requirements are added to MYPYPATH
# Since the "gdb" Python package is available only inside GDB, it is not
# possible to install it through pip, so stub tests cannot install it.
if dist_name == "gdb":
dists_to_install[:] = dists_to_install[1:]
pip_cmd = [pip_exe, "install", *dists_to_install]
try:
subprocess.run(pip_cmd, check=True, capture_output=True)
@@ -118,6 +124,10 @@ def run_stubtest(
if not setup_uwsgi_stubtest_command(dist, venv_dir, stubtest_cmd):
return False
if dist_name == "gdb":
if not setup_gdb_stubtest_command(venv_dir, stubtest_cmd):
return False
try:
subprocess.run(stubtest_cmd, env=stubtest_env, check=True, capture_output=True)
except subprocess.CalledProcessError as e:
@@ -153,6 +163,88 @@ def run_stubtest(
return True
def setup_gdb_stubtest_command(venv_dir: Path, stubtest_cmd: list[str]) -> bool:
"""
Use wrapper scripts to run stubtest inside gdb.
The wrapper script is used to pass the arguments to the gdb script.
"""
if sys.platform == "win32":
print_error("gdb is not supported on Windows")
return False
try:
gdb_version = subprocess.check_output(["gdb", "--version"], text=True, stderr=subprocess.STDOUT)
except FileNotFoundError:
print_error("gdb is not installed")
return False
if "Python scripting is not supported in this copy of GDB" in gdb_version:
print_error("Python scripting is not supported in this copy of GDB")
return False
gdb_script = venv_dir / "gdb_stubtest.py"
wrapper_script = venv_dir / "gdb_wrapper.py"
gdb_script_contents = dedent(
f"""
import json
import os
import site
import sys
import traceback
from glob import glob
# Add the venv site-packages to sys.path. gdb doesn't use the virtual environment.
# Taken from https://github.com/pwndbg/pwndbg/blob/83d8d95b576b749e888f533ce927ad5a77fb957b/gdbinit.py#L37
site_pkgs_path = glob(os.path.join({str(venv_dir)!r}, "lib/*/site-packages"))[0]
site.addsitedir(site_pkgs_path)
exit_code = 1
try:
# gdb wraps stdout and stderr without a .fileno
# colored output in mypy tries to access .fileno()
sys.stdout.fileno = sys.__stdout__.fileno
sys.stderr.fileno = sys.__stderr__.fileno
from mypy.stubtest import main
sys.argv = json.loads(os.environ.get("STUBTEST_ARGS"))
exit_code = main()
except Exception:
traceback.print_exc()
finally:
gdb.execute(f"quit {{exit_code}}")
"""
)
gdb_script.write_text(gdb_script_contents)
wrapper_script_contents = dedent(
f"""
import json
import os
import subprocess
import sys
stubtest_env = os.environ | {{"STUBTEST_ARGS": json.dumps(sys.argv)}}
gdb_cmd = [
"gdb",
"--quiet",
"--nx",
"--batch",
"--command",
{str(gdb_script)!r},
]
r = subprocess.run(gdb_cmd, env=stubtest_env)
sys.exit(r.returncode)
"""
)
wrapper_script.write_text(wrapper_script_contents)
# replace "-m mypy.stubtest" in stubtest_cmd with the path to our wrapper script
assert stubtest_cmd[1:3] == ["-m", "mypy.stubtest"]
stubtest_cmd[1:3] = [str(wrapper_script)]
return True
def setup_uwsgi_stubtest_command(dist: Path, venv_dir: Path, stubtest_cmd: list[str]) -> bool:
"""Perform some black magic in order to run stubtest inside uWSGI.