290 lines
10 KiB
Python
Raw Normal View History

import os
import re
import pathlib
import subprocess
import sys
import tempfile
# replace python3 with sys.executable, we will do everything in the same envrionment
pythonCommand = sys.executable
print('Python command:', pythonCommand)
tools_dir = os.path.dirname(os.path.realpath(__file__))
def get_app_data_dir(app_name):
home = os.path.expanduser("~")
if os.name == "nt": # For Windows
appPath = os.path.join(home, "AppData", "Roaming", app_name)
else: # For Unix and Linux
appPath = os.path.join(home, ".local", "share", app_name)
if not os.path.exists(appPath):
os.makedirs(appPath)
return appPath
def get_pythoncmd_in_env(venvdir, envname):
"""
return pythoncmd in virtual env
"""
pythonCommandEnv = venvdir + "/" + envname + "/Scripts/python"
try:
subprocess.run([pythonCommandEnv, "-V"], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
return pythonCommandEnv
except Exception:
pass
pythonCommandEnv = venvdir + "/" + envname + "/bin/python"
try:
subprocess.run([pythonCommandEnv, "-V"], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
return pythonCommandEnv
except Exception:
return ''
def pip_cmd_run(pipcmd, with_user: bool, with_local_source: bool):
"""
run pip cmd
with_user: --user
with_local_source: -i https://pypi.tuna.tsinghua.edu.cn/simple
"""
if with_user:
pipcmd.append("--user")
if with_local_source:
pipcmd.append("-i")
pipcmd.append("https://pypi.tuna.tsinghua.edu.cn/simple")
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
try:
# before command run, output runnning command
print("run command: ", *pipcmd)
subprocess.run(pipcmd, check=True, stdout=sys.stdout, stderr=temp_file, text=True)
return True, ''
except Exception as error:
temp_file.flush()
error_out = temp_file.read()
print(error)
return False, error_out
def pip_cmd_with_retries(pipcmd, retry_times: int, with_user: bool):
"""
when pip cmd fails, then retry for retry_times
with_user: --user whether use --user, if pipcmd error output include --user, then remove --user while retry
if pipcmd error output not include --user, then retry with with_local_source
"""
with_local_source = False
for i in range(0, retry_times):
pipcmd_copy = pipcmd.copy()
ret, error = pip_cmd_run(pipcmd_copy, with_user, with_local_source)
if ret:
return True
if error.find(b"--user") > -1:
with_user = False
else:
with_local_source = True
return False
def pip_install_devchat(pythoncmd):
# run command: {pythoncmd} -m pip install devchat
# check if devchat is installed
# if not, install devchat
# first step: check if devchat is installed
# try:
# # before command run, output runnning command
# print("run command: ", pythoncmd, "-m", "pip", "show", "devchat")
# subprocess.run([pythoncmd, "-m", "pip", "show", "devchat"], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
# pipCommandEnv = pythoncmd.replace("/python", "/devchat")
# print("==> devchatCommandEnv: ", pipCommandEnv)
# return True
# except Exception as error:
# # devchat is not installed
# print('devchat is not installed')
# print(error)
# pass
# second step: install devchat
if (pip_cmd_with_retries([pythoncmd, "-m", "pip", "install", "devchat", "--force"], 3, False)):
# get parent path for pythoncmd
pythoncmd_parent_path = pathlib.Path(pythoncmd).parent
pip_command_env = os.path.join(pythoncmd_parent_path, "devchat")
print("==> devchatCommandEnv: ", pip_command_env)
return True
else:
print('devchat install failed')
return False
def virtualenv_create_venv(pythoncmd, venvdir, envname):
"""
virtualenvcmd is virtualenv with absolute path
create virtual env by virtualenvcmd
if env already exists, and pip has installed, then return pipcmd in virtual env
else if env already exists, delete it.
return pipcmd in virtual env
"""
def pipcmd_exists(pythoncmd, venvdir, envname):
"""
check whether pip command installed
"""
try:
pythonCommandEnv = get_pythoncmd_in_env(venvdir, envname)
if not pythonCommandEnv:
return False
# before command run, output runnning command
print("run command: ", pythonCommandEnv, "-m", "pip", "--version")
subprocess.run([pythonCommandEnv, "-m", "pip", "--version"], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
return True
except Exception:
return False
def extract_actual_location(text):
match = re.search(rb'Actual location:\s+"(.*?)"', text)
if match:
return match.group(1)
else:
return None
# second step: check if env already exists
if os.path.exists(venvdir + "/" + envname):
# check if pip is installed
# if pip is installed, return pipcmd in virtual env
# else delete env
# call pipcmd --version to check if pip is installed
if pipcmd_exists(pythoncmd, venvdir, envname):
return get_pythoncmd_in_env(venvdir, envname)
else:
# delete env by virtualenvcmd
print("delete env: ", venvdir + "/" + envname)
try:
# before command run, output runnning command
print("run command: ", pythoncmd, "-m", "virtualenv", "--clear", venvdir + "/" + envname)
subprocess.run([pythoncmd, "-m", "virtualenv", "--clear", venvdir + "/" + envname], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
except Exception as error:
print('delete env failed')
print(error)
return False
# third step: create env by virtualenvcmd
# handle error while creating env
print("create env: ", venvdir + "/" + envname)
try:
# before command run, output runnning command
print("run command: ", pythoncmd, "-m virtualenv", venvdir + "/" + envname)
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
try:
subprocess.run([pythoncmd, "-m", "virtualenv", venvdir + "/" + envname], check=True, stdout=temp_file, stderr=sys.stderr, text=True)
pythonCmd = get_pythoncmd_in_env(venvdir, envname)
if pythonCmd == '':
temp_file.flush()
outputText = temp_file.read()
pythonCmd = extract_actual_location(outputText)
if pythonCmd is None:
print('create env failed')
print(error)
return False
else:
pythonCmd = pythonCmd.decode()
return pythonCmd
except Exception as error:
print('create env failed')
print(error)
return False
except Exception as error:
print('create env failed')
print(error)
return False
def pip_install_virtualenv(pythoncmd):
# run command: {pythoncmd} -m pip install virtualenv
# check if virtualenv is installed
# if not, install virtualenv
# first step: check if virtualenv is installed
try:
# before command run, output runnning command
print("run command: ", pythoncmd, "-m", "pip", "show", "virtualenv")
subprocess.run([pythoncmd, "-m", "pip", "show", "virtualenv"], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
return True
except Exception:
# virtualenv is not installed
print('virtualenv is not installed')
pass
# second step: install virtualenv
if (pip_cmd_with_retries([pythoncmd, "-m", "pip", "install", "virtualenv"], 4, True)):
return True
else:
print('virtualenv install failed')
return False
def install_pip(pythoncmd):
# run command: {pythoncmd} {tools_dir}/get-pip.py --force-reinstall
# check if pip is installed
# if not, install pip
# first step: check if pip is installed
try:
# before command run, output runnning command
print("run command: ", pythoncmd, "-m", "pip", "--version")
subprocess.run([pythoncmd, "-m", "pip", "--version"], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
return True
except Exception:
# pip is not installed
print('pip is not installed')
pass
# second step: install pip
try:
# before command run, output runnning command
print("run command: ", pythoncmd, tools_dir + "/get-pip.py", "--force-reinstall")
# redirect output to stdout
subprocess.run([pythoncmd, tools_dir + "/get-pip.py", "--force-reinstall"], check=True, stdout=sys.stdout, stderr=sys.stderr, text=True)
return True
except Exception as error:
print('pip install failed')
print(error)
return False
def main():
"""
install pip
install virtualenv
create virtualenv
install devchat
"""
pythoncommand = pythonCommand
venvdir = get_app_data_dir('devchat')
envname = "devchat"
# install pip
if not install_pip(pythoncommand):
return False
# install virtualenv
if not pip_install_virtualenv(pythoncommand):
return False
# create virtualenv
envPythonCmd = virtualenv_create_venv(pythoncommand, venvdir, envname)
if not envPythonCmd:
return False
# install devchat
if not pip_install_devchat(envPythonCmd):
return False
return True
if __name__ == "__main__":
if not main():
sys.exit(1)
sys.exit(0)