def processResults(result): result = result.get() self._checkInProgress = False if result == 'URLError': if not silentUnlessUpdate: MultiMessageBox(self._networkFailureMsg % self._updatesURL, self._caption, parent=parentWindow, icon=self._icon, style=wx.OK | SOT) return active = self._esky.active_version if cfg: oldPath = cfg.GetPath() cfg.SetPath('/autoUpdate') today = int(wx.DateTime.Today().GetJulianDayNumber()) cfg.WriteInt('lastCheck', today) cfg.Write('lastCheckVersion', active) cfg.Flush() cfg.SetPath(oldPath) newest, chLogTxt = result if newest is None: if not silentUnlessUpdate: MultiMessageBox( "You are already running the newest verison of %s." % self.GetAppDisplayName(), self._caption, parent=parentWindow, icon=self._icon, style=wx.OK | SOT) return self._parentWindow = parentWindow resp = MultiMessageBox( "A new version of %s is available.\n\n" "You are currently running verison %s; version %s is now " "available for download. Do you wish to install it now?" % (self.GetAppDisplayName(), active, newest), self._caption, msg2=chLogTxt, style=wx.YES_NO | SOT, parent=parentWindow, icon=self._icon, btnLabels={ wx.ID_YES: "Yes, install now", wx.ID_NO: "No, maybe later" }) if resp != wx.YES: return # Ok, there is a little trickery going on here. We don't know yet if # the user wants to restart the application after the update is # complete, but since atexit functions are executed in a LIFO order we # need to registar our function before we call auto_update and Esky # possibly registers its own atexit function, because we want ours to # be run *after* theirs. So we'll create an instance of an info object # and register its method now, and then fill in the details below # once we decide what we want to do. class RestartInfo(object): def __init__(self): self.exe = None def restart(self): if self.exe is not None: # Execute the program, replacing this process os.execv(self.exe, [self.exe] + sys.argv[1:]) info = RestartInfo() atexit.register(info.restart) try: # Let Esky handle all the rest of the update process so we can # take advantage of the error checking and priviledge elevation # (if neccessary) that they have done so we don't have to worry # about that ourselves like we would if we broke down the proccess # into component steps. self._esky.auto_update(self._updateProgress) except UpdateAbortedError: MultiMessageBox("Update canceled.", self._caption, parent=parentWindow, icon=self._icon, style=wx.OK | SOT) if self._pd: self._pd.Destroy() self.InitUpdates(self._updatesURL, self._changelogURL, self._icon) return # Ask the user if they want the application to be restarted. resp = MultiMessageBox( "The upgrade to %s %s is ready to use; the application will " "need to be restarted to begin using the new release.\n\n" "Restart %s now?" % (self.GetAppDisplayName(), newest, self.GetAppDisplayName()), self._caption, style=wx.YES_NO | SOT, parent=parentWindow, icon=self._icon, btnLabels={ wx.ID_YES: "Yes, restart now", wx.ID_NO: "No, I'll restart later" }) if resp == wx.YES: # Close all windows in this application... for w in wx.GetTopLevelWindows(): if isinstance(w, wx.Dialog): w.Destroy() elif isinstance(w, wx.Frame): w.Close(True) # force close (can't be cancelled) wx.Yield() # ...find the path of the esky bootstrap/wrapper program... exe = esky.util.appexe_from_executable(sys.executable) # ...and tell our RestartInfo object about it. info.exe = exe # Make sure the CWD not in the current version's appdir, so it can # hopefully be cleaned up either as we exit or as the next verison # is starting. os.chdir(os.path.dirname(exe)) # With all the top level windows closed the MainLoop should exit # automatically, but just in case tell it to exit so we can have a # normal-as-possible shutdown of this process. Hopefully there # isn't anything happening after we return from this function that # matters. self.ExitMainLoop() return
def CheckForUpdate(self, silentUnlessUpdate=False, parentWindow=None): """ This method will check for the availability of a new update, and will prompt the user with details if there is one there. By default it will also tell the user if there is not a new update, but you can pass silentUnlessUpdate=True to not bother the user if there isn't a new update available. This method should be called from an event handler for a "Check for updates" menu item, or something similar. """ if not isFrozenApp: return False active = self._esky.active_version try: newest = self._esky.find_update() except urllib2.URLError: if not silentUnlessUpdate: MultiMessageBox( "Unable to connect to %s to check for updates." % self._updatesURL, self._caption, parent=parentWindow, icon=self._icon) return False if newest is None: if not silentUnlessUpdate: MultiMessageBox( "You are already running the newest verison of %s." % self.GetAppDisplayName(), self._caption, parent=parentWindow, icon=self._icon) return True self._parentWindow = parentWindow chLogTxt = "" if self._changelogURL: # if self._changelogURL is not None then fetch it and display the # text in the next dialog try: req = urllib2.urlopen(self._changelogURL, timeout=4) chLogTxt = req.read() req.close() except: pass resp = MultiMessageBox( "A new version of %s is available.\n\n" "You are currently running verison %s, version %s is now " "available for download. Do you wish to install it now?" % (self.GetAppDisplayName(), active, newest), self._caption, msg2=chLogTxt, style=wx.YES_NO, parent=parentWindow, icon=self._icon, btnLabels={ wx.ID_YES: "Yes, install now", wx.ID_NO: "No, maybe later" }) if resp != wx.YES: return True # Ok, there is a little trickery going on here. We don't know yet if # the user wants to restart the application after the update is # complete, but since atexit functions are executed in a LIFO order we # need to registar our function before we call auto_update and Esky # possibly registers its own atexit function, because we want ours to # be run *after* theirs. So we'll create an instance of an info object # and register its method now, and then fill in the details below # once we decide what we want to do. class RestartInfo(object): def __init__(self): self.exe = None def restart(self): if self.exe is not None: # Execute the program, replacing this process os.execv(self.exe, [self.exe] + sys.argv[1:]) info = RestartInfo() atexit.register(info.restart) try: # Let Esky handle all the rest of the update process so we can # take advantage of the error checking and priviledge elevation # (if neccessary) that they have done so we don't have to worry # about that ourselves like we would if we broke down the proccess # into component steps. self._esky.auto_update(self._updateProgress) except UpdateAbortedError: self._esky.cleanup() MultiMessageBox("Update aborted.", self._caption, parent=parentWindow, icon=self._icon) return False # Ask the user if they want the application to be restarted. resp = MultiMessageBox( "The upgrade to %s %s is ready to use, the application will " "need to be restarted to begin using the new release.\n\n" "Restart %s now?" % (self.GetAppDisplayName(), newest, self.GetAppDisplayName()), self._caption, style=wx.YES_NO, parent=parentWindow, icon=self._icon, btnLabels={ wx.ID_YES: "Yes, restart now", wx.ID_NO: "No, I'll restart later" }) if resp == wx.YES: # Close all windows in this application... for w in wx.GetTopLevelWindows(): if isinstance(w, wx.Dialog): w.Destroy() elif isinstance(w, wx.Frame): w.Close(True) # force close (can't be cancelled) wx.Yield() # ...find the path of the esky bootstrap/wrapper program... exe = esky.util.appexe_from_executable(sys.executable) # ...and tell our RestartInfo object about it. info.exe = exe # Make sure the CWD not in the current version's appdir, so it can # hopefully be cleaned up either as we exit or as the next verison # is starting. os.chdir(os.path.dirname(exe)) # With all the top level windows closed the MainLoop should exit # automatically, but just in case tell it to exit so we can have a # normal-as-possible shutdown of this process. Hopefully there # isn't anything happening after we return from this function that # matters. self.ExitMainLoop() return True