Skip to content

Commit ab58036

Browse files
committed
comments: Address UUID + other comments
1 parent 9f6b61a commit ab58036

4 files changed

Lines changed: 95 additions & 18 deletions

File tree

frontend/src/utils/__tests__/local-variables.test.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,26 @@ describe("unmangleLocal", () => {
2424
it("returns null for non-mangled strings", () => {
2525
expect(unmangleLocal("just_a_variable")).toBeNull();
2626
expect(unmangleLocal("_private")).toBeNull();
27-
expect(unmangleLocal("_cell_Hbol_")).toBeNull();
27+
});
28+
29+
it("handles the single-underscore local", () => {
30+
// `_` is a valid local name (variables.py:62-63), mangling to
31+
// `_cell_<id>_` with no trailing suffix.
32+
expect(unmangleLocal("_cell_Hbol_")).toEqual({
33+
cellId: "Hbol",
34+
name: "_",
35+
});
36+
});
37+
38+
it("handles UUID-style cell ids", () => {
39+
// External / VSCode notebooks use `external_prefix()` which is a
40+
// `uuid4()` (hyphenated).
41+
expect(
42+
unmangleLocal("_cell_c9bf9e57-1685-4c89-bafb-ff5af830be8a_a"),
43+
).toEqual({
44+
cellId: "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
45+
name: "_a",
46+
});
2847
});
2948

3049
it("does not match marimo cell file paths", () => {
@@ -67,6 +86,39 @@ describe("splitMangledLocals", () => {
6786
const path = "/tmp/marimo_42/__marimo__cell_Hbol_.py";
6887
expect(splitMangledLocals(path)).toEqual([path]);
6988
});
89+
90+
it("ignores `_cell_...` substrings preceded by `_`", () => {
91+
// Mirrors `(?<!_)` in `variables.py`: a leading `_` (e.g. inside
92+
// `__marimo__cell_<id>_<...>`) means this is not a mangle the compiler
93+
// produced, so we must not demangle it.
94+
const text = "see __marimo__cell_Hbol_a for details";
95+
expect(splitMangledLocals(text)).toEqual([text]);
96+
});
97+
98+
it("splits a UUID-style cell id", () => {
99+
expect(
100+
splitMangledLocals(
101+
"name '_cell_c9bf9e57-1685-4c89-bafb-ff5af830be8a_a' is not defined",
102+
),
103+
).toEqual([
104+
"name '",
105+
{
106+
cellId: "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
107+
name: "_a",
108+
},
109+
"' is not defined",
110+
]);
111+
});
112+
113+
it("splits the single-underscore local", () => {
114+
expect(
115+
splitMangledLocals("name '_cell_Hbol_' is not defined"),
116+
).toEqual([
117+
"name '",
118+
{ cellId: "Hbol", name: "_" },
119+
"' is not defined",
120+
]);
121+
});
70122
});
71123

72124
describe("containsMangledLocal", () => {
@@ -81,4 +133,8 @@ describe("containsMangledLocal", () => {
81133
containsMangledLocal("/tmp/marimo_42/__marimo__cell_Hbol_.py"),
82134
).toBe(false);
83135
});
136+
137+
it("ignores `_cell_...` substrings preceded by `_`", () => {
138+
expect(containsMangledLocal("see __marimo__cell_Hbol_a")).toBe(false);
139+
});
84140
});

frontend/src/utils/local-variables.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,15 @@ import type { CellId } from "@/core/cells/ids";
1111
* Mirrors `marimo/_ast/variables.py`.
1212
*/
1313

14-
// Matches `_cell_<cell_id><name>` where the cell id has no underscores and
15-
// `<name>` begins with `_` (the original ref was a local underscore name).
16-
// Python mangle is `f"_cell_{cell_id}{ref}"` (variables.py:41), so the only
17-
// `_` between the id and the name is the leading `_` of the name itself.
18-
// Non-greedy id group + name group that must start with `_` correctly
19-
// recovers the boundary.
20-
//
21-
// Anchored on a single leading `_cell_`, so the compiled cell file path
22-
// `__marimo__cell_<id>_.py` (two leading underscores, no trailing `_<name>`)
23-
// does not match.
24-
const MANGLED_LOCAL_PATTERN = String.raw`_cell_([^\W_]\w*?)(_\w+)`;
25-
const ANCHORED_RE = new RegExp(`^${MANGLED_LOCAL_PATTERN}$`);
14+
// Matches `_cell_<cell_id><name>` for normal ids and UUIDs. The `[\w-]`
15+
// id class admits hyphens; the `_\w*` name group admits the bare `_`
16+
// local; the `(?<!_)` lookbehind skips `__marimo__cell_...` paths.
17+
// Mirrors `_MANGLED_LOCAL_IN_TEXT_RE` in `variables.py`.
18+
const MANGLED_LOCAL_BODY = String.raw`_cell_([^\W_][\w-]*?)(_\w*)`;
19+
const MANGLED_LOCAL_PATTERN = String.raw`(?<!_)${MANGLED_LOCAL_BODY}`;
20+
// Strict (whole-string) form for `unmangleLocal`; the leading `^` makes the
21+
// lookbehind trivially satisfied, so use the bare body.
22+
const ANCHORED_RE = new RegExp(`^${MANGLED_LOCAL_BODY}$`);
2623
const UNANCHORED_RE = new RegExp(MANGLED_LOCAL_PATTERN);
2724
const GLOBAL_RE = new RegExp(MANGLED_LOCAL_PATTERN, "g");
2825

marimo/_ast/variables.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ def get_cell_from_local(
7171

7272

7373
# Demangle every occurrence of `_cell_<cell_id><_name>` inside a free-form
74-
# string (test output, traceback lines, etc.) back to `<_name>`. The
75-
# negative-lookbehind excludes the compiled cell-file path
76-
# `__marimo__cell_<id>_.py`, which carries a leading `_` we must not strip.
77-
_MANGLED_LOCAL_IN_TEXT_RE = re.compile(r"(?<!_)_cell_\w+?(_\w+)")
74+
# string (test output, traceback lines, etc.) back to `<_name>`. Matches
75+
# normal ids and UUIDs; the `(?<!_)` lookbehind skips the compiled cell-file
76+
# path `__marimo__cell_<id>_.py`, which carries a leading `_`.
77+
_MANGLED_LOCAL_IN_TEXT_RE = re.compile(r"(?<!_)_cell_(?:[^\W_][\w-]*?)(_\w*)")
7878

7979

8080
def demangle_locals_in_text(text: str) -> str:

tests/_ast/test_variables.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
def test_demangle_in_name_error_message() -> None:
88
assert (
9-
demangle_locals_in_text("NameError: name '_cell_vblA_a' is not defined")
9+
demangle_locals_in_text(
10+
"NameError: name '_cell_vblA_a' is not defined"
11+
)
1012
== "NameError: name '_a' is not defined"
1113
)
1214

@@ -25,3 +27,25 @@ def test_demangle_leaves_cell_file_path_alone() -> None:
2527

2628
def test_demangle_no_op_for_plain_text() -> None:
2729
assert demangle_locals_in_text("plain text") == "plain text"
30+
31+
32+
def test_demangle_handles_uuid_cell_id() -> None:
33+
# External / VSCode notebooks use `external_prefix()` (a uuid4) as the
34+
# cell-id prefix, producing hyphenated mangles.
35+
assert (
36+
demangle_locals_in_text(
37+
"NameError: name "
38+
"'_cell_c9bf9e57-1685-4c89-bafb-ff5af830be8a_a' "
39+
"is not defined"
40+
)
41+
== "NameError: name '_a' is not defined"
42+
)
43+
44+
45+
def test_demangle_handles_single_underscore_local() -> None:
46+
# `_` is a valid local name (variables.py:is_local); it mangles to
47+
# `_cell_<id>_` with no name suffix.
48+
assert (
49+
demangle_locals_in_text("NameError: name '_cell_Hbol_' is not defined")
50+
== "NameError: name '_' is not defined"
51+
)

0 commit comments

Comments
 (0)