frappe / frappe/builder

`frappe._()` does not work inside a Builder Page Data Script, even though the function is included in the sandbox globals.

Open
#626 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
2.4k
Forks
534
Avg merge
1d 2h
Merged PRs (30d)
45

Description

## Description
`frappe._()` does not work inside a Builder Page Data Script, even though the function is included in the sandbox globals.

## Steps to reproduce
1. Create a Builder Page
2. Add a **Page Data Script** containing: `data.update({"text": frappe._("Supplier")})`
3. Bind a text block's Data Key to `text`
4. 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):

```python
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 `_`:

```python
# 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

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.