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"]: