From dfe4c033dd1922a63c8393ab467e9aa58fa757e4 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Tue, 22 Jul 2025 11:41:49 -0400 Subject: refactor: update configuration handling to use object-based API --- scripts/generate_ts_schema.py | 135 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 scripts/generate_ts_schema.py (limited to 'scripts') diff --git a/scripts/generate_ts_schema.py b/scripts/generate_ts_schema.py new file mode 100644 index 0000000..dcdddf2 --- /dev/null +++ b/scripts/generate_ts_schema.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +""" +Generate TypeScript schema from Python shared_config.py + +This script reads the canonical schema from shared_config.py and generates +the corresponding TypeScript files, ensuring single source of truth. +""" + +import sys +from pathlib import Path + +# Add project root to path to import shared_config +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root)) + +from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType + + +def generate_typescript_schema(): + """Generate generatedConfigSchema.ts from Python schema""" + + # Generate enum + enum_lines = [ + "// src/config/generatedConfigSchema.ts", + "// Configuration field type enum - matches Python", + "export enum ConfigFieldType {", + " BOOLEAN = \"boolean\",", + " INTEGER = \"integer\",", + " FLOAT = \"float\",", + " STRING = \"string\"", + "}", + "", + "// Configuration field definition", + "export interface ConfigField {", + " name: string;", + " fieldType: ConfigFieldType;", + " default: boolean | number | string;", + " description: string;", + "}", + "", + "// Configuration schema - auto-generated from Python", + "export const CONFIG_SCHEMA: Record = {" + ] + + # Generate schema entries + schema_entries = [] + interface_fields = [] + defaults_fields = [] + field_types = [] + + for field_name, field_def in CONFIG_SCHEMA_DEF.items(): + # Schema entry + default_value = field_def["default"] + if isinstance(default_value, str): + default_str = f'"{default_value}"' + elif isinstance(default_value, bool): + default_str = "true" if default_value else "false" + else: + default_str = str(default_value) + + schema_entries.append(f' {field_name}: {{') + schema_entries.append(f' name: "{field_def["name"]}",') + schema_entries.append(f' fieldType: ConfigFieldType.{field_def["fieldType"].upper()},') + schema_entries.append(f' default: {default_str},') + schema_entries.append(f' description: "{field_def["description"]}"') + schema_entries.append(' },') + + # Interface field + if field_def["fieldType"] == ConfigFieldType.BOOLEAN: + ts_type = "boolean" + elif field_def["fieldType"] == ConfigFieldType.INTEGER: + ts_type = "number" + elif field_def["fieldType"] == ConfigFieldType.FLOAT: + ts_type = "number" + elif field_def["fieldType"] == ConfigFieldType.STRING: + ts_type = "string" + else: + ts_type = "any" + + interface_fields.append(f' {field_name}: {ts_type};') + defaults_fields.append(f' {field_name}: {default_str},') + field_types.append(f' {field_name}: ConfigFieldType.{field_def["fieldType"].upper()},') + + # Complete the file + all_lines = enum_lines + schema_entries + [ + "};", + "", + "// Type-safe configuration data structure", + "export interface ConfigurationData {", + ] + interface_fields + [ + "}", + "", + "// Helper functions", + "export function getFieldNames(): string[] {", + " return Object.keys(CONFIG_SCHEMA);", + "}", + "", + "export function getDefaults(): ConfigurationData {", + " return {", + ] + defaults_fields + [ + " };", + "}", + "", + "export function getFieldTypes(): Record {", + " return {", + ] + field_types + [ + " };", + "}", + "", + "" + ] + + return "\n".join(all_lines) + + +def main(): + """Main function to generate TypeScript schema""" + try: + # Generate the TypeScript content + ts_content = generate_typescript_schema() + + # Write to the target file + target_file = project_root / "src" / "config" / "generatedConfigSchema.ts" + target_file.write_text(ts_content) + + print(f"āœ… Generated {target_file} from shared_config.py") + print(f" Fields: {len(CONFIG_SCHEMA_DEF)}") + + except Exception as e: + print(f"āŒ Error generating TypeScript schema: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From df0635f1bba611b8b44975057acd579102d209dd Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Tue, 22 Jul 2025 13:06:46 -0400 Subject: further automate population of hardcoded fields --- scripts/generate_python_boilerplate.py | 319 +++++++++++++++++++++++++++++++++ scripts/generate_ts_schema.py | 18 +- 2 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 scripts/generate_python_boilerplate.py (limited to 'scripts') diff --git a/scripts/generate_python_boilerplate.py b/scripts/generate_python_boilerplate.py new file mode 100644 index 0000000..0101ae4 --- /dev/null +++ b/scripts/generate_python_boilerplate.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Generate Python boilerplate from shared_config.py + +This script generates repetitive Python code patterns from the canonical schema, +reducing manual maintenance when adding/removing configuration fields. +""" + +import sys +from pathlib import Path + +# Add project root to path to import shared_config +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root)) + +from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType + + +def get_python_type(field_type: ConfigFieldType) -> str: + """Convert ConfigFieldType to Python type annotation""" + type_map = { + ConfigFieldType.BOOLEAN: "bool", + ConfigFieldType.INTEGER: "int", + ConfigFieldType.FLOAT: "float", + ConfigFieldType.STRING: "str" + } + return type_map.get(field_type, "Any") + + +def get_env_var_name(field_name: str) -> str: + """Convert field name to environment variable name""" + env_map = { + "dxvk_frame_rate": "DXVK_FRAME_RATE", + "enable_wow64": "PROTON_USE_WOW64", + "disable_steamdeck_mode": "SteamDeck", + "mangohud_workaround": "MANGOHUD", + "disable_vkbasalt": "DISABLE_VKBASALT" + } + return env_map.get(field_name, field_name.upper()) + + +def generate_typed_dict() -> str: + """Generate ConfigurationData TypedDict""" + lines = [ + "class ConfigurationData(TypedDict):", + " \"\"\"Type-safe configuration data structure - AUTO-GENERATED\"\"\"" + ] + + for field_name, field_def in CONFIG_SCHEMA_DEF.items(): + python_type = get_python_type(ConfigFieldType(field_def["fieldType"])) + lines.append(f" {field_name}: {python_type}") + + return "\n".join(lines) + + +def generate_function_signature() -> str: + """Generate function signature for update_config and create_config_from_args""" + params = [] + + for field_name, field_def in CONFIG_SCHEMA_DEF.items(): + python_type = get_python_type(ConfigFieldType(field_def["fieldType"])) + default = field_def["default"] + + # Format default value + if isinstance(default, str): + default_str = f'"{default}"' + elif isinstance(default, bool): + default_str = str(default) + else: + default_str = str(default) + + params.append(f"{field_name}: {python_type} = {default_str}") + + return ",\n ".join(params) + + +def generate_config_dict_creation() -> str: + """Generate dictionary creation for create_config_from_args""" + lines = [" return cast(ConfigurationData, {"] + + for field_name in CONFIG_SCHEMA_DEF.keys(): + lines.append(f' "{field_name}": {field_name},') + + lines.append(" })") + return "\n".join(lines) + + +def generate_script_parsing() -> str: + """Generate script content parsing logic""" + lines = [] + + script_fields = [ + (field_name, field_def) + for field_name, field_def in CONFIG_SCHEMA_DEF.items() + if field_def.get("location") == "script" + ] + + for field_name, field_def in script_fields: + env_var = get_env_var_name(field_name) + field_type = ConfigFieldType(field_def["fieldType"]) + + if field_type == ConfigFieldType.BOOLEAN: + if field_name == "disable_steamdeck_mode": + # Special case: SteamDeck=0 means disable_steamdeck_mode=True + lines.append(f' elif key == "{env_var}":') + lines.append(f' script_values["{field_name}"] = value == "0"') + else: + lines.append(f' elif key == "{env_var}":') + lines.append(f' script_values["{field_name}"] = value == "1"') + elif field_type == ConfigFieldType.INTEGER: + lines.append(f' elif key == "{env_var}":') + lines.append(' try:') + lines.append(f' script_values["{field_name}"] = int(value)') + lines.append(' except ValueError:') + lines.append(' pass') + elif field_type == ConfigFieldType.FLOAT: + lines.append(f' elif key == "{env_var}":') + lines.append(' try:') + lines.append(f' script_values["{field_name}"] = float(value)') + lines.append(' except ValueError:') + lines.append(' pass') + elif field_type == ConfigFieldType.STRING: + lines.append(f' elif key == "{env_var}":') + lines.append(f' script_values["{field_name}"] = value') + + return "\n".join(lines) + + +def generate_script_generation() -> str: + """Generate script content generation logic""" + lines = [] + + script_fields = [ + (field_name, field_def) + for field_name, field_def in CONFIG_SCHEMA_DEF.items() + if field_def.get("location") == "script" + ] + + for field_name, field_def in script_fields: + env_var = get_env_var_name(field_name) + field_type = ConfigFieldType(field_def["fieldType"]) + + if field_type == ConfigFieldType.BOOLEAN: + if field_name == "disable_steamdeck_mode": + # Special case: disable_steamdeck_mode=True should export SteamDeck=0 + lines.append(f' if config.get("{field_name}", False):') + lines.append(f' lines.append("export {env_var}=0")') + else: + lines.append(f' if config.get("{field_name}", False):') + lines.append(f' lines.append("export {env_var}=1")') + elif field_type in [ConfigFieldType.INTEGER, ConfigFieldType.FLOAT]: + default = field_def["default"] + if field_name == "dxvk_frame_rate": + # Special handling for DXVK_FRAME_RATE (only export if > 0) + lines.append(f' {field_name} = config.get("{field_name}", {default})') + lines.append(f' if {field_name} > 0:') + lines.append(f' lines.append(f"export {env_var}={{{field_name}}}")') + else: + lines.append(f' {field_name} = config.get("{field_name}", {default})') + lines.append(f' if {field_name} != {default}:') + lines.append(f' lines.append(f"export {env_var}={{{field_name}}}")') + elif field_type == ConfigFieldType.STRING: + lines.append(f' {field_name} = config.get("{field_name}", "")') + lines.append(f' if {field_name}:') + lines.append(f' lines.append(f"export {env_var}={{{field_name}}}")') + + return "\n".join(lines) + + +def generate_log_statement() -> str: + """Generate logging statement with all field values""" + field_parts = [] + + for field_name in CONFIG_SCHEMA_DEF.keys(): + field_parts.append(f"{field_name}={{{field_name}}}") + + log_format = ", ".join(field_parts) + return f' self.log.info(f"Updated lsfg TOML configuration: {log_format}")' + + +def generate_complete_schema_file() -> str: + """Generate complete config_schema_generated.py file""" + lines = [ + '"""', + 'Auto-generated configuration schema components from shared_config.py', + 'DO NOT EDIT THIS FILE MANUALLY - it will be overwritten on build', + '"""', + '', + 'from typing import TypedDict, Dict, Any, Union, cast', + 'from enum import Enum', + 'import sys', + 'from pathlib import Path', + '', + '# Import shared configuration constants', + 'sys.path.insert(0, str(Path(__file__).parent.parent.parent))', + 'from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType', + '', + '', + generate_typed_dict(), + '', + '', + 'def get_script_parsing_logic():', + ' """Return the script parsing logic as a callable"""', + ' def parse_script_values(lines):', + ' script_values = {}', + ' for line in lines:', + ' line = line.strip()', + ' if not line or line.startswith("#") or not line.startswith("export "):', + ' continue', + ' if "=" in line:', + ' export_line = line[len("export "):]', + ' key, value = export_line.split("=", 1)', + ' key = key.strip()', + ' value = value.strip()', + '', + ' # Auto-generated parsing logic:', + f'{generate_script_parsing().replace(" elif", " if")}', + '', + ' return script_values', + ' return parse_script_values', + '', + '', + 'def get_script_generation_logic():', + ' """Return the script generation logic as a callable"""', + ' def generate_script_lines(config):', + ' lines = []', + f'{generate_script_generation()}', + ' return lines', + ' return generate_script_lines', + '', + '', + 'def get_function_parameters() -> str:', + ' """Return function signature parameters"""', + f' return """{generate_function_signature()}"""', + '', + '', + 'def create_config_dict(**kwargs) -> ConfigurationData:', + ' """Create configuration dictionary from keyword arguments"""', + f'{generate_config_dict_creation().replace(" return cast(ConfigurationData, {", " return cast(ConfigurationData, {").replace(" })", " })")}', + '', + '', + '# Field lists for dynamic operations', + f'TOML_FIELDS = {[name for name, field in CONFIG_SCHEMA_DEF.items() if field.get("location") == "toml"]}', + f'SCRIPT_FIELDS = {[name for name, field in CONFIG_SCHEMA_DEF.items() if field.get("location") == "script"]}', + f'ALL_FIELDS = {list(CONFIG_SCHEMA_DEF.keys())}', + '' + ] + + return '\n'.join(lines) + + +def generate_complete_configuration_helpers() -> str: + """Generate configuration_helpers_generated.py file""" + + # Generate the log format string using config parameter + log_parts = [] + for field_name in CONFIG_SCHEMA_DEF.keys(): + log_parts.append(f"{field_name}={{config['{field_name}']}}") + log_format = ", ".join(log_parts) + + lines = [ + '"""', + 'Auto-generated configuration helper functions from shared_config.py', + 'DO NOT EDIT THIS FILE MANUALLY - it will be overwritten on build', + '"""', + '', + 'from typing import Dict, Any', + 'from .config_schema_generated import ConfigurationData, ALL_FIELDS', + '', + '', + 'def log_configuration_update(logger, config: ConfigurationData) -> None:', + ' """Log configuration update with all field values"""', + f' logger.info(f"Updated lsfg TOML configuration: {log_format}")', + '', + '', + 'def get_config_field_names() -> list[str]:', + ' """Get all configuration field names"""', + ' return ALL_FIELDS.copy()', + '', + '', + 'def extract_config_values(config: ConfigurationData) -> Dict[str, Any]:', + ' """Extract configuration values as a dictionary"""', + ' return {field: config[field] for field in ALL_FIELDS}', + '' + ] + + return '\n'.join(lines) + + +def main(): + """Generate complete Python configuration files""" + try: + # Create generated files in py_modules/lsfg_vk/ + target_dir = project_root / "py_modules" / "lsfg_vk" + + # Generate the complete schema file + schema_content = generate_complete_schema_file() + schema_file = target_dir / "config_schema_generated.py" + schema_file.write_text(schema_content) + print(f"āœ… Generated {schema_file.relative_to(project_root)}") + + # Generate configuration helpers + helpers_content = generate_complete_configuration_helpers() + helpers_file = target_dir / "configuration_helpers_generated.py" + helpers_file.write_text(helpers_content) + print(f"āœ… Generated {helpers_file.relative_to(project_root)}") + + print(f"\nšŸŽÆ Ready-to-use files generated!") + print(" Import these in your main files:") + print(" - from .config_schema_generated import ConfigurationData, get_script_parsing_logic, etc.") + print(" - from .configuration_helpers_generated import log_configuration_update, etc.") + + except Exception as e: + print(f"āŒ Error generating Python files: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_ts_schema.py b/scripts/generate_ts_schema.py index dcdddf2..1997c55 100644 --- a/scripts/generate_ts_schema.py +++ b/scripts/generate_ts_schema.py @@ -114,7 +114,7 @@ def generate_typescript_schema(): def main(): - """Main function to generate TypeScript schema""" + """Main function to generate TypeScript schema and Python boilerplate""" try: # Generate the TypeScript content ts_content = generate_typescript_schema() @@ -126,8 +126,22 @@ def main(): print(f"āœ… Generated {target_file} from shared_config.py") print(f" Fields: {len(CONFIG_SCHEMA_DEF)}") + # Also generate Python boilerplate + print("\nšŸ”„ Generating Python boilerplate...") + from pathlib import Path + import subprocess + + boilerplate_script = project_root / "scripts" / "generate_python_boilerplate.py" + result = subprocess.run([sys.executable, str(boilerplate_script)], + capture_output=True, text=True) + + if result.returncode == 0: + print(result.stdout) + else: + print(f"āš ļø Python boilerplate generation had issues:\n{result.stderr}") + except Exception as e: - print(f"āŒ Error generating TypeScript schema: {e}") + print(f"āŒ Error generating schema: {e}") sys.exit(1) -- cgit v1.2.3 From d063284dea10e82a23c2c332ecd4901d7254171b Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Tue, 22 Jul 2025 13:23:25 -0400 Subject: use generated kwargs and config in more hardcoded places --- scripts/generate_python_boilerplate.py | 16 +++++++++++++--- scripts/generate_ts_schema.py | 9 +++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/generate_python_boilerplate.py b/scripts/generate_python_boilerplate.py index 0101ae4..b16aa3f 100644 --- a/scripts/generate_python_boilerplate.py +++ b/scripts/generate_python_boilerplate.py @@ -76,12 +76,12 @@ def generate_function_signature() -> str: def generate_config_dict_creation() -> str: """Generate dictionary creation for create_config_from_args""" - lines = [" return cast(ConfigurationData, {"] + lines = [" return cast(ConfigurationData, {"] for field_name in CONFIG_SCHEMA_DEF.keys(): - lines.append(f' "{field_name}": {field_name},') + lines.append(f' "{field_name}": kwargs.get("{field_name}"),') - lines.append(" })") + lines.append(" })") return "\n".join(lines) @@ -180,6 +180,13 @@ def generate_log_statement() -> str: def generate_complete_schema_file() -> str: """Generate complete config_schema_generated.py file""" + + # Generate field name constants + field_constants = [] + for field_name in CONFIG_SCHEMA_DEF.keys(): + const_name = field_name.upper() + field_constants.append(f'{const_name} = "{field_name}"') + lines = [ '"""', 'Auto-generated configuration schema components from shared_config.py', @@ -194,6 +201,9 @@ def generate_complete_schema_file() -> str: '# Import shared configuration constants', 'sys.path.insert(0, str(Path(__file__).parent.parent.parent))', 'from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType', + '', + '# Field name constants for type-safe access', + ] + field_constants + [ '', '', generate_typed_dict(), diff --git a/scripts/generate_ts_schema.py b/scripts/generate_ts_schema.py index 1997c55..c4c0e8a 100644 --- a/scripts/generate_ts_schema.py +++ b/scripts/generate_ts_schema.py @@ -19,6 +19,12 @@ from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType def generate_typescript_schema(): """Generate generatedConfigSchema.ts from Python schema""" + # Generate field name constants + field_constants = [] + for field_name in CONFIG_SCHEMA_DEF.keys(): + const_name = field_name.upper() + field_constants.append(f'export const {const_name} = "{field_name}" as const;') + # Generate enum enum_lines = [ "// src/config/generatedConfigSchema.ts", @@ -29,6 +35,9 @@ def generate_typescript_schema(): " FLOAT = \"float\",", " STRING = \"string\"", "}", + "", + "// Field name constants for type-safe access", + ] + field_constants + [ "", "// Configuration field definition", "export interface ConfigField {", -- cgit v1.2.3