1717
1818# Third-party imports
1919from qtpy .QtCore import Qt , QThread , QTimer , Signal
20- from qtpy .QtWidgets import QMessageBox , QWidget , QProgressBar , QPushButton
20+ from qtpy .QtWidgets import (
21+ QGridLayout ,
22+ QMessageBox ,
23+ QProgressBar ,
24+ QPushButton ,
25+ QTextEdit ,
26+ QWidget ,
27+ )
2128from spyder_kernels .utils .pythonenv import is_conda_env
2229
2330# Local imports
2431from spyder import __version__
2532from spyder .api .config .mixins import SpyderConfigurationAccessor
33+ from spyder .api .fonts import SpyderFontsMixin , SpyderFontType
2634from spyder .api .translations import _
27- from spyder .config .base import is_conda_based_app
35+ from spyder .config .base import is_conda_based_app , is_installed_all_users
2836from spyder .config .gui import is_dark_interface
2937from spyder .plugins .updatemanager .workers import (
3038 UpdateType ,
3644from spyder .plugins .updatemanager .utils import get_updater_info
3745from spyder .utils .conda import find_conda , is_anaconda_pkg
3846from spyder .utils .palette import SpyderPalette
39- from spyder .utils .programs import get_temp_dir , is_program_installed
47+ from spyder .utils .programs import (
48+ get_temp_dir ,
49+ is_program_installed ,
50+ find_program
51+ )
4052from spyder .widgets .helperwidgets import MessageCheckBox
4153
4254# Logger setup
@@ -365,7 +377,7 @@ def _start_update_updater(self):
365377 )
366378
367379 self .update_updater_worker .sig_ready .connect (
368- lambda x : self ._start_download () if x else None
380+ self ._process_update_updater
369381 )
370382 self .update_updater_worker .sig_ready .connect (
371383 self .update_updater_thread .quit
@@ -379,6 +391,32 @@ def _start_update_updater(self):
379391 )
380392 self .update_updater_thread .start ()
381393
394+ def _process_update_updater (self ):
395+ """Process possible errors when updating the updater"""
396+ error = self .update_updater_worker .error
397+ if error is None :
398+ self ._start_download ()
399+ return
400+
401+ self .set_status (PENDING )
402+ if self .progress_dialog is not None :
403+ self .progress_dialog .accept ()
404+ self .progress_dialog = None
405+
406+ if isinstance (error , subprocess .CalledProcessError ):
407+ error_msg = _ ("Error updating Spyder-updater." )
408+ details = [
409+ "*** COMMAND ***" ,
410+ error .cmd .strip (),
411+ "\n *** STDOUT ***" ,
412+ error .output .strip (),
413+ "\n *** STDERR ***" ,
414+ error .stderr .strip (),
415+ ]
416+ detailed_error_messagebox (
417+ self , error_msg , details = "\n " .join (details )
418+ )
419+
382420 def _start_download (self ):
383421 """
384422 Start downloading the installer in a QThread
@@ -612,7 +650,24 @@ def _start_updater(self):
612650 cmd = [updater_path , "--update-info-file" , info_file ]
613651 if self .restart_spyder :
614652 cmd .append ("--start-spyder" )
615- subprocess .Popen (" " .join (cmd ), shell = True )
653+
654+ kwargs = dict (shell = True )
655+ if os .name == "nt" and is_installed_all_users ():
656+ # Elevate UAC
657+ kwargs .update (executable = find_program ("powershell" ))
658+ cmd = [
659+ "start" ,
660+ "-FilePath" ,
661+ f'"{ updater_path } "' ,
662+ "-ArgumentList" ,
663+ "," .join ([f"'{ a } '" for a in cmd [1 :]]),
664+ "-WindowStyle" ,
665+ "Hidden" ,
666+ "-Verb" ,
667+ "RunAs" ,
668+ ]
669+
670+ subprocess .Popen (" " .join (cmd ), ** kwargs )
616671
617672
618673class UpdateMessageBox (QMessageBox ):
@@ -622,6 +677,54 @@ def __init__(self, icon=None, text=None, parent=None):
622677 self .setTextFormat (Qt .RichText )
623678
624679
680+ class DetailedUpdateMessageBox (UpdateMessageBox , SpyderFontsMixin ):
681+ def __init__ (self , icon = None , text = None , parent = None , details = None ):
682+ super ().__init__ (icon = icon , text = text , parent = parent )
683+ self .setSizeGripEnabled (True )
684+ self .details = None
685+ self .setDetailedText (details )
686+
687+ def setDetailedText (self , details = None ):
688+ """
689+ Override setDetailedText.
690+
691+ Note: It is critical that QGridLayout.setRowStretch is called after
692+ QMessageBox.setDetailedText in order for the stretch behavior to work
693+ properly. That is the primary reason for overriding setDetailedText.
694+ """
695+ if self .details is not None :
696+ self .details .setText (details )
697+ return
698+
699+ super ().setDetailedText (details )
700+ self .details = self .findChild (QTextEdit )
701+
702+ self .details .setFont (self .get_font (SpyderFontType .Monospace ))
703+ self .details .setLineWrapMode (self .details .NoWrap )
704+ self .details .setMinimumSize (400 , 110 )
705+ self .details .setLineWrapMode (0 )
706+
707+ qgl = self .findChild (QGridLayout )
708+ qgl .setRowStretch (1 , 0 )
709+ qgl .setRowStretch (3 , 100 ) # QTextEdit should take all the stretch
710+
711+ def event (self , event ):
712+ """Override to allow resizing the dialog when details are visible."""
713+ if event .type () in (event .LayoutRequest , event .Resize ):
714+ if event .type () == event .Resize :
715+ result = super ().event (event )
716+ else :
717+ result = False
718+
719+ # Allow resize only if details is available and visible.
720+ if self .details and self .details .isVisible ():
721+ self .details .setMaximumSize (10000 , 10000 )
722+ self .setMaximumSize (10000 , 10000 )
723+
724+ return result
725+ return super ().event (event )
726+
727+
625728class UpdateMessageCheckBox (MessageCheckBox ):
626729 def __init__ (self , icon = None , text = None , parent = None ):
627730 super ().__init__ (icon = icon , text = text , parent = parent )
@@ -674,8 +777,14 @@ def error_messagebox(parent, error_msg, checkbox=False):
674777 box_class = UpdateMessageCheckBox if checkbox else UpdateMessageBox
675778 box = box_class (icon = QMessageBox .Warning , text = error_msg , parent = parent )
676779 box .setWindowTitle (_ ("Spyder update error" ))
677- box .setStandardButtons (QMessageBox .Ok )
678- box .setDefaultButton (QMessageBox .Ok )
780+ box .show ()
781+ return box
782+
783+
784+ def detailed_error_messagebox (parent , msg , details ):
785+ box = DetailedUpdateMessageBox (
786+ icon = QMessageBox .Warning , text = msg , parent = parent , details = details
787+ )
679788 box .show ()
680789 return box
681790
@@ -685,8 +794,6 @@ def info_messagebox(parent, message, version=None, checkbox=False):
685794 message = HEADER .format (version ) + message if version else message
686795 box = box_class (icon = QMessageBox .Information , text = message , parent = parent )
687796 box .setWindowTitle (_ ("New Spyder version" ))
688- box .setStandardButtons (QMessageBox .Ok )
689- box .setDefaultButton (QMessageBox .Ok )
690797 box .show ()
691798 return box
692799
@@ -773,5 +880,4 @@ def manual_update_messagebox(parent, latest_release, channel):
773880 "<br><br>For more information, visit our "
774881 "<a href=\" {}\" >installation guide</a>."
775882 ).format (URL_I )
776-
777- info_messagebox (parent , msg )
883+ return info_messagebox (parent , msg )
0 commit comments