@@ -791,6 +791,55 @@ def test_uvx_zipapps():
791791 assert b"app.pyz" in output , output .decode ("utf-8" , "replace" )
792792
793793
794+ if hasattr (os , "fork" ):
795+ def test_multiprocessing ():
796+ """
797+ Test deleting the cache with a multiprocessing app.
798+
799+ When the app forks, the cache directory should only be cleared if the main process exits.
800+ """
801+ _clean_paths (root = False )
802+
803+ secret = "XXXXXXXXXXXXX"
804+ unzip_path = (test_path / "multiprocessing_test" / "cache" ).absolute ()
805+
806+ Path ("test.so" ).touch ()
807+
808+ mock_main = Path ("mock_main.py" )
809+ mock_main .touch ()
810+ mock_main .write_text (
811+ f"def main():\n "
812+ f" import os, sys, os.path\n "
813+ f" is_parent = os.fork()\n "
814+ f" print({ secret !r} , file=sys.stderr if is_parent else sys.stdout)\n "
815+ f" if not is_parent: return\n "
816+ f" os.waitpid(is_parent, 0)\n "
817+ f" print(os.path.isdir({ str (unzip_path )!r} ))\n "
818+ )
819+ app_path = create_app (
820+ includes = "mock_main.py" ,
821+ main = "mock_main:main" ,
822+ unzip = "test.so" ,
823+ clear_zipapps_cache = True ,
824+ unzip_path = str (unzip_path ),
825+ )
826+ stdout_output , stderr_output = subprocess .Popen (
827+ [sys .executable , str (app_path )],
828+ stderr = subprocess .PIPE ,
829+ stdout = subprocess .PIPE ,
830+ text = True ,
831+ ).communicate ()
832+
833+ assert not unzip_path .exists (), "Cache dir should be deleted"
834+
835+ secret += "\n "
836+ # Test that the child printed the string successfully and no errors got raised
837+ assert stderr_output == secret , f"unexpected stderr: { stderr_output !r} "
838+ # Test that the main process prints the string sucessfully
839+ # and that the cache directory still exists after the child exits
840+ assert stdout_output == (secret + "True\n " ), f"unexpected stdout: { stdout_output !r} "
841+
842+
794843def main ():
795844 """
796845 test all cases
0 commit comments