Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **Variables shows each value's type, and opens objects whole.** Every row starts with its type,
and a container's with its size too, as PyCharm shows them: `{Point} Point(3, 4)`,
`{list: 3} […]`, `{list: 150}` for one too big to show. **Copy Value** copies the value alone,
without the type. An object with attributes, its own or its class's, now opens on everything
`dir()` lists: its own attributes, its class's, its
properties (read through their getters) and its `__slots__`, which before did not open at all.
Its methods are folded under **function variables** and its dunders under
**special variables**, as in VS Code's Python debugger, so a class no longer buries its own
methods under `__module__`, `__dict__` and the rest. A getter that runs past the time limit reads
`<getter took too long>`, and the getters after it are not run. Modules now land under
**module variables**; before, they were listed with the data.
- **Set Value in Variables.** Right-click a row while paused and choose **Set Value** to change a
local, a global, an attribute, a list item or a dict entry. The value is a Python expression,
and on any row it can use the notebook's globals, or a `.py` file's (`Point(1, 2)`, `g + 1`). In
Locals it can use the frame's locals too (`n + 1`), and a local wins over a global of the same
name. A statement such as `n = 5` is
refused rather than stored as None. A local is written into the paused frame, so the run
continues with it. What Python refuses is refused with Python's reason, such as a property with
no setter. A method's receiver (`self`, or whatever the def calls it), a tuple's items, group
rows and a saved stack's values cannot be changed, and VS Code does not offer Set Value on them.

## [1.6.0] - 2026-10-07

The engine is unchanged at GemStone 4.0.0.a4, but start with a fresh database: delete
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,13 @@ database, select that kernel from the kernel picker at the top right.
- `breakpoint()` pauses the cell and opens VS Code's debugger on it, with no setup: the Call Stack
shows the Python frames, the paused line is highlighted, and Variables shows each frame's locals
and the notebook's globals, expandable into attributes, items and entries, with classes and
functions folded into their own rows. **Continue** resumes the cell where it paused; **Stop**
functions folded into their own rows. Each value starts with its type, and a container's with its
size too (`{Point} Point(3, 4)`, `{list: 3} […]`); **Copy Value** copies the value without them.
An object with attributes, its own or its class's, opens on everything it has: its own attributes, its class's, its properties and slots,
with its methods under **function variables** and its dunders under **special variables**. Right-click a row and choose **Set Value** to change
a local, a global, an attribute, a list item or a dict entry. The value you type is a Python
expression, and the run continues with it. It can use the notebook's globals, or a `.py` file's,
on any row, and on a Locals row the frame's locals too. **Continue** resumes the cell where it paused; **Stop**
ends it, along with any cells queued after it. Stepping is not supported yet, and neither are red
dots in a cell (they work in `.py` files; see above). Right-click a Variables row and choose **Add to Persisted Objects…** to keep that object: it
goes in `gemdb.root` under the key you give (one is suggested) and is written at the notebook's
Expand Down
13 changes: 12 additions & 1 deletion docs/grail.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,18 @@ reads locals with `PyFrame ___pyLocalsFromFrameContentsList___:` (which merges
a Python frame's Smalltalk frames and adds `self`), in the same walk as the
stack so a frame cannot be paired with another's locals, and keeps every
object it may expand in a per-pause registry in `SessionTemps`, dropped before
the evaluation resumes (`pauseVariables.ts`). Every query made while paused
the evaluation resumes (`pauseVariables.ts`). An object's rows are its
`__dir__` read through `___pyAttrLoad___:`, so a `__slots__` object (no
`__dict__`, no Smalltalk instance variables) opens too, and Grail names a bound
method's type `BoundMethod`, not `method`. Set Value writes a local back into
the paused frame with `GsProcess>>_frameAt:tempAt:put:`, at the levels the walk
recorded for that frame; `_frameAt:tempNamed:put:` fails on a local a block
closes over, which lives in a VariableContext (measured:
rtErrShouldNotImplement). Only a temp can be written that way, and two names
in Locals are not temps: a method's receiver, which Grail adds under whatever
name the def gave it; and a classmethod's `cls`, which Grail leaves out of
Locals altogether (measured). A parameter of a plain function
spelled `self` is left out too, by `___isInternalTempName___`. Every query made while paused
goes through `queryWhilePaused` in `session.ts` — nonblocking, one at a time,
soft-broken past a two-second budget — never `execute`, because a Variables
row runs the user's own `__repr__`. Measured traps there: every Smalltalk
Expand Down
Loading
Loading