Description
frappe._() does not work inside a Builder Page Data Script, even though the function is included in the sandbox globals.
Steps to reproduce
- Create a Builder Page
- Add a Page Data Script containing:
data.update({"text": frappe._("Supplier")})
- Bind a text block's Data Key to
text
- Preview the page
Expected behavior
The translated string appears (e.g. "Lieferant" for German).
Actual behavior
'str object' has no attribute 'get' — or an empty string is rendered.
Root cause
File: builder/builder/utils.py — function get_safer_globals()
The _ function is only available inside the frappe namespace (line 241):
frappe=NamespaceDict(
...
_=frappe._, # line 241 — only here
...
)
When the sandbox script calls frappe._("text"), RestrictedPython compiles the attribute access frappe._ into _getattr_(frappe, "_"). The _getattr_ guard (from frappe.utils.safe_exec._validate_attribute_read) blocks all attribute names starting with _:
# frappe/utils/safe_exec.py line 571:
if name.startswith("_"):
raise AttributeError(f'"{name}" is an invalid attribute name because it starts with "_"')
This means frappe._() can never succeed — the guard kills it before the function is ever reached.
Fix
Add _=frappe._ to the top-level of the out NamespaceDict, matching how Frappe's own get_safe_globals() works:
Environment
- Frappe/ERPNext version: v16
- Builder version: 1.24.7
Description
frappe._()does not work inside a Builder Page Data Script, even though the function is included in the sandbox globals.Steps to reproduce
data.update({"text": frappe._("Supplier")})textExpected behavior
The translated string appears (e.g.
"Lieferant"for German).Actual behavior
'str object' has no attribute 'get'— or an empty string is rendered.Root cause
File:
builder/builder/utils.py— functionget_safer_globals()The
_function is only available inside thefrappenamespace (line 241):When the sandbox script calls
frappe._("text"), RestrictedPython compiles the attribute accessfrappe._into_getattr_(frappe, "_"). The_getattr_guard (fromfrappe.utils.safe_exec._validate_attribute_read) blocks all attribute names starting with_:This means
frappe._()can never succeed — the guard kills it before the function is ever reached.Fix
Add
_=frappe._to the top-level of theoutNamespaceDict, matching how Frappe's ownget_safe_globals()works:Environment