scripts/uninstall.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7192
a22eee00b052
child 7210
8fe313d039e6
diff -r f99d60d6b59b -r 2602857055c5 scripts/uninstall.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/uninstall.py	Sun Apr 14 15:09:21 2019 +0200
@@ -0,0 +1,514 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+# This is the uninstall script for eric6.
+#
+
+"""
+Uninstallation script for the eric6 IDE and all eric6 related tools.
+"""
+
+from __future__ import unicode_literals, print_function
+
+import sys
+import os
+import shutil
+import glob
+import distutils.sysconfig
+
+if sys.version_info[0] == 2:
+    try:
+        from PyQt5 import sip
+    except ImportError:
+        import sip
+    sip.setapi('QString', 2)
+else:
+    raw_input = input
+
+# get a local eric6config.py out of the way
+if os.path.exists("eric6config.py"):
+    os.rename("eric6config.py", "eric6config.py.orig")
+from eric6config import getConfig
+
+# Define the globals.
+progName = None
+currDir = os.getcwd()
+pyModDir = None
+progLanguages = ["Python", "Ruby", "QSS"]
+includePythonVariant = False
+defaultMacAppBundleName = "eric6.app"
+defaultMacAppBundlePath = "/Applications"
+settingsNameOrganization = "Eric6"
+settingsNameGlobal = "eric6"
+
+# Define file name markers for Python variants
+PythonMarkers = {
+    2: "_py2",
+    3: "_py3",
+}
+
+
+def exit(rcode=0):
+    """
+    Exit the uninstall script.
+    
+    @param rcode result code to report back (integer)
+    """
+    global currDir
+    
+    # restore the local eric6config.py
+    if os.path.exists("eric6config.py.orig"):
+        if os.path.exists("eric6config.py"):
+            os.remove("eric6config.py")
+        os.rename("eric6config.py.orig", "eric6config.py")
+    
+    if sys.platform.startswith(("win", "cygwin")):
+        # different meaning of input between Py2 and Py3
+        try:
+            input("Press enter to continue...")
+        except (EOFError, SyntaxError):
+            pass
+    
+    os.chdir(currDir)
+    
+    sys.exit(rcode)
+
+
+def usage(rcode=2):
+    """
+    Display a usage message and exit.
+
+    @param rcode return code passed back to the calling process (integer)
+    """
+    global progName
+
+    print("Usage:")
+    print("    {0} [-h]".format(progName))
+    print("where:")
+    print("    -h             display this help message")
+    print("    -y             remove executables with Python variant in name")
+
+    exit(rcode)
+
+
+def initGlobals():
+    """
+    Set the values of globals that need more than a simple assignment.
+    """
+    global pyModDir
+
+    pyModDir = distutils.sysconfig.get_python_lib(True)
+
+
+def wrapperNames(dname, wfile):
+    """
+    Create the platform specific names for the wrapper script.
+    
+    @param dname name of the directory to place the wrapper into
+    @param wfile basename (without extension) of the wrapper script
+    @return the names of the wrapper scripts
+    """
+    if sys.platform.startswith(("win", "cygwin")):
+        wnames = (dname + "\\" + wfile + ".cmd",
+                  dname + "\\" + wfile + ".bat")
+    else:
+        wnames = (dname + "/" + wfile, )
+
+    return wnames
+
+
+def uninstallEric():
+    """
+    Uninstall the eric files.
+    """
+    global pyModDir
+    
+    # Remove the menu entry for Linux systems
+    if sys.platform.startswith("linux"):
+        uninstallLinuxSpecifics()
+    # Remove the Desktop and Start Menu entries for Windows systems
+    elif sys.platform.startswith(("win", "cygwin")):
+        uninstallWindowsLinks()
+    
+    # Remove the wrapper scripts
+    rem_wnames = [
+        "eric6_api", "eric6_compare",
+        "eric6_configure", "eric6_diff",
+        "eric6_doc", "eric6_qregularexpression",
+        "eric6_qregexp", "eric6_re",
+        "eric6_trpreviewer", "eric6_uipreviewer",
+        "eric6_unittest", "eric6",
+        "eric6_tray", "eric6_editor",
+        "eric6_plugininstall", "eric6_pluginuninstall",
+        "eric6_pluginrepository", "eric6_sqlbrowser",
+        "eric6_webbrowser", "eric6_iconeditor",
+        "eric6_snap", "eric6_hexeditor", "eric6_browser",
+        "eric6_shell",
+    ]
+    if includePythonVariant:
+        marker = PythonMarkers[sys.version_info.major]
+        rem_wnames = [n + marker for n in rem_wnames]
+    
+    try:
+        for rem_wname in rem_wnames:
+            for rwname in wrapperNames(getConfig('bindir'), rem_wname):
+                if os.path.exists(rwname):
+                    os.remove(rwname)
+        
+        # Cleanup our config file(s)
+        for name in ['eric6config.py', 'eric6config.pyc', 'eric6.pth']:
+            e5cfile = os.path.join(pyModDir, name)
+            if os.path.exists(e5cfile):
+                os.remove(e5cfile)
+            e5cfile = os.path.join(pyModDir, "__pycache__", name)
+            path, ext = os.path.splitext(e5cfile)
+            for f in glob.glob("{0}.*{1}".format(path, ext)):
+                os.remove(f)
+        
+        # Cleanup the install directories
+        for name in ['ericExamplesDir', 'ericDocDir', 'ericDTDDir',
+                     'ericCSSDir', 'ericIconDir', 'ericPixDir',
+                     'ericTemplatesDir', 'ericCodeTemplatesDir',
+                     'ericOthersDir', 'ericStylesDir', 'ericDir']:
+            dirpath = getConfig(name)
+            if os.path.exists(dirpath):
+                shutil.rmtree(dirpath, True)
+        
+        # Cleanup translations
+        for name in glob.glob(
+                os.path.join(getConfig('ericTranslationsDir'), 'eric6_*.qm')):
+            if os.path.exists(name):
+                os.remove(name)
+        
+        # Cleanup API files
+        apidir = getConfig('apidir')
+        if apidir:
+            for progLanguage in progLanguages:
+                for name in getConfig('apis'):
+                    apiname = os.path.join(apidir, progLanguage.lower(), name)
+                    if os.path.exists(apiname):
+                        os.remove(apiname)
+                for apiname in glob.glob(
+                        os.path.join(apidir, progLanguage.lower(), "*.bas")):
+                    if os.path.basename(apiname) != "eric6.bas":
+                        os.remove(apiname)
+        
+        if sys.platform == "darwin":
+            # delete the Mac app bundle
+            uninstallMacAppBundle()
+        
+        # remove plug-in directories
+        removePluginDirectories()
+        
+        # remove the eric data directory
+        removeDataDirectory()
+        
+        # remove the eric configuration directory
+        removeConfigurationData()
+        
+        print("\nUninstallation completed")
+    except (IOError, OSError) as msg:
+        sys.stderr.write(
+            'Error: {0}\nTry uninstall with admin rights.\n'.format(msg))
+        exit(7)
+
+
+def uninstallWindowsLinks():
+    """
+    Clean up the Desktop and Start Menu entries for Windows.
+    """
+    try:
+        from pywintypes import com_error        # __IGNORE_WARNING__
+    except ImportError:
+        # links were not created by install.py
+        return
+    
+    regPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" + \
+              "\\User Shell Folders"
+    
+    # 1. cleanup desktop links
+    regName = "Desktop"
+    desktopEntry = getWinregEntry(regName, regPath)
+    if desktopEntry:
+        desktopFolder = os.path.normpath(os.path.expandvars(desktopEntry))
+        for linkName in windowsDesktopNames():
+            linkPath = os.path.join(desktopFolder, linkName)
+            if os.path.exists(linkPath):
+                try:
+                    os.remove(linkPath)
+                except EnvironmentError:
+                    # maybe restrictions prohibited link removal
+                    print("Could not remove '{0}'.".format(linkPath))
+    
+    # 2. cleanup start menu entry
+    regName = "Programs"
+    programsEntry = getWinregEntry(regName, regPath)
+    if programsEntry:
+        programsFolder = os.path.normpath(os.path.expandvars(programsEntry))
+        eric6EntryPath = os.path.join(programsFolder, windowsProgramsEntry())
+        if os.path.exists(eric6EntryPath):
+            try:
+                shutil.rmtree(eric6EntryPath)
+            except EnvironmentError:
+                # maybe restrictions prohibited link removal
+                print("Could not remove '{0}'.".format(eric6EntryPath))
+
+
+def uninstallLinuxSpecifics():
+    """
+    Uninstall Linux specific files.
+    """
+    if os.getuid() == 0:
+        for name in ["/usr/share/pixmaps/eric.png",
+                     "/usr/share/pixmaps/ericWeb.png"]:
+            if os.path.exists(name):
+                os.remove(name)
+        if includePythonVariant:
+            marker = PythonMarkers[sys.version_info.major]
+        else:
+            marker = ""
+        for name in [
+            "/usr/share/applications/eric6" + marker + ".desktop",
+            "/usr/share/appdata/eric6" + marker + ".appdata.xml",
+            "/usr/share/metainfo/eric6" + marker + ".appdata.xml",
+            "/usr/share/applications/eric6_webbrowser" + marker +
+            ".desktop",
+            "/usr/share/applications/eric6_browser" + marker +
+            ".desktop",
+            "/usr/share/pixmaps/eric" + marker + ".png",
+            "/usr/share/pixmaps/ericWeb" + marker + ".png",
+        ]:
+            if os.path.exists(name):
+                os.remove(name)
+    elif os.getuid() >= 1000:
+        # it is assumed that user ids start at 1000
+        for name in ["~/.local/share/pixmaps/eric.png",
+                     "~/.local/share/pixmaps/ericWeb.png"]:
+            path = os.path.expanduser(name)
+            if os.path.exists(path):
+                os.remove(path)
+        if includePythonVariant:
+            marker = PythonMarkers[sys.version_info.major]
+        else:
+            marker = ""
+        for name in [
+            "~/.local/share/applications/eric6" + marker + ".desktop",
+            "~/.local/share/appdata/eric6" + marker + ".appdata.xml",
+            "~/.local/share/metainfo/eric6" + marker + ".appdata.xml",
+            "~/.local/share/applications/eric6_webbrowser" + marker +
+            ".desktop",
+            "~/.local/share/applications/eric6_browser" + marker +
+            ".desktop",
+            "~/.local/share/pixmaps/eric" + marker + ".png",
+            "~/.local/share/pixmaps/ericWeb" + marker + ".png",
+        ]:
+            path = os.path.expanduser(name)
+            if os.path.exists(path):
+                os.remove(path)
+
+
+def uninstallMacAppBundle():
+    """
+    Uninstall the macOS application bundle.
+    """
+    if os.path.exists("/Developer/Applications/Eric6"):
+        shutil.rmtree("/Developer/Applications/Eric6")
+    try:
+        macAppBundlePath = getConfig("macAppBundlePath")
+        macAppBundleName = getConfig("macAppBundleName")
+    except AttributeError:
+        macAppBundlePath = defaultMacAppBundlePath
+        macAppBundleName = defaultMacAppBundleName
+    for bundlePath in [os.path.join(defaultMacAppBundlePath,
+                                    macAppBundleName),
+                       os.path.join(macAppBundlePath,
+                                    macAppBundleName),
+                       ]:
+        if os.path.exists(bundlePath):
+            shutil.rmtree(bundlePath)
+
+
+def removePluginDirectories():
+    """
+    Remove the plug-in directories.
+    """
+    pathsToRemove = []
+    
+    globalPluginsDir = os.path.join(getConfig('mdir'), "eric6plugins")
+    if os.path.exists(globalPluginsDir):
+        pathsToRemove.append(globalPluginsDir)
+    
+    localPluginsDir = os.path.join(getConfigDir(), "eric6plugins")
+    if os.path.exists(localPluginsDir):
+        pathsToRemove.append(localPluginsDir)
+    
+    if pathsToRemove:
+        print("Found these plug-in directories")
+        for path in pathsToRemove:
+            print("  - {0}".format(path))
+        answer = "c"
+        while answer not in ["y", "Y", "n", "N", ""]:
+            answer = raw_input(
+                "Shall these directories be removed (y/N)? ")
+        if answer in ["y", "Y"]:
+            for path in pathsToRemove:
+                shutil.rmtree(path)
+
+
+def removeDataDirectory():
+    """
+    Remove the eric data directory.
+    """
+    cfg = getConfigDir()
+    if os.path.exists(cfg):
+        print("Found the eric data directory")
+        print("  - {0}".format(cfg))
+        answer = "c"
+        while answer not in ["y", "Y", "n", "N", ""]:
+            answer = raw_input(
+                "Shall this directory be removed (y/N)? ")
+        if answer in ["y", "Y"]:
+            shutil.rmtree(cfg)
+
+
+def removeConfigurationData():
+    """
+    Remove the eric configuration directory.
+    """
+    try:
+        from PyQt5.QtCore import QSettings
+    except ImportError:
+        try:
+            from PyQt4.QtCore import QSettings
+        except ImportError:
+            print("No PyQt variant installed. The configuration directory")
+            print("cannot be determined. You have to remove it manually.\n")
+            return
+    
+    settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
+                         settingsNameOrganization, settingsNameGlobal)
+    settingsDir = os.path.dirname(settings.fileName())
+    if os.path.exists(settingsDir):
+        print("Found the eric configuration directory")
+        print("  - {0}".format(settingsDir))
+        answer = "c"
+        while answer not in ["y", "Y", "n", "N", ""]:
+            answer = raw_input(
+                "Shall this directory be removed (y/N)? ")
+        if answer in ["y", "Y"]:
+            shutil.rmtree(settingsDir)
+
+
+def getConfigDir():
+    """
+    Module function to get the name of the directory storing the config data.
+    
+    @return directory name of the config dir (string)
+    """
+    cdn = ".eric6"
+    return os.path.join(os.path.expanduser("~"), cdn)
+
+
+def getWinregEntry(name, path):
+    """
+    Function to get an entry from the Windows Registry.
+    
+    @param name variable name
+    @type str
+    @param path registry path of the variable
+    @type str
+    @return value of requested registry variable
+    @rtype any
+    """
+    # From http://stackoverflow.com/a/35286642
+    import winreg
+    try:
+        registryKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0,
+                                     winreg.KEY_READ)
+        value, _ = winreg.QueryValueEx(registryKey, name)
+        winreg.CloseKey(registryKey)
+        return value
+    except WindowsError:
+        return None
+
+
+def windowsDesktopNames():
+    """
+    Function to generate the link names for the Windows Desktop.
+    
+    @return list of desktop link names
+    @rtype list of str
+    """
+    majorVersion, minorVersion = sys.version_info[:2]
+    linkTemplates = [
+        "eric6 (Python {0}.{1}).lnk",
+        "eric6 Browser (Python {0}.{1}).lnk",
+    ]
+    
+    return [l.format(majorVersion, minorVersion) for l in linkTemplates]
+
+
+def windowsProgramsEntry():
+    """
+    Function to generate the name of the Start Menu top entry.
+    
+    @return name of the Start Menu top entry
+    @rtype str
+    """
+    majorVersion, minorVersion = sys.version_info[:2]
+    return "eric6 (Python {0}.{1})".format(majorVersion, minorVersion)
+
+
+def main(argv):
+    """
+    The main function of the script.
+
+    @param argv list of command line arguments
+    """
+    import getopt
+
+    global includePythonVariant
+    
+    initGlobals()
+
+    # Parse the command line.
+    global progName
+    progName = os.path.basename(argv[0])
+
+    try:
+        optlist, args = getopt.getopt(argv[1:], "hy")
+    except getopt.GetoptError:
+        usage()
+
+    global platBinDir
+
+    for opt, _arg in optlist:
+        if opt == "-h":
+            usage(0)
+        if opt == "-y":
+            includePythonVariant = True
+    
+    print("\nUninstalling eric6 ...")
+    uninstallEric()
+    print("\nUninstallation complete.")
+    print()
+    
+    exit(0)
+
+
+if __name__ == "__main__":
+    try:
+        main(sys.argv)
+    except SystemExit:
+        raise
+    except Exception:
+        print("""An internal error occured.  Please report all the output of"""
+              """ the program,\n"""
+              """including the following traceback, to"""
+              """ eric-bugs@eric-ide.python-projects.org.\n""")
+        raise
+
+#
+# eflag: noqa = M801

eric ide

mercurial