diff --git a/installer/__main__.py b/installer/__main__.py index 1b23f84ba..b974619f3 100644 --- a/installer/__main__.py +++ b/installer/__main__.py @@ -1,38 +1,28 @@ import traceback -from helper import log_init, log_close -from unix_windows import IS_WIN +try: + from helper import log_init, log_close + from unix_windows import IS_WIN -def main(): - try: - log_init() + log_init() - import os - os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + import os + os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - import steps.a_setup_virtualenv - import steps.b_pip - import steps.c_nltk + import steps.a_setup_virtualenv + import steps.b_pip + import steps.c_nltk + if not IS_WIN: + # TODO Optional requirements on windows + import steps.d_optional + import steps.e_launcher - if not IS_WIN: - # TODO Optional requirements on windows - import steps.d_optional - import steps.e_launcher - - except SystemExit: - # Expected Error - pass - except Exception as e: - handle_unexpected_error(e) - -def handle_unexpected_error(exception): +except SystemExit: + # Expected Error + pass +except BaseException: print("\n\n") print("An unexpected error occurred. Please open an issue on github!") - print("Here is the error:") + print("here is the error:") print('') traceback.print_exc() - # You can log the exception to a file or external logging system if needed - log_close() - -if __name__ == "__main__": - main() diff --git a/installer/helper.py b/installer/helper.py index b2f2e6311..28d01cdc7 100644 --- a/installer/helper.py +++ b/installer/helper.py @@ -7,63 +7,89 @@ from tempfile import NamedTemporaryFile from unix_windows import IS_WIN -SUPPORTED_SHELLS = ['bash', 'zsh'] +SUPPORTED_SHELLS = [ + 'bash', + 'zsh', +] -class Logger: - def __init__(self): - self.debug_log = None +# python 2/3 compatibility +try: + input = raw_input +except NameError: + pass - def init_log(self): - self.debug_log = NamedTemporaryFile(delete=False, mode="w") - print(f"Logging to {self.debug_log.name}") - def log(self, msg): - try: - self.debug_log.write(f"{msg}\n") - except Exception as e: - self.print_log('------------------------------') - print("Logging failed?") - print(repr(e)) - print(str(e)) - print(str(e.args)) - print('msg:') - try: - print(str(msg)) - except BaseException: - print('msg unprintable') - print('-----------------------------') - - def print_log(self, msg): - self.log(msg) - print(msg) - - def close_log(self): - self.debug_log.close() - -logger = Logger() +def executable_exists(name): + binary_path = shutil.which(name) + return binary_path is not None and os.access(binary_path, os.X_OK) + + +debug_log = None + + +def log_init(): + global debug_log + debug_log = NamedTemporaryFile(delete=False, mode="w") + print("Logging to {}".format(debug_log.name)) + + +def log_close(): + debug_log.close() + def fail(msg, fatal=False): - logger.print_log("Installation failed") - logger.log(msg) + log("Installation failed") + log(msg) print(msg) print('') print('') if fatal: - logger.log("FATAL!") - print("Installation failed with an unexpected error. This should not have happened.") - print(f"Please check logs at \"{logger.debug_log.name}\". If you open a bug report, please include this file.") + log("FATAL!") + print("Installation failed with unexpected error - This should not have happened.") + print("Please check logs at \"{}\". If you open a bug report, please include this file.".format(debug_log.name)) else: print("Installation failed!") - logger.close_log() + debug_log.close() sys.exit(1) + +def log(msg): + try: + debug_log.write(msg) + debug_log.write('\n') + except Exception as e: + print('------------------------------') + print("Logging failed?") + print(repr(e)) + print(str(e)) + print(str(e.args)) + print('msg:') + try: + print(str(msg)) + except BaseException: + print('msg unprintable') + print('-----------------------------') + + +def printlog(msg): + log(msg) + print(msg) + + def section(title): - logger.print_log(f"\n{'{:=^50}'.format(f' {title} ')}") + printlog("\n{:=^50}".format(" {} ".format(title))) + + +spinning = True + def spinning_cursor_start(): + global spinning + spinning = True + def spin_start(): time.sleep(0.1) - while getattr(spinning_cursor_start, "spinning", True): + while spinning: for cursor in '|/-\\': sys.stdout.write(cursor) sys.stdout.flush() @@ -72,75 +98,105 @@ def spin_start(): Thread(target=spin_start).start() + def spinning_cursor_stop(): - spinning_cursor_start.spinning = False + global spinning + spinning = False + def user_input(items): - logger.log("User input:") + log("User input:") for x, item in enumerate(items): - logger.print_log(f"{x + 1}) {item[0]}") + printlog("{}) {}".format((x + 1), item[0])) while True: - number = input(f"Select number 1-{len(items)}: ") + number = input("Select number {}-{}: ".format(1, len(items))) try: number = int(number) - 1 except ValueError: - logger.log(f"> User input {number} - not a number") + log("> User input {} - not a number".format(number)) continue - if 0 <= number < len(items): - logger.log(f"> User input {number} - ok: {items[number][1]}") + if number >= 0 and number < len(items): + log("> User input {} - ok: {}".format(number, items[number][1])) return items[number][1] else: - logger.log(f"> User input {number} - out of range 1 - {len(items)}") + log("> User input {} - out of range {} - {}".format(number, 1, len(items))) - +def confirm_user_input(confirmation_message : str) -> bool: + log("User Confirmation") + printlog(confirmation_message) + confirm = input("input 'y' to confirm, 'n' to cancel: ") + confirm_bool = True if confirm in ['y', 'Y', 'yes', 'YES'] else False; + log("User chose to {} the operation.".format("cancel" if confirm_bool == False else "confirm")) + return confirm_bool def shell(cmd): - class ShellException(Exception): - pass + class Fail: + def should_not_fail(self, msg=''): + fail(msg, fatal=True) + + def success(self): + return False + + def __str__(self): + return "FAIL {}".format(self.exception) - logger.log("_" * 40) - logger.log(f"Shell: {cmd}") + class Success: + def should_not_fail(self, msg=''): + pass + + def success(self): + return True + + def __str__(self): + return "OK" + + exit_code = Success() + + log("_" * 40) + log("Shell: {}".format(cmd)) spinning_cursor_start() cli_output = '' try: cli_output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, universal_newlines=True) except subprocess.CalledProcessError as e: - raise ShellException(str(e), output=str(e.output)) + exit_code = Fail() + exit_code.exception = str(e) + cli_output += str(e.output) - + # python 2 compatibility try: cli_output = cli_output.decode("utf-8") except AttributeError: pass + exit_code.cli_output = cli_output - logger.log(cli_output) - logger.log(f"Shell: Exit OK") - logger.log("-" * 40) - logger.log("") + log(cli_output) + log("Shell: Exit {}".format(str(exit_code))) + log("-" * 40) + log("") spinning_cursor_stop() time.sleep(0.5) sys.stdout.write(' \b') - return cli_output + return exit_code + def get_default_shell(): + ''' + Determine and return the default shell + of the current logged in user. + Args: + None + Returns: + shell name (str) + ''' try: bin_path = os.environ.get('SHELL') return bin_path.split(os.path.sep)[-1] except AttributeError: + # SHELL environment not set return None - -if __name__ == "__main__": - try: - logger.init_log() - - except ShellException as se: - fail(f"Shell command failed: {se}", fatal=True) - except Exception as e: - fail(f"An unexpected error occurred: {e}", fatal=True) - finally: - logger.close_log() diff --git a/installer/unix_windows.py b/installer/unix_windows.py index f446330bd..af0f787e4 100644 --- a/installer/unix_windows.py +++ b/installer/unix_windows.py @@ -1,37 +1,36 @@ import os -def get_system_commands(): - is_windows = os.name == 'nt' +if os.name == 'nt': + IS_WIN = True +else: + IS_WIN = False - if is_windows: - if os.system('py --version') == 0: - py3_cmd = "py -3" - virtualenv_cmd = "py -3 -m virtualenv" - else: - py3_cmd = "python" - virtualenv_cmd = "python -m virtualenv" - virtualenv_python = "env\\Scripts\\python.exe" - virtualenv_pip = "env\\Scripts\\pip.exe" - virtualenv_install_msg = f"""\ - Note that virtualenv must work with Python 3! - - You could do: - {py3_cmd} -m ensurepip - {py3_cmd} -m pip install virtualenv - """ +if IS_WIN: + if os.system('py --version') == 0: + PY3 = "py -3" + VIRTUALENV_CMD = "py -3 -m virtualenv" else: - py3_cmd = "python3" - virtualenv_cmd = "virtualenv --python=python3" - virtualenv_python = "env/bin/python" - virtualenv_pip = "env/bin/pip" - virtualenv_install_msg = """\ - For example on Ubuntu you could do - > [sudo] apt install virtualenv - Or use Pip: - > [sudo] pip install virtualenv - """ + PY3 = "python" + VIRTUALENV_CMD = "python -m virtualenv" + VIRTUALENV_PYTHON = "env\\Scripts\\python.exe" + VIRTUALENV_PIP = "env\\Scripts\\pip.exe" + VIRTUALENV_INSTALL_MSG = """\ +Note that virtualenv must work with Python 3! - return py3_cmd, virtualenv_cmd, virtualenv_python, virtualenv_pip, virtualenv_install_msg +You could do: +{PY3} -m ensurepip +{PY3} -m pip install virtualenv +""".format(PY3=PY3) -PY3, VIRTUALENV_CMD, VIRTUALENV_PYTHON, VIRTUALENV_PIP, VIRTUALENV_INSTALL_MSG = get_system_commands() +else: + PY3 = "python3" + VIRTUALENV_CMD = "virtualenv --python=python3" + VIRTUALENV_PYTHON = "env/bin/python" + VIRTUALENV_PIP = "env/bin/pip" + VIRTUALENV_INSTALL_MSG = """\ +For example on Ubuntu you could do + > [sudo] apt install virtualenv +Or use Pip: + > [sudo] pip install virtualenv +"""