sqlspec 0.21.0__tar.gz → 0.21.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (323) hide show
  1. {sqlspec-0.21.0 → sqlspec-0.21.1}/PKG-INFO +1 -1
  2. {sqlspec-0.21.0 → sqlspec-0.21.1}/pyproject.toml +6 -5
  3. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/driver/mixins/_result_tools.py +41 -6
  4. sqlspec-0.21.1/sqlspec/utils/data_transformation.py +120 -0
  5. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/text.py +27 -19
  6. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/type_guards.py +74 -0
  7. sqlspec-0.21.1/tests/unit/test_utils/test_data_transformation.py +343 -0
  8. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_text.py +1 -36
  9. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_type_guards.py +105 -0
  10. {sqlspec-0.21.0 → sqlspec-0.21.1}/uv.lock +102 -102
  11. {sqlspec-0.21.0 → sqlspec-0.21.1}/.gitignore +0 -0
  12. {sqlspec-0.21.0 → sqlspec-0.21.1}/.pre-commit-config.yaml +0 -0
  13. {sqlspec-0.21.0 → sqlspec-0.21.1}/CONTRIBUTING.rst +0 -0
  14. {sqlspec-0.21.0 → sqlspec-0.21.1}/LICENSE +0 -0
  15. {sqlspec-0.21.0 → sqlspec-0.21.1}/Makefile +0 -0
  16. {sqlspec-0.21.0 → sqlspec-0.21.1}/NOTICE +0 -0
  17. {sqlspec-0.21.0 → sqlspec-0.21.1}/README.md +0 -0
  18. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/__init__.py +0 -0
  19. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/__main__.py +0 -0
  20. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/__metadata__.py +0 -0
  21. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/_serialization.py +0 -0
  22. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/_sql.py +0 -0
  23. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/_typing.py +0 -0
  24. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/__init__.py +0 -0
  25. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/adbc/__init__.py +0 -0
  26. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/adbc/_types.py +0 -0
  27. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/adbc/config.py +0 -0
  28. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/adbc/driver.py +0 -0
  29. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/aiosqlite/__init__.py +0 -0
  30. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/aiosqlite/_types.py +0 -0
  31. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/aiosqlite/config.py +0 -0
  32. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/aiosqlite/driver.py +0 -0
  33. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/aiosqlite/pool.py +0 -0
  34. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncmy/__init__.py +0 -0
  35. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncmy/_types.py +0 -0
  36. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncmy/config.py +0 -0
  37. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncmy/driver.py +0 -0
  38. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncpg/__init__.py +0 -0
  39. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncpg/_types.py +0 -0
  40. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncpg/config.py +0 -0
  41. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/asyncpg/driver.py +0 -0
  42. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/bigquery/__init__.py +0 -0
  43. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/bigquery/_types.py +0 -0
  44. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/bigquery/config.py +0 -0
  45. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/bigquery/driver.py +0 -0
  46. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/duckdb/__init__.py +0 -0
  47. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/duckdb/_types.py +0 -0
  48. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/duckdb/config.py +0 -0
  49. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/duckdb/driver.py +0 -0
  50. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/duckdb/pool.py +0 -0
  51. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/oracledb/__init__.py +0 -0
  52. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/oracledb/_types.py +0 -0
  53. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/oracledb/config.py +0 -0
  54. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/oracledb/driver.py +0 -0
  55. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/oracledb/migrations.py +0 -0
  56. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psqlpy/__init__.py +0 -0
  57. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psqlpy/_types.py +0 -0
  58. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psqlpy/config.py +0 -0
  59. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psqlpy/driver.py +0 -0
  60. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psycopg/__init__.py +0 -0
  61. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psycopg/_types.py +0 -0
  62. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psycopg/config.py +0 -0
  63. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/psycopg/driver.py +0 -0
  64. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/sqlite/__init__.py +0 -0
  65. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/sqlite/_types.py +0 -0
  66. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/sqlite/config.py +0 -0
  67. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/sqlite/driver.py +0 -0
  68. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/adapters/sqlite/pool.py +0 -0
  69. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/base.py +0 -0
  70. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/__init__.py +0 -0
  71. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_base.py +0 -0
  72. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_column.py +0 -0
  73. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_ddl.py +0 -0
  74. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_delete.py +0 -0
  75. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_expression_wrappers.py +0 -0
  76. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_insert.py +0 -0
  77. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_merge.py +0 -0
  78. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_parsing_utils.py +0 -0
  79. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_select.py +0 -0
  80. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/_update.py +0 -0
  81. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/__init__.py +0 -0
  82. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_cte_and_set_ops.py +0 -0
  83. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_delete_operations.py +0 -0
  84. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_insert_operations.py +0 -0
  85. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_join_operations.py +0 -0
  86. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_merge_operations.py +0 -0
  87. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_order_limit_operations.py +0 -0
  88. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_pivot_operations.py +0 -0
  89. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_select_operations.py +0 -0
  90. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_update_operations.py +0 -0
  91. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/builder/mixins/_where_clause.py +0 -0
  92. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/cli.py +0 -0
  93. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/config.py +0 -0
  94. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/__init__.py +0 -0
  95. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/cache.py +0 -0
  96. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/compiler.py +0 -0
  97. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/filters.py +0 -0
  98. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/hashing.py +0 -0
  99. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/parameters.py +0 -0
  100. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/result.py +0 -0
  101. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/splitter.py +0 -0
  102. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/core/statement.py +0 -0
  103. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/driver/__init__.py +0 -0
  104. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/driver/_async.py +0 -0
  105. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/driver/_common.py +0 -0
  106. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/driver/_sync.py +0 -0
  107. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/driver/mixins/__init__.py +0 -0
  108. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/driver/mixins/_sql_translator.py +0 -0
  109. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/exceptions.py +0 -0
  110. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/__init__.py +0 -0
  111. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/aiosql/__init__.py +0 -0
  112. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/aiosql/adapter.py +0 -0
  113. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/litestar/__init__.py +0 -0
  114. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/litestar/_utils.py +0 -0
  115. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/litestar/cli.py +0 -0
  116. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/litestar/config.py +0 -0
  117. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/litestar/handlers.py +0 -0
  118. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/litestar/plugin.py +0 -0
  119. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/extensions/litestar/providers.py +0 -0
  120. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/loader.py +0 -0
  121. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/migrations/__init__.py +0 -0
  122. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/migrations/base.py +0 -0
  123. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/migrations/commands.py +0 -0
  124. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/migrations/loaders.py +0 -0
  125. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/migrations/runner.py +0 -0
  126. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/migrations/tracker.py +0 -0
  127. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/migrations/utils.py +0 -0
  128. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/protocols.py +0 -0
  129. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/py.typed +0 -0
  130. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/storage/__init__.py +0 -0
  131. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/storage/backends/__init__.py +0 -0
  132. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/storage/backends/base.py +0 -0
  133. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/storage/backends/fsspec.py +0 -0
  134. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/storage/backends/obstore.py +0 -0
  135. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/storage/capabilities.py +0 -0
  136. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/storage/registry.py +0 -0
  137. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/typing.py +0 -0
  138. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/__init__.py +0 -0
  139. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/correlation.py +0 -0
  140. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/deprecation.py +0 -0
  141. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/fixtures.py +0 -0
  142. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/logging.py +0 -0
  143. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/module_loader.py +0 -0
  144. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/serializers.py +0 -0
  145. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/singleton.py +0 -0
  146. {sqlspec-0.21.0 → sqlspec-0.21.1}/sqlspec/utils/sync_tools.py +0 -0
  147. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/__init__.py +0 -0
  148. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/conftest.py +0 -0
  149. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/__init__.py +0 -0
  150. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/asset_maintenance.sql +0 -0
  151. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/ddls-mysql-collection.sql +0 -0
  152. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/ddls-postgres-collection.sql +0 -0
  153. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/example_usage.py +0 -0
  154. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/init.sql +0 -0
  155. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-config.sql +0 -0
  156. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-data_types.sql +0 -0
  157. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-database_details.sql +0 -0
  158. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-engines.sql +0 -0
  159. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-hostname.sql +0 -0
  160. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-plugins.sql +0 -0
  161. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-process_list.sql +0 -0
  162. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-resource-groups.sql +0 -0
  163. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-schema_objects.sql +0 -0
  164. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-table_details.sql +0 -0
  165. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/collection-users.sql +0 -0
  166. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/mysql/init.sql +0 -0
  167. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/oracle.ddl.sql +0 -0
  168. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-applications.sql +0 -0
  169. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-aws_extension_dependency.sql +0 -0
  170. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-aws_oracle_exists.sql +0 -0
  171. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-bg_writer_stats.sql +0 -0
  172. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-calculated_metrics.sql +0 -0
  173. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-data_types.sql +0 -0
  174. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-database_details.sql +0 -0
  175. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-extensions.sql +0 -0
  176. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-index_details.sql +0 -0
  177. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-pglogical-details.sql +0 -0
  178. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-privileges.sql +0 -0
  179. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-replication_slots.sql +0 -0
  180. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-replication_stats.sql +0 -0
  181. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-schema_details.sql +0 -0
  182. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-schema_objects.sql +0 -0
  183. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-settings.sql +0 -0
  184. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-source_details.sql +0 -0
  185. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/collection-table_details.sql +0 -0
  186. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/extended-collection-all-databases.sql +0 -0
  187. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/postgres/init.sql +0 -0
  188. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/readiness-check.sql +0 -0
  189. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/fixtures/sql_utils.py +0 -0
  190. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/__init__.py +0 -0
  191. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/conftest.py +0 -0
  192. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/__init__.py +0 -0
  193. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/__init__.py +0 -0
  194. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/conftest.py +0 -0
  195. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_adbc_arrow_features.py +0 -0
  196. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_adbc_backends.py +0 -0
  197. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_adbc_connection.py +0 -0
  198. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_adbc_driver.py +0 -0
  199. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_adbc_edge_cases.py +0 -0
  200. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_adbc_results.py +0 -0
  201. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_migrations.py +0 -0
  202. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_adbc/test_parameter_styles.py +0 -0
  203. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_aiosqlite/__init__.py +0 -0
  204. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_aiosqlite/conftest.py +0 -0
  205. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_aiosqlite/test_connection.py +0 -0
  206. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_aiosqlite/test_driver.py +0 -0
  207. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_aiosqlite/test_migrations.py +0 -0
  208. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_aiosqlite/test_parameter_styles.py +0 -0
  209. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_aiosqlite/test_pooling.py +0 -0
  210. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncmy/__init__.py +0 -0
  211. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncmy/conftest.py +0 -0
  212. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncmy/test_asyncmy_features.py +0 -0
  213. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncmy/test_config.py +0 -0
  214. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncmy/test_driver.py +0 -0
  215. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncmy/test_migrations.py +0 -0
  216. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncmy/test_parameter_styles.py +0 -0
  217. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncpg/__init__.py +0 -0
  218. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncpg/conftest.py +0 -0
  219. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncpg/test_connection.py +0 -0
  220. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncpg/test_driver.py +0 -0
  221. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncpg/test_execute_many.py +0 -0
  222. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncpg/test_migrations.py +0 -0
  223. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_asyncpg/test_parameter_styles.py +0 -0
  224. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_bigquery/__init__.py +0 -0
  225. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_bigquery/conftest.py +0 -0
  226. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_bigquery/test_bigquery_features.py +0 -0
  227. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_bigquery/test_config.py +0 -0
  228. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_bigquery/test_connection.py +0 -0
  229. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_bigquery/test_driver.py +0 -0
  230. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/__init__.py +0 -0
  231. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/test_connection.py +0 -0
  232. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/test_driver.py +0 -0
  233. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/test_execute_many.py +0 -0
  234. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/test_migrations.py +0 -0
  235. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/test_mixed_parameter_styles.py +0 -0
  236. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/test_parameter_styles.py +0 -0
  237. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/test_pooling.py +0 -0
  238. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_duckdb/utils.py +0 -0
  239. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/__init__.py +0 -0
  240. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/conftest.py +0 -0
  241. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/test_connection.py +0 -0
  242. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/test_driver_async.py +0 -0
  243. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/test_driver_sync.py +0 -0
  244. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/test_execute_many.py +0 -0
  245. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/test_migrations.py +0 -0
  246. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/test_oracle_features.py +0 -0
  247. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_oracledb/test_parameter_styles.py +0 -0
  248. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psqlpy/__init__.py +0 -0
  249. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psqlpy/conftest.py +0 -0
  250. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psqlpy/test_connection.py +0 -0
  251. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psqlpy/test_driver.py +0 -0
  252. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psqlpy/test_migrations.py +0 -0
  253. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psqlpy/test_parameter_styles.py +0 -0
  254. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psqlpy/test_psqlpy_features.py +0 -0
  255. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/__init__.py +0 -0
  256. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/conftest.py +0 -0
  257. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/test_async_copy.py +0 -0
  258. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/test_connection.py +0 -0
  259. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/test_driver.py +0 -0
  260. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/test_execute_many.py +0 -0
  261. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/test_migrations.py +0 -0
  262. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_psycopg/test_parameter_styles.py +0 -0
  263. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_sqlite/__init__.py +0 -0
  264. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_sqlite/conftest.py +0 -0
  265. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_sqlite/test_driver.py +0 -0
  266. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_sqlite/test_migrations.py +0 -0
  267. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_sqlite/test_parameter_styles.py +0 -0
  268. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_sqlite/test_pooling.py +0 -0
  269. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_adapters/test_sqlite/test_query_mixin.py +0 -0
  270. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_loader/__init__.py +0 -0
  271. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_loader/test_file_system_loading.py +0 -0
  272. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/integration/test_migrations/__init__.py +0 -0
  273. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/conftest.py +0 -0
  274. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_adapters/__init__.py +0 -0
  275. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_adapters/conftest.py +0 -0
  276. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_adapters/test_adapter_implementations.py +0 -0
  277. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_adapters/test_async_adapters.py +0 -0
  278. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_adapters/test_sync_adapters.py +0 -0
  279. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_base/__init__.py +0 -0
  280. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_base/test_sql_integration.py +0 -0
  281. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_base/test_sqlspec_class.py +0 -0
  282. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_builder/__init__.py +0 -0
  283. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_builder/test_insert_builder.py +0 -0
  284. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_builder/test_parameter_naming.py +0 -0
  285. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_builder_parameter_naming.py +0 -0
  286. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_core/test_cache.py +0 -0
  287. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_core/test_compiler.py +0 -0
  288. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_core/test_filters.py +0 -0
  289. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_core/test_hashing.py +0 -0
  290. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_core/test_parameters.py +0 -0
  291. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_core/test_result.py +0 -0
  292. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_core/test_statement.py +0 -0
  293. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_extensions/__init__.py +0 -0
  294. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_extensions/test_litestar/__init__.py +0 -0
  295. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_extensions/test_litestar/test_config.py +0 -0
  296. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_loader/__init__.py +0 -0
  297. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_loader/test_cache_integration.py +0 -0
  298. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_loader/test_fixtures_directory_loading.py +0 -0
  299. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_loader/test_loading_patterns.py +0 -0
  300. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_loader/test_sql_file_loader.py +0 -0
  301. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_migrations/__init__.py +0 -0
  302. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_migrations/test_migration.py +0 -0
  303. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_migrations/test_migration_commands.py +0 -0
  304. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_migrations/test_migration_execution.py +0 -0
  305. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_migrations/test_migration_runner.py +0 -0
  306. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_parsing_utils.py +0 -0
  307. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_sql_factory.py +0 -0
  308. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/__init__.py +0 -0
  309. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_correlation.py +0 -0
  310. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_deprecation.py +0 -0
  311. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_fixtures.py +0 -0
  312. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_logging.py +0 -0
  313. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_module_loader.py +0 -0
  314. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_serializers.py +0 -0
  315. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_singleton.py +0 -0
  316. {sqlspec-0.21.0 → sqlspec-0.21.1}/tests/unit/test_utils/test_sync_tools.py +0 -0
  317. {sqlspec-0.21.0 → sqlspec-0.21.1}/tools/__init__.py +0 -0
  318. {sqlspec-0.21.0 → sqlspec-0.21.1}/tools/build_docs.py +0 -0
  319. {sqlspec-0.21.0 → sqlspec-0.21.1}/tools/local-infra.sh +0 -0
  320. {sqlspec-0.21.0 → sqlspec-0.21.1}/tools/pypi_readme.py +0 -0
  321. {sqlspec-0.21.0 → sqlspec-0.21.1}/tools/sphinx_ext/__init__.py +0 -0
  322. {sqlspec-0.21.0 → sqlspec-0.21.1}/tools/sphinx_ext/changelog.py +0 -0
  323. {sqlspec-0.21.0 → sqlspec-0.21.1}/tools/sphinx_ext/missing_references.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlspec
