scripts/install.py

branch
eric7-maintenance
changeset 9371
1da8bc75946f
parent 9305
3b7ef53c34c7
parent 9366
57980203d6f8
child 9379
f23c43e18046
diff -r 90dc02c8a384 -r 1da8bc75946f scripts/install.py
--- a/scripts/install.py	Fri Sep 02 14:10:44 2022 +0200
+++ b/scripts/install.py	Sat Oct 01 13:06:10 2022 +0200
@@ -41,10 +41,10 @@
 forceCleanDesktopLinks = False
 doCompile = True
 yes2All = False
-ignorePyqt6Tools = False
+withPyqt6Tools = False
 verbose = False
 cfg = {}
-progLanguages = ["Python", "Ruby", "QSS"]
+progLanguages = ["MicroPython", "Python3", "QSS"]
 sourceDir = "eric"
 eric7SourceDir = ""
 configName = "eric7config.py"
@@ -100,7 +100,7 @@
     print()
 
     if sys.platform.startswith(("win", "cygwin")):
-        with contextlib.suppress():
+        with contextlib.suppress(EOFError):
             input("Press enter to continue...")  # secok
 
     os.chdir(currDir)
@@ -162,15 +162,16 @@
         print("    -p python  path of the python executable")
         print("               (default: {0})".format(macPythonExe))
     print("    -c         don't cleanup old installation first")
-    if sys.platform.startswith(("win", "cygwin")):
-        print("    --clean-desktop delete desktop links before installation")
-    print("    --no-info  don't create the install info file")
-    print("    --no-tools don't install qt6-applications")
     print("    -v, --verbose print some more information")
     print("    -x         don't perform dependency checks (use on your own" " risk)")
     print("    -z         don't compile the installed python files")
     print("    --yes      answer 'yes' to all questions")
     print()
+    if sys.platform.startswith(("win", "cygwin")):
+        print("    --clean-desktop delete desktop links before installation")
+    print("    --no-info  don't create the install info file")
+    print("    --with-tools don't install qt6-applications")
+    print()
     print("The file given to the -f option must be valid Python code" " defining a")
     print(
         "dictionary called 'cfg' with the keys 'ericDir', 'ericPixDir',"
@@ -264,6 +265,9 @@
     text = text.replace("@MARKER@", "")
     text = text.replace("@PY_MARKER@", "")
 
+    dstPath = os.path.dirname(dst)
+    if not os.path.isdir(dstPath):
+        os.makedirs(dstPath)
     with open(dst, "w", encoding="utf-8") as f:
         f.write(text)
     os.chmod(dst, 0o644)
@@ -294,6 +298,9 @@
         .replace("@DATE@", time.strftime("%Y-%m-%d"))
     )
 
+    dstPath = os.path.dirname(dst)
+    if not os.path.isdir(dstPath):
+        os.makedirs(dstPath)
     with open(dst, "w", encoding="utf-8") as f:
         f.write(text)
     os.chmod(dst, 0o644)
@@ -584,14 +591,24 @@
             apidir = getConfig("apidir")
             for progLanguage in progLanguages:
                 for name in getConfig("apis"):
+                    # step 1: programming language as given
+                    apiname = os.path.join(apidir, progLanguage, name)
+                    if os.path.exists(apiname):
+                        os.remove(apiname)
+                    # step 2: programming language as lowercase
                     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) != "eric7.bas":
-                        os.remove(apiname)
+                    os.path.join(apidir, progLanguage, "*.bas")
+                ) + glob.glob(os.path.join(apidir, progLanguage.lower(), "*.bas")):
+                    os.remove(apiname)
+
+                # remove empty directories
+                with contextlib.suppress(FileNotFoundError, OSError):
+                    os.rmdir(os.path.join(apidir, progLanguage))
+                with contextlib.suppress(FileNotFoundError, OSError):
+                    os.rmdir(os.path.join(apidir, progLanguage.lower()))
 
         if sys.platform == "darwin":
             # delete the Mac app bundle
@@ -903,79 +920,23 @@
 
     # install the API file
     if installApis:
