hotdata 0.9.1__tar.gz → 0.10.0__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.
Files changed (327) hide show
  1. {hotdata-0.9.1 → hotdata-0.10.0}/PKG-INFO +1 -1
  2. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api_client.py +0 -93
  3. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/configuration.py +5 -38
  4. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata.egg-info/PKG-INFO +1 -1
  5. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata.egg-info/SOURCES.txt +1 -2
  6. {hotdata-0.9.1 → hotdata-0.10.0}/pyproject.toml +1 -1
  7. hotdata-0.10.0/tests/test_api_token_auth.py +156 -0
  8. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_arrow.py +0 -5
  9. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_config_auth_surface.py +2 -4
  10. hotdata-0.9.1/hotdata/_auth.py +0 -458
  11. hotdata-0.9.1/tests/test_auth.py +0 -1239
  12. {hotdata-0.9.1 → hotdata-0.10.0}/README.md +0 -0
  13. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/__init__.py +0 -0
  14. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/_retry.py +0 -0
  15. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/_upload_watchdog.py +0 -0
  16. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/__init__.py +0 -0
  17. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/connection_types_api.py +0 -0
  18. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/connections_api.py +0 -0
  19. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/database_context_api.py +0 -0
  20. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/databases_api.py +0 -0
  21. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/embedding_providers_api.py +0 -0
  22. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/indexes_api.py +0 -0
  23. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/information_schema_api.py +0 -0
  24. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/jobs_api.py +0 -0
  25. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/query_api.py +0 -0
  26. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/query_runs_api.py +0 -0
  27. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/refresh_api.py +0 -0
  28. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/results_api.py +0 -0
  29. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/saved_queries_api.py +0 -0
  30. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/secrets_api.py +0 -0
  31. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/uploads_api.py +0 -0
  32. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/usage_api.py +0 -0
  33. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api/workspaces_api.py +0 -0
  34. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/api_response.py +0 -0
  35. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/arrow.py +0 -0
  36. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/exceptions.py +0 -0
  37. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/__init__.py +0 -0
  38. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/add_managed_schema_request.py +0 -0
  39. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/add_managed_table_decl.py +0 -0
  40. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/add_managed_table_request.py +0 -0
  41. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/api_error_detail.py +0 -0
  42. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/api_error_response.py +0 -0
  43. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/async_query_response.py +0 -0
  44. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/attach_database_catalog_request.py +0 -0
  45. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/batch_create_upload_request.py +0 -0
  46. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/batch_create_upload_response.py +0 -0
  47. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/boolean_profile_detail.py +0 -0
  48. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/bulk_create_databases_request.py +0 -0
  49. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/bulk_create_databases_result.py +0 -0
  50. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/categorical_profile_detail.py +0 -0
  51. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/category_value_info.py +0 -0
  52. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_definition.py +0 -0
  53. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_info.py +0 -0
  54. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_profile_detail.py +0 -0
  55. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_profile_detail_one_of.py +0 -0
  56. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_profile_detail_one_of1.py +0 -0
  57. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_profile_detail_one_of2.py +0 -0
  58. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_profile_detail_one_of3.py +0 -0
  59. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_profile_detail_one_of4.py +0 -0
  60. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_profile_info.py +0 -0
  61. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/column_type_spec.py +0 -0
  62. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/connection_health_response.py +0 -0
  63. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/connection_info.py +0 -0
  64. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/connection_refresh_result.py +0 -0
  65. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/connection_schema_error.py +0 -0
  66. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/connection_type_detail.py +0 -0
  67. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/connection_type_summary.py +0 -0
  68. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_connection_request.py +0 -0
  69. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_connection_response.py +0 -0
  70. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_database_request.py +0 -0
  71. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_database_response.py +0 -0
  72. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_embedding_provider_request.py +0 -0
  73. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_embedding_provider_response.py +0 -0
  74. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_index_request.py +0 -0
  75. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_saved_query_request.py +0 -0
  76. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_secret_request.py +0 -0
  77. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_secret_response.py +0 -0
  78. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_upload_request.py +0 -0
  79. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_workspace_request.py +0 -0
  80. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/create_workspace_response.py +0 -0
  81. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_attachment_info.py +0 -0
  82. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_batch_response.py +0 -0
  83. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_context_entry.py +0 -0
  84. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_count_response.py +0 -0
  85. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_default_schema_decl.py +0 -0
  86. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_default_table_decl.py +0 -0
  87. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_detail_response.py +0 -0
  88. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/database_summary.py +0 -0
  89. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/delete_database_batch_response.py +0 -0
  90. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/discovery_status.py +0 -0
  91. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/embedding_provider_response.py +0 -0
  92. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/error.py +0 -0
  93. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/execute_saved_query_request.py +0 -0
  94. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/finalize_upload_part.py +0 -0
  95. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/finalize_upload_request.py +0 -0
  96. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/finalize_upload_response.py +0 -0
  97. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/fork_database_request.py +0 -0
  98. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/get_connection_response.py +0 -0
  99. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/get_database_context_response.py +0 -0
  100. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/get_result_response.py +0 -0
  101. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/get_secret_response.py +0 -0
  102. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/index_entry_response.py +0 -0
  103. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/index_info_response.py +0 -0
  104. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/index_status.py +0 -0
  105. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/information_schema_response.py +0 -0
  106. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/job_result.py +0 -0
  107. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/job_status.py +0 -0
  108. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/job_status_response.py +0 -0
  109. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/job_type.py +0 -0
  110. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_connection_types_response.py +0 -0
  111. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_connections_response.py +0 -0
  112. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_database_contexts_response.py +0 -0
  113. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_databases_response.py +0 -0
  114. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_embedding_providers_response.py +0 -0
  115. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_indexes_page_response.py +0 -0
  116. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_indexes_response.py +0 -0
  117. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_jobs_response.py +0 -0
  118. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_query_runs_response.py +0 -0
  119. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_results_response.py +0 -0
  120. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_saved_queries_response.py +0 -0
  121. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_saved_query_versions_response.py +0 -0
  122. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_secrets_response.py +0 -0
  123. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/list_workspaces_response.py +0 -0
  124. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/load_managed_table_request.py +0 -0
  125. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/load_managed_table_response.py +0 -0
  126. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/managed_schema_response.py +0 -0
  127. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/managed_table_response.py +0 -0
  128. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/mint_upload_parts_request.py +0 -0
  129. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/mint_upload_parts_response.py +0 -0
  130. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/minted_upload_part_response.py +0 -0
  131. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/numeric_profile_detail.py +0 -0
  132. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/query_request.py +0 -0
  133. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/query_response.py +0 -0
  134. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/query_run_info.py +0 -0
  135. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/refresh_request.py +0 -0
  136. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/refresh_response.py +0 -0
  137. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/refresh_warning.py +0 -0
  138. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/result_info.py +0 -0
  139. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/results_format_query.py +0 -0
  140. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/saved_query_detail.py +0 -0
  141. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/saved_query_summary.py +0 -0
  142. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/saved_query_version_info.py +0 -0
  143. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/schema_refresh_result.py +0 -0
  144. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/secret_metadata_response.py +0 -0
  145. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/submit_job_response.py +0 -0
  146. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/table_info.py +0 -0
  147. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/table_partition_key.py +0 -0
  148. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/table_profile_response.py +0 -0
  149. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/table_refresh_error.py +0 -0
  150. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/table_refresh_result.py +0 -0
  151. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/table_sort_key.py +0 -0
  152. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/temporal_profile_detail.py +0 -0
  153. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/text_profile_detail.py +0 -0
  154. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/update_embedding_provider_request.py +0 -0
  155. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/update_embedding_provider_response.py +0 -0
  156. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/update_saved_query_request.py +0 -0
  157. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/update_secret_request.py +0 -0
  158. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/update_secret_response.py +0 -0
  159. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/upload_session_response.py +0 -0
  160. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/upsert_database_context_request.py +0 -0
  161. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/upsert_database_context_response.py +0 -0
  162. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/workspace_detail.py +0 -0
  163. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/workspace_list_item.py +0 -0
  164. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/models/workspace_usage_response.py +0 -0
  165. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/py.typed +0 -0
  166. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/query.py +0 -0
  167. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/rest.py +0 -0
  168. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata/uploads.py +0 -0
  169. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata.egg-info/dependency_links.txt +0 -0
  170. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata.egg-info/requires.txt +0 -0
  171. {hotdata-0.9.1 → hotdata-0.10.0}/hotdata.egg-info/top_level.txt +0 -0
  172. {hotdata-0.9.1 → hotdata-0.10.0}/setup.cfg +0 -0
  173. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_add_managed_schema_request.py +0 -0
  174. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_add_managed_table_decl.py +0 -0
  175. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_add_managed_table_request.py +0 -0
  176. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_api_client_close.py +0 -0
  177. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_api_error_detail.py +0 -0
  178. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_api_error_response.py +0 -0
  179. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_async_query_response.py +0 -0
  180. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_attach_database_catalog_request.py +0 -0
  181. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_batch_create_upload_request.py +0 -0
  182. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_batch_create_upload_response.py +0 -0
  183. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_boolean_profile_detail.py +0 -0
  184. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_bulk_create_databases_request.py +0 -0
  185. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_bulk_create_databases_result.py +0 -0
  186. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_categorical_profile_detail.py +0 -0
  187. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_category_value_info.py +0 -0
  188. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_definition.py +0 -0
  189. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_info.py +0 -0
  190. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_profile_detail.py +0 -0
  191. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_profile_detail_one_of.py +0 -0
  192. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_profile_detail_one_of1.py +0 -0
  193. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_profile_detail_one_of2.py +0 -0
  194. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_profile_detail_one_of3.py +0 -0
  195. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_profile_detail_one_of4.py +0 -0
  196. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_profile_info.py +0 -0
  197. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_column_type_spec.py +0 -0
  198. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connection_health_response.py +0 -0
  199. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connection_info.py +0 -0
  200. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connection_refresh_result.py +0 -0
  201. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connection_schema_error.py +0 -0
  202. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connection_type_detail.py +0 -0
  203. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connection_type_summary.py +0 -0
  204. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connection_types_api.py +0 -0
  205. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_connections_api.py +0 -0
  206. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_connection_request.py +0 -0
  207. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_connection_response.py +0 -0
  208. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_database_request.py +0 -0
  209. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_database_response.py +0 -0
  210. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_embedding_provider_request.py +0 -0
  211. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_embedding_provider_response.py +0 -0
  212. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_index_request.py +0 -0
  213. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_saved_query_request.py +0 -0
  214. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_secret_request.py +0 -0
  215. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_secret_response.py +0 -0
  216. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_upload_request.py +0 -0
  217. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_workspace_request.py +0 -0
  218. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_create_workspace_response.py +0 -0
  219. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_attachment_info.py +0 -0
  220. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_batch_response.py +0 -0
  221. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_context_api.py +0 -0
  222. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_context_entry.py +0 -0
  223. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_count_response.py +0 -0
  224. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_default_schema_decl.py +0 -0
  225. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_default_table_decl.py +0 -0
  226. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_detail_response.py +0 -0
  227. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_database_summary.py +0 -0
  228. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_databases_api.py +0 -0
  229. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_delete_database_batch_response.py +0 -0
  230. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_discovery_status.py +0 -0
  231. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_embedding_provider_response.py +0 -0
  232. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_embedding_providers_api.py +0 -0
  233. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_error.py +0 -0
  234. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_execute_saved_query_request.py +0 -0
  235. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_finalize_upload_part.py +0 -0
  236. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_finalize_upload_request.py +0 -0
  237. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_finalize_upload_response.py +0 -0
  238. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_fork_database_request.py +0 -0
  239. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_get_connection_response.py +0 -0
  240. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_get_database_context_response.py +0 -0
  241. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_get_result_response.py +0 -0
  242. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_get_secret_response.py +0 -0
  243. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_index_entry_response.py +0 -0
  244. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_index_info_response.py +0 -0
  245. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_index_status.py +0 -0
  246. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_indexes_api.py +0 -0
  247. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_information_schema_api.py +0 -0
  248. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_information_schema_response.py +0 -0
  249. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_job_result.py +0 -0
  250. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_job_status.py +0 -0
  251. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_job_status_response.py +0 -0
  252. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_job_type.py +0 -0
  253. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_jobs_api.py +0 -0
  254. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_connection_types_response.py +0 -0
  255. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_connections_response.py +0 -0
  256. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_database_contexts_response.py +0 -0
  257. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_databases_response.py +0 -0
  258. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_embedding_providers_response.py +0 -0
  259. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_indexes_page_response.py +0 -0
  260. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_indexes_response.py +0 -0
  261. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_jobs_response.py +0 -0
  262. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_query_runs_response.py +0 -0
  263. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_results_response.py +0 -0
  264. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_saved_queries_response.py +0 -0
  265. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_saved_query_versions_response.py +0 -0
  266. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_secrets_response.py +0 -0
  267. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_list_workspaces_response.py +0 -0
  268. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_load_managed_table_request.py +0 -0
  269. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_load_managed_table_response.py +0 -0
  270. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_managed_schema_response.py +0 -0
  271. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_managed_table_response.py +0 -0
  272. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_mint_upload_parts_request.py +0 -0
  273. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_mint_upload_parts_response.py +0 -0
  274. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_minted_upload_part_response.py +0 -0
  275. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_numeric_profile_detail.py +0 -0
  276. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_query_api.py +0 -0
  277. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_query_request.py +0 -0
  278. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_query_response.py +0 -0
  279. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_query_run_info.py +0 -0
  280. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_query_runs_api.py +0 -0
  281. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_refresh_api.py +0 -0
  282. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_refresh_request.py +0 -0
  283. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_refresh_response.py +0 -0
  284. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_refresh_warning.py +0 -0
  285. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_result_info.py +0 -0
  286. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_results_api.py +0 -0
  287. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_results_format_query.py +0 -0
  288. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_saved_queries_api.py +0 -0
  289. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_saved_query_detail.py +0 -0
  290. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_saved_query_summary.py +0 -0
  291. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_saved_query_version_info.py +0 -0
  292. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_schema_refresh_result.py +0 -0
  293. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_secret_metadata_response.py +0 -0
  294. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_secrets_api.py +0 -0
  295. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_submit_job_response.py +0 -0
  296. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_table_info.py +0 -0
  297. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_table_partition_key.py +0 -0
  298. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_table_profile_response.py +0 -0
  299. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_table_refresh_error.py +0 -0
  300. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_table_refresh_result.py +0 -0
  301. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_table_sort_key.py +0 -0
  302. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_temporal_profile_detail.py +0 -0
  303. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_text_profile_detail.py +0 -0
  304. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_update_embedding_provider_request.py +0 -0
  305. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_update_embedding_provider_response.py +0 -0
  306. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_update_saved_query_request.py +0 -0
  307. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_update_secret_request.py +0 -0
  308. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_update_secret_response.py +0 -0
  309. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_upload_session_response.py +0 -0
  310. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_uploads_api.py +0 -0
  311. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_upsert_database_context_request.py +0 -0
  312. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_upsert_database_context_response.py +0 -0
  313. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_usage_api.py +0 -0
  314. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_workspace_detail.py +0 -0
  315. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_workspace_list_item.py +0 -0
  316. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_workspace_usage_response.py +0 -0
  317. {hotdata-0.9.1 → hotdata-0.10.0}/test/test_workspaces_api.py +0 -0
  318. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_query.py +0 -0
  319. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_retry.py +0 -0
  320. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_update_changelog.py +0 -0
  321. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_upload_codex_fixes.py +0 -0
  322. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_upload_multipart_resilient.py +0 -0
  323. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_upload_parity_fixes.py +0 -0
  324. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_upload_resilient.py +0 -0
  325. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_upload_retry_policy.py +0 -0
  326. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_upload_watchdog.py +0 -0
  327. {hotdata-0.9.1 → hotdata-0.10.0}/tests/test_uploads.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hotdata