3
- Version: 0.21.0
3
+ Version: 0.21.1
4
4
  Summary: SQL Experiments in Python
5
5
  Project-URL: Discord, https://discord.gg/litestar
6
6
  Project-URL: Issue, https://github.com/litestar-org/sqlspec/issues/
@@ -13,7 +13,7 @@ maintainers = [{ name = "Litestar Developers", email = "hello@litestar.dev" }]
13
13
  name = "sqlspec"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.9, <4.0"
16
- version = "0.21.0"
16
+ version = "0.21.1"
17
17
 
18
18
  [project.urls]
19
19
  Discord = "https://discord.gg/litestar"
@@ -161,10 +161,11 @@ include = [
161
161
  "sqlspec/loader.py", # Loader module
162
162
 
163
163
  # === UTILITY MODULES ===
164
- "sqlspec/utils/text.py", # Text utilities
165
- "sqlspec/utils/sync_tools.py", # Synchronous utility functions
166
- "sqlspec/utils/type_guards.py", # Type guard utilities
167
- "sqlspec/utils/fixtures.py", # File fixture loading
164
+ "sqlspec/utils/text.py", # Text utilities
165
+ "sqlspec/utils/sync_tools.py", # Synchronous utility functions
166
+ "sqlspec/utils/type_guards.py", # Type guard utilities
167
+ "sqlspec/utils/fixtures.py", # File fixture loading
168
+ "sqlspec/utils/data_transformation.py", # Data transformation utilities
168
169
  ]
