Source code for simstack.models.simstack_model

from functools import wraps
from typing import TypeVar, Type, Any, get_type_hints, overload

from simstack.util.cleaned_json_schema import cleaned_json_schema
from simstack.util.custom_model_dump import custom_model_dump
from simstack.util.default_from_dict import default_from_dict, default_from_model
from simstack.util.generate_ui_schema import generate_ui_schema
from simstack.util.ui_tools import ui_make_title

T = TypeVar("T")


@overload
def simstack_model(cls: Type[T]) -> Type[T]:
    ...


[docs] def simstack_model(cls: T) -> T: """ Decorates a given class to equip it with default implementations of utility methods for handling operations such as dictionary conversion, schema generation, and UI schema generation. """ # Function to create a properly typed wrapper that preserves docstrings def create_typed_wrapper(func, first_param_name="this_class"): # Get original type hints original_hints = get_type_hints(func) # Create a wrapper with the correct parameters @wraps(func) # This preserves metadata like docstrings def wrapper(*args, **kwargs): return func(*args, **kwargs) # Add explicit docstring if not preserved by wraps if not wrapper.__doc__ and func.__doc__: wrapper.__doc__ = func.__doc__ # Copy the original function's annotations wrapper.__annotations__ = { first_param_name: Type[Any], # the first param is now the class **{ k: v for k, v in original_hints.items() if k != "return" and k != first_param_name }, } # Preserve the return annotation if it exists if "return" in original_hints: wrapper.__annotations__["return"] = original_hints["return"] return wrapper # Create typed wrappers for all functions with explicit docstrings default_class_methods = { "json_schema": create_typed_wrapper(cleaned_json_schema), "ui_schema": create_typed_wrapper(generate_ui_schema), "ui_make_title": create_typed_wrapper(ui_make_title), "from_dict": create_typed_wrapper(default_from_dict), "from_model": create_typed_wrapper(default_from_model), #'make_column_defs': create_typed_wrapper(make_column_defs_helper), } # Add methods only if they don't exist for method_name, default_implementation in default_class_methods.items(): if not hasattr(cls, method_name): setattr(cls, method_name, classmethod(default_implementation)) default_methods = { "custom_model_dump": custom_model_dump, #'make_table_entries': make_table_entries_helper } # Add methods only if they don't exist for method_name, default_implementation in default_methods.items(): if not hasattr(cls, method_name): setattr(cls, method_name, default_implementation) # Add a marker attribute to identify decorated classes setattr(cls, "_is_simstack_model", True) # Return the original class without casting - the overload handles the typing return cls
[docs] def is_simstack_model(cls: Type) -> bool: """ Check if a class has been decorated with @simstack_model. Args: cls: The class to check Returns: bool: True if the class was decorated with @simstack_model, False otherwise """ return getattr(cls, "_is_simstack_model", False)