From 8095b9e83f56df82733a8725e327d1be9d1706a4 Mon Sep 17 00:00:00 2001 From: paugier Date: Sat, 12 Sep 2026 21:30:07 +0200 Subject: [PATCH] _human_render(self, parent) for more readable redshift output --- spy/backend/spy.py | 12 ++++--- spy/fqn.py | 29 ++++++++++++----- spy/tests/compiler/test_linearize.py | 48 ++++++++++++++-------------- spy/tests/test_backend_spy.py | 8 ++--- spy/tests/test_doppler.py | 36 ++++++++++----------- 5 files changed, 75 insertions(+), 58 deletions(-) diff --git a/spy/backend/spy.py b/spy/backend/spy.py index 9355bf089..2995c4742 100644 --- a/spy/backend/spy.py +++ b/spy/backend/spy.py @@ -85,8 +85,8 @@ def dump_mod(self, modname: str) -> str: ): aliases.append((attr, w_obj)) for attr, w_obj in aliases: - fqn_str = w_obj.fqn.human_name(self.vm) - self.out.wl(f"{attr} = `{fqn_str}`") + fqn_str = self.fmt_fqn(w_obj.fqn) + self.out.wl(f"{attr} = {fqn_str}") if aliases: self.out.wl() @@ -153,7 +153,7 @@ def fmt_w_obj(self, w_obj: W_Object) -> str: if isinstance(w_obj, W_Type) and issubclass(w_obj.pyclass, W_InterpList): # this is a ugly special case for now, we need to find a better # solution - return w_obj.fqn.human_name(self.vm) + return self.fmt_fqn(w_obj.fqn) # # this assumes that w_obj has a valid FQN fqn = self.vm.reverse_lookup_global(w_obj) @@ -164,7 +164,11 @@ def fmt_fqn(self, fqn: FQN) -> str: if self.fqn_format == "full": name = str(fqn) elif self.fqn_format == "short": - name = fqn.human_name(self.vm) # don't show builtins:: + # don't show builtins::, and don't repeat the current module + # name for FQNs nested inside it (at any depth, including + # inside generic qualifiers) + parent = FQN(self.modname) if self.modname else None + name = fqn.human_name(self.vm, parent=parent) else: assert False # diff --git a/spy/fqn.py b/spy/fqn.py index 69be2ac21..63815bca6 100644 --- a/spy/fqn.py +++ b/spy/fqn.py @@ -205,9 +205,14 @@ def _fullname(self, human: bool) -> str: def fullname(self) -> str: return self._fullname(human=False) - def _human_render(self) -> str: + def _human_render(self, parent: Optional["FQN"] = None) -> str: """ Render a FQN to string, special casing builtins::, def, etc. + + If `parent` is given and `self` starts with `parent`'s parts, that common + prefix is stripped from the rendered output. The match is on the + structured NSPart tuples, not on the rendered string, so it can never + misfire on a merely-similar-looking name. """ is_def = ( len(self.parts) == 2 @@ -225,7 +230,7 @@ def _human_render(self) -> str: d = "@blue.generic def" else: d = "@blue.metafunc def" - quals = [q._human_render() for q in p1.qualifiers] + quals = [q._human_render(parent) for q in p1.qualifiers] p = ", ".join(quals[:-1]) r = quals[-1] if r == "types::NoneType": @@ -240,19 +245,22 @@ def _human_render(self) -> str: if is_varargs_param: p1 = self.parts[1] assert len(p1.qualifiers) == 1 - return f"*{p1.qualifiers[0]._human_render()}" + return f"*{p1.qualifiers[0]._human_render(parent)}" parts = self.parts if str(parts[0]) == "builtins": parts = parts[1:] + elif parent is not None and parent.parts: + pparts = parent.parts + if len(parts) > len(pparts) and parts[: len(pparts)] == pparts: + parts = parts[len(pparts) :] rendered = [] for part in parts: name = part.name if part.qualifiers: - name = ( - f"{name}[{', '.join(q._human_render() for q in part.qualifiers)}]" - ) + quals_str = ", ".join(q._human_render(parent) for q in part.qualifiers) + name = f"{name}[{quals_str}]" if part.suffix: name += f"#{part.suffix}" rendered.append(name) @@ -323,13 +331,18 @@ def debug_human_name(self) -> str: """ return self._human_render() - def human_name(self, vm: Any) -> str: + def human_name(self, vm: Any, parent: Optional["FQN"] = None) -> str: """ Render the FQN for end-user display, honoring vm.fqn_human_aliases. If no VM is available, use debug_human_name instead. + + If `parent` is given, it is stripped from the output wherever it + appears as a genuine namespace prefix (see _human_render). """ human_fqn = self._resolve_aliases(vm, frozenset()) - return human_fqn._human_render() + if parent is not None: + parent = parent._resolve_aliases(vm, frozenset()) + return human_fqn._human_render(parent) def human_symbol_name(self, vm: Any) -> str: human_fqn = self._resolve_aliases(vm, frozenset()) diff --git a/spy/tests/compiler/test_linearize.py b/spy/tests/compiler/test_linearize.py index eabea538c..7fe3551ce 100644 --- a/spy/tests/compiler/test_linearize.py +++ b/spy/tests/compiler/test_linearize.py @@ -72,9 +72,9 @@ def foo() -> i32: if self.backend == "linearize": expected = """ def foo() -> i32: - $v0: i32 = `test::f1`() - $v1: i32 = `test::f2`() - return `test::sub`($v0, $v1) + $v0: i32 = f1() + $v1: i32 = f2() + return sub($v0, $v1) """ self.assert_linearize("foo", expected) @@ -92,7 +92,7 @@ def foo(x: i32) -> i32: if self.backend == "linearize": expected = """ def foo(x: i32) -> i32: - return `test::add`(x + 1, x + 2) + return add(x + 1, x + 2) """ self.assert_linearize("foo", expected) @@ -122,8 +122,8 @@ def foo(a: i32, c: i32, d: i32) -> i32: expected = """ def foo(a: i32, c: i32, d: i32) -> i32: $v0: i32 = a - $v1: i32 = `test::g`() - return `test::bar`($v0, 1 + $v1, c, d) + $v1: i32 = g() + return bar($v0, 1 + $v1, c, d) """ self.assert_linearize("foo", expected) @@ -147,7 +147,7 @@ def foo(a: i32) -> i32: if self.backend == "linearize": expected = """ def foo(a: i32) -> i32: - return `test::foo4`(a, 100, `test::V`, a) + return foo4(a, 100, V, a) """ self.assert_linearize("foo", expected) @@ -170,7 +170,7 @@ def foo(x: bool) -> i32: expected = """ def foo(x: bool) -> i32: if x: - return `test::g`() + return g() else: return 0 """ @@ -193,7 +193,7 @@ def foo() -> i32: expected = """ def foo() -> i32: x: i32 = 0 - x = `test::g`() + x = g() return x """ self.assert_linearize("foo", expected) @@ -231,7 +231,7 @@ def f2(x: bool) -> bool: expected_f1 = """ def f1(x: bool) -> bool: if x: - y: bool = `test::side_effect`() + y: bool = side_effect() $v0: bool = y else: $v0 = x @@ -240,7 +240,7 @@ def f1(x: bool) -> bool: self.assert_linearize("f1", expected_f1) expected_f2 = """ def f2(x: bool) -> bool: - return x and `test::side_effect`() + return x and side_effect() """ self.assert_linearize("f2", expected_f2) @@ -270,7 +270,7 @@ def foo(x: bool) -> bool: if x: $v0: bool = x else: - y: bool = `test::side_effect`() + y: bool = side_effect() $v0 = y return $v0 """ @@ -350,11 +350,11 @@ def foo() -> i32: expected = """ def foo() -> i32: while True: - $v0: i32 = `test::tick`() + $v0: i32 = tick() if `operator::bool_not`($v0 < 3): break - `_print::println[i32]`(`test::N`) - return `test::N` + `_print::println[i32]`(N) + return N """ self.assert_linearize("foo", expected) @@ -380,9 +380,9 @@ def foo(x: i32) -> i32: expected = """ def foo(x: i32) -> i32: $v0: i32 = x - $v1: i32 = `test::g`() + $v1: i32 = g() $v2: i32 = x := $v1 - return `test::foo3`($v0, $v2, x) + return foo3($v0, $v2, x) """ self.assert_linearize("foo", expected) @@ -405,10 +405,10 @@ def foo() -> i32: if self.backend == "linearize": expected = """ def foo() -> i32: - $v0: i32 = `test::V` - $v1: i32 = `test::g`() - $v2: i32 = `test::V` := $v1 - return `test::foo3`($v0, $v2, `test::V`) + $v0: i32 = V + $v1: i32 = g() + $v2: i32 = V := $v1 + return foo3($v0, $v2, V) """ self.assert_linearize("foo", expected) @@ -495,11 +495,11 @@ def foo() -> i32: if self.backend == "linearize": expected = """ def foo() -> i32: - a: i32 = `test::f`() + a: i32 = f() $v0: i32 = a - b: i32 = `test::g`() + b: i32 = g() $v1: i32 = b - return `test::add`($v0, $v1) + return add($v0, $v1) """ self.assert_linearize("foo", expected) diff --git a/spy/tests/test_backend_spy.py b/spy/tests/test_backend_spy.py index 47fb07dcb..c4151c49d 100644 --- a/spy/tests/test_backend_spy.py +++ b/spy/tests/test_backend_spy.py @@ -254,13 +254,13 @@ def foo() -> None: add_f64(3.4, 5.6) """) self.assert_dump(""" - add_i32 = `test::add[i32]` - add_f64 = `test::add[f64]` + add_i32 = `add[i32]` + add_f64 = `add[f64]` - def `test::add[i32]`(x: i32, y: i32) -> i32: + def `add[i32]`(x: i32, y: i32) -> i32: return x + y - def `test::add[f64]`(x: f64, y: f64) -> f64: + def `add[f64]`(x: f64, y: f64) -> f64: return x + y def foo() -> None: diff --git a/spy/tests/test_doppler.py b/spy/tests/test_doppler.py index ee3d9adda..376e54480 100644 --- a/spy/tests/test_doppler.py +++ b/spy/tests/test_doppler.py @@ -115,7 +115,7 @@ def inc(x: i32) -> i32: return x + 1 def foo() -> i32: - return `test::inc`(5) + return inc(5) """ self.redshift(src) self.assert_dump(expected) @@ -172,7 +172,7 @@ def inc(x: i32) -> i32: def foo() -> i32: x: i32 = 0 - y: i32 = `test::inc`(x := 1) + y: i32 = inc(x := 1) return x + y """) @@ -194,8 +194,8 @@ def foo(x: i32) -> None: def main() -> None: x: i32 = 0 - `test::foo`(x := 1) - `test::foo`(2) + foo(x := 1) + foo(2) `_print::println[i32]`(x) `_print::println[str]`('2') """) @@ -213,9 +213,9 @@ def foo() -> i32: """) self.assert_dump(""" def foo() -> i32: - return `test::make_fn::fn`(21) + return `make_fn::fn`(21) - def `test::make_fn::fn`(x: i32) -> i32: + def `make_fn::fn`(x: i32) -> i32: return x * 2 """) @@ -236,14 +236,14 @@ def main() -> None: """) self.assert_dump(""" def main() -> None: - `test::make_foo::foo`() + `make_foo::foo`() - def `test::make_foo::fn`() -> None: + def `make_foo::fn`() -> None: `_print::println[str]`('fn') - def `test::make_foo::foo`() -> None: - `test::make_foo::fn`() - `test::make_foo::fn`() + def `make_foo::foo`() -> None: + `make_foo::fn`() + `make_foo::fn`() """) def test_binops(self): @@ -312,13 +312,13 @@ def foo() -> None: """) self.assert_dump(""" def foo() -> None: - x: i32 = `test::add[i32]::impl`(1, 2) - y: str = `test::add[str]::impl`('a', 'b') + x: i32 = `add[i32]::impl`(1, 2) + y: str = `add[str]::impl`('a', 'b') - def `test::add[i32]::impl`(x: i32, y: i32) -> i32: + def `add[i32]::impl`(x: i32, y: i32) -> i32: return x + y - def `test::add[str]::impl`(x: str, y: str) -> str: + def `add[str]::impl`(x: str, y: str) -> str: return `_str::methods::__add__`(x, y) """) @@ -330,7 +330,7 @@ def foo() -> None: """) self.assert_dump(""" def foo() -> None: - `test::x` = 1 + x = 1 """) def test_format_prebuilt_exception(self): @@ -538,7 +538,7 @@ def bar(x: f64) -> None: pass def foo(x: i32) -> f64: - `test::bar`(`operator::i32_to_f64`(x)) + bar(`operator::i32_to_f64`(x)) flag: bool = `operator::i32_to_bool`(x) if `operator::i32_to_bool`(x): pass @@ -564,7 +564,7 @@ def bar(x: f64) -> None: pass def foo() -> f64: - `test::bar`(42.0) + bar(42.0) flag: bool = True if True: pass