Skip to content

perf: defer optional typing imports#634

Merged
cofin merged 7 commits into
mainfrom
feat/lazy-typing-imports
Jul 10, 2026
Merged

perf: defer optional typing imports#634
cofin merged 7 commits into
mainfrom
feat/lazy-typing-imports

Conversation

@cofin

@cofin cofin commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

  • resolve heavy optional typing exports lazily on first access
  • preserve precise analyzer-visible vendor types when their extras are installed
  • preserve stable runtime shims and Protocol fallbacks when optional dependencies are absent
  • defer optional imports in serializers, storage, schema, and type-guard consumers
  • keep DEFAULT_TYPE_ENCODERS fully dict-compatible with thread-safe, failure-retryable lazy registration

Compatibility

Users do not need optional libraries installed for SQLSpec imports or runtime annotations to work. Missing dependencies resolve to stable cached shims; installed dependencies retain their precise static vendor types. In a no-extras consumer type-checking environment, optional vendor aliases resolve as Any because the vendor type definitions are unavailable; the explicit SQLSpec Stub and Protocol types remain available where callers need a dependency-independent contract.

Public exports, dir(), identity, pickle/repr behavior, cattrs fallback names, dict mutation/union behavior, and runtime type-adapter behavior are covered.

Performance

Bare import sqlspec median time decreased from 681 ms to 202.467 ms (70.27%). The import regression guard confirms pandas, polars, PyArrow, Litestar, Pydantic, OpenTelemetry, and Prometheus are not imported eagerly.

Validation

  • lazy-export, fallback-shim, optional-boundary, serializer, storage, concurrency, retry, and dict-contract tests
  • Ruff, mypy, pyright, docs, compiled acceptance tests, and MyPyC wheel build
  • full GitHub unit/integration matrix on Python 3.10-3.14

All required GitHub checks pass on the final commit.

@cofin cofin marked this pull request as ready for review July 9, 2026 23:04
@cofin cofin marked this pull request as draft July 9, 2026 23:05
@codecov-commenter

codecov-commenter commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.04563% with 63 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.86%. Comparing base (27863f0) to head (b9d781b).

Files with missing lines Patch % Lines
sqlspec/utils/serializers/_json.py 68.42% 29 Missing and 1 partial ⚠️
sqlspec/_typing.py 85.00% 15 Missing ⚠️
sqlspec/utils/type_guards.py 52.00% 8 Missing and 4 partials ⚠️
sqlspec/typing.py 84.21% 3 Missing ⚠️
sqlspec/utils/module_loader.py 80.00% 1 Missing and 1 partial ⚠️
sqlspec/utils/schema.py 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #634      +/-   ##
==========================================
+ Coverage   75.79%   75.86%   +0.06%     
==========================================
  Files         473      473              
  Lines       65121    65236     +115     
  Branches     9713     9721       +8     
==========================================
+ Hits        49361    49492     +131     
+ Misses      12518    12499      -19     
- Partials     3242     3245       +3     
Flag Coverage Δ
integration 59.74% <63.49%> (+0.06%) ⬆️
py3.10 74.11% <75.28%> (+0.07%) ⬆️
py3.11 74.12% <75.28%> (+0.06%) ⬆️
py3.12 74.12% <75.28%> (+0.06%) ⬆️
py3.13 74.12% <75.28%> (+0.06%) ⬆️
py3.14 75.05% <76.04%> (+0.07%) ⬆️
unit 62.56% <76.04%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sqlspec/storage/backends/base.py 81.08% <ø> (-0.17%) ⬇️
sqlspec/storage/backends/obstore.py 83.87% <100.00%> (-0.05%) ⬇️
sqlspec/utils/serializers/_schema.py 86.41% <100.00%> (+0.14%) ⬆️
sqlspec/utils/schema.py 83.92% <80.00%> (-0.10%) ⬇️
sqlspec/utils/module_loader.py 80.41% <80.00%> (-0.18%) ⬇️
sqlspec/typing.py 92.68% <84.21%> (+0.68%) ⬆️
sqlspec/utils/type_guards.py 77.53% <52.00%> (-1.70%) ⬇️
sqlspec/_typing.py 79.16% <85.00%> (+23.84%) ⬆️
sqlspec/utils/serializers/_json.py 74.12% <68.42%> (-2.99%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cofin cofin marked this pull request as ready for review July 10, 2026 05:19
@cofin cofin merged commit ff0b8cb into main Jul 10, 2026
25 checks passed
@cofin cofin deleted the feat/lazy-typing-imports branch July 10, 2026 05:28
cofin added a commit that referenced this pull request Jul 10, 2026
## Summary

- dedicate one long-lived listener resource per native backend instance
while publishers use short pooled sessions
- add typed sync/async `publish_many()` with ordered IDs, explicit
grouped transactions, and an ordered fallback
- bulk-insert durable rows and emit one compact `{marker_id,
batch_size}` wakeup marker per channel atomically
- recover missing markers through independent `event_poll_interval`
reconciliation and immediately drain every recovered row
- cancel async listener waits concurrently, bound sync joins, and
prevent table polling faster than the configured interval
- preserve durable batch delivery order and add
`SQLSpecChannelsBackend.publish_many(data, channels)` for consumer
integration
- document canonical transport semantics, adapter support, connection
ownership, and recovery behavior

## Integration

This PR now targets `main` directly after #633 and #634 merged. Its
event-only commits are rebased onto merge commit `ff0b8cb51`, so the
final branch validates the consolidated C4 contracts and lazy-typing
implementation together.

## Correctness notes

Durable rows are the source of truth. Native markers are wakeup hints,
not batch envelopes. Row insertion and marker publication share one
explicit transaction, marker publication failure rolls back the rows,
duplicate markers are suppressed, and one reconciliation timeout enters
drain mode until the queue is empty. Durable transports remain
competing-consumer queues rather than browser fan-out.

JSON payloads and metadata remain native values through batch parameter
preparation so adapter-specific encoders receive the same input shape as
single-event publication.

## Validation

- listener lifecycle and native backend factory tests
- 1,000-event batch, ordering, transaction rollback, autocommit
restoration, session/statement count, empty/fallback, marker
loss/duplication, polling, and metadata tests
- real asyncpg, psqlpy, and psycopg PostgreSQL integration coverage for
single-plus-batch IDs, delivery order, payloads, and metadata
- SQLite durable order and Litestar channel integrations
- focused event suite, Ruff, mypy, pyright, docs, and docs linkcheck
- full GitHub CI is running for the final post-merge rebased commit
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