InvokeAI/scripts/update_config_docstring.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

83 lines
3.4 KiB
Python
Raw Normal View History

import os
from typing import Literal, get_args, get_type_hints
from invokeai.app.services.config.config_default import InvokeAIAppConfig
_excluded = {"schema_version", "legacy_models_yaml_path"}
def generate_config_docstrings() -> str:
"""Helper function for mkdocs. Generates a docstring for the InvokeAIAppConfig class.
You shouldn't run this manually. Instead, run `scripts/update-config-docstring.py` to update the docstring.
A makefile target is also available: `make update-config-docstring`.
See that script for more information about why this is necessary.
"""
docstring = ' """Invoke\'s global app configuration.\n\n'
docstring += " Typically, you won't need to interact with this class directly. Instead, use the `get_config` function from `invokeai.app.services.config` to get a singleton config object.\n\n"
docstring += " Attributes:\n"
field_descriptions: list[str] = []
type_hints = get_type_hints(InvokeAIAppConfig)
for k, v in InvokeAIAppConfig.model_fields.items():
if v.exclude or k in _excluded:
continue
field_type = type_hints.get(k)
extra = ""
if getattr(field_type, "__origin__", None) is Literal:
# Get options for literals - the docs generator can't pull these out
options = [f"`{str(x)}`" for x in get_args(field_type)]
extra = f"<br>Valid values: {', '.join(options)}"
field_descriptions.append(f" {k}: {v.description}{extra}")
docstring += "\n".join(field_descriptions)
docstring += '\n """'
return docstring
2024-03-15 11:00:37 +00:00
# The pydantic app config can be documented automatically using mkdocs, but this requires that the docstring
# for the class is kept up to date. We use a pydantic model for the app config. Each config setting is a field
# with a `description` parameter. It is tedious to update both the description _and_ the docstring for the class.
#
# This script parses the pydantic model, generates a valid docstring and replaces the existing docstring in the file,
# so you don't need to worry about keeping the docstring up to date.
#
# A test is provided to ensure that the docstring is up to date. If the test fails, run this script.
def main():
# Change working directory to the repo root
os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
refactor(config): simplified config - Remove OmegaConf. It functioned as an intermediary data format, between YAML/argparse and pydantic. It's not necessary - we can parse YAML or CLI args directly with pydantic. - Remove dynamic CLI args. Only `root` is explicitly supported. This greatly simplifies config handling. Configuration is done by editing the YAML file. Frequently-used args can be added if there is a demand. - A separate arg parser is created to handle the slimmed-down CLI args. It's run immediately in the `invokeai-web` script to handle `--version` and `--help`. It is also used inside the singleton config getter (see below). - Remove categories from the config. Our settings model is mostly flat. Handling categories adds complexity for both us and users - we have to handle transforming a flat config to categorized config (and vice-versa), while users have to be careful with indentation in their YAML file. - Add a `meta` key to the config file. Currently, this holds the config schema version only. It is not a part of the config object itself. - Remove legacy settings that are no longer referenced, or were effectively no-op settings when referenced in code. - Implement simple migration logic to for v3 configs. If migration is successful, the v3 config file is backed up to `invokeai.yaml.bak` and the new config written to `invokeai.yaml`. - Previously, the singleton config was accessed by calling `InvokeAIAppConfig.get_config()`. This returned an instance of `InvokeAIAppConfig`, which _also_ has the `get_config` function. This created to a confusing situation where you weren't sure if you needed to call `get_config` or just use the config object. This method is replaced by a standalone `get_config` function which returns a singleton config object. - Wrap CLI arg parsing (for `root`) and loading/migrating `invokeai.yaml` into the new `get_config()` function. - Move `generate_config_docstrings` into standalone utility function. - Make `root` a private attr (`_root`). This reduces the temptation to directly modify and or use this sensitive field and ensures it is neither serialized nor read from input data. Use `root_path` to access the resolved root path, or `set_root` to set the root to something.
2024-03-11 11:45:24 +00:00
docstring = generate_config_docstrings()
# Replace the docstring in the file
with open("invokeai/app/services/config/config_default.py", "r") as f:
lines = f.readlines()
# Find the class definition line
class_def_index = next(i for i, line in enumerate(lines) if "class InvokeAIAppConfig" in line)
# Find the existing docstring start and end lines
docstring_start_index = next(i for i, line in enumerate(lines[class_def_index:]) if '"""' in line) + class_def_index
docstring_end_index = (
next(i for i, line in enumerate(lines[docstring_start_index + 1 :]) if '"""' in line)
+ docstring_start_index
+ 1
)
# Replace the existing docstring with the new one, plus some line breaks in between. This _should_ result in a
# correctly-formatted file with no syntax errors.
lines = lines[:docstring_start_index] + [docstring, "\n"] + lines[docstring_end_index + 1 :]
# Write the modified lines back to the file
with open("invokeai/app/services/config/config_default.py", "w") as f:
f.writelines(lines)
if __name__ == "__main__":
main()