# import modules import common # app common functions import json # json manipulation import os # for os data, filesystem manipulation import subprocess # for running shell commands import sys # for system commands import traceback # for errors # get env env = common.prepare_env() # get environment variables # width for labels WIDTH = 70 # bucket for cli args args = [] # pip exe path PIPEXE = "" # py exe path # py version # py minor version PYTHON_EXECUTABLE = os.path.splitext(sys.executable.split(os.path.sep).pop())[0] # get command to run python PYTHON_VERSION = sys.version.split(" ")[0] PYTHON_MINOR_VERSION = '.'.join(PYTHON_VERSION.split(".")[:2]) # pip string version # pip float version PIP_VERSION = "" PIP_FLOAT_VERSION = 0 # success SUCCESS = False # bucket for versions VERSIONS = {} # process module output # read output from installing # print relevant info # print unknown stuff def process_module_output(lines): for line in lines: # if there's an error, print it and bail if "status 'error'" in line.strip(): print( "🔴[%s] %s" % ( "_", line.strip() ) ) return # sys.exit(1) # if it's already satisfied or building a wheel, print version data elif "already satisfied" in line or \ "Building wheel" in line or \ "Created wheel" in line: modulename = print_module_line(line) if "=" not in modulename and VERSIONS[modulename]["installed"] != VERSIONS[modulename]["latest"]: # install modules from list ret = subprocess.run( [ *args, "-m", PIPEXE, "install", "--upgrade", f"{modulename}" ], capture_output=True, text=True ) # if there's output if ret.stdout.strip(): process_module_output(ret.stdout.strip().split("\n")) # ignore lines about certain things elif "Attempting uninstall" in line or \ "Collecting" in line or \ "Downloading" in line or \ "eta 0:00:00" in line or \ "Found existing" in line or \ "Installing collected" in line or \ "Preparing metadata" in line or \ "Successfully built" in line or \ "Successfully installed" in line or \ "Successfully uninstalled" in line or \ "Stored in" in line or \ "Uninstalling " in line or \ "Using cached" in line: pass # else, I don't know what it is, print it else: print(line.strip()) print("") # print module line # name, installed version, latest version def print_module_line(line): global VERSIONS # is it already installed? satisfied = line.strip().split(" in ") # get the installed version sver = ((len(satisfied) > 1) and satisfied[1].split("(").pop().replace(")", "")) or "" # if we're making a wheel if "Created wheel" in line: line = line.strip().split(':') satisfied = [line[0]] sver = line[1].split('-')[1] # get module name modulename = satisfied[0].replace("Requirement already satisfied: ", "") # save info for later use VERSIONS[modulename] = { "installed": sver, "latest": (sver and get_module_version(satisfied[0].split(" ")[-1])).strip() or "" } # print what we found print( ( "[%s] %s\t%s\t%s" % ( "Building wheel" in line and '.' or "X", satisfied[0].ljust(len("Requirement already satisfied: ") + len("python-bps-continued")), VERSIONS[modulename]["installed"], VERSIONS[modulename]["latest"] ) ) ) # return the name of this module return modulename # get module version # get installed version def get_module_version(module): # pip index versions [module] // >= 21.2 # pip install [module]== // >= 21.1 # pip install --use-deprecated=legacy-resolver [module]== // >= 20.3 # pip install [module]== // >= 9.0 # pip install [module]==blork // < 9.0 global args global PIPEXE global PIP_FLOAT_VERSION ret = "" ver = "" # based on version of pip, get the installation status of a module if float(PIP_FLOAT_VERSION) >= 21.2: ret = subprocess.run( [ *args, "-m", PIPEXE, "index", "versions", module ], capture_output=True, text=True ) lines = ret.stdout.strip().split("\n") lines = lines[2::] vers = (list(map(lambda x: x.split(' ')[-1], lines))) if len(vers) > 1: ver = vers[1] elif float(PIP_FLOAT_VERSION) >= 21.1: ret = subprocess.run( [ *args, "-m", PIPEXE, "install", f"{module}==" ], capture_output=True, text=True ) elif float(PIP_FLOAT_VERSION) >= 20.3: ret = subprocess.run( [ *args, "-m", PIPEXE, "install", "--use-deprecated=legacy-resolver", f"{module}==" ], capture_output=True, text=True ) elif float(PIP_FLOAT_VERSION) >= 9.0: ret = subprocess.run( [ *args, "-m", PIPEXE, "install", f"{module}==" ], capture_output=True, text=True ) elif float(PIP_FLOAT_VERSION) < 9.0: ret = subprocess.run( [ *args, "-m", PIPEXE, "install", f"{module}==blork" ], capture_output=True, ext=True ) # if ver == "" and ret.stderr.strip(): # ver = (ret.stderr.strip().split("\n")[0].split(",")[-1].replace(')', '')).strip() # return what we found return ver # get python info def python_info(): global args global PYTHON_VERSION # get python debug info ret = subprocess.run([*args, "--version"], capture_output=True, text=True) if ret.stdout.strip(): PYTHON_VERSION = ret.stdout.strip().split(" ")[1] PY_STRING = ( "%s\t%s\t%s" % ( ((isinstance(args[0], list) and " ".join( args[0])) or args[0]).strip(), PYTHON_VERSION, sys.platform ) ) print(PY_STRING) print('.' * WIDTH) # get pip info def pip_info(): global args global PIPEXE global PIPEXE global VERSIONS # get pip debug info ret = subprocess.run( [ *args, "-m", PIPEXE, "--version" ], capture_output=True, text=True ) if ret.stdout.strip(): if " from " in ret.stdout.strip(): PIP_VERSION = ret.stdout.strip().split(" from ")[0].split(" ")[1] if PIP_VERSION: b, f, a = PIP_VERSION.partition('.') global PIP_FLOAT_VERSION PIP_FLOAT_VERSION = b+f+a.replace('.', '') PIP_LATEST = get_module_version("pip") VERSIONS["py"] = { "version": PYTHON_VERSION, "platform": sys.platform } VERSIONS["pip"] = { "version": [ PIP_VERSION, PIP_FLOAT_VERSION ], "latest": PIP_LATEST } PIP_STRING = ( "%s\t%s\t%s\t%s\t%s\t%s" % ( ((isinstance(args[0], list) and " ".join( args[0])) or args[0]).strip(), PYTHON_VERSION, sys.platform, PIPEXE, PIP_VERSION, PIP_LATEST ) ) print(PIP_STRING) print('.' * WIDTH) # upgrade pip def pip_upgrade(): global args global PIPEXE # upgrade pip ret = subprocess.run( [ *args, "-m", PIPEXE, "install", "--upgrade", "pip" ], capture_output=True, text=True ) # get output if ret.stdout.strip(): # if it's not already satisfied, update it if "already satisfied" not in ret.stdout.strip(): print(ret.stdout.strip()) pip_info() # install modules def install_modules(): global args global PIPEXE global SUCCESS # install modules from list ret = subprocess.run( [ *args, "-m", PIPEXE, "install", "-r", os.path.join( ".", "resources", "app", "meta", "manifests", "pip_requirements.txt" ) ], capture_output=True, text=True ) # if there's output if ret.stdout.strip(): process_module_output(ret.stdout.strip().split("\n")) manifests_path = os.path.join(".", "resources", "user", "meta", "manifests") if not os.path.isdir(manifests_path): os.makedirs(manifests_path) with open(os.path.join(manifests_path, "settings.json"), "w+") as settings: settings.write( json.dumps( { "py": args, "pip": PIPEXE, "pipline": " ".join(args) + " -m " + PIPEXE, "versions": VERSIONS }, indent=2 ) ) with open(os.path.join(manifests_path, "pipline.txt"), "w+") as settings: settings.write(" ".join(args) + " -m " + PIPEXE) SUCCESS = True def main(): global args global PIPEXE global SUCCESS # print python debug info heading = ( "%s-%s-%s" % ( PYTHON_EXECUTABLE, PYTHON_VERSION, sys.platform ) ) print(heading) print('=' * WIDTH) # figure out pip executable PIPEXE = "pip" if "windows" in env["OS_NAME"] else "pip3" PIPEXE = "pip" if "osx" in env["OS_NAME"] and "actions" in env["CI_SYSTEM"] else PIPEXE PIP_VERSION = "" # holder for pip's version SUCCESS = False # foreach py executable for PYEXE in ["py", "python3", "python"]: if SUCCESS: continue args = [] # if it's the py launcher, specify the version if PYEXE == "py": PYEXE = [PYEXE, "-" + PYTHON_MINOR_VERSION] # if it ain't windows, skip it if "windows" not in env["OS_NAME"]: continue # build executable command if isinstance(PYEXE, list): args = [*PYEXE] else: args = [PYEXE] try: python_info() # foreach pip executable for PIPEXE in ["pip3", "pip"]: pip_info() pip_upgrade() install_modules() # if something else went fucky, print it except Exception as e: traceback.print_exc() if __name__ == "__main__": main()