-        for progLanguage in progLanguages:
-            apidir = os.path.join(cfg["apidir"], progLanguage.lower())
-            print("Installing {0} API files to '{1}'.".format(progLanguage, apidir))
-            if not os.path.exists(apidir):
-                os.makedirs(apidir)
-            for apiName in glob.glob(
-                os.path.join(eric7SourceDir, "APIs", progLanguage, "*.api")
-            ):
-                try:
-                    shutilCopy(apiName, apidir)
-                except OSError:
-                    print("Could not install '{0}' (no permission).".format(apiName))
-            for apiName in glob.glob(
-                os.path.join(eric7SourceDir, "APIs", progLanguage, "*.bas")
-            ):
-                try:
-                    shutilCopy(apiName, apidir)
-                except OSError:
-                    print("Could not install '{0}' (no permission).".format(apiName))
-            if progLanguage == "Python":
-                # copy Python3 API files to the same destination
+        if os.access(cfg["apidir"], os.W_OK):
+            for progLanguage in progLanguages:
+                apidir = os.path.join(cfg["apidir"], progLanguage)
+                print("Installing {0} API files to '{1}'.".format(progLanguage, apidir))
+                if not os.path.exists(apidir):
+                    os.makedirs(apidir)
                 for apiName in glob.glob(
-                    os.path.join(eric7SourceDir, "APIs", "Python3", "*.api")
-                ):
-                    try:
-                        shutilCopy(apiName, apidir)
-                    except OSError:
-                        print(
-                            "Could not install '{0}' (no permission).".format(apiName)
-                        )
-                for apiName in glob.glob(
-                    os.path.join(eric7SourceDir, "APIs", "Python3", "*.bas")
+                    os.path.join(eric7SourceDir, "APIs", progLanguage, "*.api")
                 ):
-                    if os.path.exists(
-                        os.path.join(
-                            apidir, os.path.basename(apiName.replace(".bas", ".api"))
-                        )
-                    ):
-                        try:
-                            shutilCopy(apiName, apidir)
-                        except OSError:
-                            print(
-                                "Could not install '{0}' (no permission).".format(
-                                    apiName
-                                )
-                            )
-
-                # copy MicroPython API files to the same destination
+                    shutilCopy(apiName, apidir)
                 for apiName in glob.glob(
-                    os.path.join(eric7SourceDir, "APIs", "MicroPython", "*.api")
+                    os.path.join(eric7SourceDir, "APIs", progLanguage, "*.bas")
                 ):
-                    try:
-                        shutilCopy(apiName, apidir)
-                    except OSError:
-                        print(
-                            "Could not install '{0}' (no permission).".format(apiName)
-                        )
-                for apiName in glob.glob(
-                    os.path.join(eric7SourceDir, "APIs", "MicroPython", "*.bas")
-                ):
-                    if os.path.exists(
-                        os.path.join(
-                            apidir, os.path.basename(apiName.replace(".bas", ".api"))
-                        )
-                    ):
-                        try:
-                            shutilCopy(apiName, apidir)
-                        except OSError:
-                            print(
-                                "Could not install '{0}' (no permission).".format(
-                                    apiName
-                                )
-                            )
+                    shutilCopy(apiName, apidir)
+        else:
+            print("The API directory '{0}' is not writable.".format(cfg["apidir"]))
+            print("Use the API files provided by the 'API Files' plug-in.")
 
     # Create menu entry for Linux systems
     if sys.platform.startswith("linux"):
@@ -1366,20 +1327,6 @@
                 glob.glob(os.path.join(eric7SourceDir, "APIs", progLanguage, "*.api"))
             ):
                 apis.append(os.path.basename(apiName))
