Skip to content

Add e2b_id: the Python half of the prefixed-ID format - #1628

Open
mishushakov wants to merge 3 commits into
prefixed-id-packagesfrom
python-id
Open

mishushakov wants to merge 3 commits into
prefixed-id-packagesfrom
python-id

Conversation

@mishushakov

@mishushakov mishushakov commented Jul 30, 2026 •

Copy link
Copy Markdown
Member

Stack: 2 of 2, on top of #1627. Review that one first — this PR's diff is the Python package plus the wiring that ties the two halves together.

e2b_id is the same format as @e2b/id, name for name: create_id, encode_id, decode_id, parse_id, is_id, create_uuid, encode_bytes/decode_bytes. Standard library only — no dependencies at all, so an ID codec every service depends on can never be the reason a resolution fails. It is the TypeID encoding E2B's Go services use (packages/shared/pkg/id in e2b-dev/infra), and the Go suite's own vectors are pinned here.

Part of SDK-303

Usage

from e2b_id import create_id, decode_id, encode_id, is_id, parse_id

create_id("project")                                          # 'prj_01kyjhkfvse96rg4d2qzd9enft'
encode_id("volume", "019fa519-bfc5-784d-9386-a5d7a93a692a")   # 'vol_01kyjhkfy5f16s71n5tymkmt9a'
decode_id("volume", "vol_01kyjhkfy5f16s71n5tymkmt9a")         # UUID('019fa519-bfc5-784d-9386-a5d7a93a692a')
parse_id("vol_01kyjhkfy5f16s71n5tymkmt9a")                    # ParsedId(kind='volume', uuid=UUID('019fa519-…'))
is_id("project", "vol_01kyjhkfy5f16s71n5tymkmt9a")            # False

InvalidIdException subclasses ValueError and carries reason ("kind" | "prefix" | "length" | "alphabet" | "overflow" | "uuid") plus actual_kind.

UUIDs are read and minted the way github.com/google/uuid does it, as in @e2b/id: encode_id takes a uuid.UUID or exactly the four string spellings Go's Parse takes — narrower than uuid.UUID(), which also strips hyphens from anywhere — and create_uuid is NewV7 line for line, with the sequence taken from the clock's sub-millisecond bits and bumped so every UUID a process mints is strictly greater than the last.

Where it differs from @e2b/id

Only downstream of JavaScript having no UUID type: this package moves uuid.UUID objects where @e2b/id moves canonical hex strings. decode_id and ParsedId.uuid return a uuid.UUID, encode_id accepts either, and there is no uuid_to_bytes/bytes_to_uuid because the standard library already is those.

