Source code for sqlspec.core.parameters._registry

"""Driver parameter profile registry and StatementConfig factory."""

from collections.abc import Callable
from typing import TYPE_CHECKING, Any, Final, Literal, cast

import sqlspec.exceptions
from sqlspec.core.parameters._types import DriverParameterProfile, ParameterStyleConfig
from sqlspec.utils.serializers import from_json, to_json

if TYPE_CHECKING:
    from collections.abc import Mapping

    from sqlspec.core.statement import StatementConfig

__all__ = (
    "DRIVER_PARAMETER_PROFILES",
    "build_statement_config_from_profile",
    "get_driver_profile",
    "register_driver_profile",
)

_DEFAULT_JSON_SERIALIZER: Final[Callable[[Any], str]] = to_json
_DEFAULT_JSON_DESERIALIZER: Final[Callable[[str], Any]] = from_json

DRIVER_PARAMETER_PROFILES: Final[dict[str, DriverParameterProfile]] = {}


[docs] def get_driver_profile(adapter_key: str) -> "DriverParameterProfile": """Return the registered parameter profile for the specified adapter. Args: adapter_key: Adapter identifier (case-insensitive). Returns: Registered :class:`DriverParameterProfile` instance. Raises: ImproperConfigurationError: If the adapter does not have a profile. """ key = adapter_key.lower() try: return DRIVER_PARAMETER_PROFILES[key] except KeyError as exc: msg = f"No driver parameter profile registered for adapter '{adapter_key}'." raise sqlspec.exceptions.ImproperConfigurationError(msg) from exc
[docs] def register_driver_profile( adapter_key: str, profile: "DriverParameterProfile", *, allow_override: bool = False ) -> None: """Register a driver profile under the canonical adapter key. Args: adapter_key: Adapter identifier (case-insensitive). profile: Profile describing parameter behaviour. allow_override: Whether to replace an existing entry. Raises: ImproperConfigurationError: If attempting to register a duplicate profile. """ key = adapter_key.lower() if not allow_override and key in DRIVER_PARAMETER_PROFILES: msg = f"Profile already registered for adapter '{adapter_key}'." raise sqlspec.exceptions.ImproperConfigurationError(msg) DRIVER_PARAMETER_PROFILES[key] = profile
[docs] def build_statement_config_from_profile( profile: "DriverParameterProfile", *, parameter_overrides: "dict[str, Any] | None" = None, statement_overrides: "dict[str, Any] | None" = None, json_serializer: "Callable[[Any], str] | None" = None, json_deserializer: "Callable[[str], Any] | None" = None, ) -> "StatementConfig": """Construct a :class:`StatementConfig` seeded from a driver profile. Args: profile: Driver profile providing default parameter behaviour. parameter_overrides: Optional overrides for parameter config fields. statement_overrides: Optional overrides for resulting statement config. json_serializer: Optional JSON serializer supplied by the adapter. json_deserializer: Optional JSON deserializer supplied by the adapter. Returns: New :class:`StatementConfig` instance with merged configuration. """ parameter_config = _style_config_from_profile(profile, parameter_overrides, json_serializer, json_deserializer) from sqlspec.core.statement import StatementConfig as _StatementConfig statement_kwargs: dict[str, Any] = {} if profile.default_dialect is not None: statement_kwargs["dialect"] = profile.default_dialect if profile.statement_kwargs: statement_kwargs.update(profile.statement_kwargs) if statement_overrides: statement_kwargs.update(statement_overrides) filtered_statement_kwargs = {k: v for k, v in statement_kwargs.items() if v is not None} return _StatementConfig(parameter_config=parameter_config, **filtered_statement_kwargs)
def _style_config_from_profile( profile: "DriverParameterProfile", parameter_overrides: "dict[str, Any] | None", json_serializer: "Callable[[Any], str] | None", json_deserializer: "Callable[[str], Any] | None", ) -> "ParameterStyleConfig": """Build a :class:`ParameterStyleConfig` instance from a driver profile. Args: profile: Source driver profile. parameter_overrides: Optional overrides applied before instantiation. json_serializer: Adapter-provided JSON serializer. json_deserializer: Adapter-provided JSON deserializer. Returns: Configured :class:`ParameterStyleConfig` ready for statement construction. """ overrides = dict(parameter_overrides or {}) supported_styles_override = overrides.pop("supported_parameter_styles", None) execution_styles_override = overrides.pop("supported_execution_parameter_styles", None) type_coercion_override = overrides.pop("type_coercion_map", None) json_serializer_override = overrides.pop("json_serializer", None) json_deserializer_override = overrides.pop("json_deserializer", None) tuple_strategy_override = overrides.pop("json_tuple_strategy", None) supported_styles = ( set(supported_styles_override) if supported_styles_override is not None else set(profile.supported_styles) ) if execution_styles_override is None: execution_supported = ( set(profile.supported_execution_styles) if profile.supported_execution_styles is not None else None ) else: execution_supported = set(execution_styles_override) if execution_styles_override is not None else None type_map = ( dict(type_coercion_override) if type_coercion_override is not None else dict(profile.custom_type_coercions) ) parameter_kwargs: dict[str, Any] = { "default_parameter_style": overrides.pop("default_parameter_style", profile.default_style), "supported_parameter_styles": supported_styles, "supported_execution_parameter_styles": execution_supported, "default_execution_parameter_style": overrides.pop( "default_execution_parameter_style", profile.default_execution_style ), "type_coercion_map": type_map, "has_native_list_expansion": overrides.pop("has_native_list_expansion", profile.has_native_list_expansion), "needs_static_script_compilation": overrides.pop( "needs_static_script_compilation", profile.needs_static_script_compilation ), "allow_mixed_parameter_styles": overrides.pop( "allow_mixed_parameter_styles", profile.allow_mixed_parameter_styles ), "preserve_parameter_format": overrides.pop("preserve_parameter_format", profile.preserve_parameter_format), "preserve_original_params_for_many": overrides.pop( "preserve_original_params_for_many", profile.preserve_original_params_for_many ), "strict_named_parameters": overrides.pop("strict_named_parameters", profile.strict_named_parameters), "output_transformer": overrides.pop("output_transformer", profile.default_output_transformer), "ast_transformer": overrides.pop("ast_transformer", profile.default_ast_transformer), } parameter_kwargs = {k: v for k, v in parameter_kwargs.items() if v is not None} strategy = profile.json_serializer_strategy serializer_value = json_serializer_override or json_serializer deserializer_value = json_deserializer_override or json_deserializer if serializer_value is None: serializer_value = profile.extras.get("default_json_serializer", _DEFAULT_JSON_SERIALIZER) if deserializer_value is None: deserializer_value = profile.extras.get("default_json_deserializer", _DEFAULT_JSON_DESERIALIZER) serializer = cast("Callable[[Any], str]", serializer_value) deserializer = cast("Callable[[str], Any] | None", deserializer_value) if strategy == "driver": parameter_kwargs["json_serializer"] = serializer parameter_kwargs["json_deserializer"] = deserializer parameter_kwargs.update(overrides) parameter_config = ParameterStyleConfig(**parameter_kwargs) if strategy == "helper": tuple_strategy = tuple_strategy_override or profile.extras.get("json_tuple_strategy", "list") tuple_strategy_literal = cast("Literal['list', 'tuple']", tuple_strategy) parameter_config = parameter_config.with_json_serializers( serializer, tuple_strategy=tuple_strategy_literal, deserializer=deserializer ) elif strategy == "driver": parameter_config = parameter_config.replace(json_serializer=serializer, json_deserializer=deserializer) type_overrides = profile.extras.get("type_coercion_overrides") if type_overrides: coercion_overrides = cast("Mapping[type, Callable[[Any], Any]]", type_overrides) updated_map: dict[type, Callable[[Any], Any]] = {} updated_map.update(parameter_config.type_coercion_map) updated_map.update(coercion_overrides) parameter_config = parameter_config.replace(type_coercion_map=updated_map) return parameter_config