Skip to content

Commit

Permalink
Fixes for gdb stubs (python#13169)
Browse files Browse the repository at this point in the history
* gdb: Clarify a comment
* gdb: Fix gdb.unwinder.Unwinder.__call__ argument. It takes a gdb.PendingFrame, not a gdb.Frame.
* gdb: Unwinders may implement a proto without subclassing gdb.unwinder.Unwinder
* gdb: Fix Breakpoint.__init__

 1. `line` should be `int|str`, not just `int` (IDK what a string means,
    but that it can be a string is clear if you read
    py-breakpoint.c:bppy_init().
 2. `type` argument should be able to be passed to the "location" form,
    not just the "spec" form, even if
    https://sourceware.org/gdb/current/onlinedocs/gdb.html/Breakpoints-In-Python.html
    neglects to mention it (don't worry, I'll be submitting a patch to fix
    the doc soon).
 3. Fix the positional argument order (based on GDB's sources, it isn't
    really documented)
 4. Use more `@overloads` to enforce that at least 1 of `function`,
    `label`, or `line` are given in the location form.
  • Loading branch information
LukeShu authored Dec 3, 2024
1 parent fcf30cf commit 18d27d7
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 13 deletions.
2 changes: 1 addition & 1 deletion stubs/gdb/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
version = "15.0.*"
# This is the official web portal for GDB,
# This is the official web portal for the GDB Git repo,
# see https://sourceware.org/gdb/current/ for other ways of obtaining the source code.
upstream_repository = "https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=tree"
extra_description = """\
Expand Down
135 changes: 126 additions & 9 deletions stubs/gdb/gdb/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import threading
from _typeshed import Incomplete
from collections.abc import Callable, Iterator, Mapping, Sequence
from contextlib import AbstractContextManager
from typing import Any, Final, Generic, Literal, Protocol, TypeVar, final, overload
from typing import Any, Final, Generic, Literal, Protocol, TypeVar, final, overload, type_check_only
from typing_extensions import TypeAlias, deprecated

import gdb.FrameDecorator
import gdb.types
import gdb.unwinder
import gdb.xmethod

# The following submodules are automatically imported
Expand Down Expand Up @@ -289,7 +288,15 @@ class PendingFrame:
class UnwindInfo:
def add_saved_register(self, reg: str | RegisterDescriptor | int, value: Value, /) -> None: ...

frame_unwinders: list[gdb.unwinder.Unwinder]
@type_check_only
class _Unwinder(Protocol):
@property
def name(self) -> str: ...
enabled: bool

def __call__(self, pending_frame: PendingFrame) -> UnwindInfo | None: ...

frame_unwinders: list[_Unwinder]

# Inferiors

Expand Down Expand Up @@ -468,7 +475,7 @@ class Progspace:
pretty_printers: list[_PrettyPrinterLookupFunction]
type_printers: list[gdb.types._TypePrinter]
frame_filters: dict[str, _FrameFilter]
frame_unwinders: list[gdb.unwinder.Unwinder]
frame_unwinders: list[_Unwinder]
missing_debug_handlers: Incomplete

def block_for_pc(self, pc: int, /) -> Block | None: ...
Expand All @@ -493,7 +500,7 @@ class Objfile:
pretty_printers: list[_PrettyPrinterLookupFunction]
type_printers: list[gdb.types._TypePrinter]
frame_filters: dict[str, _FrameFilter]
frame_unwinders: list[gdb.unwinder.Unwinder]
frame_unwinders: list[_Unwinder]
is_file: bool

def is_valid(self) -> bool: ...
Expand Down Expand Up @@ -664,21 +671,131 @@ class LineTable:
# Breakpoints

class Breakpoint:

# The where="spec" form of __init__(). See py-breakpoints.c:bppy_init():keywords for the positional order.
@overload
def __init__(
self,
# where
spec: str,
# options
type: int = ...,
wp_class: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...

# The where="location" form of __init__(). A watchpoint (`type=BP_WATCHPOINT`) cannot be created with this form.
#
# We exclude the `wp_class` (watchpoint class) option here, even though py-breakpoints.c accepts it. It doesn't make sense
# unless type==BP_WATCHPOINT, and is silently ignored in those cases; allowing it in those cases is likely an oversight, not
# an intentional allowance.
#
# We repeat this 7 times because the type system doesn't have simple a way for us to say "at least one of `function`, `label`,
# or `line`", so we must repeat it for each combination of the 3.
#
# The len=3 combination.
@overload
def __init__(
self,
*,
# where
source: str = ...,
function: str,
label: str,
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
# The 3 len=2 combinations.
@overload
def __init__(
self,
*,
source: str = ...,
# where
label: str,
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self,
*,
source: str = ...,
# where
function: str,
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self, spec: str, type: int = ..., wp_class: int = ..., internal: bool = ..., temporary: bool = ..., qualified: bool = ...
self,
*,
source: str = ...,
# where
function: str,
label: str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
# The 3 len=1 combinations.
@overload
def __init__(
self,
*,
source: str = ...,
function: str = ...,
label: str = ...,
line: int = ...,
# where
function: str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self,
*,
source: str = ...,
# where
label: str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...
@overload
def __init__(
self,
*,
source: str = ...,
# where
line: int | str,
# options
type: int = ...,
internal: bool = ...,
temporary: bool = ...,
qualified: bool = ...,
) -> None: ...

# Methods.
def stop(self) -> bool: ...
def is_valid(self) -> bool: ...
def delete(self) -> None: ...
Expand Down
5 changes: 2 additions & 3 deletions stubs/gdb/gdb/unwinder.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import gdb
from gdb import Frame, UnwindInfo

class FrameId:
def __init__(self, sp: gdb.Value | int, pc: gdb.Value | int, special: gdb.Value | int | None = None) -> None: ...
Expand All @@ -16,6 +15,6 @@ class Unwinder:
enabled: bool

def __init__(self, name: str) -> None: ...
def __call__(self, pending_frame: Frame) -> UnwindInfo | None: ...
def __call__(self, pending_frame: gdb.PendingFrame) -> gdb.UnwindInfo | None: ...

def register_unwinder(locus: gdb.Objfile | gdb.Progspace | None, unwinder: Unwinder, replace: bool = ...) -> None: ...
def register_unwinder(locus: gdb.Objfile | gdb.Progspace | None, unwinder: gdb._Unwinder, replace: bool = ...) -> None: ...

0 comments on commit 18d27d7

Please sign in to comment.