1919from packaging .utils import NormalizedName , canonicalize_name
2020from packaging .version import Version
2121from resolvelib .resolvers import ResolverException
22+ from trampoline import trampoline
2223
2324from . import (
2425 bootstrap_requirement_resolver ,
@@ -274,6 +275,16 @@ def _processing_build_requirement(self, current_req_type: RequirementType) -> bo
274275 def bootstrap (self , req : Requirement , req_type : RequirementType ) -> None :
275276 """Bootstrap a package and its dependencies.
276277
278+ Public entry point that drives the generator-based implementation
279+ via trampoline() for iterative (non-recursive) execution.
280+ """
281+ trampoline (self ._bootstrap_gen (req , req_type ))
282+
283+ def _bootstrap_gen (
284+ self , req : Requirement , req_type : RequirementType
285+ ) -> typing .Generator [typing .Any , typing .Any , None ]:
286+ """Generator that bootstraps a package and its dependencies.
287+
277288 Handles setup, validation, and error handling. Delegates actual build
278289 work to _bootstrap_impl().
279290
@@ -307,7 +318,9 @@ def bootstrap(self, req: Requirement, req_type: RequirementType) -> None:
307318
308319 # Bootstrap each resolved version
309320 for source_url , resolved_version in resolved_versions :
310- self ._bootstrap_single_version (req , req_type , source_url , resolved_version )
321+ yield self ._bootstrap_single_version (
322+ req , req_type , source_url , resolved_version
323+ )
311324
312325 # In multiple versions mode, report any failures for this requirement
313326 if self .multiple_versions and self ._failed_versions :
@@ -329,7 +342,7 @@ def _bootstrap_single_version(
329342 req_type : RequirementType ,
330343 source_url : str ,
331344 resolved_version : Version ,
332- ) -> None :
345+ ) -> typing . Generator [ typing . Any , typing . Any , None ] :
333346 """Bootstrap a single version of a package.
334347
335348 Extracted from bootstrap() to handle both single and multiple version modes.
@@ -365,7 +378,7 @@ def _bootstrap_single_version(
365378 # Track dependency chain - context manager ensures cleanup even on exception
366379 with self ._track_why (req_type , req , resolved_version ):
367380 try :
368- self ._bootstrap_impl (
381+ yield self ._bootstrap_impl (
369382 req , req_type , source_url , resolved_version , build_sdist_only
370383 )
371384 except Exception as err :
@@ -400,7 +413,7 @@ def _bootstrap_impl(
400413 source_url : str ,
401414 resolved_version : Version ,
402415 build_sdist_only : bool ,
403- ) -> None :
416+ ) -> typing . Generator [ typing . Any , typing . Any , None ] :
404417 """Internal implementation - performs the actual bootstrap work.
405418
406419 Called by bootstrap() after setup, validation, and seen-checking.
@@ -453,7 +466,7 @@ def _bootstrap_impl(
453466 )
454467
455468 # Build from source (handles test-mode fallback internally)
456- build_result = self ._build_from_source (
469+ build_result = yield self ._build_from_source (
457470 req = req ,
458471 resolved_version = resolved_version ,
459472 source_url = source_url ,
@@ -520,9 +533,7 @@ def _bootstrap_impl(
520533 self .progressbar .update_total (len (install_dependencies ))
521534 for dep in self ._sort_requirements (install_dependencies ):
522535 with req_ctxvar_context (dep ):
523- # In test mode, bootstrap() catches and records failures internally.
524- # In normal mode, it raises immediately which we propagate.
525- self .bootstrap (req = dep , req_type = RequirementType .INSTALL )
536+ yield self ._bootstrap_gen (req = dep , req_type = RequirementType .INSTALL )
526537 self .progressbar .update ()
527538
528539 # Clean up build directories
@@ -643,15 +654,15 @@ def _prepare_build_dependencies(
643654 resolved_version : Version | None ,
644655 sdist_root_dir : pathlib .Path ,
645656 build_env : build_environment .BuildEnvironment ,
646- ) -> set [Requirement ]:
657+ ) -> typing . Generator [ typing . Any , typing . Any , set [Requirement ] ]:
647658 # build system
648659 build_system_dependencies = dependencies .get_build_system_dependencies (
649660 ctx = self .ctx ,
650661 req = req ,
651662 version = resolved_version ,
652663 sdist_root_dir = sdist_root_dir ,
653664 )
654- self ._handle_build_requirements (
665+ yield self ._handle_build_requirements (
655666 req ,
656667 RequirementType .BUILD_SYSTEM ,
657668 build_system_dependencies ,
@@ -667,7 +678,7 @@ def _prepare_build_dependencies(
667678 sdist_root_dir = sdist_root_dir ,
668679 build_env = build_env ,
669680 )
670- self ._handle_build_requirements (
681+ yield self ._handle_build_requirements (
671682 req ,
672683 RequirementType .BUILD_BACKEND ,
673684 build_backend_dependencies ,
@@ -681,7 +692,7 @@ def _prepare_build_dependencies(
681692 sdist_root_dir = sdist_root_dir ,
682693 build_env = build_env ,
683694 )
684- self ._handle_build_requirements (
695+ yield self ._handle_build_requirements (
685696 req ,
686697 RequirementType .BUILD_SDIST ,
687698 build_sdist_dependencies ,
@@ -702,14 +713,12 @@ def _handle_build_requirements(
702713 req : Requirement ,
703714 build_type : RequirementType ,
704715 build_dependencies : set [Requirement ],
705- ) -> None :
716+ ) -> typing . Generator [ typing . Any , typing . Any , None ] :
706717 self .progressbar .update_total (len (build_dependencies ))
707718
708719 for dep in self ._sort_requirements (build_dependencies ):
709720 with req_ctxvar_context (dep ):
710- # In test mode, bootstrap() catches and records failures internally.
711- # In normal mode, it raises immediately which we propagate.
712- self .bootstrap (req = dep , req_type = build_type )
721+ yield self ._bootstrap_gen (req = dep , req_type = build_type )
713722 self .progressbar .update ()
714723
715724 def _download_prebuilt (
@@ -896,7 +905,7 @@ def _build_from_source(
896905 build_sdist_only : bool ,
897906 cached_wheel_filename : pathlib .Path | None ,
898907 unpacked_cached_wheel : pathlib .Path | None ,
899- ) -> SourceBuildResult :
908+ ) -> typing . Generator [ typing . Any , typing . Any , SourceBuildResult ] :
900909 """Build package from source.
901910
902911 Orchestrates download, preparation, build environment setup, and build.
@@ -939,9 +948,7 @@ def _build_from_source(
939948 )
940949
941950 # Prepare build dependencies (always needed)
942- # Note: This may recursively call bootstrap() for build deps,
943- # which has its own error handling.
944- self ._prepare_build_dependencies (
951+ yield self ._prepare_build_dependencies (
945952 req = req ,
946953 resolved_version = resolved_version ,
947954 sdist_root_dir = sdist_root_dir ,
@@ -1334,11 +1341,13 @@ def _get_version_from_package_metadata(
13341341 ctx = self .ctx ,
13351342 parent_dir = source_dir .parent ,
13361343 )
1337- build_dependencies = self ._prepare_build_dependencies (
1338- req = req ,
1339- resolved_version = None ,
1340- sdist_root_dir = source_dir ,
1341- build_env = build_env ,
1344+ build_dependencies = trampoline (
1345+ self ._prepare_build_dependencies (
1346+ req = req ,
1347+ resolved_version = None ,
1348+ sdist_root_dir = source_dir ,
1349+ build_env = build_env ,
1350+ )
13421351 )
13431352 build_env .install (build_dependencies )
13441353
0 commit comments