-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/watchpoints #13248
base: main
Are you sure you want to change the base?
Feat/watchpoints #13248
Changes from all commits
59bc6cb
fa52c08
bb1bee3
9453cd8
700240d
54a3ee1
fcb9691
7ae5b5c
ec50c22
3a1f444
45e8f97
1a3d9ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
watchpoints.all |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
version = "0.2.5" | ||
upstream_repository = "https://github.com/gaogaotiantian/watchpoints" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from collections.abc import Callable | ||
from typing import Final | ||
from typing_extensions import LiteralString, Unpack | ||
|
||
from .watch import Watch | ||
|
||
__version__: Final[LiteralString] | ||
|
||
watch: Watch | ||
unwatch: Final[Callable[[Unpack[tuple[object, ...]]], None]] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import ast | ||
|
||
def ast_parse_node(node: ast.expr) -> ast.Module: ... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import ast | ||
from collections.abc import Iterable | ||
from types import FrameType | ||
|
||
def getline(frame: FrameType) -> str: ... | ||
def getargnodes(frame: FrameType) -> Iterable[tuple[ast.expr, str]]: ... |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,68 @@ | ||||||
import threading | ||||||
from _typeshed import SupportsWrite, TraceFunction | ||||||
from collections.abc import Callable | ||||||
from pdb import Pdb | ||||||
from types import FrameType | ||||||
from typing import Any, Literal, Protocol, TypeVar | ||||||
from typing_extensions import TypeAlias | ||||||
|
||||||
from .watch_element import WatchElement | ||||||
|
||||||
_T = TypeVar("_T") | ||||||
|
||||||
# Alias used for fields that must always be valid identifiers | ||||||
# A string `x` counts as a valid identifier if both the following are True | ||||||
# (1) `x.isidentifier()` evaluates to `True` | ||||||
# (2) `keyword.iskeyword(x)` evaluates to `False` | ||||||
_Identifier: TypeAlias = str | ||||||
|
||||||
class Watch: | ||||||
# User-defined callbacks passed to `__call__()` or `config()` set as instance variables have arguments of type `Any` to be | ||||||
# compatible with more precisely-annotated signatures. | ||||||
|
||||||
custom_printer: Callable[[Any], None] | None | ||||||
enable: bool | ||||||
file: str | SupportsWrite[str] | None | ||||||
pdb: Pdb | None | ||||||
pdb_enable: bool | ||||||
set_lock: threading.Lock | ||||||
stack_limit: int | None | ||||||
tracefunc_lock: threading.Lock | ||||||
tracefunc_stack: list[TraceFunction | None] | ||||||
watch_list: list[WatchElement] | ||||||
|
||||||
def __init__(self) -> None: ... | ||||||
def __call__( | ||||||
self, | ||||||
*args: object, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like these are unused, consider using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding of the library is that it inspects the expressions of positional-only arguments passed to Passing nothing will cause nothing to be watched, so I'm also tempted to enforce at least 1 argument to be passed here, but let me know if it's still desirable to use |
||||||
alias: str = ..., | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one and most of the others support None, since the runtime does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unlike in
Let me know if we'd still prefer |
||||||
callback: Callable[[FrameType, WatchElement, tuple[str, str, int | None]], None] = ..., | ||||||
cmp: Callable[[Any, Any], bool] = ..., # User-defined comparison callback; compares 2 arguments of any type | ||||||
copy: Callable[[_T], _T] = ..., | ||||||
# User-defined printing callback; writes a string representation of any object to a stream | ||||||
custom_printer: Callable[[Any], None] = ..., | ||||||
deepcopy: bool = False, | ||||||
file: str | SupportsWrite[str] = ..., | ||||||
stack_limit: int | None = 5, | ||||||
track: Literal["object", "variable"] = ..., | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Or does it also support other kinds of Sequences? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The documentation describes |
||||||
when: Callable[[Any], bool] = ..., # User-defined callback for conditional watchpoints | ||||||
) -> None: ... | ||||||
def config( | ||||||
self, | ||||||
*, | ||||||
callback: Callable[[FrameType, WatchElement, tuple[str, str, int | None]], None] = ..., | ||||||
pdb: Literal[True] = ..., | ||||||
file: str | SupportsWrite[str] = ..., | ||||||
stack_limit: int | None = 5, | ||||||
custom_printer: Callable[[Any], None] = ..., # User-defined printing callback | ||||||
) -> None: ... | ||||||
def install(self, func: _Identifier = "watch") -> None: ... | ||||||
def restore(self) -> None: ... | ||||||
def start_trace(self, frame: FrameType) -> None: ... | ||||||
def stop_trace(self, frame: FrameType) -> None: ... | ||||||
def tracefunc(self, frame: FrameType, event: str, arg: object) -> _TraceFunc: ... | ||||||
def uninstall(self, func: _Identifier = "watch") -> None: ... | ||||||
def unwatch(self, *args: object) -> None: ... | ||||||
|
||||||
class _TraceFunc(Protocol): | ||||||
def __call__(self, frame: FrameType, event: str, arg: object) -> _TraceFunc: ... | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these parameters be positional-only? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import ast | ||
from collections.abc import Callable, Iterable | ||
from types import FrameType | ||
from typing import Any, Literal, TypeVar | ||
from typing_extensions import TypeAlias | ||
|
||
from .watch_print import WatchPrint | ||
|
||
_T = TypeVar("_T") | ||
_TrackKind: TypeAlias = Literal["object", "variable"] | list[Literal["object", "variable"]] | ||
|
||
class WatchElement: | ||
# User-defined callbacks passed to `__init__` set as instance variables have arguments of type `Any` to be | ||
# compatible with more precisely-annotated signatures. These callbacks are passed from `watchpoints.watch.Watch`. | ||
|
||
alias: str | None | ||
attr: str | None | ||
cmp: Callable[[Any, Any], bool] | None | ||
copy: Callable[[Any], object] | None # User-defined copy callback | ||
default_alias: str | None | ||
deepcopy: bool | ||
exist: bool | ||
frame: FrameType | ||
localvar: str | None | ||
obj: Any | ||
parent: Any | ||
prev_obj: Any | ||
prev_obj_repr: str | ||
subscr: Any | ||
watch_print: WatchPrint | ||
when: Callable[[Any], bool] | None | ||
|
||
def __init__( | ||
self, | ||
frame: FrameType, | ||
node: ast.expr, | ||
*, | ||
alias: str | None = ..., | ||
callback: Callable[[FrameType, WatchElement, tuple[str, str, int | None]], None] | None = ..., | ||
cmp: Callable[[Any, Any], bool] | None = ..., # User-defined comparison callback | ||
copy: Callable[[_T], _T] | None = ..., | ||
deepcopy: bool = False, | ||
default_alias: str | None = ..., | ||
track: _TrackKind = ..., | ||
watch_print: WatchPrint = ..., | ||
when: Callable[[Any], bool] | None = ..., # User-defined callback for conditional watchpoints | ||
) -> None: ... | ||
def belong_to(self, lst: Iterable[object]) -> bool: ... | ||
def changed(self, frame: FrameType) -> tuple[bool, bool]: ... | ||
def obj_changed(self, other: object) -> bool: ... | ||
def same(self, other: object) -> bool: ... | ||
@property | ||
def track(self) -> _TrackKind: ... | ||
@track.setter | ||
def track(self, val: _TrackKind) -> None: ... | ||
def update(self) -> None: ... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from _typeshed import SupportsWrite | ||
from collections.abc import Callable | ||
from types import FrameType | ||
from typing import Any | ||
|
||
from .watch_element import WatchElement | ||
|
||
class WatchPrint: | ||
# User-defined callbacks passed to `__init__` set as instance variables have arguments of type `Any` to be | ||
# compatible with more precisely-annotated signatures. These callbacks are passed from `watchpoints.watch.Watch`. | ||
|
||
custom_printer: Callable[[Any], None] | None | ||
file: str | SupportsWrite[str] | None | ||
stack_limit: int | None | ||
|
||
def __init__( | ||
self, | ||
file: str | SupportsWrite[str] | None = ..., | ||
stack_limit: int | None = ..., | ||
custom_printer: Callable[[Any], None] | None = ..., # User-defined printing callback | ||
) -> None: ... | ||
def __call__(self, frame: FrameType, elem: WatchElement, exec_info: tuple[str, str, int | None]) -> None: ... | ||
def getsourceline(self, exec_info: tuple[str, str, int | None]) -> str: ... | ||
def printer(self, obj: object) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Several callback types this package have
Any
as argument type(s), which are assigned with user-defined callbacks. I avoided usingobject
here to be compatible with user-defined callbacks, which may annotate the parameters with more precise types.cmp
) callbacks: Accepts any 2 objects and compare their equalitycopy
: Makes a copy of any objectwhen
callbacks: Inspect/trace/watch an objectwhen
a condition is fulfilled; takes any object and outputs abool
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, but please add a comment to the code explaining this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in fcb9691