3
- Version: 0.9.1
3
+ Version: 0.10.0
4
4
  Summary: Hotdata API
5
5
  Author-email: Hotdata <developers@hotdata.dev>
6
6
  License: MIT
@@ -281,105 +281,12 @@ class ApiClient:
281
281
  body=body, post_params=post_params,
282
282
  _request_timeout=_request_timeout
283
283
  )
284
- if response_data.status == 401:
285
- # `header_params` is Optional on this method. `param_serialize`
286
- # always passes a dict so nothing in-tree omits it, but a direct
287
- # caller that does would otherwise crash HERE, on the failure
288
- # path, rather than anywhere it could be noticed.
289
- header_params = header_params if header_params is not None else {}
290
- fresh = self._remint_bearer(header_params)
291
- if fresh is not None:
292
- # Drain the response being discarded. urllib3 only returns a
293
- # connection to the pool once its body is consumed or
294
- # released; abandoning it un-read leaks the connection for
295
- # the life of the pool, and a client that 401s on every
296
- # request would exhaust it.
297
- try:
298
- response_data.read()
299
- except Exception: # noqa: BLE001 -- draining must not mask the retry
300
- pass
301
- header_params["Authorization"] = fresh
302
- response_data = self.rest_client.request(
303
- method, url,
304
- headers=header_params,
305
- body=body, post_params=post_params,
306
- _request_timeout=_request_timeout
307
- )
308
284
 
