Source code for sqlspec.utils.type_guards

"""Type guard functions for runtime type checking in SQLSpec.

This module provides type-safe runtime checks that help the type checker
understand type narrowing, replacing defensive hasattr() and duck typing patterns.
"""

import inspect
import sys
from collections.abc import Sequence
from collections.abc import Set as AbstractSet
from dataclasses import Field
from dataclasses import fields as dataclasses_fields
from dataclasses import is_dataclass as dataclasses_is_dataclass
from typing import TYPE_CHECKING, Any, Literal, cast

from sqlglot import exp
from typing_extensions import is_typeddict

from sqlspec._typing import Empty
from sqlspec.protocols import (
    DictProtocol,
    HasExpressionAndParametersProtocol,
    HasExpressionAndSQLProtocol,
    HasExpressionProtocol,
    HasMigrationConfigProtocol,
    HasParameterBuilderProtocol,
    HasSQLGlotExpressionProtocol,
    HasStatementConfigFactoryProtocol,
    HasValueProtocol,
    SupportsArrowResults,
    WithMethodProtocol,
)
from sqlspec.typing import (
    ATTRS_INSTALLED,
    LITESTAR_INSTALLED,
    MSGSPEC_INSTALLED,
    PYDANTIC_INSTALLED,
    DataclassProtocol,
    Struct,
)
from sqlspec.utils.text import camelize, kebabize, pascalize

if TYPE_CHECKING:
    from typing import TypeGuard

    from sqlspec._typing import AttrsInstanceStub, BaseModelStub, DTODataStub, StructStub
    from sqlspec.core import StatementFilter
    from sqlspec.core.parameters import TypedParameter
    from sqlspec.protocols import (
        ArrowTableStatsProtocol,
        AsyncDeleteProtocol,
        AsyncReadableProtocol,
        AsyncReadBytesProtocol,
        AsyncWriteBytesProtocol,
        CursorMetadataProtocol,
        HasAddListenerProtocol,
        HasAsDictProtocol,
        HasConfigProtocol,
        HasConnectionConfigProtocol,
        HasDatabaseUrlAndBindKeyProtocol,
        HasErrorsProtocol,
        HasExtensionConfigProtocol,
        HasFieldNameProtocol,
        HasFilterAttributesProtocol,
        HasGetDataProtocol,
        HasLastRowIdProtocol,
        HasNameProtocol,
        HasNotifiesProtocol,
        HasRowcountProtocol,
        HasSqliteErrorProtocol,
        HasSqlStateProtocol,
        HasStatementTypeProtocol,
        HasTracerProviderProtocol,
        HasTypeCodeProtocol,
        HasTypecodeProtocol,
        HasTypecodeSizedProtocol,
        HasWhereProtocol,
        MappingLikeProtocol,
        NotificationProtocol,
        PipelineCapableProtocol,
        QueryResultProtocol,
        ReadableProtocol,
        SpanAttributeProtocol,
        SupportsArrayProtocol,
        SupportsCloseProtocol,
        SupportsDtypeStrProtocol,
        SupportsJsonTypeProtocol,
    )
    from sqlspec.typing import SupportedSchemaModel

__all__ = (
    "dataclass_to_dict",
    "expression_has_limit",
    "extract_dataclass_fields",
    "extract_dataclass_items",
    "get_initial_expression",
    "get_literal_parent",
    "get_msgspec_rename_config",
    "get_node_expressions",
    "get_node_this",
    "get_param_style_and_name",
    "get_value_attribute",
    "has_add_listener",
    "has_array_interface",
    "has_arrow_table_stats",
    "has_asdict_method",
    "has_config_attribute",
    "has_connection_config",
    "has_cursor_metadata",
    "has_database_url_and_bind_key",
    "has_dict_attribute",
    "has_dtype_str",
    "has_errors",
    "has_expression_and_parameters",
    "has_expression_and_sql",
    "has_expression_attr",
    "has_expressions_attribute",
    "has_extension_config",
    "has_field_name",
    "has_filter_attributes",
    "has_get_data",
    "has_lastrowid",
    "has_migration_config",
    "has_name",
    "has_notifies",
    "has_parameter_builder",
    "has_parent_attribute",
    "has_pipeline_capability",
    "has_query_result_metadata",
    "has_rowcount",
    "has_span_attribute",
    "has_sqlglot_expression",
    "has_sqlite_error",
    "has_sqlstate",
    "has_statement_config_factory",
    "has_statement_type",
    "has_this_attribute",
    "has_tracer_provider",
    "has_type_code",
    "has_typecode",
    "has_typecode_and_len",
    "has_value_attribute",
    "has_with_method",
    "is_async_readable",
    "is_attrs_instance",
    "is_attrs_instance_with_field",
    "is_attrs_instance_without_field",
    "is_attrs_schema",
    "is_copy_statement",
    "is_dataclass",
    "is_dataclass_instance",
    "is_dataclass_with_field",
    "is_dataclass_without_field",
    "is_dict",
    "is_dict_row",
    "is_dict_with_field",
    "is_dict_without_field",
    "is_dto_data",
    "is_expression",
    "is_iterable_parameters",
    "is_local_path",
    "is_mapping_like",
    "is_msgspec_struct",
    "is_msgspec_struct_with_field",
    "is_msgspec_struct_without_field",
    "is_notification",
    "is_number_literal",
    "is_pydantic_model",
    "is_pydantic_model_with_field",
    "is_pydantic_model_without_field",
    "is_readable",
    "is_schema",
    "is_schema_or_dict",
    "is_schema_or_dict_with_field",
    "is_schema_or_dict_without_field",
    "is_schema_with_field",
    "is_schema_without_field",
    "is_statement_filter",
    "is_string_literal",
    "is_typed_dict",
    "is_typed_parameter",
    "resolve_row_format",
    "supports_arrow_results",
    "supports_async_delete",
    "supports_async_read_bytes",
    "supports_async_write_bytes",
    "supports_close",
    "supports_json_type",
    "supports_where",
)


def is_readable(obj: Any) -> "TypeGuard[ReadableProtocol]":
    """Check if an object is readable (has a read method)."""
    try:
        return callable(obj.read)
    except AttributeError:
        return False
def is_async_readable(obj: Any) -> "TypeGuard[AsyncReadableProtocol]": """Check if an object exposes an async read method.""" try: return callable(obj.read) and inspect.iscoroutinefunction(obj.read) except AttributeError: return False