-            if progLanguage == "Python":
-                # treat Python3 API files the same as Python API files
-                for apiName in sorted(
-                    glob.glob(os.path.join(eric7SourceDir, "APIs", "Python3", "*.api"))
-                ):
-                    apis.append(os.path.basename(apiName))
-
-                # treat MicroPython API files the same as Python API files
-                for apiName in sorted(
-                    glob.glob(
-                        os.path.join(eric7SourceDir, "APIs", "MicroPython", "*.api")
-                    )
-                ):
-                    apis.append(os.path.basename(apiName))
 
     macConfig = (
         (
@@ -1738,7 +1685,7 @@
             exit(1)
     print("Found PyQt6-QScintilla")
 
-    impModulesList = [
+    pyqt6BaseModulesList = [
         "PyQt6.QtGui",
         "PyQt6.QtNetwork",
         "PyQt6.QtPrintSupport",
@@ -1747,20 +1694,13 @@
         "PyQt6.QtSvgWidgets",
         "PyQt6.QtWidgets",
     ]
-    optionalModulesList = {
+    requiredModulesList = {
         # key is pip project name
         # value is tuple of package name, pip install constraint
-        "docutils": ("docutils", ""),
-        "Markdown": ("markdown", ""),
-        "pyyaml": ("yaml", ""),
         "tomlkit": ("tomlkit", ""),
-        "chardet": ("chardet", ""),
         "asttokens": ("asttokens", ""),
         "EditorConfig": ("editorconfig", ""),
-        "Send2Trash": ("send2trash", ""),
         "Pygments": ("pygments", ""),
-        "pyenchant": ("enchant", ""),
-        "wheel": ("wheel", ""),
         "parso": ("parso", ""),
         "jedi": ("jedi", ""),
         "packaging": ("packaging", ""),
@@ -1769,15 +1709,26 @@
         "trove-classifiers": ("trove_classifiers", ""),
         "black": ("black", ">=22.6.0"),
     }
-    if not ignorePyqt6Tools:
+    optionalModulesList = {
+        # key is pip project name
+        # value is tuple of package name, pip install constraint
+        "docutils": ("docutils", ""),
+        "Markdown": ("markdown", ""),
+        "pyyaml": ("yaml", ""),
+        "chardet": ("chardet", ""),
+        "Send2Trash": ("send2trash", ""),
+        "pyenchant": ("enchant", ""),
+        "wheel": ("wheel", ""),
+    }
+    if withPyqt6Tools:
         optionalModulesList["qt6-applications"] = ("qt6_applications", "")
 
-    # check mandatory modules
+    # check mandatory PyQt6 modules
     modulesOK = True
-    for impModule in impModulesList:
-        name = impModule.split(".")[1]
+    for pyqt6BaseModule in pyqt6BaseModulesList:
+        name = pyqt6BaseModule.split(".")[1]
         try:
-            __import__(impModule)
+            __import__(pyqt6BaseModule)
             print("Found", name)
         except ImportError as err:
             print("Sorry, please install {0}.".format(name))
@@ -1787,7 +1738,32 @@
     if not modulesOK:
         exit(1)
 
+    # check required modules
+    requiredMissing = False
+    for requiredPackage in requiredModulesList:
+        try:
+            __import__(requiredModulesList[requiredPackage][0])
+            print("Found", requiredPackage)
+        except ImportError as err:
+            if isSudo:
+                print("Required '{0}' could not be detected.".format(requiredPackage))
+                requiredMissing = True
+            else:
+                msg = "Required '{0}' could not be detected.{1}".format(
+                    requiredPackage, "\nError: {0}".format(err) if verbose else ""
+                )
+                pipInstall(
+                    requiredPackage + requiredModulesList[requiredPackage][1],
+                    msg,
+                    force=True,
+                )
+    if requiredMissing:
+        print("Some required packages are missing and could not be installed.")
+        print("Install them manually with:")
+        print("    {0} install-dependencies.py --required".format(sys.executable))
+
     # check optional modules
+    optionalMissing = False
     for optPackage in optionalModulesList:
         try:
             __import__(optionalModulesList[optPackage][0])
@@ -1795,11 +1771,20 @@
         except ImportError as err:
             if isSudo:
                 print("Optional '{0}' could not be detected.".format(optPackage))
+                optionalMissing = True
             else:
                 msg = "Optional '{0}' could not be detected.{1}".format(
                     optPackage, "\nError: {0}".format(err) if verbose else ""
                 )
                 pipInstall(optPackage + optionalModulesList[optPackage][1], msg)
+    if optionalMissing:
+        print("Some optional packages are missing and could not be installed.")
+        print("Install them manually with:")
+        print("    {0} install-dependencies.py --optional".format(sys.executable))
+
+    if requiredMissing and optionalMissing:
+        print("Alternatively you may install all of them with:")
+        print("    {0} install-dependencies.py --all".format(sys.executable))
 
     # determine the platform dependent black list
     if sys.platform.startswith(("win", "cygwin")):
@@ -2090,7 +2075,7 @@
     global macAppBundlePath, macAppBundleName, macPythonExe
     global installApis, doCleanDesktopLinks, yes2All
     global createInstallInfoFile, installCwd
-    global ignorePyqt6Tools
+    global withPyqt6Tools
     global verbose
 
     if sys.version_info < (3, 7, 0) or sys.version_info > (3, 99, 99):
@@ -2175,8 +2160,8 @@
             doCleanDesktopLinks = True
         elif opt == "--yes":
             yes2All = True
-        elif opt == "--no-tools":
-            ignorePyqt6Tools = True
+        elif opt == "--with-tools":
+            withPyqt6Tools = True
         elif opt == "--no-info":
             createInstallInfoFile = False
         elif opt in ["-v", "--verbose"]:

eric ide

mercurial