309
285
  except ApiException as e:
310
286
  raise e
311
287
 
312
288
  return response_data
313
289
 
314
- def _remint_bearer(self, header_params):
315
- """A replacement `Authorization` value after a 401, or None to give up.
316
-
317
- ONCE per request, and only for a bearer this client minted. A 401 on an
318
- exchanged JWT is recoverable in a way it is not for a caller-supplied
319
- credential: the SDK swapped an API token for a short-lived JWT behind the
320
- caller's back, so a JWT the server refuses is the SDK's problem to fix,
321
- not an error the caller can act on.
322
-
323
- The proactive `_exp` check covers the expected case. This covers the one
324
- it cannot: a token the client still believed in but the server rejected —
325
- clock skew, a revoked JWT, or a mint that reported a lifetime the token
326
- did not have. Returns None when there is nothing to re-mint (no manager,
327
- a caller-supplied JWT, exchange opted out) so the 401 surfaces unchanged.
328
-
329
- Never loops. A second 401 is a real rejection and is returned to the
330
- caller; retrying a genuine auth failure would only turn one clear error
331
- into a slower one.
332
- """
333
- from hotdata._auth import TokenExchangeError
334
-
335
- config = self.configuration
336
- manager = getattr(config, "_token_manager", None)
337
- if manager is None or not getattr(manager, "_needs_exchange", False):
338
- return None
339
-
340
- # Only a header THIS manager produced. `_request_auth` lets a caller
341
- # override auth for a single request, and a per-request credential that
342
- # the server rejects is the caller's to deal with -- replacing it with a
343
- # managed token would silently answer a different question than the one
344
- # asked, and would do it only on the failure path where it is hardest to
345
- # notice.
346
- stale = manager._jwt
347
- before = header_params.get("Authorization")
348
- if stale is not None and before == "Bearer " + stale:
349
- # We carried the token the manager still holds: clear it so the read
350
- # below mints. Compare-and-clear, so a thread that is beaten to it
351
- # takes the winner's token rather than clearing it again.
352
- manager.invalidate(only_if=stale)
353
- elif before == "Bearer " + (manager._prev_jwt or "\0"):
354
- # We carried the token the manager has ALREADY rotated away from --
355
- # another request 401'd on it first and minted the replacement. Do
356
- # not invalidate: retry with what it minted. Without this the winner
357
- # recovers and every sibling fails, since their header no longer
358
- # matches the current token, which is the stampede case the
359
- # compare-and-clear was added for in the first place.
360
- pass
361
- else:
362
- # A credential this manager never issued -- `_request_auth`, or an
363
- # endpoint with no auth at all. Not ours to replace.
364
- return None
365
- try:
366
- token = config.api_key
367
- except TokenExchangeError:
368
- # The commonest cause of a 401 is a revoked or expired API token, and
369
- # re-minting from it fails for the same reason. `invalidate` cleared
370
- # the refresh token, so this raise has no best-effort path behind it.
371
- # Surfacing it here would replace the server's own 401 -- body and
372
- # all -- with an exchange error from a retry the caller never asked
373
- # for, turning a clear answer into a confusing one.
374
- return None
375
- if token is None:
376
- return None
377
- after = "Bearer " + token
378
- # A re-mint that hands back the same credential means the server would
379
- # refuse it again; sending it a second time buys a round trip and the
380
- # same 401. This is the replayed-cache case, so it is not hypothetical.
381
- return None if after == before else after
382
-
383
290
  def response_deserialize(
384
291
  self,
385
292
  response_data: rest.RESTResponse,
@@ -220,13 +220,8 @@ conf = hotdata.Configuration(
220
220
  self.temp_folder_path = None
221
221
  """Temp file folder for downloading files
222
222
  """
223
- # Transparent API-token -> JWT exchange. `api_key` is a property whose
224
- # getter returns a live JWT minted from this credential (see _auth.py);
225
- # the manager is created eagerly here (never lazily in the getter) so
226
- # concurrent first requests don't each build one. The setter rebuilds it.
227
- from hotdata._auth import _TokenManager
228
- self._token_manager = _TokenManager(api_key, self) if api_key is not None else None
229
- """Hotdata API key, sent as `Authorization: Bearer <key>`."""
223
+ self.api_key = api_key
224
+ """Hotdata API key, sent verbatim as `Authorization: Bearer <key>`."""
230
225
  # apiKey-security values (X-Workspace-Id), keyed by scheme name. Read by
231
226
  # the generated `auth_settings()` below.
232
227
  self.api_keys = dict(api_keys) if api_keys else {}
@@ -343,20 +338,13 @@ conf = hotdata.Configuration(
343
338
  result = cls.__new__(cls)
344
339
  memo[id(self)] = result
345
340
  for k, v in self.__dict__.items():
346
- # _token_manager holds a threading.Lock and a urllib3 PoolManager,
347
- # neither of which is deepcopy-able; rebuild it below from the
348
- # (deepcopy-safe) credential string instead.
349
- if k not in ('logger', 'logger_file_handler', '_token_manager'):
341
+ if k not in ('logger', 'logger_file_handler'):
350
342
  setattr(result, k, copy.deepcopy(v, memo))
351
343
  # shallow copy of loggers
352
344
  result.logger = copy.copy(self.logger)
353
345
  # use setters to configure loggers
354
346
  result.logger_file = self.logger_file
355
347
  result.debug = self.debug
356
- # rebuild the token manager bound to the copy (never deepcopy lock/pool)
357
- from hotdata._auth import _TokenManager
358
- tm = self._token_manager
359
- result._token_manager = _TokenManager(tm._credential, result) if tm else None
360
348
  return result
361
349
 
362
350
  def __setattr__(self, name: str, value: Any) -> None:
@@ -501,26 +489,6 @@ conf = hotdata.Configuration(
501
489
 
502
490
  return None
503
491
 
504
- @property
505
- def api_key(self) -> Optional[str]:
506
- """Live bearer credential, sent as `Authorization: Bearer <value>`.
507
-
508
- Backed by the regeneration-immune `_TokenManager` (see `hotdata._auth`):
509
- an opaque API token is transparently exchanged for a short-lived JWT and
510
- kept fresh, while a credential already shaped like a JWT (or exchange
511
- opted out) is returned unchanged. `auth_settings()` reads this on every
512
- request, so the wire always carries a current token.
513
- """
514
- # Read the manager once: a concurrent `api_key` reset could otherwise
515
- # set it to None between the check and the `.bearer_value()` call.
516
- tm = self._token_manager
517
- return None if tm is None else tm.bearer_value()
518
-
519
- @api_key.setter
520
- def api_key(self, value: Optional[str]) -> None:
521
- from hotdata._auth import _TokenManager
522
- self._token_manager = _TokenManager(value, self) if value is not None else None
523
-
524
492
  @property
525
493
  def workspace_id(self) -> Optional[str]:
526
494
  """Public id of the target workspace (sent as `X-Workspace-Id`)."""
@@ -555,9 +523,8 @@ conf = hotdata.Configuration(
555
523
  :return: The Auth Settings information dict.
556
524
  """
557
525
  auth: AuthSettings = {}
558
- # Resolve the bearer token once: `api_key` is a property that may mint a
559
- # JWT and take the token-manager lock, so a second read would lock twice
560
- # and could race a concurrent `api_key` reset (yielding `Bearer None`).
526
+ # Read the credential once so a concurrent `api_key` reset can't yield
527
+ # `Bearer None` between the check and the header build.
561
528
  BearerAuth_token = self.api_key
562
529
  if BearerAuth_token is not None:
563
530
  auth['BearerAuth'] = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hotdata
3
- Version: 0.9.1
3
+ Version: 0.10.0
4
4
  Summary: Hotdata API
5
5
  Author-email: Hotdata <developers@hotdata.dev>
6
6
  License: MIT
@@ -2,7 +2,6 @@ README.md
2
2
  pyproject.toml
3
3
  setup.cfg
4
4
  hotdata/__init__.py
5
- hotdata/_auth.py
6
5
  hotdata/_retry.py
7
6
  hotdata/_upload_watchdog.py
8
7
  hotdata/api_client.py
@@ -310,8 +309,8 @@ test/test_workspace_detail.py
310
309
  test/test_workspace_list_item.py
311
310
  test/test_workspace_usage_response.py
312
311
  test/test_workspaces_api.py
312
+ tests/test_api_token_auth.py
313
313
  tests/test_arrow.py
314
- tests/test_auth.py
315
314
  tests/test_config_auth_surface.py
316
315
  tests/test_query.py
317
316
  tests/test_retry.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hotdata"
3
- version = "0.9.1"
3
+ version = "0.10.0"
4
4
  description = "Hotdata API"
5
5
  authors = [
6
6
  {name = "Hotdata",email = "developers@hotdata.dev"},
@@ -0,0 +1,156 @@
1
+ """The API token is the only credential: it goes on the wire verbatim.
2
+
3
+ Hotdata deprecated the API-token -> JWT key exchange. The SDK used to hold a
4
+ hand-written `_TokenManager` (`hotdata/_auth.py`) that POSTed the configured
5
+ token to `/v1/auth/jwt`, cached the returned JWT, and refreshed it with a
6
+ `refresh_token` grant; `Configuration.api_key` was a property that minted on
7
+ every read. All of that is gone -- the configured token is sent as
8
+ `Authorization: Bearer <token>` unchanged.
9
+
10
+ These tests pin that end to end: the token reaches the wire as-is, no token
11
+ endpoint is ever contacted, and the exchange machinery cannot creep back in via
12
+ the generated `configuration.py` or its mustache template.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import copy
18
+ import importlib
19
+ from pathlib import Path
20
+
21
+ import pytest
22
+ import urllib3
23
+
24
+ from hotdata import ApiClient, Configuration
25
+ from hotdata.api.workspaces_api import WorkspacesApi
26
+
27
+ REPO_ROOT = Path(__file__).resolve().parents[1]
28
+ CONFIGURATION_TEMPLATE = REPO_ROOT / ".openapi-generator-templates" / "configuration.mustache"
29
+ CONFIGURATION_MODULE = REPO_ROOT / "hotdata" / "configuration.py"
30
+
31
+ # An opaque (non-JWT-shaped) token: exactly the credential the old exchange
32
+ # would have swapped for a JWT before sending.
33
+ API_TOKEN = "hd_opaque_api_token"
34
+
35
+
36
+ def _config(**kwargs) -> Configuration:
37
+ return Configuration(host="https://api.example.test", api_key=API_TOKEN, **kwargs)
38
+
39
+
40
+ def _no_network(monkeypatch: pytest.MonkeyPatch) -> None:
41
+ """Make any outbound urllib3 request an immediate failure.
42
+
43
+ The exchange built its own pool manager, so a surviving mint would show up
44
+ here rather than as a silently different header.
45
+ """
46
+
47
+ def explode(*args, **kwargs): # pragma: no cover - only runs on regression
48
+ raise AssertionError(f"unexpected outbound request: {args!r} {kwargs!r}")
49
+
50
+ monkeypatch.setattr(urllib3.PoolManager, "request", explode)
51
+ monkeypatch.setattr(urllib3.PoolManager, "urlopen", explode)
52
+
53
+
54
+ def test_api_token_is_sent_verbatim_as_bearer(monkeypatch: pytest.MonkeyPatch) -> None:
55
+ """The configured token itself is the bearer credential."""
56
+ _no_network(monkeypatch)
57
+ config = _config()
58
+
59
+ assert config.auth_settings()["BearerAuth"]["value"] == f"Bearer {API_TOKEN}"
60
+
61
+
62
+ def test_authorization_header_carries_the_api_token(monkeypatch: pytest.MonkeyPatch) -> None:
63
+ """Serializing a real operation puts the raw token in the header."""
64
+ _no_network(monkeypatch)
65
+ api = WorkspacesApi(ApiClient(_config(workspace_id="ws_abc")))
66
+
67
+ _method, _url, headers, *_rest = api._list_workspaces_serialize(
68
+ organization_public_id=None,
69
+ _request_auth=None,
70
+ _content_type=None,
71
+ _headers=None,
72
+ _host_index=0,
73
+ )
74
+
75
+ assert headers["Authorization"] == f"Bearer {API_TOKEN}"
76
+
77
+
78
+ def test_api_token_is_read_repeatedly_without_exchange(monkeypatch: pytest.MonkeyPatch) -> None:
79
+ """Every request re-reads the credential; none of those reads mint anything."""
80
+ _no_network(monkeypatch)
81
+ config = _config()
82
+
83
+ values = {config.auth_settings()["BearerAuth"]["value"] for _ in range(5)}
84
+
85
+ assert values == {f"Bearer {API_TOKEN}"}
86
+
87
+
88
+ def test_api_token_can_be_replaced_and_cleared(monkeypatch: pytest.MonkeyPatch) -> None:
89
+ """`api_key` stays writable, and clearing it drops the bearer header."""
90
+ _no_network(monkeypatch)
91
+ config = _config()
92
+
93
+ config.api_key = "hd_rotated"
94
+ assert config.auth_settings()["BearerAuth"]["value"] == "Bearer hd_rotated"
95
+
96
+ config.api_key = None
97
+ assert "BearerAuth" not in config.auth_settings()
98
+
99
+
100
+ def test_deepcopy_preserves_the_api_token(monkeypatch: pytest.MonkeyPatch) -> None:
101
+ """`ApiClient` deepcopies the configuration; the credential must survive it.
102
+
103
+ The exchange needed a `__deepcopy__` special case because the token manager
104
+ held a lock and a pool manager. A plain string needs none, but the copy must
105
+ still authenticate.
106
+ """
107
+ _no_network(monkeypatch)
108
+ config = _config(workspace_id="ws_abc")
109
+
110
+ clone = copy.deepcopy(config)
111
+
112
+ assert clone.api_key == API_TOKEN
113
+ assert clone.auth_settings()["BearerAuth"]["value"] == f"Bearer {API_TOKEN}"
114
+
115
+
116
+ def test_jwt_opt_out_env_var_is_inert(monkeypatch: pytest.MonkeyPatch) -> None:
117
+ """`HOTDATA_DISABLE_JWT_EXCHANGE` was the exchange escape hatch.
118
+
119
+ With no exchange left there is nothing to disable, so the variable must not
120
+ change behavior -- callers who still export it get the same raw token.
121
+ """
122
+ _no_network(monkeypatch)
123
+ monkeypatch.setenv("HOTDATA_DISABLE_JWT_EXCHANGE", "0")
124
+
125
+ assert _config().auth_settings()["BearerAuth"]["value"] == f"Bearer {API_TOKEN}"
126
+
127
+
128
+ def test_auth_exchange_module_is_gone() -> None:
129
+ """`hotdata._auth` existed only to run the key exchange."""
130
+ with pytest.raises(ModuleNotFoundError):
131
+ importlib.import_module("hotdata._auth")
132
+
133
+
134
+ def test_api_key_is_a_plain_attribute() -> None:
135
+ """A property getter is what made every read a potential mint."""
136
+ assert not isinstance(getattr(Configuration, "api_key", None), property)
137
+
138
+
139
+ @pytest.mark.parametrize(
140
+ "path",
141
+ [CONFIGURATION_MODULE, CONFIGURATION_TEMPLATE],
142
+ ids=["generated", "template"],
143
+ )
144
+ def test_no_exchange_machinery_in_configuration(path: Path) -> None:
145
+ """Editing only the generated file would be undone by the next regen."""
146
+ source = path.read_text()
147
+
148
+ for needle in (
149
+ "_TokenManager",
150
+ "_token_manager",
151
+ "_auth import",
152
+ "/v1/auth/jwt",
153
+ "refresh_token",
154
+ "HOTDATA_DISABLE_JWT_EXCHANGE",
155
+ ):
156
+ assert needle not in source, f"{path.name} still references {needle}"
@@ -87,11 +87,6 @@ def _install_fake_response(
87
87
  ) -> None:
88
88
  """Replace RESTClientObject.request with a stub that records the call."""
89
89
 
90
- # The api_key used here ("test-key") is a dummy, not a real token. Disable
91
- # transparent JWT exchange so auth_settings() does not try to mint one
92
- # against a non-existent endpoint when the (stubbed) request is built.
93
- monkeypatch.setenv("HOTDATA_DISABLE_JWT_EXCHANGE", "1")
94
-
95
90
  from hotdata import rest
96
91
 
97
92
  def fake_request(
@@ -32,13 +32,11 @@ from hotdata.api.results_api import ResultsApi
32
32
  REPO_ROOT = Path(__file__).resolve().parents[1]
33
33
  CONFIGURATION_TEMPLATE = REPO_ROOT / ".openapi-generator-templates" / "configuration.mustache"
34
34
 
35
- # A JWT-shaped credential is returned as-is by _TokenManager, so no exchange
36
- # request is ever made from these tests.
37
- FAKE_JWT = "eyJfake.payload.signature"
35
+ FAKE_API_TOKEN = "hd_fake_api_token"
38
36
 
39
37
 
40
38
  def _config(**kwargs) -> Configuration:
41
- return Configuration(host="https://api.example.test", api_key=FAKE_JWT, **kwargs)
39
+ return Configuration(host="https://api.example.test", api_key=FAKE_API_TOKEN, **kwargs)
42
40
 
43
41
 
44
42
  def _typed_api_key_kwargs() -> list[str]: