From 3c8384ee71b191b4e12ce6e55ad4af51677b1349 Mon Sep 17 00:00:00 2001 From: kingbri Date: Sat, 21 Sep 2024 14:33:15 -0400 Subject: [PATCH] Start: Fix startup with new argparser Since the full argparser requires pydantic, gate it until all dependencies are installed. Also if the venv is deleted, assume that start_options.json is invalid as well. Signed-off-by: kingbri --- common/args.py | 13 +++++++-- start.bat | 5 ++++ start.py | 78 ++++++++++++++++++++++++++++++++++++++------------ start.sh | 5 ++++ 4 files changed, 79 insertions(+), 22 deletions(-) diff --git a/common/args.py b/common/args.py index 3863580..39ba293 100644 --- a/common/args.py +++ b/common/args.py @@ -1,10 +1,11 @@ """Argparser for overriding config values""" import argparse +from typing import Optional from pydantic import BaseModel from common.config_models import TabbyConfigModel -from common.utils import is_list_type, unwrap_optional_type +from common.utils import is_list_type, unwrap, unwrap_optional_type def add_field_to_group(group, field_name, field_type, field) -> None: @@ -23,12 +24,18 @@ def add_field_to_group(group, field_name, field_type, field) -> None: group.add_argument(f"--{field_name}", **kwargs) -def init_argparser() -> argparse.ArgumentParser: +def init_argparser( + existing_parser: Optional[argparse.ArgumentParser] = None, +) -> argparse.ArgumentParser: """ Initializes an argparse parser based on a Pydantic config schema. + + If an existing provider is given, use that. """ - parser = argparse.ArgumentParser(description="TabbyAPI server") + parser = unwrap( + existing_parser, argparse.ArgumentParser(description="TabbyAPI server") + ) # Loop through each top-level field in the config for field_name, field_info in TabbyConfigModel.model_fields.items(): diff --git a/start.bat b/start.bat index 4ae3247..2f14878 100644 --- a/start.bat +++ b/start.bat @@ -12,6 +12,11 @@ if exist "%CONDA_PREFIX%" ( if not exist "venv\" ( echo Venv doesn't exist! Creating one for you. python -m venv venv + + if exist "start_options.json" ( + echo Removing old start_options.json + del start_options.json + ) ) call .\venv\Scripts\activate.bat diff --git a/start.py b/start.py index 7e7776d..b660917 100644 --- a/start.py +++ b/start.py @@ -10,8 +10,6 @@ import sys from shutil import copyfile import traceback -from common.args import convert_args_to_dict, init_argparser - start_options = {} @@ -93,6 +91,20 @@ def get_install_features(lib_name: str = None): return install_features +def create_argparser(): + try: + from common.args import init_argparser + + return init_argparser() + except ModuleNotFoundError: + print( + "Pydantic not found. Showing an abridged help menu.\n" + "Run this script once to install dependencies.\n" + ) + + return argparse.ArgumentParser() + + def add_start_args(parser: argparse.ArgumentParser): """Add start script args to the provided parser""" start_group = parser.add_argument_group("start") @@ -151,9 +163,25 @@ if __name__ == "__main__": subprocess.run(["pip", "-V"]) # Create an argparser and add extra startup script args - parser = init_argparser() + # Try creating a full argparser if pydantic is installed + # Otherwise, create an abridged one solely for startup + try: + from common.args import init_argparser + + parser = init_argparser() + has_full_parser = True + except ModuleNotFoundError: + parser = argparse.ArgumentParser( + description="Abridged TabbyAPI start script parser.", + epilog=( + "Some dependencies were not found to display the full argparser. " + "Run the script once to install/update them." + ), + ) + has_full_parser = False + add_start_args(parser) - args = parser.parse_args() + args, _ = parser.parse_known_args() script_ext = "bat" if platform.system() == "Windows" else "sh" start_options_path = pathlib.Path("start_options.json") @@ -176,6 +204,7 @@ if __name__ == "__main__": # Set variables that rely on start options first_run = not start_options.get("first_run_done") + # Set gpu_lib for dependency install if args.gpu_lib: print("Overriding GPU lib name from args.") gpu_lib = args.gpu_lib @@ -184,25 +213,13 @@ if __name__ == "__main__": else: gpu_lib = None - # Create a config if it doesn't exist - # This is not necessary to run TabbyAPI, but is new user proof - config_path = ( - pathlib.Path(args.config) if args.config else pathlib.Path("config.yml") - ) - if not config_path.exists(): - sample_config_path = pathlib.Path("config_sample.yml") - copyfile(sample_config_path, config_path) - - print( - "A config.yml wasn't found.\n" - f"Created one at {str(config_path.resolve())}" - ) - + # Pull from GitHub if args.update_repository: print("Pulling latest changes from Github.") pull_command = "git pull" subprocess.run(pull_command.split(" ")) + # Install/update dependencies if first_run or args.update_deps: install_command = ["pip", "install", "-U"] @@ -231,6 +248,7 @@ if __name__ == "__main__": "inside the `update_scripts` folder." ) + # First run options if first_run: start_options["first_run_done"] = True @@ -245,12 +263,34 @@ if __name__ == "__main__": "will reinstall TabbyAPI as a first-time user." ) - # Import entrypoint after installing all requirements + # Expand the parser if it's not fully created + if not has_full_parser: + from common.args import init_argparser + + parser = init_argparser(parser) + args = parser.parse_args() + + # Assume all dependencies are installed from here try: + from common.args import convert_args_to_dict from main import entrypoint converted_args = convert_args_to_dict(args, parser) + # Create a config if it doesn't exist + # This is not necessary to run TabbyAPI, but is new user proof + config_path = ( + pathlib.Path(args.config) if args.config else pathlib.Path("config.yml") + ) + if not config_path.exists(): + sample_config_path = pathlib.Path("config_sample.yml") + copyfile(sample_config_path, config_path) + + print( + "A config.yml wasn't found.\n" + f"Created one at {str(config_path.resolve())}" + ) + print("Starting TabbyAPI...") entrypoint(converted_args) except (ModuleNotFoundError, ImportError): diff --git a/start.sh b/start.sh index 9318931..5c4a7b3 100755 --- a/start.sh +++ b/start.sh @@ -8,6 +8,11 @@ else if [ ! -d "venv" ]; then echo "Venv doesn't exist! Creating one for you." python3 -m venv venv + + if [ -f "start_options.json" ]; then + echo "Removing old start_options.json" + rm -rf start_options.json + fi fi echo "Activating venv"