Notes for reviewers

  • The codec is base64.b32encode behind a translation table: four zero bytes in front make 20, which is 32 RFC 4648 digits with no padding, of which the last 26 are the TypeID suffix once mapped onto the Crockford alphabet. The tests check that against an arithmetic encoder stated independently of it.
  • Agreement with the JS half is enforced, not asserted. Both suites carry the same golden vectors (the Go side's among them), build the same 1306-value corpus from the same seeded LCG, and pin the same CORPUS_DIGEST, so a format change on one side alone fails on both.
  • Wired into the release pipeline as e2b-id on PyPI. Versioned from 0.0.0 with an "Initial release" changeset so the next release mints 0.1.0, gated on the id_tests workflow in release.yml, and published by the same postPublish step python-sdk uses: uv publish over PyPI's OIDC trusted publishing, no token. The e2b-id project already exists on PyPI as a 0.0.0 placeholder with the trusted publisher attached, so 0.1.0 is the first real version and the release needs no manual step.

Verification

124 tests; ruff and ty clean; uv build succeeds. The id_tests workflow runs both suites on ubuntu-22.04 and windows-latest.

🤖 Generated with Claude Code


Devin Review

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

cursor Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

PR Summary

Low Risk
New isolated codec package and pipeline wiring; no auth or runtime service behavior changes.

Overview
Adds the packages/python-id (e2b-id on PyPI) standard-library codec and tests aligned with @e2b/id, a minor changeset for @e2b/python-id, and hooks it into ID combined tests, lint/typecheck, PR artifacts, and release preflight/itinerary. @e2b/id docs and vector tests now reference the shared Python vectors and corpus digest. No issues found in the changed CI comment script or workspace layout.

Reviewed by Cursor Bugbot for commit 20103e3. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 20103e3

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-id Minor

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

@github-actions

github-actions Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from d00c315. Download artifacts from this workflow run.

JS SDK (e2b@2.63.1-python-id.0, with @e2b/dockerfile-utils@0.1.2-python-id.0):

npm install ./e2b-dockerfile-utils-0.1.2-python-id.0.tgz ./e2b-2.63.1-python-id.0.tgz

CLI (@e2b/cli@2.23.1-python-id.0):

npm install ./e2b-cli-2.23.1-python-id.0.tgz

Code Interpreter JS SDK (@e2b/code-interpreter@2.9.2-python-id.0):

npm install ./e2b-code-interpreter-2.9.2-python-id.0.tgz

Desktop JS SDK (@e2b/desktop@2.5.1-python-id.0):

npm install ./e2b-desktop-2.5.1-python-id.0.tgz

ID JS (@e2b/id@0.0.1-python-id.0):

npm install ./e2b-id-0.0.1-python-id.0.tgz

Python SDK (e2b==2.63.0+python.id, with e2b-dockerfile-utils==0.1.1+python.id):

pip install ./e2b_dockerfile_utils-0.1.1+python.id-py3-none-any.whl ./e2b-2.63.0+python.id-py3-none-any.whl

Code Interpreter Python SDK (e2b-code-interpreter==2.11.9+python.id):

pip install ./e2b_code_interpreter-2.11.9+python.id-py3-none-any.whl

Desktop Python SDK (e2b-desktop==2.7.2+python.id):

pip install ./e2b_desktop-2.7.2+python.id-py3-none-any.whl

ID Python (e2b-id==0.0.0+python.id):

pip install ./e2b_id-0.0.0+python.id-py3-none-any.whl

@mishushakov
mishushakov marked this pull request as ready for review November 12, 2026 16:03
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T16:06:57.624926Z 6da96f9 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

claude[bot]

This comment was marked as resolved.

mishushakov and others added 3 commits November 13, 2026 16:29
The same format as @e2b/id, name for name: create_id, encode_id,
decode_id, parse_id, is_id, create_uuid, encode_bytes/decode_bytes.
Standard library only — no dependencies at all, so an ID codec that
every service depends on can never be the reason a resolution fails.

The codec is base64.b32encode behind a translation table: four zero
bytes in front make 20, which is 32 RFC 4648 digits with no padding, of
which the last 26 are the TypeID suffix once mapped onto the Crockford
alphabet. The tests check that against an arithmetic encoder stated
independently of it, and against the Go suite's own vectors.

Agreement with the JS half is enforced rather than asserted. Both suites
carry the same golden vectors — the Go side's among them — both build
the same 1306-value corpus from the same seeded LCG, and both pin the
same CORPUS_DIGEST over it, so a format change on one side alone fails
on both.

UUIDs are read and minted the way github.com/google/uuid does it, as
in @e2b/id: encode_id takes exactly the four spellings Go's Parse takes
— narrower than uuid.UUID(), which also strips hyphens from anywhere —
and create_uuid is NewV7 line for line, the sequence being the clock's
sub-millisecond bits bumped so every UUID a process mints is strictly
greater than the last.

The one place the surfaces differ is downstream of JavaScript having no
UUID type: this package moves uuid.UUID objects where @e2b/id moves
canonical hex strings, so decode_id and ParsedId.uuid return UUIDs and
there is no uuid_to_bytes/bytes_to_uuid — the standard library already
is those. uuids.py and the README both enumerate it.

Also wires the two together: the id_tests workflow grows a Python leg,
lint and typecheck sync the new uv env, and js-id's docs now name their
counterpart.

Wired into the release pipeline as e2b-id on PyPI: versioned from
0.0.0 with an "Initial release" changeset so the next release mints
0.1.0, gated on the id_tests workflow in release.yml, and published by
the same postPublish step python-sdk uses — `uv publish` over PyPI's
OIDC trusted publishing, no token. The e2b-id project already exists on
PyPI as a 0.0.0 placeholder with the trusted publisher attached, so
0.1.0 is the first real version and publishing needs no manual step.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
decode_id and parse_id raise InvalidIdException("prefix") for a non-string
id (None, a number, bytes, ...) instead of an AttributeError, matching
@e2b/id. decode_id also names no kind for an id with no separator, as
parse_id and @e2b/id already do, so "prj" is no longer reported as a
project ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds tests/test_uuids.py, the counterpart of @e2b/id's uuid.test.ts:
hex_digits_of against the standard library over the shared corpus, each of
Go's four forms probed one character short, one long and with a bad digit
(plus the spellings uuid.UUID accepts and Go does not), non-string UUIDs,
and create_uuid on fresh module state: version and variant on every mint,
the 48-bit timestamp at its boundaries, random bits confined to rand_b,
the clock-derived sequence counting up, resetting and carrying.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

cla-signed review-requested Posted in #code-review-requests on Slack

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant