@@ -864,6 +864,56 @@ def test_PYTHONHOME_in_venv(self):
864864 actual = getpath (ns , expected )
865865 self .assertEqual (expected , actual )
866866
867+ def test_venv_w_symlinked_base_executable (self ):
868+ """
869+ If we symlink the base executable, and point to it via home in pyvenv.cfg,
870+ we should have it as sys.executable (and sys.prefix should be the resolved location)
871+ """
872+ ns = MockPosixNamespace (
873+ argv0 = "/venv/bin/python3" ,
874+ PREFIX = "/some/_internal/prefix" ,
875+ base_exec_prefix = "/foo/bar"
876+ )
877+ # Setup venv
878+ ns .add_known_xfile ("/venv/bin/python3" )
879+ ns .add_known_xfile ("/usr/local/bin/python3" )
880+ ns .add_known_xfile ("/some/_internal/prefix/bin/python3" )
881+
882+ ns .add_known_file ("/venv/pyvenv.cfg" , [
883+ # The published based executable location is /usr/local/bin - we don't want to
884+ # expose /some/internal/directory (this location can change under our feet)
885+ r"home = /usr/local/bin"
886+ ])
887+ ns .add_known_link ("/venv/bin/python3" , "/usr/local/bin/python3" )
888+ ns .add_known_link ("/usr/local/bin/python3" , "/some/_internal/prefix/bin/python3" )
889+
890+ ns .add_known_file ("/some/_internal/prefix/lib/python9.8/os.py" )
891+ ns .add_known_dir ("/some/_internal/prefix/lib/python9.8/lib-dynload" )
892+
893+ # Put a file completely outside of /usr/local to validate that the issue
894+ # in https://github.com/python/cpython/issues/106045 is resolved.
895+ ns .add_known_dir ("/usr/lib/python9.8/lib-dynload" )
896+
897+ expected = dict (
898+ executable = "/venv/bin/python3" ,
899+ prefix = "/venv" ,
900+ exec_prefix = "/venv" ,
901+ base_prefix = "/some/_internal/prefix" ,
902+ base_exec_prefix = "/some/_internal/prefix" ,
903+ # It is important to maintain the link to the original executable, as this
904+ # is used when creating a new virtual environment (which should also have home
905+ # set to /usr/local/bin to avoid bleeding the internal path to the venv)
906+ base_executable = "/usr/local/bin/python3" ,
907+ module_search_paths_set = 1 ,
908+ module_search_paths = [
909+ "/some/_internal/prefix/lib/python98.zip" ,
910+ "/some/_internal/prefix/lib/python9.8" ,
911+ "/some/_internal/prefix/lib/python9.8/lib-dynload" ,
912+ ],
913+ )
914+ actual = getpath (ns , expected )
915+ self .assertEqual (expected , actual )
916+
867917# ******************************************************************************
868918
869919DEFAULT_NAMESPACE = dict (
0 commit comments