Repository navigation
Show types, whole objects and Set Value in the debugger's Variables - #123
Merged
Merged
Conversation
Every Variables row now leads with its type, and a container's with its
size, as PyCharm shows them ({Point} Point(3, 4), {list: 3} [...]). Copy
Value still copies the value alone: the adapter answers VS Code's
clipboard evaluate with the badge taken off.
An object opens on everything dir() lists - its own attributes, its
class's, properties and __slots__ - with dunders folded under special
variables and methods under function variables. dir() runs only when a
row is opened, so listing a page costs what it did before. An instance
whose data all comes from its class opens too; functions and empty
objects stay leaves. Modules now land under module variables, and a
getter that runs past the time budget reads <getter took too long>
instead of holding up the pause.
Set Value changes a local, a global, an attribute, a list item or a dict
entry. The value is a Python expression that can use the notebook's (or
__main__'s) globals, with a frame's locals on top. A local is written
back into the paused frame with GsProcess>>_frameAt:tempAt:put:, so the
run continues with it. Statements and empty values are refused, and a
method's receiver, a tuple's items, group rows and saved stacks are
read-only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
jgfoster
approved these changes
Oct 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
When a run stops at
breakpoint(), the Variables pane is where a Python developer looks first, and GemDB's showed less than VS Code's Python debugger or PyCharm:__dict__, with a class burying its methods among__module__,__dict__and the other dunders;This is a first step toward making a paused program's objects easy to inspect, and it's confined to the Variables pane. Evaluating expressions (the Debug Console, watches, hover) and an inspector view of its own are later steps.
What
Types. Every row leads with its type, and a container's with its size, as PyCharm shows them:
{Point} Point(3, 4),{int} 7,{list: 3} [...];{list: 150}for a container too big to show.Copy Value copies the value alone. The adapter declares
supportsClipboardContext, and VS Code's Copy Value then asks it, throughevaluatein theclipboardcontext, what to copy. The adapter answers by taking the badge off the row's text, without a query. Every otherevaluateis still refused.Whole objects. An object opens on everything
dir()lists:__slots__;Along with it:
<getter took too long>, and no further getters run, so one slow property can't hold up the pause.Set Value. Right-click a local, a global, an attribute, a list item or a dict entry. The value is a Python expression:
__main__'s for a.pyrun;A local is written back into the paused frame, so the run continues with it. This uses
GsProcess>>_frameAt:tempAt:put:, because_frameAt:tempNamed:put:fails on a local a block closes over, which lives in a VariableContext.What can't be changed:
n = 5, and an empty value, are refused. Unwrapped, Grail would runn = 5as a module and store None.self, or whatever the def calls it), a tuple's items, group rows and a saved stack's values are marked read-only, so VS Code doesn't offer Set Value on them.Worth checking
__dict__load, plus one__slots__load when there's no__dict__. The fulldir()runs only when a row is opened.dir()on every row, and a page of 500 objects took about 980 ms. This version measured 13–14 ms in the gem against 13 formain, timed inside the query.dir().indexOfIdentical:against___framesAndLevelsOfSuspendedProcess___'s levels). The listing reads__dir__and___pyAttrLoad___:.docs/grail.mdrecords what was measured, including two Grail facts the tests now depend on:clsnever appears in Locals;selfis hidden by___isInternalTempName___.GemNativeCodeEnabled=0; CI runs 2. The Grail locals test notes the same gap for reading, and CI is where this gets its first native-code run.Testing
breakpoint.test.ts, against a real 4.0.0.a4 stone:try:;debugger.test.ts.npm test(769 passed),breakpoint.test.ts(22 passed), lint, format check and both typechecks all pass.cli.test.tsandrepl.test.tsfail on my machine with or without this change. They driveout/gemdb-shell.jsas a process.🤖 Generated with Claude Code