Skip to content

fix(python-sdk): strip OSC escape sequences in build logs#1540

Closed
anxkhn wants to merge 1 commit into
e2b-dev:mainfrom
anxkhn:patch-7
Closed

fix(python-sdk): strip OSC escape sequences in build logs#1540
anxkhn wants to merge 1 commit into
e2b-dev:mainfrom
anxkhn:patch-7

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What

strip_ansi_escape_codes in the Python SDK only matched CSI escape sequences, so
OSC sequences (ESC ] ... ST) leaked their string terminator and payload into
template build-log messages. The set-terminal-title sequence \x1b]0;title\x07,
which is common in npm and other CLI output, left title\x07 in the log.

This is a follow-up to #1522 (colon-separated SGR), the same
strip_ansi_escape_codes vs stripAnsi parity gap. That PR widened the CSI
parameter class to match the JS SDK but did not add OSC handling, so OSC
sequences remained a divergence: the JS SDK's stripAnsi
(packages/js-sdk/src/utils.ts) already strips them via a dedicated OSC branch,
the Python port did not.

strip_ansi_escape_codes is consumed by LogEntry.__post_init__
(packages/python-sdk/e2b/template/logger.py), so the leftover bytes showed up in
Python build logs only.

The fix

Port the Python regex to mirror packages/js-sdk/src/utils.ts byte for byte: add
a dedicated OSC alternative next to the existing CSI branch, so both SDKs strip
the same input.

# packages/python-sdk/e2b/template/utils.py
st = r"(?:\u0007|\u001B\u005C|\u009C)"
# OSC sequences only: ESC ] ... ST (non-greedy until the first ST)
osc = rf"(?:\u001B\][\s\S]*?{st})"
# CSI and related: ESC/C1, optional intermediates, optional params
# (supports ; and :) then final byte
csi = r"[\u001B\u009B][\[\]()#;?]*(?:\d{1,4}(?:[;:]\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]"
ansi_escape = re.compile(f"{osc}|{csi}", re.UNICODE)

This matches the JS reference (ansiRegex: st / osc / csi / osc|csi) and
keeps the colon-SGR fix from #1522.

Usage example (before / after)

from e2b.template.utils import strip_ansi_escape_codes

# set-terminal-title (BEL terminator), common in npm/CLI output
strip_ansi_escape_codes("\x1b]0;my title\x07AFTER")
# before: "y title\x07AFTER"   after: "AFTER"

# set-terminal-title (ESC-backslash terminator)
strip_ansi_escape_codes("\x1b]0;my title\x1b\\AFTER")
# before: "y title\x1b\\AFTER"   after: "AFTER"

# hyperlink
strip_ansi_escape_codes("\x1b]8;;http://example.com\x07link\x1b]8;;\x07")
# after: "link"

# CSI / SGR sequences already worked and still do
strip_ansi_escape_codes("\x1b[38:5:82mX\x1b[0m")  # "X"  (unchanged)

Tests

Extended the offline unit tests at
packages/python-sdk/tests/shared/template/utils/test_strip_ansi_escape_codes.py
(no API key or sandbox needed) with OSC cases: set-terminal-title (BEL),
ESC-backslash terminator, and hyperlink, alongside the existing CSI/colon
regressions from #1522. All pass locally (pytest on the file: 10 passed).

Changeset

.changeset/python-strip-ansi-osc.md (patch on @e2b/python-sdk).

strip_ansi_escape_codes only removed CSI escape sequences, so OSC
sequences (ESC ] ... string-terminator) leaked their terminator and
payload into template build log messages. The set-terminal-title
sequence \x1b]0;title\x07, common in npm/CLI output, left "title\x07"
in the log; the JavaScript SDK's stripAnsi already stripped these.

Port the Python regex to mirror js-sdk/src/utils.ts by adding a
dedicated OSC alternative alongside the existing CSI branch, so both
SDKs strip the same input. Extend the offline unit tests with OSC
cases (set-terminal-title, hyperlink, ESC-backslash terminator) and
add a changeset.
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a6aa248

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@e2b/python-sdk Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cla-bot cla-bot Bot added the cla-signed label Jul 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6aa2480c8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

# CSI and related: ESC/C1, optional intermediates, optional params
# (supports ; and :) then final byte
csi = (
r"[\u001B\u009B][\[\]()#;?]*(?:\d{1,4}(?:[;:]\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve removal of ST-terminated DCS sequences

When a build log contains a DCS/control string such as \x1bP1;2\x1b\\, this branch now treats ESC P as a complete CSI-style escape because P is in the final-byte class, so strip_ansi_escape_codes returns 1;2\x1b\\... instead of removing the sequence. The previous ST-terminated branch removed these ANSI control strings, so this OSC-only rewrite reintroduces raw control payloads/terminators into Python build log messages for tools that emit DCS, such as tmux/kitty/sixel output.

Useful? React with 👍 / 👎.

@mishushakov

Copy link
Copy Markdown
Member

No need, we will just vendor in the strip-ansi library for Python

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants