feat(sdist): dynamic setup.py evaluator for build dep extraction#874
Merged
feat(sdist): dynamic setup.py evaluator for build dep extraction#874
Conversation
Exec setup.py with a capturing setup() and mock import system to extract setup_requires and install_requires from legacy packages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b6711b6 to
12b280e
Compare
|
- _SetupCapture now inherits BaseException so `except Exception:` in setup.py files can't swallow the capture signal - Catch BaseException (not just Exception) so sys.exit() doesn't crash detect() - Accept *args in _fake_setup for ancient positional-arg setup() calls - Save/restore sys.path and os.getcwd() to prevent state leaks - Add 11 new test cases covering real-world setup.py patterns: __main__ guard, qualified distutils call, file I/O failure, setup() never called, sys.exit(), platform conditionals, pkg_resources import, os.chdir(), sys.path mutation, Extension objects, setup.cfg+setup.py merging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The build dep discovery logging via print() was very noisy during normal Bazel builds. Remove the function and its call sites entirely. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Adds a dynamic
setup.pyevaluator to the sdist configure tool. Instead of trying to statically analyze setup.py via AST (which only covers a shrinking subset of simple cases), weexec()the file with a capturingsetup()injected into module globals and a mock import system that preventsImportErroron missing packages.This is inherently unsound — setup.py is arbitrary Python — but we're already running in Bazel's sandbox with a hermetic interpreter, so the blast radius is contained. The approach handles dynamic patterns (conditionals, function calls, file reads, platform checks) that real-world legacy packages actually use.
How it works
_fake_setup(*args, **kwargs)captures all keyword args then raises_SetupCaptureto abort execution_SetupCaptureinherits fromBaseExceptionsoexcept Exception:blocks in setup.py can't swallow it_MockModuleprovides a recursive mock object for any attribute access, preventing crashes fromfrom mypackage import __version__etc._MockImportFinderintercepts imports viasys.meta_path, returning mock modules for anything not in stdlibsetuptools.setup,distutils.core.setup, and baresetupin globals all route to the capturing functionsys.modules,sys.meta_path,sys.argv,sys.path,os.getcwd()) is fully restored in afinallyblockBaseExceptioncatch handlesSystemExitfromsys.exit()calls gracefullyExtracted
setup_requiresfeed intobuild_requiresandextra_deps.install_requiresis reported in a newsetup_py_install_requiresfield.Also removes the noisy
_log_build_dep_infoprint statements that spammed Bazel output during every sdist build.Changes are visible to end-users: no
Test plan