Skip to content

Conversation

@cofin
Copy link
Member

@cofin cofin commented Oct 8, 2025

Summary

Adds optional schema_type parameter to SQLResult data retrieval methods, enabling direct type-safe data transformation across the entire result API. Also refactors to_schema() functionality into a standalone utility module to eliminate circular imports.

Changes

Core Functionality

New schema_type parameter added to:

  • SQLResult.get_data(schema_type=...) - returns list[SchemaT]
  • SQLResult.all(schema_type=...) - returns list[SchemaT]
  • SQLResult.one(schema_type=...) - returns SchemaT (validated)
  • SQLResult.one_or_none(schema_type=...) - returns SchemaT | None
  • SQLResult.get_first(schema_type=...) - returns SchemaT | None

Refactored schema transformation:

  • Created sqlspec/utils/schema.py with standalone to_schema() function
  • Moved all schema conversion logic from driver mixin to utils
  • Eliminated circular import dependencies between core and driver modules
  • ToSchemaMixin now delegates to standalone function (backward compatible)

API Examples

# Before: multiple steps required
result = session.execute(query)
data = result.get_data()
users = session.to_schema(data, schema_type=User)

# After: direct transformation
result = session.execute(query)
users = result.all(schema_type=User)  # list[User]
user = result.one(schema_type=User)   # User (validated)
user = result.one_or_none(schema_type=User)  # User | None
user = result.get_first(schema_type=User)    # User | None

Documentation Updates (13 files)

  • Replaced result.data with result.all() throughout documentation
  • Replaced result.data[0] with result.one() or result.get_first()
  • Updated all examples to demonstrate proper helper method usage
  • Enhanced API reference with comprehensive examples
  • Showed best practices for type-safe result handling

Architecture Benefits

No circular imports: sqlspec/utils/schema.py can be imported from anywhere
Not mypyc-compiled: utils/ directory excluded from compilation for flexibility
Type-safe: Full TypeVar support for dataclasses, TypedDict, Pydantic, msgspec, attrs
Backward compatible: All existing code continues to work

Test Coverage

  • ✅ Added tests for get_data(schema_type=...) with dataclass and TypedDict
  • ✅ Added tests for all(schema_type=...)
  • ✅ Added tests for one(schema_type=...)
  • ✅ Added tests for one_or_none(schema_type=...)
  • ✅ Added tests for get_first(schema_type=...)
  • ✅ All existing tests pass
  • ✅ Type checks pass (mypy + pyright)

Related

Builds on #105 which fixed TypedDict type hints for schema_type parameter in driver methods.

cofin and others added 5 commits October 8, 2025 16:01
- Define TypedDictT TypeVar in typing.py and export at top level
- Add TypedDict-specific overloads to all driver methods
- Update implementation signatures to accept both ModelDTOT and TypedDictT

Fixes type checking errors when using TypedDict classes with schema_type
parameter. TypedDict is a special typing form that cannot be unified with
regular class types in type[...] contexts, requiring separate overloads.

Resolves Pyright reportCallIssue errors for select/select_one methods.
- Replace ModelDTOT/TypedDictT pattern with single SchemaT TypeVar
- Define SchemaT in typing.py for shared use across async/sync drivers
- Use type[Any] and Any return in implementations to satisfy mypy
- Update overloads to use single type[SchemaT] parameter

This resolves mypy's limitation with type[Union[...]] by using the
modern overload pattern: specific overloads with generic implementation.

Fixes Pyright and mypy type checking errors when using TypedDict classes
with schema_type parameter. Both type checkers now pass cleanly:
- Pyright: 0 errors, 0 warnings
- Mypy: Success on 444 source files
Moves schema transformation logic from driver mixin to a standalone
function in sqlspec/utils/schema.py to eliminate circular import
dependencies and improve code organization.

Changes:
- Created sqlspec/utils/schema.py with standalone to_schema() function
- Updated ToSchemaMixin to delegate to standalone function
- Updated SQLResult.get_data() to use standalone function directly
- Removed all nested imports from both locations
- All helper functions (_DEFAULT_TYPE_DECODERS, _default_msgspec_deserializer,
  _is_list_type_target, _convert_numpy_to_list) moved to new module
- Updated test imports to reference new location

Benefits:
- Eliminates circular import issues between core and driver modules
- Makes to_schema() accessible from any part of the codebase
- Cleaner separation of concerns

All type checks pass (mypy + pyright) and all tests pass.
…st()

Extends schema_type parameter support to all SQLResult methods that
return row data, providing consistent type-safe data transformation
across the entire result API.

Code Changes:
- Added schema_type parameter with overloads to all()
- Added schema_type parameter with overloads to one()
- Added schema_type parameter with overloads to one_or_none()
- Added schema_type parameter with overloads to get_first()
- All methods use to_schema() for transformation when schema_type provided
- Added comprehensive tests for all new parameters

Documentation Changes (13 files updated):
- Replaced result.data with result.all() throughout documentation
- Replaced result.data[0] with result.one() or result.get_first()
- Replaced result.get_data()[0] with result.one(schema_type=...)
- Updated examples to show proper helper method usage
- Enhanced API reference with comprehensive examples
- Demonstrated best practices across all documentation

Benefits:
- Consistent API: schema_type works everywhere you get data
- Type safety: Full TypeVar support with proper overloads
- Better validation: one() raises if not exactly one result
- Clearer intent: Method names express expectations
- Ergonomic: No need to call separate transformation methods

Examples:
  result = session.execute(query)
  users = result.all(schema_type=User)         # list[User]
  user = result.one(schema_type=User)          # User (validated)
  user = result.one_or_none(schema_type=User)  # User | None
  user = result.get_first(schema_type=User)    # User | None (no validation)
  count = result.scalar()                      # First column value

All type checks pass (mypy + pyright) and all tests pass.
@cofin cofin merged commit 523e48b into main Oct 9, 2025
10 checks passed
@cofin cofin deleted the get-data-schema-type branch October 9, 2025 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants