-
Notifications
You must be signed in to change notification settings - Fork 39
Refactor(bootstrapper): add force_prebuilt flag, remove _handle_test_mode_failure #894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
LalatenduMohanty
wants to merge
2
commits into
python-wheel-build:main
Choose a base branch
from
LalatenduMohanty:test-mode-cleanup-2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -277,12 +277,55 @@ def bootstrap(self, req: Requirement, req_type: RequirementType) -> None: | |
| self._bootstrap_impl( | ||
| req, req_type, source_url, resolved_version, build_sdist_only | ||
| ) | ||
| except Exception as err: | ||
| except Exception as build_error: | ||
| if not self.test_mode: | ||
| raise | ||
| self._record_test_mode_failure( | ||
| req, str(resolved_version), err, "bootstrap" | ||
|
|
||
| # Test mode: attempt pre-built fallback (may resolve different version) | ||
| logger.warning( | ||
| "test mode: build failed for %s==%s, attempting pre-built fallback: %s", | ||
| req.name, | ||
| resolved_version, | ||
| build_error, | ||
| ) | ||
| try: | ||
| wheel_url, fallback_version = self._resolve_prebuilt_with_history( | ||
| req=req, | ||
| req_type=req_type, | ||
| ) | ||
| if fallback_version != resolved_version: | ||
| logger.warning( | ||
| "test mode: version mismatch for %s - requested %s, fallback %s", | ||
| req.name, | ||
| resolved_version, | ||
| fallback_version, | ||
| ) | ||
| # wheel_url passed as source_url; force_prebuilt ensures it's | ||
| # treated as a wheel URL, not an sdist URL | ||
| self._bootstrap_impl( | ||
| req, | ||
| req_type, | ||
| wheel_url, | ||
| fallback_version, | ||
| build_sdist_only, | ||
| force_prebuilt=True, | ||
| ) | ||
| logger.info( | ||
| "test mode: successfully used pre-built wheel for %s==%s", | ||
| req.name, | ||
| fallback_version, | ||
| ) | ||
| except Exception as fallback_error: | ||
| logger.error( | ||
| "test mode: pre-built fallback also failed for %s: %s", | ||
| req.name, | ||
| fallback_error, | ||
| exc_info=True, | ||
| ) | ||
| # Record the original build error, not the fallback error | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to record the original error even if the fallback is successful. We could also record the fallback error, as additional information, because it would be useful to know that we weren't able to do anything at all for the package. |
||
| self._record_test_mode_failure( | ||
| req, str(resolved_version), build_error, "bootstrap" | ||
| ) | ||
|
|
||
| def _bootstrap_impl( | ||
| self, | ||
|
|
@@ -291,6 +334,7 @@ def _bootstrap_impl( | |
| source_url: str, | ||
| resolved_version: Version, | ||
| build_sdist_only: bool, | ||
| force_prebuilt: bool = False, | ||
| ) -> None: | ||
| """Internal implementation - performs the actual bootstrap work. | ||
|
|
||
|
|
@@ -299,9 +343,11 @@ def _bootstrap_impl( | |
| Args: | ||
| req: The requirement to bootstrap. | ||
| req_type: The type of requirement. | ||
| source_url: The resolved source URL. | ||
| source_url: The resolved source URL (sdist or wheel URL). | ||
| resolved_version: The resolved version. | ||
| build_sdist_only: Whether to build only sdist (no wheel). | ||
| force_prebuilt: If True, treat source_url as a wheel URL and skip | ||
| source build. Used for test-mode fallback after build failure. | ||
|
|
||
| Error Handling: | ||
| Fatal errors (source build, prebuilt download) raise exceptions | ||
|
|
@@ -322,7 +368,7 @@ def _bootstrap_impl( | |
| cached_wheel_filename: pathlib.Path | None = None | ||
| unpacked_cached_wheel: pathlib.Path | None = None | ||
|
|
||
| if pbi.pre_built: | ||
| if pbi.pre_built or force_prebuilt: | ||
| wheel_filename, unpack_dir = self._download_prebuilt( | ||
| req=req, | ||
| req_type=req_type, | ||
|
|
@@ -343,12 +389,11 @@ def _bootstrap_impl( | |
| req, resolved_version | ||
| ) | ||
|
|
||
| # Build from source (handles test-mode fallback internally) | ||
| # Build from source | ||
| build_result = self._build_from_source( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| source_url=source_url, | ||
| req_type=req_type, | ||
| build_sdist_only=build_sdist_only, | ||
| cached_wheel_filename=cached_wheel_filename, | ||
| unpacked_cached_wheel=unpacked_cached_wheel, | ||
|
|
@@ -779,166 +824,72 @@ def _build_from_source( | |
| req: Requirement, | ||
| resolved_version: Version, | ||
| source_url: str, | ||
| req_type: RequirementType, | ||
| build_sdist_only: bool, | ||
| cached_wheel_filename: pathlib.Path | None, | ||
| unpacked_cached_wheel: pathlib.Path | None, | ||
| ) -> SourceBuildResult: | ||
| """Build package from source. | ||
|
|
||
| Orchestrates download, preparation, build environment setup, and build. | ||
| In test mode, attempts pre-built fallback on failure. | ||
|
|
||
| Raises: | ||
| Exception: In normal mode, if build fails. | ||
| In test mode, only if build fails AND fallback also fails. | ||
| Exception: If any step fails. In test mode, bootstrap() handles | ||
| fallback to pre-built wheels. | ||
| """ | ||
| try: | ||
| # Download and prepare source (if no cached wheel) | ||
| if not unpacked_cached_wheel: | ||
| logger.debug("no cached wheel, downloading sources") | ||
| source_filename = self._download_source( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| source_url=source_url, | ||
| ) | ||
| sdist_root_dir = self._prepare_source( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| source_filename=source_filename, | ||
| ) | ||
| else: | ||
| logger.debug(f"have cached wheel in {unpacked_cached_wheel}") | ||
| sdist_root_dir = unpacked_cached_wheel / unpacked_cached_wheel.stem | ||
|
|
||
| assert sdist_root_dir is not None | ||
|
|
||
| if sdist_root_dir.parent.parent != self.ctx.work_dir: | ||
| raise ValueError( | ||
| f"'{sdist_root_dir}/../..' should be {self.ctx.work_dir}" | ||
| ) | ||
| unpack_dir = sdist_root_dir.parent | ||
|
|
||
| build_env = self._create_build_env( | ||
| # Download and prepare source (if no cached wheel) | ||
| if not unpacked_cached_wheel: | ||
| logger.debug("no cached wheel, downloading sources") | ||
| source_filename = self._download_source( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| parent_dir=sdist_root_dir.parent, | ||
| ) | ||
|
|
||
| # Prepare build dependencies (always needed) | ||
| # Note: This may recursively call bootstrap() for build deps, | ||
| # which has its own error handling. | ||
| self._prepare_build_dependencies(req, sdist_root_dir, build_env) | ||
|
|
||
| # Build wheel or sdist | ||
| wheel_filename, sdist_filename = self._do_build( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| sdist_root_dir=sdist_root_dir, | ||
| build_env=build_env, | ||
| build_sdist_only=build_sdist_only, | ||
| cached_wheel_filename=cached_wheel_filename, | ||
| ) | ||
|
|
||
| source_type = sources.get_source_type(self.ctx, req) | ||
|
|
||
| return SourceBuildResult( | ||
| wheel_filename=wheel_filename, | ||
| sdist_filename=sdist_filename, | ||
| unpack_dir=unpack_dir, | ||
| sdist_root_dir=sdist_root_dir, | ||
| build_env=build_env, | ||
| source_type=source_type, | ||
| source_url=source_url, | ||
| ) | ||
|
|
||
| except Exception as build_error: | ||
| if not self.test_mode: | ||
| raise | ||
|
|
||
| # Test mode: attempt pre-built fallback | ||
| fallback_result = self._handle_test_mode_failure( | ||
| sdist_root_dir = self._prepare_source( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| req_type=req_type, | ||
| build_error=build_error, | ||
| source_filename=source_filename, | ||
| ) | ||
| if fallback_result is None: | ||
| # Fallback failed, re-raise for bootstrap() to catch | ||
| raise | ||
|
|
||
| return fallback_result | ||
| else: | ||
| logger.debug(f"have cached wheel in {unpacked_cached_wheel}") | ||
| sdist_root_dir = unpacked_cached_wheel / unpacked_cached_wheel.stem | ||
|
|
||
| def _handle_test_mode_failure( | ||
| self, | ||
| req: Requirement, | ||
| resolved_version: Version, | ||
| req_type: RequirementType, | ||
| build_error: Exception, | ||
| ) -> SourceBuildResult | None: | ||
| """Handle build failure in test mode by attempting pre-built fallback. | ||
| assert sdist_root_dir is not None | ||
|
|
||
| Args: | ||
| req: The requirement that failed to build. | ||
| resolved_version: The version that was attempted. | ||
| req_type: The type of requirement (for fallback resolution). | ||
| build_error: The original exception from the build attempt. | ||
| if sdist_root_dir.parent.parent != self.ctx.work_dir: | ||
| raise ValueError(f"'{sdist_root_dir}/../..' should be {self.ctx.work_dir}") | ||
| unpack_dir = sdist_root_dir.parent | ||
|
|
||
| Returns: | ||
| SourceBuildResult if fallback succeeded, None if fallback also failed. | ||
| """ | ||
| logger.warning( | ||
| "test mode: build failed for %s==%s, attempting pre-built fallback: %s", | ||
| req.name, | ||
| resolved_version, | ||
| build_error, | ||
| build_env = self._create_build_env( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| parent_dir=sdist_root_dir.parent, | ||
| ) | ||
|
|
||
| try: | ||
| wheel_url, fallback_version = self._resolve_prebuilt_with_history( | ||
| req=req, | ||
| req_type=req_type, | ||
| ) | ||
| # Prepare build dependencies (always needed) | ||
| # Note: This may recursively call bootstrap() for build deps, | ||
| # which has its own error handling. | ||
| self._prepare_build_dependencies(req, sdist_root_dir, build_env) | ||
|
|
||
| if fallback_version != resolved_version: | ||
| logger.warning( | ||
| "test mode: version mismatch for %s - requested %s, fallback %s", | ||
| req.name, | ||
| resolved_version, | ||
| fallback_version, | ||
| ) | ||
|
|
||
| wheel_filename, unpack_dir = self._download_prebuilt( | ||
| req=req, | ||
| req_type=req_type, | ||
| resolved_version=fallback_version, | ||
| wheel_url=wheel_url, | ||
| ) | ||
|
|
||
| logger.info( | ||
| "test mode: successfully used pre-built wheel for %s==%s", | ||
| req.name, | ||
| fallback_version, | ||
| ) | ||
| # Package succeeded via fallback - no failure to record | ||
| # Build wheel or sdist | ||
| wheel_filename, sdist_filename = self._do_build( | ||
| req=req, | ||
| resolved_version=resolved_version, | ||
| sdist_root_dir=sdist_root_dir, | ||
| build_env=build_env, | ||
| build_sdist_only=build_sdist_only, | ||
| cached_wheel_filename=cached_wheel_filename, | ||
| ) | ||
|
|
||
| return SourceBuildResult( | ||
| wheel_filename=wheel_filename, | ||
| sdist_filename=None, | ||
| unpack_dir=unpack_dir, | ||
| sdist_root_dir=None, | ||
| build_env=None, | ||
| source_type=SourceType.PREBUILT, | ||
| ) | ||
| source_type = sources.get_source_type(self.ctx, req) | ||
|
|
||
| except Exception as fallback_error: | ||
| logger.error( | ||
| "test mode: pre-built fallback also failed for %s: %s", | ||
| req.name, | ||
| fallback_error, | ||
| exc_info=True, | ||
| ) | ||
| # Return None to signal failure; bootstrap() will record via re-raised exception | ||
| return None | ||
| return SourceBuildResult( | ||
| wheel_filename=wheel_filename, | ||
| sdist_filename=sdist_filename, | ||
| unpack_dir=unpack_dir, | ||
| sdist_root_dir=sdist_root_dir, | ||
| build_env=build_env, | ||
| source_type=source_type, | ||
| ) | ||
|
|
||
| def _look_for_existing_wheel( | ||
| self, | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need to resolve again, we have the version we want, we should just be able to call
_bootstrap_implagain with the force flag set to true.