From 46f9fff2104623cf5d5301c48882e4cb49ee4d30 Mon Sep 17 00:00:00 2001 From: kingbri Date: Mon, 16 Sep 2024 22:22:24 -0400 Subject: [PATCH] Config: Move config file generation to tabby_config Keep the models as a separate reference file without any extra functions. Signed-off-by: kingbri --- common/actions.py | 5 +-- common/config_models.py | 79 ---------------------------------------- common/tabby_config.py | 80 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 84 deletions(-) diff --git a/common/actions.py b/common/actions.py index ebf5539..c7f0a71 100644 --- a/common/actions.py +++ b/common/actions.py @@ -1,8 +1,7 @@ import json from loguru import logger -from common.config_models import generate_config_file -from common.tabby_config import config +from common.tabby_config import config, generate_config_file from endpoints.server import export_openapi @@ -18,10 +17,8 @@ def branch_to_actions() -> bool: "Successfully wrote OpenAPI spec to " + f"{config.actions.openapi_export_path}" ) - elif config.actions.export_config: generate_config_file(filename=config.actions.config_export_path) - else: # did not branch return False diff --git a/common/config_models.py b/common/config_models.py index bbe2d85..58ce754 100644 --- a/common/config_models.py +++ b/common/config_models.py @@ -1,11 +1,7 @@ -from inspect import getdoc from pathlib import Path from pydantic import BaseModel, ConfigDict, Field, PrivateAttr -from pydantic_core import PydanticUndefined -from textwrap import dedent from typing import List, Literal, Optional, Union -from common.utils import unwrap CACHE_SIZES = Literal["FP16", "Q8", "Q6", "Q4"] @@ -468,78 +464,3 @@ class TabbyConfigModel(BaseModel): ) model_config = ConfigDict(validate_assignment=True, protected_namespaces=()) - - -# TODO: Possibly switch to ruamel.yaml for a more native implementation -def generate_config_file( - model: BaseConfigModel = None, - filename: str = "config_sample.yml", - indentation: int = 2, -) -> None: - """Creates a config.yml file from Pydantic models.""" - - # Add a preamble - yaml = dedent(""" - # Sample YAML file for configuration. - # Comment and uncomment values as needed. - # Every value has a default within the application. - # This file serves to be a drop in for config.yml - - # Unless specified in the comments, DO NOT put these options in quotes! - # You can use https://www.yamllint.com/ if you want to check your YAML formatting.\n - """) - - schema = unwrap(model, TabbyConfigModel()) - - # TODO: Make the disordered iteration look cleaner - iter_once = False - for field, field_data in schema.model_fields.items(): - # Fetch from the existing model class if it's passed - # Probably can use this on schema too, but play it safe - if model and hasattr(model, field): - subfield_model = getattr(model, field) - else: - subfield_model = field_data.default_factory() - - if not subfield_model._metadata.include_in_config: - continue - - # Since the list is out of order with the length - # Add newlines from the beginning once one iteration finishes - # This is a sanity check for formatting - if iter_once: - yaml += "\n" - else: - iter_once = True - - for line in getdoc(subfield_model).splitlines(): - yaml += f"# {line}\n" - - yaml += f"{field}:\n" - - sub_iter_once = False - for subfield, subfield_data in subfield_model.model_fields.items(): - # Same logic as iter_once - if sub_iter_once: - yaml += "\n" - else: - sub_iter_once = True - - # If a value already exists, use it - if hasattr(subfield_model, subfield): - value = getattr(subfield_model, subfield) - elif subfield_data.default_factory: - value = subfield_data.default_factory() - else: - value = subfield_data.default - - value = value if value is not None else "" - value = value if value is not PydanticUndefined else "" - - for line in subfield_data.description.splitlines(): - yaml += f"{' ' * indentation}# {line}\n" - - yaml += f"{' ' * indentation}{subfield}: {value}\n" - - with open(filename, "w") as f: - f.write(yaml) diff --git a/common/tabby_config.py b/common/tabby_config.py index 8b92f84..8bec9ed 100644 --- a/common/tabby_config.py +++ b/common/tabby_config.py @@ -1,11 +1,14 @@ import yaml import pathlib +from inspect import getdoc +from pydantic_core import PydanticUndefined from loguru import logger +from textwrap import dedent from typing import Optional from os import getenv from common.utils import unwrap, merge_dicts -from common.config_models import TabbyConfigModel, generate_config_file +from common.config_models import BaseConfigModel, TabbyConfigModel class TabbyConfig(TabbyConfigModel): @@ -159,3 +162,78 @@ class TabbyConfig(TabbyConfigModel): # Create an empty instance of the config class config: TabbyConfig = TabbyConfig() + + +# TODO: Possibly switch to ruamel.yaml for a more native implementation +def generate_config_file( + model: BaseConfigModel = None, + filename: str = "config_sample.yml", + indentation: int = 2, +) -> None: + """Creates a config.yml file from Pydantic models.""" + + # Add a preamble + yaml = dedent(""" + # Sample YAML file for configuration. + # Comment and uncomment values as needed. + # Every value has a default within the application. + # This file serves to be a drop in for config.yml + + # Unless specified in the comments, DO NOT put these options in quotes! + # You can use https://www.yamllint.com/ if you want to check your YAML formatting.\n + """) + + schema = unwrap(model, TabbyConfigModel()) + + # TODO: Make the disordered iteration look cleaner + iter_once = False + for field, field_data in schema.model_fields.items(): + # Fetch from the existing model class if it's passed + # Probably can use this on schema too, but play it safe + if model and hasattr(model, field): + subfield_model = getattr(model, field) + else: + subfield_model = field_data.default_factory() + + if not subfield_model._metadata.include_in_config: + continue + + # Since the list is out of order with the length + # Add newlines from the beginning once one iteration finishes + # This is a sanity check for formatting + if iter_once: + yaml += "\n" + else: + iter_once = True + + for line in getdoc(subfield_model).splitlines(): + yaml += f"# {line}\n" + + yaml += f"{field}:\n" + + sub_iter_once = False + for subfield, subfield_data in subfield_model.model_fields.items(): + # Same logic as iter_once + if sub_iter_once: + yaml += "\n" + else: + sub_iter_once = True + + # If a value already exists, use it + if hasattr(subfield_model, subfield): + value = getattr(subfield_model, subfield) + elif subfield_data.default_factory: + value = subfield_data.default_factory() + else: + value = subfield_data.default + + value = value if value is not None else "" + value = value if value is not PydanticUndefined else "" + + for line in subfield_data.description.splitlines(): + yaml += f"{' ' * indentation}# {line}\n" + + yaml += f"{' ' * indentation}{subfield}: {value}\n" + + with open(filename, "w") as f: + f.write(yaml)