Skip to content

fix(zipapp): reduce bundled interpreter size - #4165

Merged
rickeylev merged 12 commits into
bazel-contrib:mainfrom
gfrankliu:fix/py-zipapp-size
Sep 27, 2026
Merged

rickeylev merged 12 commits into
bazel-contrib:mainfrom
gfrankliu:fix/py-zipapp-size

Conversation

@gfrankliu

@gfrankliu gfrankliu commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Bazel reports source symlinks as regular files, causing Python executable aliases to be copied into zipapps as full binaries. Hermetic runtimes also include shared libpython artifacts even when the interpreter links Python statically.

Preserve source symlinks through sandbox indirection and exclude libpython shared objects from runtime files while retaining them for explicit native dependencies. Add regression coverage for archive structure and native extension loading.

Fixes #3534
Fixes #4163

Bazel reports source symlinks as regular files, causing Python executable aliases to be copied into zipapps as full binaries. Hermetic runtimes also include shared libpython artifacts even when the interpreter links Python statically.

Preserve source symlinks through sandbox indirection and exclude libpython shared objects from runtime files while retaining them for explicit native dependencies. Add regression coverage for archive structure and native extension loading.
Copilot AI lite review requested due to automatic review settings September 17, 2026 19:51

Copilot AI 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.

🟡 Changes recommended

The new archive assertion also runs on macOS, where the runtime intentionally retains libpython dylibs.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR reduces zipapp sizes by preserving Python executable symlinks and excluding unused Linux libpython shared objects.

Changes:

  • Detects source symlinks hidden behind Bazel sandbox indirection.
  • Excludes shared libpython files from hermetic runtime files.
  • Adds archive and native-extension regression coverage.
File summaries
File Description
tools/zipapp/zipper.py Preserves sandboxed source symlinks.
tests/tools/zipapp/zipper_test.py Tests symlink archive behavior.
tests/py_zipapp/venv_zipapp_test.py Checks archive structure and runtime contents.
tests/py_zipapp/main.py Exercises native _ssl loading.
python/private/hermetic_runtime_repo_setup.bzl Excludes Linux shared libpython objects.
news/py-zipapp-size.fixed.md Documents the fix.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/py_zipapp/venv_zipapp_test.py
gfrankliu and others added 2 commits September 17, 2026 13:36
Address PR review finding: the runtime-size assertion should mirror the Linux-only .so exclusion. macOS hermetic runtimes intentionally retain libpython dylibs, so only reject libpython shared-object entries.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@gfrankliu

Copy link
Copy Markdown
Contributor Author

The two failures in buildkite seems to relate to using python 3.13.1. The transition where Astral switched python-build-standalone to statically link libpython directly into the bin/python executable occurred in May 2025. The custom install-only-stripped artifact predates Astral’s May 2025 static-link transition, and that version of python 3.13.1 still dynamically links libpython, which is removed in this PR, causing test failure.

A few options:

  1. revert the removal of libpython.so in my PR and live with the extra 250MB in zipapp.
  2. use an Astral python build version after May 2025, eg: https://github.com/astral-sh/python-build-standalone/releases/tag/20250604 gives 3.14.0b1, 3.13.4, 3.12.11, 3.11.13, 3.10.18 and 3.9.23. So any of those versions, or higher would have statically linked python executable. We can change https://github.com/bazel-contrib/rules_python/blob/main/MODULE.bazel#L195 https://github.com/bazel-contrib/rules_python/blob/main/tests/toolchains/BUILD.bazel#L29 etc from 3.13.1 to 3.13.x (x>=4).
  3. add some auto detection of which Astral version is used and then decide if libpython is needed. (not sure if this is possible).

Thoughts?

Custom hermetic distributions may use dynamically linked interpreters and require libpython.so.1.0 at runtime. The zipapp size optimization incorrectly removed that library from every Linux runtime.

Restore the existing versioned-library inclusion and keep the zipapp optimization limited to preserving executable symlinks.
Astral Python Standalone builds from 20250604 onward include libpython statically in the interpreter, so packaging their shared libpython objects adds substantial unused size to self-contained zipapps.

Recognize only official Astral release URLs at or after that build date and exclude libpython shared objects for those runtimes. Unknown, custom, and older distributions retain the existing versioned libraries for dynamically linked interpreters.
@gfrankliu

Copy link
Copy Markdown
Contributor Author

Implemented the Policy:

  • Official Astral builds dated 20250604+: exclude libpython*.so*.
  • Older, custom, or unknown builds: retain versioned libraries.

@aignas aignas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, but it would be good to expose a python extension configuration to disable the auto-detection. Please let me know if you would like to do this as part of this PR.

I am OK to just do python_repository attribute with an "auto" default where the user can very easily do a patch for now to override this if needed.

Comment thread python/private/pbs_manifest.bzl
Comment thread python/private/python_repository.bzl
Comment thread python/private/pbs_manifest.bzl
Comment thread python/private/python_repository.bzl Outdated
The static libpython transition started with the 20250517 Astral build, not 20250604. Also allow users to force inclusion or exclusion when auto-detection does not match a mirrored or customized runtime.

Expose the auto/include/exclude mode through the Python extension overrides and document the Starlark helper argument types.
Document the auto, include, and exclude modes exposed by the Python extension, including the 20250517 Astral cutoff and fallback behavior for unknown runtimes.

Also standardize indentation in the generated hermetic runtime BUILD definition.
@gfrankliu

gfrankliu commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor Author

It seems one of the CI jobs hangs, how to re-trigger it?

@gfrankliu

Copy link
Copy Markdown
Contributor Author

@aignas @rickeylev Any more comments? Or can this be merged?

@rickeylev

Copy link
Copy Markdown
Collaborator

Thanks for the PR! Yeah, we've been debating about how to exclude libpython, since its often not needed.

Excluding shared libpython.so files by default from recognized Astral Python Standalone runtimes affects all targets using the hermetic toolchain, not just py_zipapp.

Add news/4165.changed.md to document the default behavior change and the libpython override attribute.
@rickeylev
rickeylev enabled auto-merge September 27, 2026 19:40
@rickeylev
rickeylev added this pull request to the merge queue Sep 27, 2026
Merged via the queue into bazel-contrib:main with commit fd22cff Sep 27, 2026
5 checks passed
@rickeylev

Copy link
Copy Markdown
Collaborator

/backport

@github-actions

Copy link
Copy Markdown
Contributor

Workflow failed for command pr-backport (comment).

See workflow run for logs.

@rickeylev

Copy link
Copy Markdown
Collaborator

/backport

@github-actions github-actions Bot mentioned this pull request Sep 27, 2026
7 tasks
github-actions Bot pushed a commit that referenced this pull request Sep 27, 2026
Bazel reports source symlinks as regular files, causing Python
executable aliases to be copied into zipapps as full binaries. Hermetic
runtimes also include shared libpython artifacts even when the
interpreter links Python statically.

Preserve source symlinks through sandbox indirection and exclude
libpython shared objects from runtime files while retaining them for
explicit native dependencies. Add regression coverage for archive
structure and native extension loading.

Fixes #3534
Fixes #4163

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Richard Levasseur <richardlev@gmail.com>
(cherry picked from commit fd22cff)

Work towards #4175
rickeylev pushed a commit to rickeylev/rules_python that referenced this pull request Sep 27, 2026
Updates CHANGELOG.md and removes news files for backports:
- bazel-contrib#4165

Work towards bazel-contrib#4175
Release-Tracking-Issue: bazel-contrib#4175

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@rickeylev

Copy link
Copy Markdown
Collaborator

posterity

add some auto detection of which Astral version is used and then decide if libpython is needed. (not sure if this is possible).

I was playing with this idea a bit a couple months ago. Its possible, but also somewhat of a pain. During the repo phase, we could inspect the binary and look at its linkage. That incurs having to use some host-compatible tool to do that. TBH I like this idea the best, but don't have time to set that up. Full compatibility would need {elf,macho,winpe} x {win,mac,linux} x {arm,x86} tools available.

A variation of the above was to look at PYTHON.json, which i think did tell the linkage, but the install_only archives don't contain python.json.

The second idea I was toying with was to have a build action inspect things and then conditionally emit libpython in the depsets. At the time, this ran into the bug where generated files couldn't be put into the runtime (now fixed). However, I then ran into the problem of having a conditional file output in bazel. Best I could come up with was either (1) generate an empty .so file (valid, but feels hacky) or (2) directory artifacts (not sure how this would fit into things more broadly).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zipapp exploded the python interpreter bin/python size exploded 2000 times larger

4 participants