Sun, 30 Mar 2025 15:01:40 +0200
Updated Russian translations.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2016 - 2025 Detlev Offenbach <detlev@die-offenbachs.de> # # This is the install script for the eric debug client. It may be used # to just install the debug clients for remote debugging. # """ Installation script for the eric debug clients. """ import argparse import compileall import contextlib import fnmatch import importlib import io import json import os import re import shutil import subprocess # secok import sys import sysconfig # Define the globals. currDir = os.getcwd() modDir = None pyModDir = None distDir = None installPackage = "eric7" doCleanup = True doCompile = True proxy = None sourceDir = "eric" eric7SourceDir = "" def exit(rcode=0): """ Exit the install script. @param rcode result code to report back @type int """ global currDir if sys.platform.startswith("win"): with contextlib.suppress(EOFError): input("Press enter to continue...") # secok os.chdir(currDir) sys.exit(rcode) def initGlobals(): """ Module function to set the values of globals that need more than a simple assignment. """ global modDir, pyModDir modDir = sysconfig.get_path("platlib") if not os.access(modDir, os.W_OK): # can't write to the standard path, use the 'user' path instead if sys.platform.startswith(("win", "cygwin")): scheme = "nt_user" elif sys.platform == "darwin": scheme = "osx_framework_user" else: scheme = "posix_user" modDir = sysconfig.get_path("platlib", scheme) pyModDir = modDir def copyTree(src, dst, filters, excludeDirs=None, excludePatterns=None): """ Copy files of a directory tree. @param src name of the source directory @type str @param dst name of the destination directory @type str @param filters list of filter pattern determining the files to be copied @type list of str @param excludeDirs list of (sub)directories to exclude from copying @type list of str @param excludePatterns list of filter pattern determining the files to be skipped @type str """ if excludeDirs is None: excludeDirs = [] if excludePatterns is None: excludePatterns = [] try: names = os.listdir(src) except OSError: # ignore missing directories return for name in names: skipIt = False for excludePattern in excludePatterns: if fnmatch.fnmatch(name, excludePattern): skipIt = True break if not skipIt: srcname = os.path.join(src, name) dstname = os.path.join(dst, name) for fileFilter in filters: if fnmatch.fnmatch(srcname, fileFilter): if not os.path.isdir(dst): os.makedirs(dst) shutil.copy2(srcname, dstname) os.chmod(dstname, 0o644) break else: if os.path.isdir(srcname) and srcname not in excludeDirs: copyTree(srcname, dstname, filters, excludePatterns=excludePatterns) def cleanupSource(dirName): """ Cleanup the sources directory to get rid of leftover files and directories. @param dirName name of the directory to prune @type str """ # step 1: delete the __pycache__ directory and all *.pyc files if os.path.exists(os.path.join(dirName, "__pycache__")): shutil.rmtree(os.path.join(dirName, "__pycache__")) for name in [f for f in os.listdir(dirName) if fnmatch.fnmatch(f, "*.pyc")]: os.remove(os.path.join(dirName, name)) # step 2: descent into subdirectories and delete them if empty for name in os.listdir(dirName): name = os.path.join(dirName, name) if os.path.isdir(name): cleanupSource(name) if len(os.listdir(name)) == 0: os.rmdir(name) def cleanUp(): """ Uninstall the old eric debug client files. """ global pyModDir, installPackage try: # Cleanup the install directories dirname = os.path.join(pyModDir, installPackage) if os.path.exists(dirname): shutil.rmtree(dirname, ignore_errors=True) except OSError as msg: sys.stderr.write("Error: {0}\nTry install with admin rights.\n".format(msg)) exit(7) def shutilCopy(src, dst, perm=0o644): """ Wrapper function around shutil.copy() to ensure the permissions. @param src source file name @type str @param dst destination file name or directory name @type str @param perm permissions to be set @type int """ shutil.copy(src, dst) if os.path.isdir(dst): dst = os.path.join(dst, os.path.basename(src)) os.chmod(dst, perm) def installEricDebugClients(): """ Actually perform the installation steps. @return result code @rtype int """ global distDir, sourceDir, modDir # set install prefix, if not None targetDir = ( os.path.normpath(os.path.join(distDir, installPackage)) if distDir else os.path.join(modDir, installPackage) ) try: # Install the files # copy the various parts of eric debug clients copyTree( os.path.join(eric7SourceDir, "DebugClients"), os.path.join(targetDir, "DebugClients"), ["*.py", "*.pyc", "*.pyo", "*.pyw"], ) # copy the top level package file shutilCopy(os.path.join(eric7SourceDir, "__init__.py"), targetDir) # copy the license file shutilCopy(os.path.join(sourceDir, "docs", "LICENSE.txt"), targetDir) except OSError as msg: sys.stderr.write("\nError: {0}\nTry install with admin rights.\n".format(msg)) return 7 return 0 def pipInstall(packageName, message, force=True): """ Install the given package via pip. @param packageName name of the package to be installed @type str @param message message to be shown to the user @type str @param force flag indicating to perform the installation without asking the user @type bool @return flag indicating a successful installation @rtype bool """ global proxy ok = False if force: answer = "y" else: print( "{0}\nShall '{1}' be installed using pip? (Y/n)".format( message, packageName ), end=" ", ) answer = input() # secok if answer in ("", "Y", "y"): args = [ sys.executable, "-m", "pip", "install", "--prefer-binary", "--upgrade", ] if proxy: args.append(f"--proxy={proxy}") args.append(packageName) exitCode = subprocess.run(args).returncode # secok ok = exitCode == 0 return ok def isPipOutdated(): """ Check, if pip is outdated. @return flag indicating an outdated pip @rtype bool """ global proxy try: args = [ sys.executable, "-m", "pip", "list", "--outdated", "--format=json", ] if proxy: args.append(f"--proxy={proxy}") pipOut = ( subprocess.run( # secok args, check=True, capture_output=True, text=True, ) .stdout.strip() .splitlines()[0] ) # only the first line contains the JSON data except (OSError, subprocess.CalledProcessError): pipOut = "[]" # default empty list try: jsonList = json.loads(pipOut) except Exception: jsonList = [] for package in jsonList: if isinstance(package, dict) and package["name"] == "pip": print( "'pip' is outdated (installed {0}, available {1})".format( package["version"], package["latest_version"] ) ) return True return False def updatePip(): """ Update the installed pip package. """ global proxy print("Shall 'pip' be updated (recommended)? (Y/n)", end=" ") answer = input() # secok if answer in ("", "Y", "y"): args = [ sys.executable, "-m", "pip", "install", "--upgrade", ] if proxy: args.append(f"--proxy={proxy}") args.append("pip") subprocess.run(args) # secok def doDependancyChecks(): """ Perform some dependency checks. """ try: isSudo = os.getuid() == 0 and sys.platform != "darwin" # disregard sudo installs on macOS except AttributeError: isSudo = False print("Checking dependencies") # update pip first even if we don't need to install anything if not isSudo and isPipOutdated(): updatePip() print("\n") # perform dependency checks if sys.version_info < (3, 9, 0) or sys.version_info >= (3, 14, 0): print("Sorry, you must have Python 3.9.0 or higher, but less 3.14.0.") print("Yours is {0}.".format(".".join(str(v) for v in sys.version_info[:3]))) exit(5) requiredModulesList = { # key is pip project name # value is tuple of package name, pip install constraint "coverage": ("coverage", ">=6.5.0"), } # check required modules print("Required Packages") print("-----------------") requiredMissing = False for requiredPackage in sorted(requiredModulesList): try: importlib.import_module(requiredModulesList[requiredPackage][0]) print("Found", requiredPackage) except ImportError as err: if isSudo: print("Required '{0}' could not be detected.".format(requiredPackage)) requiredMissing = True else: pipInstall( requiredPackage + requiredModulesList[requiredPackage][1], "Required '{0}' could not be detected.{1}".format( requiredPackage, "\nError: {0}".format(err) ), force=True, ) if requiredMissing: print("Some required packages are missing and could not be installed.") print("Install them manually.") print() print("All dependencies ok.") print() def createArgumentParser(): """ Function to create an argument parser. @return created argument parser object @rtype argparse.ArgumentParser """ parser = argparse.ArgumentParser( description="Install eric7 debug clients from the source code tree." ) parser.add_argument( "-d", metavar="dir", default=modDir, help="directory where eric debug client files will be installed" " (default: {0})".format(modDir), ) if not sys.platform.startswith(("win", "cygwin")): parser.add_argument( "-i", metavar="dir", default=distDir, help="temporary install prefix (default: {0})".format(distDir), ) parser.add_argument( "-c", action="store_false", help="don't cleanup old installation first", ) parser.add_argument( "-z", action="store_false", help="don't compile the installed python files", ) parser.add_argument( "--proxy", default=None, metavar="url", help="HTTP proxy url will be used with pip (default: no proxy used)", ) return parser def main(argv): """ The main function of the script. @param argv the list of command line arguments @type list of str """ global modDir, doCleanup, doCompile, distDir, proxy global sourceDir, eric7SourceDir if sys.version_info < (3, 9, 0) or sys.version_info >= (4, 0, 0): print("Sorry, the eric debugger requires Python 3.9 or better for running.") exit(5) if os.path.dirname(argv[0]): os.chdir(os.path.dirname(argv[0])) initGlobals() parser = createArgumentParser() args = parser.parse_args() modDir = args.d doCleanup = args.c doCompile = args.z proxy = args.proxy if not sys.platform.startswith(("win", "cygwin")) and args.i: distDir = os.path.normpath(args.i) # check dependencies doDependancyChecks() installFromSource = not os.path.isdir(sourceDir) if installFromSource: sourceDir = os.path.abspath("..") eric7SourceDir = ( os.path.join(sourceDir, "eric7") if os.path.exists(os.path.join(sourceDir, "eric7")) else os.path.join(sourceDir, "src", "eric7") ) # cleanup source if installing from source if installFromSource: print("Cleaning up source ...", end="", flush=True) cleanupSource(os.path.join(eric7SourceDir, "DebugClients")) print(" Done") # cleanup old installation try: if doCleanup: print("Cleaning up old installation ...", end="", flush=True) if distDir: shutil.rmtree(distDir, ignore_errors=True) else: cleanUp() print(" Done") except OSError as msg: sys.stderr.write("Error: {0}\nTry install as root.\n".format(msg)) exit(7) if doCompile: print("Compiling source files ...", end="", flush=True) skipRe = re.compile(r"DebugClients[\\/]Python[\\/]") sys.stdout = io.StringIO() if distDir: compileall.compile_dir( os.path.join(eric7SourceDir, "DebugClients"), ddir=os.path.join(distDir, modDir, installPackage), rx=skipRe, quiet=True, ) else: compileall.compile_dir( os.path.join(eric7SourceDir, "DebugClients"), ddir=os.path.join(modDir, installPackage), rx=skipRe, quiet=True, ) sys.stdout = sys.__stdout__ print(" Done") print("Installing eric debug clients ...", end="", flush=True) res = installEricDebugClients() print(" Done") print("Installation complete.") print() exit(res) 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,\nincluding the following traceback, to""" """ eric-bugs@eric-ide.python-projects.org.\n""" ) raise # # eflag: noqa = M-801