@@ -641,6 +641,7 @@ class DocBuilder:
641641 cpython_repo : Repository
642642 docs_by_version_content : bytes
643643 switchers_content : bytes
644+ built_venvs : set [Path ]
644645 build_root : Path
645646 www_root : Path
646647 select_output : Literal ["no-html" , "only-html" , "only-html-en" ] | None
@@ -804,22 +805,28 @@ def build(self) -> None:
804805 def build_venv (self ) -> None :
805806 """Build a venv for the specific Python version.
806807
807- The venv is recreated from scratch for every build: pip considers
808- a requirement satisfied when the installed version number matches,
809- even if the requirement is a direct URL now pointing at different
810- code, so a reused venv can silently keep outdated packages.
808+ The venv is created at most once per run and reused by later
809+ builds of the same version: reusing a venv across runs can
810+ silently keep outdated packages, because pip considers a
811+ requirement satisfied when the installed version number matches,
812+ even if the requirement is a direct URL now pointing at
813+ different code.
811814 """
812- requirements = list (self .build_meta .dependencies )
813- if self .includes_html :
814- # opengraph previews
815- requirements .append ("matplotlib>=3" )
816-
817815 venv_name = self .build_meta .venv_name
818816 if self .select_output is not None :
819817 # Never share a venv with a concurrent differently-selected
820818 # build, which may recreate it mid-build.
821819 venv_name += f"-{ self .select_output } "
822820 venv_path = self .build_root / venv_name
821+ if venv_path in self .built_venvs :
822+ self .venv = venv_path
823+ return
824+
825+ requirements = list (self .build_meta .dependencies )
826+ if self .includes_html :
827+ # opengraph previews
828+ requirements .append ("matplotlib>=3" )
829+
823830 venv .create (
824831 venv_path ,
825832 symlinks = os .name != "nt" ,
@@ -840,6 +847,7 @@ def build_venv(self) -> None:
840847 cwd = self .checkout / "Doc" ,
841848 )
842849 run ((python , "-m" , "pip" , "freeze" , "--all" ))
850+ self .built_venvs .add (venv_path )
843851 self .venv = venv_path
844852
845853 def setup_indexsidebar (self ) -> None :
@@ -1275,6 +1283,7 @@ def build_docs(args: argparse.Namespace) -> int:
12751283 "https://github.com/python/cpython.git" ,
12761284 args .build_root / _checkout_name (args .select_output ),
12771285 )
1286+ built_venvs : set [Path ] = set ()
12781287 while todo :
12791288 build_props = todo .pop ()
12801289 logging .root .handlers [0 ].setFormatter (
@@ -1292,6 +1301,7 @@ def build_docs(args: argparse.Namespace) -> int:
12921301 cpython_repo ,
12931302 docs_by_version_content ,
12941303 switchers_content ,
1304+ built_venvs ,
12951305 ** vars (args ),
12961306 )
12971307 built_successfully = builder .run (http , force_build = force_build )
0 commit comments