Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#2771·smolagents

BUG: LocalPythonExecutor silently ignores decorators, so a decorated function returns the undecorated result

Author: Sreekant13Created Sep 8, 2026Updated Sep 9, 2026

Describe the bug

LocalPythonExecutor never applies decorators. ast.FunctionDef.decorator_list and ast.ClassDef.decorator_list are read nowhere in local_python_executor.py: evaluate_function_def builds the function from the node and returns it, and evaluate_class_def puts each method into the class dict through the same path.

The decorated object is therefore the undecorated one, and nothing raises. That matters more than the missing feature: everywhere else the executor refuses loudly, for example InterpreterError: Forbidden function evaluation: 'super' is not among the explicitly allowed tools. Here it returns a wrong value silently, so an agent gets a plausible answer computed by code that did not run as written.

Reproduction

A decorator defined entirely inside the sandbox, using nothing restricted:

python
from smolagents.local_python_executor import LocalPythonExecutor
from smolagents.default_tools import FinalAnswerTool

ex = LocalPythonExecutor(additional_authorized_imports=[])
ex.send_tools({"final_answer": FinalAnswerTool()})

code = '''
def double(fn):
    def wrapper(*a, **k):
        return fn(*a, **k) * 2
    return wrapper

@double
def value():
    return 21

final_answer(value())
'''
print(ex(code).output)
smolagents LocalPythonExecutor -> 21
real Python                    -> 42

Two more shapes, same cause:

python
# 1. @property returns the bound method instead of the value.
class A:
    @property
    def v(self):
        return 7
A().v          # -> <bound method ...>, real Python -> 7

# 2. An UNDEFINED decorator is not an error.
@undefined_decorator
def f():
    return 1
f()            # -> 1, real Python -> NameError

Case 2 is the one I would weigh most: an undefined name is silently accepted, which is the opposite of how every other name resolution in the executor behaves.

Expected behavior

Either apply the decorators, or refuse the code the way the executor refuses everything else it does not support. Silently returning the undecorated object is the outcome that cannot be detected from the agent's side.

Notes on scope

super, repr, format, frozenset, bytes, slice and hash are also absent from BASE_PYTHON_TOOLS, so super().__init__(...) fails and ordinary inheritance does not work. Those all fail LOUDLY, so they are a separate and much smaller problem; I mention them only because I found them while checking whether @property failing was about property being unavailable. It is not: a user-defined decorator is dropped too.

I did check the tracker first. The existing decorator issues are about the @tool decorator and the timeout() decorator, and I did not find this one open or closed. I also checked that this is not a documented limitation: the class docstring scopes the restrictions to imports and built-in functions, and says the executor is not a security sandbox, which is a different claim.

Packages version

main at 30bb1161, Python 3.12.

Would a PR be welcome?

I am happy to send one, but the shape is a design call I would rather you make. Applying a decorator means calling it, and the natural implementation resolves the decorator expression and applies it to the function. Routing that through the existing call checks in evaluate_call keeps the current restrictions intact; applying it directly would not. If you would rather refuse decorated definitions than support them, that is a much smaller change and I am glad to do that instead.

Source: huggingface/smolagents

View original on GitHubView discussion on GitHub