hotdata 0.4.0__tar.gz → 0.5.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.
- {hotdata-0.4.0 → hotdata-0.5.0}/PKG-INFO +56 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/README.md +55 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/__init__.py +23 -51
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/_auth.py +110 -19
- hotdata-0.5.0/hotdata/_retry.py +112 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/__init__.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/connection_types_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/connections_api.py +13 -13
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/database_context_api.py +13 -13
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/databases_api.py +341 -10
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/embedding_providers_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/indexes_api.py +105 -952
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/information_schema_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/jobs_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/query_api.py +4 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/query_runs_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/refresh_api.py +4 -4
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/results_api.py +10 -10
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/saved_queries_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/secrets_api.py +1 -1
- hotdata-0.4.0/hotdata/api/datasets_api.py → hotdata-0.5.0/hotdata/api/uploads_api.py +261 -534
- hotdata-0.5.0/hotdata/api/usage_api.py +303 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api/workspaces_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api_client.py +2 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/configuration.py +8 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/exceptions.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/__init__.py +9 -24
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/add_managed_schema_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/add_managed_table_decl.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/add_managed_table_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/api_error_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/api_error_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/async_query_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/attach_database_catalog_request.py +1 -1
- hotdata-0.5.0/hotdata/models/batch_create_upload_request.py +96 -0
- hotdata-0.5.0/hotdata/models/batch_create_upload_response.py +96 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/boolean_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/categorical_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/category_value_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_profile_detail_one_of.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_profile_detail_one_of1.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_profile_detail_one_of2.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_profile_detail_one_of3.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_profile_detail_one_of4.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/column_profile_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/connection_health_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/connection_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/connection_refresh_result.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/connection_schema_error.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/connection_type_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/connection_type_summary.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_connection_request.py +3 -10
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_connection_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_database_request.py +5 -12
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_database_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_embedding_provider_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_embedding_provider_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_index_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_saved_query_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_secret_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_secret_response.py +1 -1
- hotdata-0.5.0/hotdata/models/create_upload_request.py +131 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_workspace_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/create_workspace_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/database_attachment_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/database_context_entry.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/database_default_schema_decl.py +2 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/database_default_table_decl.py +2 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/database_detail_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/database_summary.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/discovery_status.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/embedding_provider_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/error.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/execute_saved_query_request.py +1 -1
- hotdata-0.4.0/hotdata/models/dataset_version_summary.py → hotdata-0.5.0/hotdata/models/finalize_upload_part.py +11 -16
- hotdata-0.5.0/hotdata/models/finalize_upload_request.py +101 -0
- hotdata-0.5.0/hotdata/models/finalize_upload_response.py +102 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/get_connection_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/get_database_context_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/get_result_response.py +3 -4
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/get_secret_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/index_entry_response.py +4 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/index_info_response.py +9 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/index_status.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/information_schema_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/job_result.py +10 -24
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/job_status.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/job_status_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/job_type.py +7 -3
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_connection_types_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_connections_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_database_contexts_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_databases_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_embedding_providers_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_indexes_page_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_indexes_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_jobs_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_query_runs_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_results_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_saved_queries_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_saved_query_versions_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_secrets_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_uploads_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/list_workspaces_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/load_managed_table_request.py +12 -5
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/load_managed_table_response.py +2 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/managed_schema_response.py +2 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/managed_table_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/numeric_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/query_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/query_response.py +7 -7
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/query_run_info.py +17 -3
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/refresh_request.py +2 -9
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/refresh_response.py +10 -24
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/refresh_warning.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/result_info.py +1 -1
- hotdata-0.5.0/hotdata/models/results_format_query.py +41 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/saved_query_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/saved_query_summary.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/saved_query_version_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/schema_refresh_result.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/secret_metadata_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/submit_job_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/table_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/table_profile_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/table_refresh_error.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/table_refresh_result.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/temporal_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/text_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/update_embedding_provider_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/update_embedding_provider_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/update_saved_query_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/update_secret_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/update_secret_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/upload_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/upload_response.py +1 -1
- hotdata-0.5.0/hotdata/models/upload_session_response.py +116 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/upsert_database_context_request.py +2 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/upsert_database_context_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/workspace_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/models/workspace_list_item.py +1 -1
- hotdata-0.4.0/hotdata/models/dataset_summary.py → hotdata-0.5.0/hotdata/models/workspace_usage_response.py +20 -26
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/rest.py +1 -1
- hotdata-0.5.0/hotdata/uploads.py +981 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata.egg-info/PKG-INFO +56 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata.egg-info/SOURCES.txt +23 -49
- {hotdata-0.4.0 → hotdata-0.5.0}/pyproject.toml +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_add_managed_schema_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_add_managed_table_decl.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_add_managed_table_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_api_error_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_api_error_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_async_query_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_attach_database_catalog_request.py +1 -1
- hotdata-0.5.0/test/test_batch_create_upload_request.py +71 -0
- hotdata-0.5.0/test/test_batch_create_upload_response.py +79 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_boolean_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_categorical_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_category_value_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_profile_detail_one_of.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_profile_detail_one_of1.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_profile_detail_one_of2.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_profile_detail_one_of3.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_profile_detail_one_of4.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_column_profile_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connection_health_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connection_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connection_refresh_result.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connection_schema_error.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connection_type_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connection_type_summary.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connection_types_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_connections_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_connection_request.py +2 -3
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_connection_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_database_request.py +2 -3
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_database_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_embedding_provider_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_embedding_provider_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_index_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_saved_query_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_secret_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_secret_response.py +1 -1
- hotdata-0.5.0/test/test_create_upload_request.py +59 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_workspace_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_create_workspace_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_database_attachment_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_database_context_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_database_context_entry.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_database_default_schema_decl.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_database_default_table_decl.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_database_detail_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_database_summary.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_databases_api.py +8 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_discovery_status.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_embedding_provider_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_embedding_providers_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_error.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_execute_saved_query_request.py +1 -1
- hotdata-0.5.0/test/test_finalize_upload_part.py +55 -0
- hotdata-0.5.0/test/test_finalize_upload_request.py +56 -0
- hotdata-0.4.0/test/test_refresh_dataset_response.py → hotdata-0.5.0/test/test_finalize_upload_response.py +17 -16
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_get_connection_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_get_database_context_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_get_result_response.py +2 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_get_secret_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_index_entry_response.py +2 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_index_info_response.py +2 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_index_status.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_indexes_api.py +1 -22
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_information_schema_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_information_schema_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_job_result.py +6 -9
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_job_status.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_job_status_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_job_type.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_jobs_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_connection_types_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_connections_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_database_contexts_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_databases_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_embedding_providers_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_indexes_page_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_indexes_response.py +3 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_jobs_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_query_runs_response.py +5 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_results_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_saved_queries_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_saved_query_versions_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_secrets_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_uploads_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_list_workspaces_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_load_managed_table_request.py +2 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_load_managed_table_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_managed_schema_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_managed_table_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_numeric_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_query_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_query_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_query_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_query_run_info.py +3 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_query_runs_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_refresh_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_refresh_request.py +1 -2
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_refresh_response.py +1 -5
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_refresh_warning.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_result_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_results_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_results_format_query.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_saved_queries_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_saved_query_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_saved_query_summary.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_saved_query_version_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_schema_refresh_result.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_secret_metadata_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_secrets_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_submit_job_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_table_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_table_profile_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_table_refresh_error.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_table_refresh_result.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_temporal_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_text_profile_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_update_embedding_provider_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_update_embedding_provider_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_update_saved_query_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_update_secret_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_update_secret_response.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_upload_info.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_upload_response.py +1 -1
- hotdata-0.5.0/test/test_upload_session_response.py +68 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_uploads_api.py +22 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_upsert_database_context_request.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_upsert_database_context_response.py +1 -1
- hotdata-0.5.0/test/test_usage_api.py +39 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_workspace_detail.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_workspace_list_item.py +1 -1
- hotdata-0.5.0/test/test_workspace_usage_response.py +60 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_workspaces_api.py +1 -1
- {hotdata-0.4.0 → hotdata-0.5.0}/tests/test_auth.py +195 -2
- hotdata-0.5.0/tests/test_retry.py +210 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/tests/test_update_changelog.py +4 -0
- hotdata-0.5.0/tests/test_uploads.py +1357 -0
- hotdata-0.4.0/hotdata/api/uploads_api.py +0 -587
- hotdata-0.4.0/hotdata/models/column_type_spec.py +0 -117
- hotdata-0.4.0/hotdata/models/create_dataset_request.py +0 -108
- hotdata-0.4.0/hotdata/models/create_dataset_response.py +0 -99
- hotdata-0.4.0/hotdata/models/dataset_source.py +0 -180
- hotdata-0.4.0/hotdata/models/dataset_source_one_of.py +0 -101
- hotdata-0.4.0/hotdata/models/dataset_source_one_of1.py +0 -99
- hotdata-0.4.0/hotdata/models/dataset_source_one_of2.py +0 -101
- hotdata-0.4.0/hotdata/models/dataset_source_one_of3.py +0 -101
- hotdata-0.4.0/hotdata/models/dataset_source_one_of4.py +0 -101
- hotdata-0.4.0/hotdata/models/get_dataset_response.py +0 -120
- hotdata-0.4.0/hotdata/models/inline_data.py +0 -92
- hotdata-0.4.0/hotdata/models/inline_dataset_source.py +0 -92
- hotdata-0.4.0/hotdata/models/list_dataset_versions_response.py +0 -107
- hotdata-0.4.0/hotdata/models/list_datasets_response.py +0 -105
- hotdata-0.4.0/hotdata/models/refresh_dataset_response.py +0 -95
- hotdata-0.4.0/hotdata/models/results_format_query.py +0 -41
- hotdata-0.4.0/hotdata/models/saved_query_dataset_source.py +0 -95
- hotdata-0.4.0/hotdata/models/sql_query_dataset_source.py +0 -102
- hotdata-0.4.0/hotdata/models/update_dataset_request.py +0 -107
- hotdata-0.4.0/hotdata/models/update_dataset_response.py +0 -104
- hotdata-0.4.0/hotdata/models/upload_dataset_source.py +0 -97
- hotdata-0.4.0/hotdata/models/url_dataset_source.py +0 -97
- hotdata-0.4.0/test/test_column_type_spec.py +0 -57
- hotdata-0.4.0/test/test_create_dataset_request.py +0 -57
- hotdata-0.4.0/test/test_create_dataset_response.py +0 -63
- hotdata-0.4.0/test/test_dataset_source.py +0 -80
- hotdata-0.4.0/test/test_dataset_source_one_of.py +0 -59
- hotdata-0.4.0/test/test_dataset_source_one_of1.py +0 -56
- hotdata-0.4.0/test/test_dataset_source_one_of2.py +0 -57
- hotdata-0.4.0/test/test_dataset_source_one_of3.py +0 -59
- hotdata-0.4.0/test/test_dataset_source_one_of4.py +0 -65
- hotdata-0.4.0/test/test_dataset_summary.py +0 -66
- hotdata-0.4.0/test/test_dataset_version_summary.py +0 -59
- hotdata-0.4.0/test/test_datasets_api.py +0 -74
- hotdata-0.4.0/test/test_get_dataset_response.py +0 -80
- hotdata-0.4.0/test/test_inline_data.py +0 -58
- hotdata-0.4.0/test/test_inline_dataset_source.py +0 -63
- hotdata-0.4.0/test/test_list_dataset_versions_response.py +0 -75
- hotdata-0.4.0/test/test_list_datasets_response.py +0 -81
- hotdata-0.4.0/test/test_saved_query_dataset_source.py +0 -54
- hotdata-0.4.0/test/test_sql_query_dataset_source.py +0 -55
- hotdata-0.4.0/test/test_update_dataset_request.py +0 -54
- hotdata-0.4.0/test/test_update_dataset_response.py +0 -62
- hotdata-0.4.0/test/test_upload_dataset_source.py +0 -57
- hotdata-0.4.0/test/test_url_dataset_source.py +0 -57
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/api_response.py +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/arrow.py +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/py.typed +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata/query.py +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata.egg-info/dependency_links.txt +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata.egg-info/requires.txt +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/hotdata.egg-info/top_level.txt +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/setup.cfg +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/test/test_api_client_close.py +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/tests/test_arrow.py +0 -0
- {hotdata-0.4.0 → hotdata-0.5.0}/tests/test_query.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hotdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Hotdata API
|
|
5
5
|
Author-email: Hotdata <developers@hotdata.dev>
|
|
6
6
|
License: MIT
|
|
@@ -107,6 +107,61 @@ with ApiClient(Configuration(api_key="...", workspace_id="...")) as client:
|
|
|
107
107
|
|
|
108
108
|
Both methods accept `offset` and `limit` for pagination. They raise `hotdata.arrow.ResultNotReadyError` if the result is still pending or processing — poll `results.get_result(result_id)` until `status == "ready"` first.
|
|
109
109
|
|
|
110
|
+
## File uploads
|
|
111
|
+
|
|
112
|
+
`hotdata.uploads.UploadsApi` (also the default `hotdata.UploadsApi`) adds
|
|
113
|
+
`upload_file`, which uploads a local file **directly to object storage** and
|
|
114
|
+
finalizes it in one call. It opens an upload session, `PUT`s the bytes straight
|
|
115
|
+
to storage — a single `PUT` for a small file, concurrent part `PUT`s for a large
|
|
116
|
+
one — then finalizes. The bytes never round-trip through the API.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from hotdata import ApiClient, Configuration, UploadsApi
|
|
120
|
+
|
|
121
|
+
with ApiClient(Configuration(api_key="...", workspace_id="...")) as client:
|
|
122
|
+
uploads = UploadsApi(client)
|
|
123
|
+
|
|
124
|
+
finalized = uploads.upload_file(
|
|
125
|
+
"data.parquet",
|
|
126
|
+
content_type="application/parquet",
|
|
127
|
+
progress=lambda done, total: print(f"{done}/{total} bytes"),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Pass finalized.upload_id to the managed-table load endpoint.
|
|
131
|
+
print(finalized.upload_id)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`upload_file` accepts a path, raw `bytes`, or a seekable binary file object
|
|
135
|
+
(`size` is inferred for all three; a file object is read from its current
|
|
136
|
+
position to the end). The SDK picks single vs. multipart from the size,
|
|
137
|
+
auto-scales the part size, and bounds part concurrency to a peak-memory budget
|
|
138
|
+
(override with `part_size` / `max_concurrency` / `part_retry`). Storage `PUT`s go
|
|
139
|
+
through a dedicated, header-isolated connection pool, so the SDK's auth and
|
|
140
|
+
workspace headers never reach object storage (which would otherwise reject the
|
|
141
|
+
upload). Finalize is sent with retries disabled so the exactly-once call is never
|
|
142
|
+
accidentally replayed.
|
|
143
|
+
|
|
144
|
+
Failures surface as a typed hierarchy under `hotdata.uploads.UploadError`:
|
|
145
|
+
`StorageError` (storage returned a non-2xx), `StorageTransportError` (the PUT
|
|
146
|
+
failed before any response), `MissingETagError`, `MalformedSessionError`, and
|
|
147
|
+
`SizeLimitError`. Opening the session or finalizing raises the usual
|
|
148
|
+
`hotdata.exceptions.ApiException` — for example a `501` `PRESIGN_UNSUPPORTED`,
|
|
149
|
+
meaning the backend cannot issue upload URLs.
|
|
150
|
+
|
|
151
|
+
For that fallback (or to upload from a non-seekable stream), use `upload_stream`,
|
|
152
|
+
which sends the bytes to the legacy `POST /v1/files` endpoint in one request,
|
|
153
|
+
streaming a file object without buffering it in memory:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
with open("data.parquet", "rb") as f:
|
|
157
|
+
resp = uploads.upload_stream(f, content_type="application/parquet")
|
|
158
|
+
print(resp.id)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Note `upload_file` shadows the generated raw-body `upload_file(body=...)`; that
|
|
162
|
+
raw operation is still reachable at
|
|
163
|
+
`hotdata.api.uploads_api.UploadsApi.upload_file`.
|
|
164
|
+
|
|
110
165
|
## API reference
|
|
111
166
|
|
|
112
167
|
Generated Markdown for every operation and model is in [`docs/`](https://github.com/hotdata-dev/sdk-python/tree/main/docs):
|
|
@@ -89,6 +89,61 @@ with ApiClient(Configuration(api_key="...", workspace_id="...")) as client:
|
|
|
89
89
|
|
|
90
90
|
Both methods accept `offset` and `limit` for pagination. They raise `hotdata.arrow.ResultNotReadyError` if the result is still pending or processing — poll `results.get_result(result_id)` until `status == "ready"` first.
|
|
91
91
|
|
|
92
|
+
## File uploads
|
|
93
|
+
|
|
94
|
+
`hotdata.uploads.UploadsApi` (also the default `hotdata.UploadsApi`) adds
|
|
95
|
+
`upload_file`, which uploads a local file **directly to object storage** and
|
|
96
|
+
finalizes it in one call. It opens an upload session, `PUT`s the bytes straight
|
|
97
|
+
to storage — a single `PUT` for a small file, concurrent part `PUT`s for a large
|
|
98
|
+
one — then finalizes. The bytes never round-trip through the API.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from hotdata import ApiClient, Configuration, UploadsApi
|
|
102
|
+
|
|
103
|
+
with ApiClient(Configuration(api_key="...", workspace_id="...")) as client:
|
|
104
|
+
uploads = UploadsApi(client)
|
|
105
|
+
|
|
106
|
+
finalized = uploads.upload_file(
|
|
107
|
+
"data.parquet",
|
|
108
|
+
content_type="application/parquet",
|
|
109
|
+
progress=lambda done, total: print(f"{done}/{total} bytes"),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Pass finalized.upload_id to the managed-table load endpoint.
|
|
113
|
+
print(finalized.upload_id)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`upload_file` accepts a path, raw `bytes`, or a seekable binary file object
|
|
117
|
+
(`size` is inferred for all three; a file object is read from its current
|
|
118
|
+
position to the end). The SDK picks single vs. multipart from the size,
|
|
119
|
+
auto-scales the part size, and bounds part concurrency to a peak-memory budget
|
|
120
|
+
(override with `part_size` / `max_concurrency` / `part_retry`). Storage `PUT`s go
|
|
121
|
+
through a dedicated, header-isolated connection pool, so the SDK's auth and
|
|
122
|
+
workspace headers never reach object storage (which would otherwise reject the
|
|
123
|
+
upload). Finalize is sent with retries disabled so the exactly-once call is never
|
|
124
|
+
accidentally replayed.
|
|
125
|
+
|
|
126
|
+
Failures surface as a typed hierarchy under `hotdata.uploads.UploadError`:
|
|
127
|
+
`StorageError` (storage returned a non-2xx), `StorageTransportError` (the PUT
|
|
128
|
+
failed before any response), `MissingETagError`, `MalformedSessionError`, and
|
|
129
|
+
`SizeLimitError`. Opening the session or finalizing raises the usual
|
|
130
|
+
`hotdata.exceptions.ApiException` — for example a `501` `PRESIGN_UNSUPPORTED`,
|
|
131
|
+
meaning the backend cannot issue upload URLs.
|
|
132
|
+
|
|
133
|
+
For that fallback (or to upload from a non-seekable stream), use `upload_stream`,
|
|
134
|
+
which sends the bytes to the legacy `POST /v1/files` endpoint in one request,
|
|
135
|
+
streaming a file object without buffering it in memory:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
with open("data.parquet", "rb") as f:
|
|
139
|
+
resp = uploads.upload_stream(f, content_type="application/parquet")
|
|
140
|
+
print(resp.id)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Note `upload_file` shadows the generated raw-body `upload_file(body=...)`; that
|
|
144
|
+
raw operation is still reachable at
|
|
145
|
+
`hotdata.api.uploads_api.UploadsApi.upload_file`.
|
|
146
|
+
|
|
92
147
|
## API reference
|
|
93
148
|
|
|
94
149
|
Generated Markdown for every operation and model is in [`docs/`](https://github.com/hotdata-dev/sdk-python/tree/main/docs):
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"""
|
|
6
6
|
Hotdata API
|
|
7
7
|
|
|
8
|
-
Powerful data platform API for
|
|
8
|
+
Powerful data platform API for managed databases, queries, and analytics.
|
|
9
9
|
|
|
10
10
|
The version of the OpenAPI document: 1.0.0
|
|
11
11
|
Contact: developers@hotdata.dev
|
|
@@ -28,7 +28,6 @@ __all__ = [
|
|
|
28
28
|
"ConnectionsApi",
|
|
29
29
|
"DatabaseContextApi",
|
|
30
30
|
"DatabasesApi",
|
|
31
|
-
"DatasetsApi",
|
|
32
31
|
"EmbeddingProvidersApi",
|
|
33
32
|
"IndexesApi",
|
|
34
33
|
"InformationSchemaApi",
|
|
@@ -40,6 +39,7 @@ __all__ = [
|
|
|
40
39
|
"SavedQueriesApi",
|
|
41
40
|
"SecretsApi",
|
|
42
41
|
"UploadsApi",
|
|
42
|
+
"UsageApi",
|
|
43
43
|
"WorkspacesApi",
|
|
44
44
|
"ApiResponse",
|
|
45
45
|
"ApiClient",
|
|
@@ -57,6 +57,8 @@ __all__ = [
|
|
|
57
57
|
"ApiErrorResponse",
|
|
58
58
|
"AsyncQueryResponse",
|
|
59
59
|
"AttachDatabaseCatalogRequest",
|
|
60
|
+
"BatchCreateUploadRequest",
|
|
61
|
+
"BatchCreateUploadResponse",
|
|
60
62
|
"BooleanProfileDetail",
|
|
61
63
|
"CategoricalProfileDetail",
|
|
62
64
|
"CategoryValueInfo",
|
|
@@ -68,7 +70,6 @@ __all__ = [
|
|
|
68
70
|
"ColumnProfileDetailOneOf3",
|
|
69
71
|
"ColumnProfileDetailOneOf4",
|
|
70
72
|
"ColumnProfileInfo",
|
|
71
|
-
"ColumnTypeSpec",
|
|
72
73
|
"ConnectionHealthResponse",
|
|
73
74
|
"ConnectionInfo",
|
|
74
75
|
"ConnectionRefreshResult",
|
|
@@ -79,14 +80,13 @@ __all__ = [
|
|
|
79
80
|
"CreateConnectionResponse",
|
|
80
81
|
"CreateDatabaseRequest",
|
|
81
82
|
"CreateDatabaseResponse",
|
|
82
|
-
"CreateDatasetRequest",
|
|
83
|
-
"CreateDatasetResponse",
|
|
84
83
|
"CreateEmbeddingProviderRequest",
|
|
85
84
|
"CreateEmbeddingProviderResponse",
|
|
86
85
|
"CreateIndexRequest",
|
|
87
86
|
"CreateSavedQueryRequest",
|
|
88
87
|
"CreateSecretRequest",
|
|
89
88
|
"CreateSecretResponse",
|
|
89
|
+
"CreateUploadRequest",
|
|
90
90
|
"CreateWorkspaceRequest",
|
|
91
91
|
"CreateWorkspaceResponse",
|
|
92
92
|
"DatabaseAttachmentInfo",
|
|
@@ -95,29 +95,21 @@ __all__ = [
|
|
|
95
95
|
"DatabaseDefaultTableDecl",
|
|
96
96
|
"DatabaseDetailResponse",
|
|
97
97
|
"DatabaseSummary",
|
|
98
|
-
"DatasetSource",
|
|
99
|
-
"DatasetSourceOneOf",
|
|
100
|
-
"DatasetSourceOneOf1",
|
|
101
|
-
"DatasetSourceOneOf2",
|
|
102
|
-
"DatasetSourceOneOf3",
|
|
103
|
-
"DatasetSourceOneOf4",
|
|
104
|
-
"DatasetSummary",
|
|
105
|
-
"DatasetVersionSummary",
|
|
106
98
|
"DiscoveryStatus",
|
|
107
99
|
"EmbeddingProviderResponse",
|
|
108
100
|
"Error",
|
|
109
101
|
"ExecuteSavedQueryRequest",
|
|
102
|
+
"FinalizeUploadPart",
|
|
103
|
+
"FinalizeUploadRequest",
|
|
104
|
+
"FinalizeUploadResponse",
|
|
110
105
|
"GetConnectionResponse",
|
|
111
106
|
"GetDatabaseContextResponse",
|
|
112
|
-
"GetDatasetResponse",
|
|
113
107
|
"GetResultResponse",
|
|
114
108
|
"GetSecretResponse",
|
|
115
109
|
"IndexEntryResponse",
|
|
116
110
|
"IndexInfoResponse",
|
|
117
111
|
"IndexStatus",
|
|
118
112
|
"InformationSchemaResponse",
|
|
119
|
-
"InlineData",
|
|
120
|
-
"InlineDatasetSource",
|
|
121
113
|
"JobResult",
|
|
122
114
|
"JobStatus",
|
|
123
115
|
"JobStatusResponse",
|
|
@@ -126,8 +118,6 @@ __all__ = [
|
|
|
126
118
|
"ListConnectionsResponse",
|
|
127
119
|
"ListDatabaseContextsResponse",
|
|
128
120
|
"ListDatabasesResponse",
|
|
129
|
-
"ListDatasetVersionsResponse",
|
|
130
|
-
"ListDatasetsResponse",
|
|
131
121
|
"ListEmbeddingProvidersResponse",
|
|
132
122
|
"ListIndexesPageResponse",
|
|
133
123
|
"ListIndexesResponse",
|
|
@@ -147,19 +137,16 @@ __all__ = [
|
|
|
147
137
|
"QueryRequest",
|
|
148
138
|
"QueryResponse",
|
|
149
139
|
"QueryRunInfo",
|
|
150
|
-
"RefreshDatasetResponse",
|
|
151
140
|
"RefreshRequest",
|
|
152
141
|
"RefreshResponse",
|
|
153
142
|
"RefreshWarning",
|
|
154
143
|
"ResultInfo",
|
|
155
144
|
"ResultsFormatQuery",
|
|
156
|
-
"SavedQueryDatasetSource",
|
|
157
145
|
"SavedQueryDetail",
|
|
158
146
|
"SavedQuerySummary",
|
|
159
147
|
"SavedQueryVersionInfo",
|
|
160
148
|
"SchemaRefreshResult",
|
|
161
149
|
"SecretMetadataResponse",
|
|
162
|
-
"SqlQueryDatasetSource",
|
|
163
150
|
"SubmitJobResponse",
|
|
164
151
|
"TableInfo",
|
|
165
152
|
"TableProfileResponse",
|
|
@@ -167,21 +154,19 @@ __all__ = [
|
|
|
167
154
|
"TableRefreshResult",
|
|
168
155
|
"TemporalProfileDetail",
|
|
169
156
|
"TextProfileDetail",
|
|
170
|
-
"UpdateDatasetRequest",
|
|
171
|
-
"UpdateDatasetResponse",
|
|
172
157
|
"UpdateEmbeddingProviderRequest",
|
|
173
158
|
"UpdateEmbeddingProviderResponse",
|
|
174
159
|
"UpdateSavedQueryRequest",
|
|
175
160
|
"UpdateSecretRequest",
|
|
176
161
|
"UpdateSecretResponse",
|
|
177
|
-
"UploadDatasetSource",
|
|
178
162
|
"UploadInfo",
|
|
179
163
|
"UploadResponse",
|
|
164
|
+
"UploadSessionResponse",
|
|
180
165
|
"UpsertDatabaseContextRequest",
|
|
181
166
|
"UpsertDatabaseContextResponse",
|
|
182
|
-
"UrlDatasetSource",
|
|
183
167
|
"WorkspaceDetail",
|
|
184
168
|
"WorkspaceListItem",
|
|
169
|
+
"WorkspaceUsageResponse",
|
|
185
170
|
]
|
|
186
171
|
|
|
187
172
|
# import apis into sdk package
|
|
@@ -189,7 +174,6 @@ from hotdata.api.connection_types_api import ConnectionTypesApi as ConnectionTyp
|
|
|
189
174
|
from hotdata.api.connections_api import ConnectionsApi as ConnectionsApi
|
|
190
175
|
from hotdata.api.database_context_api import DatabaseContextApi as DatabaseContextApi
|
|
191
176
|
from hotdata.api.databases_api import DatabasesApi as DatabasesApi
|
|
192
|
-
from hotdata.api.datasets_api import DatasetsApi as DatasetsApi
|
|
193
177
|
from hotdata.api.embedding_providers_api import EmbeddingProvidersApi as EmbeddingProvidersApi
|
|
194
178
|
from hotdata.api.indexes_api import IndexesApi as IndexesApi
|
|
195
179
|
from hotdata.api.information_schema_api import InformationSchemaApi as InformationSchemaApi
|
|
@@ -201,6 +185,7 @@ from hotdata.api.results_api import ResultsApi as ResultsApi
|
|
|
201
185
|
from hotdata.api.saved_queries_api import SavedQueriesApi as SavedQueriesApi
|
|
202
186
|
from hotdata.api.secrets_api import SecretsApi as SecretsApi
|
|
203
187
|
from hotdata.api.uploads_api import UploadsApi as UploadsApi
|
|
188
|
+
from hotdata.api.usage_api import UsageApi as UsageApi
|
|
204
189
|
from hotdata.api.workspaces_api import WorkspacesApi as WorkspacesApi
|
|
205
190
|
|
|
206
191
|
# import ApiClient
|
|
@@ -222,6 +207,8 @@ from hotdata.models.api_error_detail import ApiErrorDetail as ApiErrorDetail
|
|
|
222
207
|
from hotdata.models.api_error_response import ApiErrorResponse as ApiErrorResponse
|
|
223
208
|
from hotdata.models.async_query_response import AsyncQueryResponse as AsyncQueryResponse
|
|
224
209
|
from hotdata.models.attach_database_catalog_request import AttachDatabaseCatalogRequest as AttachDatabaseCatalogRequest
|
|
210
|
+
from hotdata.models.batch_create_upload_request import BatchCreateUploadRequest as BatchCreateUploadRequest
|
|
211
|
+
from hotdata.models.batch_create_upload_response import BatchCreateUploadResponse as BatchCreateUploadResponse
|
|
225
212
|
from hotdata.models.boolean_profile_detail import BooleanProfileDetail as BooleanProfileDetail
|
|
226
213
|
from hotdata.models.categorical_profile_detail import CategoricalProfileDetail as CategoricalProfileDetail
|
|
227
214
|
from hotdata.models.category_value_info import CategoryValueInfo as CategoryValueInfo
|
|
@@ -233,7 +220,6 @@ from hotdata.models.column_profile_detail_one_of2 import ColumnProfileDetailOneO
|
|
|
233
220
|
from hotdata.models.column_profile_detail_one_of3 import ColumnProfileDetailOneOf3 as ColumnProfileDetailOneOf3
|
|
234
221
|
from hotdata.models.column_profile_detail_one_of4 import ColumnProfileDetailOneOf4 as ColumnProfileDetailOneOf4
|
|
235
222
|
from hotdata.models.column_profile_info import ColumnProfileInfo as ColumnProfileInfo
|
|
236
|
-
from hotdata.models.column_type_spec import ColumnTypeSpec as ColumnTypeSpec
|
|
237
223
|
from hotdata.models.connection_health_response import ConnectionHealthResponse as ConnectionHealthResponse
|
|
238
224
|
from hotdata.models.connection_info import ConnectionInfo as ConnectionInfo
|
|
239
225
|
from hotdata.models.connection_refresh_result import ConnectionRefreshResult as ConnectionRefreshResult
|
|
@@ -244,14 +230,13 @@ from hotdata.models.create_connection_request import CreateConnectionRequest as
|
|
|
244
230
|
from hotdata.models.create_connection_response import CreateConnectionResponse as CreateConnectionResponse
|
|
245
231
|
from hotdata.models.create_database_request import CreateDatabaseRequest as CreateDatabaseRequest
|
|
246
232
|
from hotdata.models.create_database_response import CreateDatabaseResponse as CreateDatabaseResponse
|
|
247
|
-
from hotdata.models.create_dataset_request import CreateDatasetRequest as CreateDatasetRequest
|
|
248
|
-
from hotdata.models.create_dataset_response import CreateDatasetResponse as CreateDatasetResponse
|
|
249
233
|
from hotdata.models.create_embedding_provider_request import CreateEmbeddingProviderRequest as CreateEmbeddingProviderRequest
|
|
250
234
|
from hotdata.models.create_embedding_provider_response import CreateEmbeddingProviderResponse as CreateEmbeddingProviderResponse
|
|
251
235
|
from hotdata.models.create_index_request import CreateIndexRequest as CreateIndexRequest
|
|
252
236
|
from hotdata.models.create_saved_query_request import CreateSavedQueryRequest as CreateSavedQueryRequest
|
|
253
237
|
from hotdata.models.create_secret_request import CreateSecretRequest as CreateSecretRequest
|
|
254
238
|
from hotdata.models.create_secret_response import CreateSecretResponse as CreateSecretResponse
|
|
239
|
+
from hotdata.models.create_upload_request import CreateUploadRequest as CreateUploadRequest
|
|
255
240
|
from hotdata.models.create_workspace_request import CreateWorkspaceRequest as CreateWorkspaceRequest
|
|
256
241
|
from hotdata.models.create_workspace_response import CreateWorkspaceResponse as CreateWorkspaceResponse
|
|
257
242
|
from hotdata.models.database_attachment_info import DatabaseAttachmentInfo as DatabaseAttachmentInfo
|
|
@@ -260,29 +245,21 @@ from hotdata.models.database_default_schema_decl import DatabaseDefaultSchemaDec
|
|
|
260
245
|
from hotdata.models.database_default_table_decl import DatabaseDefaultTableDecl as DatabaseDefaultTableDecl
|
|
261
246
|
from hotdata.models.database_detail_response import DatabaseDetailResponse as DatabaseDetailResponse
|
|
262
247
|
from hotdata.models.database_summary import DatabaseSummary as DatabaseSummary
|
|
263
|
-
from hotdata.models.dataset_source import DatasetSource as DatasetSource
|
|
264
|
-
from hotdata.models.dataset_source_one_of import DatasetSourceOneOf as DatasetSourceOneOf
|
|
265
|
-
from hotdata.models.dataset_source_one_of1 import DatasetSourceOneOf1 as DatasetSourceOneOf1
|
|
266
|
-
from hotdata.models.dataset_source_one_of2 import DatasetSourceOneOf2 as DatasetSourceOneOf2
|
|
267
|
-
from hotdata.models.dataset_source_one_of3 import DatasetSourceOneOf3 as DatasetSourceOneOf3
|
|
268
|
-
from hotdata.models.dataset_source_one_of4 import DatasetSourceOneOf4 as DatasetSourceOneOf4
|
|
269
|
-
from hotdata.models.dataset_summary import DatasetSummary as DatasetSummary
|
|
270
|
-
from hotdata.models.dataset_version_summary import DatasetVersionSummary as DatasetVersionSummary
|
|
271
248
|
from hotdata.models.discovery_status import DiscoveryStatus as DiscoveryStatus
|
|
272
249
|
from hotdata.models.embedding_provider_response import EmbeddingProviderResponse as EmbeddingProviderResponse
|
|
273
250
|
from hotdata.models.error import Error as Error
|
|
274
251
|
from hotdata.models.execute_saved_query_request import ExecuteSavedQueryRequest as ExecuteSavedQueryRequest
|
|
252
|
+
from hotdata.models.finalize_upload_part import FinalizeUploadPart as FinalizeUploadPart
|
|
253
|
+
from hotdata.models.finalize_upload_request import FinalizeUploadRequest as FinalizeUploadRequest
|
|
254
|
+
from hotdata.models.finalize_upload_response import FinalizeUploadResponse as FinalizeUploadResponse
|
|
275
255
|
from hotdata.models.get_connection_response import GetConnectionResponse as GetConnectionResponse
|
|
276
256
|
from hotdata.models.get_database_context_response import GetDatabaseContextResponse as GetDatabaseContextResponse
|
|
277
|
-
from hotdata.models.get_dataset_response import GetDatasetResponse as GetDatasetResponse
|
|
278
257
|
from hotdata.models.get_result_response import GetResultResponse as GetResultResponse
|
|
279
258
|
from hotdata.models.get_secret_response import GetSecretResponse as GetSecretResponse
|
|
280
259
|
from hotdata.models.index_entry_response import IndexEntryResponse as IndexEntryResponse
|
|
281
260
|
from hotdata.models.index_info_response import IndexInfoResponse as IndexInfoResponse
|
|
282
261
|
from hotdata.models.index_status import IndexStatus as IndexStatus
|
|
283
262
|
from hotdata.models.information_schema_response import InformationSchemaResponse as InformationSchemaResponse
|
|
284
|
-
from hotdata.models.inline_data import InlineData as InlineData
|
|
285
|
-
from hotdata.models.inline_dataset_source import InlineDatasetSource as InlineDatasetSource
|
|
286
263
|
from hotdata.models.job_result import JobResult as JobResult
|
|
287
264
|
from hotdata.models.job_status import JobStatus as JobStatus
|
|
288
265
|
from hotdata.models.job_status_response import JobStatusResponse as JobStatusResponse
|
|
@@ -291,8 +268,6 @@ from hotdata.models.list_connection_types_response import ListConnectionTypesRes
|
|
|
291
268
|
from hotdata.models.list_connections_response import ListConnectionsResponse as ListConnectionsResponse
|
|
292
269
|
from hotdata.models.list_database_contexts_response import ListDatabaseContextsResponse as ListDatabaseContextsResponse
|
|
293
270
|
from hotdata.models.list_databases_response import ListDatabasesResponse as ListDatabasesResponse
|
|
294
|
-
from hotdata.models.list_dataset_versions_response import ListDatasetVersionsResponse as ListDatasetVersionsResponse
|
|
295
|
-
from hotdata.models.list_datasets_response import ListDatasetsResponse as ListDatasetsResponse
|
|
296
271
|
from hotdata.models.list_embedding_providers_response import ListEmbeddingProvidersResponse as ListEmbeddingProvidersResponse
|
|
297
272
|
from hotdata.models.list_indexes_page_response import ListIndexesPageResponse as ListIndexesPageResponse
|
|
298
273
|
from hotdata.models.list_indexes_response import ListIndexesResponse as ListIndexesResponse
|
|
@@ -312,19 +287,16 @@ from hotdata.models.numeric_profile_detail import NumericProfileDetail as Numeri
|
|
|
312
287
|
from hotdata.models.query_request import QueryRequest as QueryRequest
|
|
313
288
|
from hotdata.models.query_response import QueryResponse as QueryResponse
|
|
314
289
|
from hotdata.models.query_run_info import QueryRunInfo as QueryRunInfo
|
|
315
|
-
from hotdata.models.refresh_dataset_response import RefreshDatasetResponse as RefreshDatasetResponse
|
|
316
290
|
from hotdata.models.refresh_request import RefreshRequest as RefreshRequest
|
|
317
291
|
from hotdata.models.refresh_response import RefreshResponse as RefreshResponse
|
|
318
292
|
from hotdata.models.refresh_warning import RefreshWarning as RefreshWarning
|
|
319
293
|
from hotdata.models.result_info import ResultInfo as ResultInfo
|
|
320
294
|
from hotdata.models.results_format_query import ResultsFormatQuery as ResultsFormatQuery
|
|
321
|
-
from hotdata.models.saved_query_dataset_source import SavedQueryDatasetSource as SavedQueryDatasetSource
|
|
322
295
|
from hotdata.models.saved_query_detail import SavedQueryDetail as SavedQueryDetail
|
|
323
296
|
from hotdata.models.saved_query_summary import SavedQuerySummary as SavedQuerySummary
|
|
324
297
|
from hotdata.models.saved_query_version_info import SavedQueryVersionInfo as SavedQueryVersionInfo
|
|
325
298
|
from hotdata.models.schema_refresh_result import SchemaRefreshResult as SchemaRefreshResult
|
|
326
299
|
from hotdata.models.secret_metadata_response import SecretMetadataResponse as SecretMetadataResponse
|
|
327
|
-
from hotdata.models.sql_query_dataset_source import SqlQueryDatasetSource as SqlQueryDatasetSource
|
|
328
300
|
from hotdata.models.submit_job_response import SubmitJobResponse as SubmitJobResponse
|
|
329
301
|
from hotdata.models.table_info import TableInfo as TableInfo
|
|
330
302
|
from hotdata.models.table_profile_response import TableProfileResponse as TableProfileResponse
|
|
@@ -332,27 +304,27 @@ from hotdata.models.table_refresh_error import TableRefreshError as TableRefresh
|
|
|
332
304
|
from hotdata.models.table_refresh_result import TableRefreshResult as TableRefreshResult
|
|
333
305
|
from hotdata.models.temporal_profile_detail import TemporalProfileDetail as TemporalProfileDetail
|
|
334
306
|
from hotdata.models.text_profile_detail import TextProfileDetail as TextProfileDetail
|
|
335
|
-
from hotdata.models.update_dataset_request import UpdateDatasetRequest as UpdateDatasetRequest
|
|
336
|
-
from hotdata.models.update_dataset_response import UpdateDatasetResponse as UpdateDatasetResponse
|
|
337
307
|
from hotdata.models.update_embedding_provider_request import UpdateEmbeddingProviderRequest as UpdateEmbeddingProviderRequest
|
|
338
308
|
from hotdata.models.update_embedding_provider_response import UpdateEmbeddingProviderResponse as UpdateEmbeddingProviderResponse
|
|
339
309
|
from hotdata.models.update_saved_query_request import UpdateSavedQueryRequest as UpdateSavedQueryRequest
|
|
340
310
|
from hotdata.models.update_secret_request import UpdateSecretRequest as UpdateSecretRequest
|
|
341
311
|
from hotdata.models.update_secret_response import UpdateSecretResponse as UpdateSecretResponse
|
|
342
|
-
from hotdata.models.upload_dataset_source import UploadDatasetSource as UploadDatasetSource
|
|
343
312
|
from hotdata.models.upload_info import UploadInfo as UploadInfo
|
|
344
313
|
from hotdata.models.upload_response import UploadResponse as UploadResponse
|
|
314
|
+
from hotdata.models.upload_session_response import UploadSessionResponse as UploadSessionResponse
|
|
345
315
|
from hotdata.models.upsert_database_context_request import UpsertDatabaseContextRequest as UpsertDatabaseContextRequest
|
|
346
316
|
from hotdata.models.upsert_database_context_response import UpsertDatabaseContextResponse as UpsertDatabaseContextResponse
|
|
347
|
-
from hotdata.models.url_dataset_source import UrlDatasetSource as UrlDatasetSource
|
|
348
317
|
from hotdata.models.workspace_detail import WorkspaceDetail as WorkspaceDetail
|
|
349
318
|
from hotdata.models.workspace_list_item import WorkspaceListItem as WorkspaceListItem
|
|
319
|
+
from hotdata.models.workspace_usage_response import WorkspaceUsageResponse as WorkspaceUsageResponse
|
|
350
320
|
|
|
351
321
|
|
|
352
322
|
# --- hand-applied: prefer the enhanced clients over the generated ones
|
|
353
323
|
# (re-applied by scripts/patch_query_exports.py after regeneration).
|
|
354
324
|
# hotdata.query.QueryApi adds 429 retry + truncation auto-follow;
|
|
355
|
-
# hotdata.arrow.ResultsApi adds Arrow IPC result fetch
|
|
356
|
-
#
|
|
325
|
+
# hotdata.arrow.ResultsApi adds Arrow IPC result fetch;
|
|
326
|
+
# hotdata.uploads.UploadsApi adds transparent presigned direct-to-storage
|
|
327
|
+
# uploads. The raw generated classes remain importable from hotdata.api.*.
|
|
357
328
|
from hotdata.query import QueryApi as QueryApi # noqa: E402,F811
|
|
358
329
|
from hotdata.arrow import ResultsApi as ResultsApi # noqa: E402,F811
|
|
330
|
+
from hotdata.uploads import UploadsApi as UploadsApi # noqa: E402,F811
|
|
@@ -28,6 +28,11 @@ Key behaviors:
|
|
|
28
28
|
* **Refresh, then re-mint** -- prefer the refresh token when available; on
|
|
29
29
|
refresh failure, re-mint from the held API token (always possible since the
|
|
30
30
|
SDK holds it). Matches the CLI.
|
|
31
|
+
* **Transient-failure retry** -- a momentary ``5xx`` or a transport error on the
|
|
32
|
+
token endpoint is retried with bounded exponential backoff + jitter
|
|
33
|
+
(``_MAX_ATTEMPTS`` total) before giving up, so a brief server-side blip does
|
|
34
|
+
not fail the caller (#113). A ``4xx`` is never retried -- a bad/expired
|
|
35
|
+
credential is not transient.
|
|
31
36
|
* **TLS/proxy reuse** -- the exchange call reuses the SDK's configured TLS,
|
|
32
37
|
client cert and proxy settings (see :func:`_pool_from_config`) so it behaves
|
|
33
38
|
like every other SDK request, with a bounded timeout so a stalled token
|
|
@@ -36,6 +41,7 @@ Key behaviors:
|
|
|
36
41
|
|
|
37
42
|
import json
|
|
38
43
|
import os
|
|
44
|
+
import random
|
|
39
45
|
import ssl
|
|
40
46
|
import threading
|
|
41
47
|
import time
|
|
@@ -47,6 +53,15 @@ _LEEWAY = 30 # refresh when <30s of life remains
|
|
|
47
53
|
_TIMEOUT = 30.0 # seconds -- never let a stalled token endpoint hang every request
|
|
48
54
|
_CLIENT_ID = "hotdata-python-sdk"
|
|
49
55
|
|
|
56
|
+
# Bounded retry of *transient* token-exchange failures (#113). A momentary 5xx
|
|
57
|
+
# or a transport error (connection/read failure) on the token endpoint should
|
|
58
|
+
# not fail the caller outright -- an immediate re-attempt typically succeeds.
|
|
59
|
+
# We retry those, but never a 4xx (a bad/expired credential is not transient).
|
|
60
|
+
_MAX_ATTEMPTS = 3 # one initial attempt + up to two retries
|
|
61
|
+
_BACKOFF_BASE = 0.1 # seconds -- first retry waits ~this, doubling thereafter
|
|
62
|
+
_BACKOFF_MAX = 2.0 # cap on a single backoff so a flapping host can't stall us
|
|
63
|
+
_BACKOFF_JITTER = 0.5 # additive jitter fraction (delay in [base, 1.5*base]) to spread retries out
|
|
64
|
+
|
|
50
65
|
# Env var that disables exchange entirely. Used as a hard escape hatch during
|
|
51
66
|
# the rollout window and for local/dev setups. Only affirmative values opt out
|
|
52
67
|
# (see _DISABLE_VALUES) so that ``=0`` / ``=false`` do NOT silently disable it.
|
|
@@ -109,8 +124,11 @@ def _pool_from_config(configuration):
|
|
|
109
124
|
pool_args["server_hostname"] = configuration.tls_server_name
|
|
110
125
|
if configuration.socket_options is not None:
|
|
111
126
|
pool_args["socket_options"] = configuration.socket_options
|
|
112
|
-
# `retries`/`maxsize` are intentionally not mirrored:
|
|
113
|
-
#
|
|
127
|
+
# `retries`/`maxsize` are intentionally not mirrored: urllib3's own Retry is
|
|
128
|
+
# left at urllib3's default (it does not retry POST status codes anyway), so
|
|
129
|
+
# we do not inherit the SDK's connection-reset retry here. Transient-failure
|
|
130
|
+
# retry for the exchange is handled explicitly in `_TokenManager._exchange`
|
|
131
|
+
# (5xx + transport errors, bounded backoff), where we can keep 4xx fatal.
|
|
114
132
|
|
|
115
133
|
if configuration.proxy:
|
|
116
134
|
if _is_socks_proxy_url(configuration.proxy):
|
|
@@ -125,6 +143,29 @@ def _pool_from_config(configuration):
|
|
|
125
143
|
return urllib3.PoolManager(**pool_args)
|
|
126
144
|
|
|
127
145
|
|
|
146
|
+
def _is_transient_status(status):
|
|
147
|
+
"""True for HTTP statuses worth retrying (server-side, likely momentary).
|
|
148
|
+
|
|
149
|
+
Only 5xx is transient: the request reached the server but it failed to
|
|
150
|
+
handle it (e.g. a brief ``500``/``503``). A 4xx -- including ``400``/``401``
|
|
151
|
+
from a bad or expired credential -- is a definitive rejection that a retry
|
|
152
|
+
will not fix, so it is never retried.
|
|
153
|
+
"""
|
|
154
|
+
return 500 <= status < 600
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _backoff_delay(attempt):
|
|
158
|
+
"""Seconds to sleep before retry number ``attempt`` (0 = first retry).
|
|
159
|
+
|
|
160
|
+
Exponential growth from ``_BACKOFF_BASE`` (doubling per attempt) capped at
|
|
161
|
+
``_BACKOFF_MAX``, plus additive jitter in ``[0, _BACKOFF_JITTER * base]`` so
|
|
162
|
+
concurrent clients retrying the same blip don't resynchronize into a thundering
|
|
163
|
+
herd. Mirrors the Rust SDK's ``backoff_delay``.
|
|
164
|
+
"""
|
|
165
|
+
base = min(_BACKOFF_BASE * (2 ** attempt), _BACKOFF_MAX)
|
|
166
|
+
return base * (1 + _BACKOFF_JITTER * random.random())
|
|
167
|
+
|
|
168
|
+
|
|
128
169
|
class _TokenManager:
|
|
129
170
|
"""Exchanges an API token for short-lived JWTs and keeps them fresh.
|
|
130
171
|
|
|
@@ -134,10 +175,11 @@ class _TokenManager:
|
|
|
134
175
|
exchanged.
|
|
135
176
|
"""
|
|
136
177
|
|
|
137
|
-
def __init__(self, credential, configuration, pool=None):
|
|
178
|
+
def __init__(self, credential, configuration, pool=None, sleep=None):
|
|
138
179
|
self._credential = credential
|
|
139
180
|
self._config = configuration # read host + TLS lazily at mint time
|
|
140
181
|
self._pool = pool # injected in tests; else built from config TLS
|
|
182
|
+
self._sleep = sleep or time.sleep # injected in tests so retry backoff is instant
|
|
141
183
|
self._lock = threading.Lock()
|
|
142
184
|
self._jwt = None
|
|
143
185
|
self._exp = 0.0
|
|
@@ -168,8 +210,18 @@ class _TokenManager:
|
|
|
168
210
|
"""
|
|
169
211
|
if not self._needs_exchange:
|
|
170
212
|
return self._credential # already a JWT (or opt-out) -> unchanged
|
|
213
|
+
# Lock-free fast path: a still-valid cached JWT needs no mint and must
|
|
214
|
+
# not block behind an in-flight (possibly retrying) mint that holds the
|
|
215
|
+
# lock for up to several timeouts. Attribute reads are atomic under the
|
|
216
|
+
# GIL; the worst a benign jwt/exp race can do is fall through to re-check
|
|
217
|
+
# under the lock, and the _LEEWAY margin keeps a token read here valid on
|
|
218
|
+
# the wire even if it is about to be rotated.
|
|
219
|
+
jwt = self._jwt
|
|
220
|
+
if jwt and time.time() < self._exp - _LEEWAY:
|
|
221
|
+
return jwt
|
|
171
222
|
with self._lock:
|
|
172
|
-
#
|
|
223
|
+
# Re-check under the lock: another thread may have minted a fresh JWT
|
|
224
|
+
# while we waited (double-checked locking).
|
|
173
225
|
if self._jwt and time.time() < self._exp - _LEEWAY:
|
|
174
226
|
return self._jwt
|
|
175
227
|
# Prefer the refresh token; on failure, drop it and re-mint below.
|
|
@@ -187,24 +239,12 @@ class _TokenManager:
|
|
|
187
239
|
# -- a non-200, a transport error, or a malformed/missing-token body --
|
|
188
240
|
# returns False so the caller re-mints from the held API token. An
|
|
189
241
|
# api_token mint instead raises TokenExchangeError on any failure, since
|
|
190
|
-
# there is no further fallback.
|
|
242
|
+
# there is no further fallback. Transient failures (5xx + transport
|
|
243
|
+
# errors) are retried inside _exchange before either outcome.
|
|
191
244
|
params["client_id"] = _CLIENT_ID
|
|
192
245
|
is_refresh = params["grant_type"] == "refresh_token"
|
|
193
246
|
try:
|
|
194
|
-
|
|
195
|
-
host = self._config.host.rstrip("/") # read host lazily -- may be set post-construct
|
|
196
|
-
resp = pool.request(
|
|
197
|
-
"POST",
|
|
198
|
-
f"{host}/v1/auth/jwt",
|
|
199
|
-
body=urlencode(params),
|
|
200
|
-
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
201
|
-
timeout=_TIMEOUT,
|
|
202
|
-
)
|
|
203
|
-
if resp.status != 200:
|
|
204
|
-
raise TokenExchangeError(
|
|
205
|
-
f"token exchange failed: {resp.status} {resp.data[:200]!r}"
|
|
206
|
-
)
|
|
207
|
-
data = json.loads(resp.data)
|
|
247
|
+
data = self._exchange(params)
|
|
208
248
|
token = data["access_token"]
|
|
209
249
|
expires_in = float(data.get("expires_in", 300))
|
|
210
250
|
except (
|
|
@@ -224,6 +264,57 @@ class _TokenManager:
|
|
|
224
264
|
self._refresh = data.get("refresh_token") or self._refresh
|
|
225
265
|
return True
|
|
226
266
|
|
|
267
|
+
def _exchange(self, params):
|
|
268
|
+
# POST the token-exchange request, retrying transient failures, and
|
|
269
|
+
# return the parsed JSON body of the 200 response.
|
|
270
|
+
#
|
|
271
|
+
# Retries 5xx responses and transport errors (urllib3 HTTPError, e.g. a
|
|
272
|
+
# connection/read failure) with bounded exponential backoff + jitter, up
|
|
273
|
+
# to _MAX_ATTEMPTS total. A 4xx is returned immediately as a fatal
|
|
274
|
+
# TokenExchangeError -- bad/expired credentials are not transient. Once
|
|
275
|
+
# the budget is exhausted, the last failure is surfaced: a 5xx as a
|
|
276
|
+
# TokenExchangeError preserving the status/body, a transport error as the
|
|
277
|
+
# raised HTTPError (which _mint wraps). JSON/missing-token errors from
|
|
278
|
+
# parsing a 200 body propagate unretried.
|
|
279
|
+
pool = self._pool or _pool_from_config(self._config) # reuses ssl_ca_cert/cert/proxy
|
|
280
|
+
host = self._config.host.rstrip("/") # read host lazily -- may be set post-construct
|
|
281
|
+
url = f"{host}/v1/auth/jwt"
|
|
282
|
+
body = urlencode(params)
|
|
283
|
+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
|
284
|
+
for attempt in range(_MAX_ATTEMPTS):
|
|
285
|
+
last = attempt == _MAX_ATTEMPTS - 1
|
|
286
|
+
try:
|
|
287
|
+
resp = pool.request(
|
|
288
|
+
"POST",
|
|
289
|
+
url,
|
|
290
|
+
body=body,
|
|
291
|
+
headers=headers,
|
|
292
|
+
timeout=_TIMEOUT,
|
|
293
|
+
# Disable urllib3's own per-request retries so this loop is
|
|
294
|
+
# the sole arbiter of the attempt budget. Otherwise urllib3's
|
|
295
|
+
# default (Retry(3)) would retry connection errors *inside*
|
|
296
|
+
# each attempt, multiplying the effective transport-attempt
|
|
297
|
+
# count well past _MAX_ATTEMPTS.
|
|
298
|
+
retries=False,
|
|
299
|
+
)
|
|
300
|
+
except urllib3.exceptions.HTTPError:
|
|
301
|
+
# Transport-level failure (connection/read error): transient, but
|
|
302
|
+
# don't retry past the budget -- re-raise for _mint to handle.
|
|
303
|
+
if last:
|
|
304
|
+
raise
|
|
305
|
+
self._sleep(_backoff_delay(attempt))
|
|
306
|
+
continue
|
|
307
|
+
if resp.status == 200:
|
|
308
|
+
return json.loads(resp.data)
|
|
309
|
+
if _is_transient_status(resp.status) and not last:
|
|
310
|
+
self._sleep(_backoff_delay(attempt))
|
|
311
|
+
continue
|
|
312
|
+
# A 4xx, or a 5xx with the retry budget exhausted: fatal, surfacing
|
|
313
|
+
# the last status/body.
|
|
314
|
+
raise TokenExchangeError(
|
|
315
|
+
f"token exchange failed: {resp.status} {resp.data[:200]!r}"
|
|
316
|
+
)
|
|
317
|
+
|
|
227
318
|
|
|
228
319
|
__all__ = [
|
|
229
320
|
"TokenExchangeError",
|