import json
import mimetypes
from enum import Enum
from pathlib import Path
from attr import define
from pydantic import BaseModel
from ruamel.yaml import YAML
class ClassOverride(BaseModel):
"""An override of a single generated class.
See https://github.com/openapi-generators/openapi-python-client#class_overrides
"""
class_name: str | None = None
module_name: str | None = None
class MetaType(str, Enum):
"""The types of metadata supported for project generation."""
NONE = "none"
POETRY = "poetry"
SETUP = "setup"
PDM = "pdm"
UV = "uv"
class ConfigFile(BaseModel):
"""Contains any configurable values passed via a config file.
See https://github.com/openapi-generators/openapi-python-client#configuration
"""
class_overrides: dict[str, ClassOverride] | None = None
content_type_overrides: dict[str, str] | None = None
project_name_override: str | None = None
package_name_override: str | None = None
package_version_override: str | None = None
use_path_prefixes_for_title_model_names: bool = True
post_hooks: list[str] | None = None
docstrings_on_attributes: bool = False
field_prefix: str = "field_"
generate_all_tags: bool = False
http_timeout: int = 5
literal_enums: bool = False
@staticmethod
def load_from_path(path: Path) -> "ConfigFile":
"""Creates a Config from provided JSON or YAML file and sets a bunch of globals from it"""
mime = mimetypes.guess_type(path.absolute().as_uri(), strict=True)[0]
if mime == "application/json":
config_data = json.loads(path.read_text())
else:
yaml = YAML(typ="safe")
config_data = yaml.load(path)
config = ConfigFile(**config_data)
return config
@define
class Config:
"""Contains all the config values for the generator, from files, defaults, and CLI arguments."""
meta_type: MetaType
class_overrides: dict[str, ClassOverride]
project_name_override: str | None
package_name_override: str | None
package_version_override: str | None
use_path_prefixes_for_title_model_names: bool
post_hooks: list[str]
docstrings_on_attributes: bool
field_prefix: str
generate_all_tags: bool
http_timeout: int
literal_enums: bool
document_source: Path | str
file_encoding: str
content_type_overrides: dict[str, str]
overwrite: bool
output_path: Path | None
@staticmethod
def from_sources(
config_file: ConfigFile,
meta_type: MetaType,
document_source: Path | str,
file_encoding: str,
overwrite: bool,
output_path: Path | None,
) -> "Config":
if config_file.post_hooks is not None:
post_hooks = config_file.post_hooks
elif meta_type == MetaType.NONE:
post_hooks = [
"ruff check . --fix-only --extend-select=I",
"ruff format .",
]
else:
post_hooks = [
"ruff check --fix-only .",
"ruff format .",
]
config = Config(
meta_type=meta_type,
class_overrides=config_file.class_overrides or {},
content_type_overrides=config_file.content_type_overrides or {},
project_name_override=config_file.project_name_override,
package_name_override=config_file.package_name_override,
package_version_override=config_file.package_version_override,
use_path_prefixes_for_title_model_names=config_file.use_path_prefixes_for_title_model_names,
post_hooks=post_hooks,
docstrings_on_attributes=config_file.docstrings_on_attributes,
field_prefix=config_file.field_prefix,
generate_all_tags=config_file.generate_all_tags,
http_timeout=config_file.http_timeout,
literal_enums=config_file.literal_enums,
document_source=document_source,
file_encoding=file_encoding,
overwrite=overwrite,
output_path=output_path,
)
return config