169
170
  mypy-args = [
170
171
  "--ignore-missing-imports",
@@ -1,3 +1,4 @@
1
+ # ruff: noqa: C901
1
2
  """Result handling and schema conversion mixins for database drivers."""
2
3
 
3
4
  import datetime
@@ -22,7 +23,16 @@ from sqlspec.typing import (
22
23
  convert,
23
24
  get_type_adapter,
24
25
  )
25
- from sqlspec.utils.type_guards import is_attrs_schema, is_dataclass, is_msgspec_struct, is_pydantic_model
26
+ from sqlspec.utils.data_transformation import transform_dict_keys
27
+ from sqlspec.utils.text import camelize, kebabize, pascalize
28
+ from sqlspec.utils.type_guards import (
29
+ get_msgspec_rename_config,
30
+ is_attrs_schema,
31
+ is_dataclass,
32
+ is_dict,
33
+ is_msgspec_struct,
34
+ is_pydantic_model,
35
+ )
26
36
 
27
37
  __all__ = ("_DEFAULT_TYPE_DECODERS", "_default_msgspec_deserializer")
28
38
 
@@ -143,21 +153,46 @@ class ToSchemaMixin:
143
153
  if isinstance(data, list):
144
154
  result: list[Any] = []
145
155
  for item in data:
146
- if hasattr(item, "keys"):
156
+ if is_dict(item):
147
157
  result.append(schema_type(**dict(item))) # type: ignore[operator]
148
158
  else:
149
159
  result.append(item)
150
160
  return result
151
- if hasattr(data, "keys"):
161
+ if is_dict(data):
152
162
  return schema_type(**dict(data)) # type: ignore[operator]
153
163
  if isinstance(data, dict):
154
164
  return schema_type(**data) # type: ignore[operator]
155
165
  return data
156
166
  if is_msgspec_struct(schema_type):
167
+ rename_config = get_msgspec_rename_config(schema_type) # type: ignore[arg-type]
157
168
  deserializer = partial(_default_msgspec_deserializer, type_decoders=_DEFAULT_TYPE_DECODERS)
158
- if not isinstance(data, Sequence):
159
- return convert(obj=data, type=schema_type, from_attributes=True, dec_hook=deserializer)
160
- return convert(obj=data, type=list[schema_type], from_attributes=True, dec_hook=deserializer) # type: ignore[valid-type]
169
+
170
+ # Transform field names if rename configuration exists
171
+ transformed_data = data
172
+ if (rename_config and is_dict(data)) or (isinstance(data, Sequence) and data and is_dict(data[0])):
173
+ try:
174
+ converter = None
175
+ if rename_config == "camel":
176
+ converter = camelize
177
+ elif rename_config == "kebab":
178
+ converter = kebabize
179
+ elif rename_config == "pascal":
180
+ converter = pascalize
181
+
182
+ if converter is not None:
183
+ if isinstance(data, Sequence):
184
+ transformed_data = [
185
+ transform_dict_keys(item, converter) if is_dict(item) else item for item in data
186
+ ]
187
+ else:
188
+ transformed_data = transform_dict_keys(data, converter) if is_dict(data) else data
189
+ except Exception as e:
190
+ logger.debug("Field name transformation failed for msgspec schema: %s", e)
191
+ transformed_data = data
192
+
193
+ if not isinstance(transformed_data, Sequence):
194
+ return convert(obj=transformed_data, type=schema_type, from_attributes=True, dec_hook=deserializer)
195
+ return convert(obj=transformed_data, type=list[schema_type], from_attributes=True, dec_hook=deserializer) # type: ignore[valid-type]
161
196
  if is_pydantic_model(schema_type):
162
197
  if not isinstance(data, Sequence):
163
198
  adapter = get_type_adapter(schema_type)
@@ -0,0 +1,120 @@
1
+ """Data transformation utilities for SQLSpec.
2
+
3
+ Provides functions for transforming data structures, particularly for
4
+ field name conversion when mapping database results to schema objects.
5
+ Used primarily for msgspec field name conversion with rename configurations.
6
+ """
7
+
8
+ from typing import Any, Callable, Union
9
+
10
+ __all__ = ("transform_dict_keys",)
11
+
12
+
13
+ def _safe_convert_key(key: Any, converter: Callable[[str], str]) -> Any:
14
+ """Safely convert a key using the converter function.
15
+
16
+ Args:
17
+ key: Key to convert (may not be a string).
18
+ converter: Function to convert string keys.
19
+
20
+ Returns:
21
+ Converted key if conversion succeeds, original key otherwise.
22
+ """
23
+ if not isinstance(key, str):
24
+ return key
25
+
26
+ try:
27
+ return converter(key)
28
+ except (TypeError, ValueError, AttributeError):
29
+ # If conversion fails, return the original key
30
+ return key
31
+
32
+
33
+ def transform_dict_keys(data: Union[dict, list, Any], converter: Callable[[str], str]) -> Union[dict, list, Any]:
34
+ """Transform dictionary keys using the provided converter function.
35
+
36
+ Recursively transforms all dictionary keys in a data structure using
37
+ the provided converter function. Handles nested dictionaries, lists
38
+ of dictionaries, and preserves non-dict values unchanged.
39
+
40
+ Args:
41
+ data: The data structure to transform. Can be a dict, list, or any other type.
42
+ converter: Function to convert string keys (e.g., camelize, kebabize).
43
+
44
+ Returns:
45
+ The transformed data structure with converted keys. Non-dict values
46
+ are returned unchanged.
47
+
48
+ Examples:
49
+ Transform snake_case keys to camelCase:
50
+
51
+ >>> from sqlspec.utils.text import camelize
52
+ >>> data = {"user_id": 123, "created_at": "2024-01-01"}
53
+ >>> transform_dict_keys(data, camelize)
54
+ {"userId": 123, "createdAt": "2024-01-01"}
55
+
56
+ Transform nested structures:
57
+
58
+ >>> nested = {
59
+ ... "user_data": {"first_name": "John", "last_name": "Doe"},
60
+ ... "order_items": [
61
+ ... {"item_id": 1, "item_name": "Product A"},
62
+ ... {"item_id": 2, "item_name": "Product B"},
63
+ ... ],
64
+ ... }
65
+ >>> transform_dict_keys(nested, camelize)
66
+ {
67
+ "userData": {
68
+ "firstName": "John",
69
+ "lastName": "Doe"
70
+ },
71
+ "orderItems": [
72
+ {"itemId": 1, "itemName": "Product A"},
73
+ {"itemId": 2, "itemName": "Product B"}
74
+ ]
75
+ }
76
+ """
77
+ if isinstance(data, dict):
78
+ return _transform_dict(data, converter)
79
+ if isinstance(data, list):
80
+ return _transform_list(data, converter)
81
+ return data
82
+
83
+
84
+ def _transform_dict(data: dict, converter: Callable[[str], str]) -> dict:
85
+ """Transform a dictionary's keys recursively.
86
+
87
+ Args:
88
+ data: Dictionary to transform.
89
+ converter: Function to convert string keys.
90
+
91
+ Returns:
92
+ Dictionary with transformed keys and recursively transformed values.
93
+ """
94
+ transformed = {}
95
+
96
+ for key, value in data.items():
97
+ # Convert the key using the provided converter function
98
+ # Use safe conversion that handles edge cases without try-except
99
+ converted_key = _safe_convert_key(key, converter)
100
+
101
+ # Recursively transform the value
102
+ transformed_value = transform_dict_keys(value, converter)
103
+
104
+ transformed[converted_key] = transformed_value
105
+
106
+ return transformed
107
+
108
+
109
+ def _transform_list(data: list, converter: Callable[[str], str]) -> list:
110
+ """Transform a list's elements recursively.
111
+
112
+ Args:
113
+ data: List to transform.
114
+ converter: Function to convert string keys in nested structures.
115
+
116
+ Returns:
117
+ List with recursively transformed elements.
118
+ """
119
+ # Use list comprehension for better performance and avoid try-except in loop
120
+ return [transform_dict_keys(item, converter) for item in data]
@@ -19,25 +19,7 @@ _SNAKE_CASE_HYPHEN_SPACE = re.compile(r"[.\s@-]+", re.UNICODE)
19
19
  _SNAKE_CASE_REMOVE_NON_WORD = re.compile(r"[^\w]+", re.UNICODE)
20
20
  _SNAKE_CASE_MULTIPLE_UNDERSCORES = re.compile(r"__+", re.UNICODE)
21
21
 
22
- __all__ = ("camelize", "check_email", "slugify", "snake_case")
23
-
24
-
25
- def check_email(email: str) -> str:
26
- """Validate an email address.
27
-
28
- Args:
29
- email: The email to validate.
30
-
31
- Raises:
32
- ValueError: If the email is invalid.
33
-
34
- Returns:
35
- The validated email.
36
- """
37
- if "@" not in email:
38
- msg = "Invalid email!"
39
- raise ValueError(msg)
40
- return email.lower()
22
+ __all__ = ("camelize", "kebabize", "pascalize", "slugify", "snake_case")
41
23
 
42
24
 
43
25
  def slugify(value: str, allow_unicode: bool = False, separator: Optional[str] = None) -> str:
@@ -80,6 +62,32 @@ def camelize(string: str) -> str:
80
62
  return "".join(word if index == 0 else word.capitalize() for index, word in enumerate(string.split("_")))
81
63
 
82
64
 
65
+ @lru_cache(maxsize=100)
66
+ def kebabize(string: str) -> str:
67
+ """Convert a string to kebab-case.
68
+
69
+ Args:
70
+ string: The string to convert.
71
+
72
+ Returns:
73
+ The kebab-case version of the string.
74
+ """
75
+ return "-".join(word.lower() for word in string.split("_") if word)
76
+
77
+
78
+ @lru_cache(maxsize=100)
79
+ def pascalize(string: str) -> str:
80
+ """Convert a string to PascalCase.
81
+
82
+ Args:
83
+ string: The string to convert.
84
+
85
+ Returns:
86
+ The PascalCase version of the string.
87
+ """
88
+ return "".join(word.capitalize() for word in string.split("_") if word)
89
+
90
+
83
91
  @lru_cache(maxsize=100)
84
92
  def snake_case(string: str) -> str:
85
93
  """Convert a string to snake_case.
@@ -6,6 +6,7 @@ understand type narrowing, replacing defensive hasattr() and duck typing pattern
6
6
 
7
7
  from collections.abc import Sequence
8
8
  from collections.abc import Set as AbstractSet
9
+ from functools import lru_cache
9
10
  from typing import TYPE_CHECKING, Any, Optional, Union, cast
10
11
 
11
12
  from sqlspec.typing import (
@@ -59,6 +60,7 @@ __all__ = (
59
60
  "extract_dataclass_items",
60
61
  "get_initial_expression",
61
62
  "get_literal_parent",
63
+ "get_msgspec_rename_config",
62
64
  "get_node_expressions",
63
65
  "get_node_this",
64
66
  "get_param_style_and_name",
@@ -429,6 +431,78 @@ def is_msgspec_struct_without_field(obj: Any, field_name: str) -> "TypeGuard[Str
429
431
  return False
430
432
 
431
433
 
434
+ @lru_cache(maxsize=500)
435
+ def _detect_rename_pattern(field_name: str, encode_name: str) -> "Optional[str]":
436
+ """Detect the rename pattern by comparing field name transformations.
437
+
438
+ Args:
439
+ field_name: Original field name (e.g., "user_id")
440
+ encode_name: Encoded field name (e.g., "userId")
441
+
442
+ Returns:
443
+ The detected rename pattern ("camel", "kebab", "pascal") or None
444
+ """
445
+ from sqlspec.utils.text import camelize, kebabize, pascalize
446
+
447
+ # Test camelCase conversion
448
+ if encode_name == camelize(field_name) and encode_name != field_name:
449
+ return "camel"
450
+
451
+ if encode_name == kebabize(field_name) and encode_name != field_name:
452
+ return "kebab"
453
+
454
+ if encode_name == pascalize(field_name) and encode_name != field_name:
455
+ return "pascal"
456
+ return None
457
+
458
+
459
+ def get_msgspec_rename_config(schema_type: type) -> "Optional[str]":
460
+ """Extract msgspec rename configuration from a struct type.
461
+
462
+ Analyzes field name transformations to detect the rename pattern used by msgspec.
463
+ Since msgspec doesn't store the original rename parameter directly, we infer it
464
+ by comparing field names with their encode_name values.
465
+
466
+ Args:
467
+ schema_type: The msgspec struct type to inspect.
468
+
469
+ Returns:
470
+ The rename configuration value ("camel", "kebab", "pascal", etc.) if detected,
471
+ None if no rename configuration exists or if not a msgspec struct.
472
+
473
+ Examples:
474
+ >>> class User(msgspec.Struct, rename="camel"):
475
+ ... user_id: int
476
+ >>> get_msgspec_rename_config(User)
477
+ "camel"
478
+
479
+ >>> class Product(msgspec.Struct):
480
+ ... product_id: int
481
+ >>> get_msgspec_rename_config(Product)
482
+ None
483
+ """
484
+ if not MSGSPEC_INSTALLED:
485
+ return None
486
+
487
+ if not is_msgspec_struct(schema_type):
488
+ return None
489
+
490
+ from msgspec import structs
491
+
492
+ fields = structs.fields(schema_type) # type: ignore[arg-type]
493
+ if not fields:
494
+ return None
495
+
496
+ # Check if any field name differs from its encode_name
497
+ for field in fields:
498
+ if field.name != field.encode_name:
499
+ # Detect the rename pattern by comparing transformations
500
+ return _detect_rename_pattern(field.name, field.encode_name)
501
+
502
+ # If all field names match their encode_name, no rename is applied
503
+ return None
504
+
505
+
432
506
  def is_attrs_instance(obj: Any) -> "TypeGuard[AttrsInstanceStub]":
433
507
  """Check if a value is an attrs class instance.
434
508