2727import subprocess
2828from typing import List
2929
30- from freecad .utils import get_python_exe
31-
3230import addonmanager_freecad_interface as fci
3331from addonmanager_pyside_interface import QObject , Signal , is_interruption_requested
3432
@@ -46,7 +44,7 @@ class DependencyInstaller(QObject):
4644 no_python_exe = Signal ()
4745 no_pip = Signal (str ) # Attempted command
4846 failure = Signal (str , str ) # Short message, detailed message
49- finished = Signal ()
47+ finished = Signal (bool ) # True if everything completed normally, otherwise false
5048
5149 def __init__ (
5250 self ,
@@ -65,17 +63,25 @@ def __init__(
6563 self .python_requires = python_requires
6664 self .python_optional = python_optional
6765 self .location = location
66+ self .required_succeeded = False
67+ self .finished_successfully = False
6868
6969 def run (self ):
7070 """Normally not called directly, but rather connected to the worker thread's started
7171 signal."""
72- if self . _verify_pip () :
72+ try :
7373 if self .python_requires or self .python_optional :
74- if not is_interruption_requested ():
75- self ._install_python_packages ()
76- if not is_interruption_requested ():
77- self ._install_addons ()
78- self .finished .emit ()
74+ if self ._verify_pip ():
75+ if not is_interruption_requested ():
76+ self ._install_python_packages ()
77+ else :
78+ self .required_succeeded = True
79+ if not is_interruption_requested ():
80+ self ._install_addons ()
81+ self .finished_successfully = self .required_succeeded
82+ except RuntimeError :
83+ pass
84+ self .finished .emit (self .finished_successfully )
7985
8086 def _install_python_packages (self ):
8187 """Install required and optional Python dependencies using pip."""
@@ -87,20 +93,20 @@ def _install_python_packages(self):
8793 if not os .path .exists (vendor_path ):
8894 os .makedirs (vendor_path )
8995
90- self ._install_required (vendor_path )
96+ self .required_succeeded = self . _install_required (vendor_path )
9197 self ._install_optional (vendor_path )
9298
9399 def _verify_pip (self ) -> bool :
94100 """Ensure that pip is working -- returns True if it is, or False if not. Also emits the
95101 no_pip signal if pip cannot execute."""
96- python_exe = self ._get_python ()
97- if not python_exe :
98- return False
99102 try :
100103 proc = self ._run_pip (["--version" ])
101104 fci .Console .PrintMessage (proc .stdout + "\n " )
105+ if proc .returncode != 0 :
106+ return False
102107 except subprocess .CalledProcessError :
103- self .no_pip .emit (f"{ python_exe } -m pip --version" )
108+ call = utils .create_pip_call ([])
109+ self .no_pip .emit (" " .join (call ))
104110 return False
105111 return True
106112
@@ -115,7 +121,6 @@ def _install_required(self, vendor_path: str) -> bool:
115121 proc = self ._run_pip (
116122 [
117123 "install" ,
118- "--disable-pip-version-check" ,
119124 "--target" ,
120125 vendor_path ,
121126 pymod ,
@@ -144,7 +149,6 @@ def _install_optional(self, vendor_path: str):
144149 proc = self ._run_pip (
145150 [
146151 "install" ,
147- "--disable-pip-version-check" ,
148152 "--target" ,
149153 vendor_path ,
150154 pymod ,
@@ -160,22 +164,13 @@ def _install_optional(self, vendor_path: str):
160164 )
161165
162166 def _run_pip (self , args ):
163- python_exe = self ._get_python ()
164- final_args = [python_exe , "-m" , "pip" ]
165- final_args .extend (args )
167+ final_args = utils .create_pip_call (args )
166168 return self ._subprocess_wrapper (final_args )
167169
168170 @staticmethod
169171 def _subprocess_wrapper (args ) -> subprocess .CompletedProcess :
170172 """Wrap subprocess call so test code can mock it."""
171- return utils .run_interruptable_subprocess (args )
172-
173- def _get_python (self ) -> str :
174- """Wrap Python access so test code can mock it."""
175- python_exe = get_python_exe ()
176- if not python_exe :
177- self .no_python_exe .emit ()
178- return python_exe
173+ return utils .run_interruptable_subprocess (args , timeout_secs = 120 )
179174
180175 def _install_addons (self ):
181176 for addon in self .addons :
0 commit comments