hotdata 0.9.0__tar.gz → 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hotdata-0.9.0 → hotdata-0.9.1}/PKG-INFO +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/__init__.py +14 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/_auth.py +138 -4
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/connections_api.py +9 -6
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/databases_api.py +1151 -32
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/uploads_api.py +12 -12
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/usage_api.py +3 -3
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api_client.py +93 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/__init__.py +7 -0
- hotdata-0.9.1/hotdata/models/bulk_create_databases_request.py +134 -0
- hotdata-0.9.1/hotdata/models/bulk_create_databases_result.py +95 -0
- hotdata-0.9.1/hotdata/models/column_definition.py +141 -0
- hotdata-0.9.1/hotdata/models/column_type_spec.py +103 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_connection_request.py +2 -2
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_database_response.py +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_embedding_provider_request.py +1 -6
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_index_request.py +15 -5
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_upload_request.py +3 -3
- hotdata-0.9.1/hotdata/models/database_batch_response.py +116 -0
- hotdata-0.9.1/hotdata/models/database_count_response.py +88 -0
- hotdata-0.9.1/hotdata/models/delete_database_batch_response.py +90 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/finalize_upload_part.py +3 -3
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/finalize_upload_request.py +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/index_entry_response.py +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/job_result.py +22 -8
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/job_type.py +3 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/load_managed_table_request.py +36 -7
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/mint_upload_parts_request.py +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/query_request.py +2 -2
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/query_response.py +3 -3
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/refresh_request.py +6 -6
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/upload_session_response.py +2 -2
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata.egg-info/PKG-INFO +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata.egg-info/SOURCES.txt +14 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/pyproject.toml +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_add_managed_schema_request.py +9 -11
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_add_managed_table_decl.py +8 -10
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_add_managed_table_request.py +8 -10
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_attach_database_catalog_request.py +3 -3
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_batch_create_upload_request.py +2 -20
- hotdata-0.9.1/test/test_bulk_create_databases_request.py +78 -0
- hotdata-0.9.1/test/test_bulk_create_databases_result.py +59 -0
- hotdata-0.9.1/test/test_column_definition.py +55 -0
- hotdata-0.9.1/test/test_column_type_spec.py +55 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connections_api.py +1 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_connection_request.py +7 -11
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_database_request.py +8 -10
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_embedding_provider_request.py +6 -6
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_index_request.py +5 -9
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_saved_query_request.py +6 -8
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_secret_request.py +4 -4
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_upload_request.py +6 -6
- hotdata-0.9.1/test/test_database_batch_response.py +62 -0
- hotdata-0.9.1/test/test_database_count_response.py +53 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_database_default_schema_decl.py +9 -11
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_database_default_table_decl.py +8 -10
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_databases_api.py +29 -1
- hotdata-0.9.1/test/test_delete_database_batch_response.py +55 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_finalize_upload_part.py +4 -4
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_finalize_upload_request.py +2 -2
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_information_schema_response.py +10 -10
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_job_result.py +9 -1
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_load_managed_table_request.py +9 -3
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_mint_upload_parts_request.py +2 -6
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_query_request.py +5 -5
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_table_info.py +10 -10
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_table_partition_key.py +4 -4
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_table_sort_key.py +4 -4
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_update_secret_request.py +2 -2
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_upsert_database_context_request.py +4 -4
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_auth.py +324 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/README.md +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/_retry.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/_upload_watchdog.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/__init__.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/connection_types_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/database_context_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/embedding_providers_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/indexes_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/information_schema_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/jobs_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/query_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/query_runs_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/refresh_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/results_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/saved_queries_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/secrets_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api/workspaces_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/api_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/arrow.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/configuration.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/exceptions.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/add_managed_schema_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/add_managed_table_decl.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/add_managed_table_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/api_error_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/api_error_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/async_query_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/attach_database_catalog_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/batch_create_upload_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/batch_create_upload_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/boolean_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/categorical_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/category_value_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_profile_detail_one_of.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_profile_detail_one_of1.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_profile_detail_one_of2.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_profile_detail_one_of3.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_profile_detail_one_of4.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/column_profile_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/connection_health_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/connection_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/connection_refresh_result.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/connection_schema_error.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/connection_type_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/connection_type_summary.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_connection_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_database_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_embedding_provider_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_saved_query_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_secret_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_secret_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_workspace_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/create_workspace_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/database_attachment_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/database_context_entry.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/database_default_schema_decl.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/database_default_table_decl.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/database_detail_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/database_summary.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/discovery_status.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/embedding_provider_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/error.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/execute_saved_query_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/finalize_upload_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/fork_database_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/get_connection_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/get_database_context_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/get_result_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/get_secret_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/index_info_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/index_status.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/information_schema_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/job_status.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/job_status_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_connection_types_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_connections_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_database_contexts_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_databases_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_embedding_providers_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_indexes_page_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_indexes_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_jobs_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_query_runs_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_results_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_saved_queries_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_saved_query_versions_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_secrets_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/list_workspaces_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/load_managed_table_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/managed_schema_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/managed_table_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/mint_upload_parts_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/minted_upload_part_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/numeric_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/query_run_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/refresh_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/refresh_warning.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/result_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/results_format_query.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/saved_query_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/saved_query_summary.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/saved_query_version_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/schema_refresh_result.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/secret_metadata_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/submit_job_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/table_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/table_partition_key.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/table_profile_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/table_refresh_error.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/table_refresh_result.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/table_sort_key.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/temporal_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/text_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/update_embedding_provider_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/update_embedding_provider_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/update_saved_query_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/update_secret_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/update_secret_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/upsert_database_context_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/upsert_database_context_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/workspace_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/workspace_list_item.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/models/workspace_usage_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/py.typed +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/query.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/rest.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata/uploads.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata.egg-info/dependency_links.txt +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata.egg-info/requires.txt +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/hotdata.egg-info/top_level.txt +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/setup.cfg +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_api_client_close.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_api_error_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_api_error_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_async_query_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_batch_create_upload_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_boolean_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_categorical_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_category_value_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_profile_detail_one_of.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_profile_detail_one_of1.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_profile_detail_one_of2.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_profile_detail_one_of3.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_profile_detail_one_of4.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_column_profile_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connection_health_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connection_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connection_refresh_result.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connection_schema_error.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connection_type_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connection_type_summary.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_connection_types_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_connection_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_database_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_embedding_provider_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_secret_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_workspace_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_create_workspace_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_database_attachment_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_database_context_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_database_context_entry.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_database_detail_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_database_summary.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_discovery_status.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_embedding_provider_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_embedding_providers_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_error.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_execute_saved_query_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_finalize_upload_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_fork_database_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_get_connection_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_get_database_context_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_get_result_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_get_secret_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_index_entry_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_index_info_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_index_status.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_indexes_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_information_schema_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_job_status.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_job_status_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_job_type.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_jobs_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_connection_types_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_connections_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_database_contexts_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_databases_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_embedding_providers_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_indexes_page_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_indexes_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_jobs_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_query_runs_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_results_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_saved_queries_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_saved_query_versions_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_secrets_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_list_workspaces_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_load_managed_table_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_managed_schema_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_managed_table_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_mint_upload_parts_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_minted_upload_part_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_numeric_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_query_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_query_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_query_run_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_query_runs_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_refresh_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_refresh_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_refresh_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_refresh_warning.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_result_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_results_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_results_format_query.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_saved_queries_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_saved_query_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_saved_query_summary.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_saved_query_version_info.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_schema_refresh_result.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_secret_metadata_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_secrets_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_submit_job_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_table_profile_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_table_refresh_error.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_table_refresh_result.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_temporal_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_text_profile_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_update_embedding_provider_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_update_embedding_provider_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_update_saved_query_request.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_update_secret_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_upload_session_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_uploads_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_upsert_database_context_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_usage_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_workspace_detail.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_workspace_list_item.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_workspace_usage_response.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/test/test_workspaces_api.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_arrow.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_config_auth_surface.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_query.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_retry.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_update_changelog.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_upload_codex_fixes.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_upload_multipart_resilient.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_upload_parity_fixes.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_upload_resilient.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_upload_retry_policy.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_upload_watchdog.py +0 -0
- {hotdata-0.9.0 → hotdata-0.9.1}/tests/test_uploads.py +0 -0
|
@@ -60,8 +60,11 @@ __all__ = [
|
|
|
60
60
|
"BatchCreateUploadRequest",
|
|
61
61
|
"BatchCreateUploadResponse",
|
|
62
62
|
"BooleanProfileDetail",
|
|
63
|
+
"BulkCreateDatabasesRequest",
|
|
64
|
+
"BulkCreateDatabasesResult",
|
|
63
65
|
"CategoricalProfileDetail",
|
|
64
66
|
"CategoryValueInfo",
|
|
67
|
+
"ColumnDefinition",
|
|
65
68
|
"ColumnInfo",
|
|
66
69
|
"ColumnProfileDetail",
|
|
67
70
|
"ColumnProfileDetailOneOf",
|
|
@@ -70,6 +73,7 @@ __all__ = [
|
|
|
70
73
|
"ColumnProfileDetailOneOf3",
|
|
71
74
|
"ColumnProfileDetailOneOf4",
|
|
72
75
|
"ColumnProfileInfo",
|
|
76
|
+
"ColumnTypeSpec",
|
|
73
77
|
"ConnectionHealthResponse",
|
|
74
78
|
"ConnectionInfo",
|
|
75
79
|
"ConnectionRefreshResult",
|
|
@@ -90,11 +94,14 @@ __all__ = [
|
|
|
90
94
|
"CreateWorkspaceRequest",
|
|
91
95
|
"CreateWorkspaceResponse",
|
|
92
96
|
"DatabaseAttachmentInfo",
|
|
97
|
+
"DatabaseBatchResponse",
|
|
93
98
|
"DatabaseContextEntry",
|
|
99
|
+
"DatabaseCountResponse",
|
|
94
100
|
"DatabaseDefaultSchemaDecl",
|
|
95
101
|
"DatabaseDefaultTableDecl",
|
|
96
102
|
"DatabaseDetailResponse",
|
|
97
103
|
"DatabaseSummary",
|
|
104
|
+
"DeleteDatabaseBatchResponse",
|
|
98
105
|
"DiscoveryStatus",
|
|
99
106
|
"EmbeddingProviderResponse",
|
|
100
107
|
"Error",
|
|
@@ -213,8 +220,11 @@ from hotdata.models.attach_database_catalog_request import AttachDatabaseCatalog
|
|
|
213
220
|
from hotdata.models.batch_create_upload_request import BatchCreateUploadRequest as BatchCreateUploadRequest
|
|
214
221
|
from hotdata.models.batch_create_upload_response import BatchCreateUploadResponse as BatchCreateUploadResponse
|
|
215
222
|
from hotdata.models.boolean_profile_detail import BooleanProfileDetail as BooleanProfileDetail
|
|
223
|
+
from hotdata.models.bulk_create_databases_request import BulkCreateDatabasesRequest as BulkCreateDatabasesRequest
|
|
224
|
+
from hotdata.models.bulk_create_databases_result import BulkCreateDatabasesResult as BulkCreateDatabasesResult
|
|
216
225
|
from hotdata.models.categorical_profile_detail import CategoricalProfileDetail as CategoricalProfileDetail
|
|
217
226
|
from hotdata.models.category_value_info import CategoryValueInfo as CategoryValueInfo
|
|
227
|
+
from hotdata.models.column_definition import ColumnDefinition as ColumnDefinition
|
|
218
228
|
from hotdata.models.column_info import ColumnInfo as ColumnInfo
|
|
219
229
|
from hotdata.models.column_profile_detail import ColumnProfileDetail as ColumnProfileDetail
|
|
220
230
|
from hotdata.models.column_profile_detail_one_of import ColumnProfileDetailOneOf as ColumnProfileDetailOneOf
|
|
@@ -223,6 +233,7 @@ from hotdata.models.column_profile_detail_one_of2 import ColumnProfileDetailOneO
|
|
|
223
233
|
from hotdata.models.column_profile_detail_one_of3 import ColumnProfileDetailOneOf3 as ColumnProfileDetailOneOf3
|
|
224
234
|
from hotdata.models.column_profile_detail_one_of4 import ColumnProfileDetailOneOf4 as ColumnProfileDetailOneOf4
|
|
225
235
|
from hotdata.models.column_profile_info import ColumnProfileInfo as ColumnProfileInfo
|
|
236
|
+
from hotdata.models.column_type_spec import ColumnTypeSpec as ColumnTypeSpec
|
|
226
237
|
from hotdata.models.connection_health_response import ConnectionHealthResponse as ConnectionHealthResponse
|
|
227
238
|
from hotdata.models.connection_info import ConnectionInfo as ConnectionInfo
|
|
228
239
|
from hotdata.models.connection_refresh_result import ConnectionRefreshResult as ConnectionRefreshResult
|
|
@@ -243,11 +254,14 @@ from hotdata.models.create_upload_request import CreateUploadRequest as CreateUp
|
|
|
243
254
|
from hotdata.models.create_workspace_request import CreateWorkspaceRequest as CreateWorkspaceRequest
|
|
244
255
|
from hotdata.models.create_workspace_response import CreateWorkspaceResponse as CreateWorkspaceResponse
|
|
245
256
|
from hotdata.models.database_attachment_info import DatabaseAttachmentInfo as DatabaseAttachmentInfo
|
|
257
|
+
from hotdata.models.database_batch_response import DatabaseBatchResponse as DatabaseBatchResponse
|
|
246
258
|
from hotdata.models.database_context_entry import DatabaseContextEntry as DatabaseContextEntry
|
|
259
|
+
from hotdata.models.database_count_response import DatabaseCountResponse as DatabaseCountResponse
|
|
247
260
|
from hotdata.models.database_default_schema_decl import DatabaseDefaultSchemaDecl as DatabaseDefaultSchemaDecl
|
|
248
261
|
from hotdata.models.database_default_table_decl import DatabaseDefaultTableDecl as DatabaseDefaultTableDecl
|
|
249
262
|
from hotdata.models.database_detail_response import DatabaseDetailResponse as DatabaseDetailResponse
|
|
250
263
|
from hotdata.models.database_summary import DatabaseSummary as DatabaseSummary
|
|
264
|
+
from hotdata.models.delete_database_batch_response import DeleteDatabaseBatchResponse as DeleteDatabaseBatchResponse
|
|
251
265
|
from hotdata.models.discovery_status import DiscoveryStatus as DiscoveryStatus
|
|
252
266
|
from hotdata.models.embedding_provider_response import EmbeddingProviderResponse as EmbeddingProviderResponse
|
|
253
267
|
from hotdata.models.error import Error as Error
|
|
@@ -39,6 +39,7 @@ Key behaviors:
|
|
|
39
39
|
endpoint fails fast instead of hanging every call.
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
|
+
import base64
|
|
42
43
|
import json
|
|
43
44
|
import os
|
|
44
45
|
import random
|
|
@@ -81,6 +82,32 @@ class TokenExchangeError(Exception):
|
|
|
81
82
|
"""
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
def _exp_from_jwt(token):
|
|
86
|
+
"""The `exp` claim of a compact JWT, as a UNIX timestamp, or None.
|
|
87
|
+
|
|
88
|
+
The claim is AUTHORITATIVE where `expires_in` is only a promise. A mint
|
|
89
|
+
endpoint that replays a cached token is required by RFC 6749 s4.2.2 to
|
|
90
|
+
report the REMAINING lifetime ("the value 3600 denotes that the access token
|
|
91
|
+
will expire in one hour from the time the response was generated"); one that
|
|
92
|
+
reports the original TTL instead hands back a token with seconds of life
|
|
93
|
+
stamped as good for minutes. Reading the claim costs a base64 decode and
|
|
94
|
+
removes the need to trust that.
|
|
95
|
+
|
|
96
|
+
Deliberately does NOT verify the signature: this is the SDK reading a token
|
|
97
|
+
it was just handed, to decide when to refresh it. Whether the token is
|
|
98
|
+
genuine is the server's call on the next request, and a forged `exp` can
|
|
99
|
+
only make this client refresh sooner. Any malformed token returns None and
|
|
100
|
+
the caller falls back to `expires_in`.
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
payload = token.split(".")[1]
|
|
104
|
+
payload += "=" * (-len(payload) % 4)
|
|
105
|
+
exp = json.loads(base64.urlsafe_b64decode(payload)).get("exp")
|
|
106
|
+
return float(exp) if exp is not None else None
|
|
107
|
+
except Exception: # noqa: BLE001 -- any malformed token falls back
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
84
111
|
def _is_socks_proxy_url(url):
|
|
85
112
|
# Mirror hotdata/rest.py.is_socks_proxy_url so the exchange pool routes
|
|
86
113
|
# SOCKS proxies the same way the generated REST client does.
|
|
@@ -184,6 +211,13 @@ class _TokenManager:
|
|
|
184
211
|
self._jwt = None
|
|
185
212
|
self._exp = 0.0
|
|
186
213
|
self._refresh = None
|
|
214
|
+
# The token this manager last served before it was invalidated. Lets a
|
|
215
|
+
# request that carried it be told apart from one carrying a credential
|
|
216
|
+
# this manager never issued -- see `ApiClient._remint_bearer`.
|
|
217
|
+
self._prev_jwt = None
|
|
218
|
+
# True when the last mint handed back the token we already held, i.e. the
|
|
219
|
+
# server has nothing newer to give. See `_refresh_margin`.
|
|
220
|
+
self._replayed = False
|
|
187
221
|
|
|
188
222
|
@property
|
|
189
223
|
def _needs_exchange(self):
|
|
@@ -217,12 +251,12 @@ class _TokenManager:
|
|
|
217
251
|
# under the lock, and the _LEEWAY margin keeps a token read here valid on
|
|
218
252
|
# the wire even if it is about to be rotated.
|
|
219
253
|
jwt = self._jwt
|
|
220
|
-
if jwt and time.time() < self._exp -
|
|
254
|
+
if jwt and time.time() < self._exp - self._refresh_margin():
|
|
221
255
|
return jwt
|
|
222
256
|
with self._lock:
|
|
223
257
|
# Re-check under the lock: another thread may have minted a fresh JWT
|
|
224
258
|
# while we waited (double-checked locking).
|
|
225
|
-
if self._jwt and time.time() < self._exp -
|
|
259
|
+
if self._jwt and time.time() < self._exp - self._refresh_margin():
|
|
226
260
|
return self._jwt
|
|
227
261
|
# Prefer the refresh token; on failure, drop it and re-mint below.
|
|
228
262
|
if self._refresh and not self._mint(
|
|
@@ -230,10 +264,63 @@ class _TokenManager:
|
|
|
230
264
|
):
|
|
231
265
|
self._refresh = None # refresh failed -> fall through to re-mint
|
|
232
266
|
# Re-mint from the held API token if we still lack a fresh JWT.
|
|
233
|
-
if not self._jwt or time.time() >= self._exp -
|
|
267
|
+
if not self._jwt or time.time() >= self._exp - self._refresh_margin():
|
|
234
268
|
self._mint({"grant_type": "api_token", "api_token": self._credential})
|
|
235
269
|
return self._jwt
|
|
236
270
|
|
|
271
|
+
def _refresh_margin(self):
|
|
272
|
+
"""How early to refresh: `_LEEWAY`, or nothing once the server replays.
|
|
273
|
+
|
|
274
|
+
The leeway exists to rotate a token before it dies. That only helps if a
|
|
275
|
+
mint can produce something newer -- and a server that replays a cached
|
|
276
|
+
token has just demonstrated it cannot. Refreshing anyway costs two token
|
|
277
|
+
endpoint round trips per request (the refresh grant returns the same
|
|
278
|
+
token, so `_refresh` survives and the api_token grant runs too), taken
|
|
279
|
+
under the lock, for the whole leeway window of every cycle.
|
|
280
|
+
|
|
281
|
+
Reading the honest `exp` is what exposes this: the inflated
|
|
282
|
+
`now + expires_in` never entered the window at all, because it had
|
|
283
|
+
already sailed past the real expiry -- a dead token instead of a slow
|
|
284
|
+
one. Dropping the margin for a replayed token keeps the honest expiry
|
|
285
|
+
without paying for advice the server has said it does not have, and the
|
|
286
|
+
401 retry remains the backstop for when the token really does die.
|
|
287
|
+
|
|
288
|
+
Clock skew is NOT handled here -- this margin only ever suppresses the
|
|
289
|
+
window `(_exp - _LEEWAY, _exp)`, and a skewed client's `_exp` is already
|
|
290
|
+
behind `now`, so nothing is left to suppress. `_mint` handles that case by
|
|
291
|
+
declining a claim that has already elapsed.
|
|
292
|
+
"""
|
|
293
|
+
return 0 if self._replayed else _LEEWAY
|
|
294
|
+
|
|
295
|
+
def invalidate(self, only_if=None):
|
|
296
|
+
"""Drop the cached JWT so the next `bearer_value()` mints a fresh one.
|
|
297
|
+
|
|
298
|
+
For the ONE case the proactive `_exp` check cannot cover: the server
|
|
299
|
+
rejected a token this client still believed in. Clears the refresh token
|
|
300
|
+
too -- a refresh is exactly what would hand back the same replayed JWT
|
|
301
|
+
that was just refused, so the next mint must go to the API token.
|
|
302
|
+
|
|
303
|
+
`only_if` makes it a COMPARE-AND-CLEAR, which is what keeps concurrent
|
|
304
|
+
401s from fighting. Several in-flight requests can carry the same doomed
|
|
305
|
+
token and 401 together; without the guard the second one clears the fresh
|
|
306
|
+
JWT the first just minted, and they take turns invalidating each other's
|
|
307
|
+
work. Passing the token that actually failed means a thread only clears
|
|
308
|
+
the state it observed.
|
|
309
|
+
|
|
310
|
+
Returns True when it cleared, False when another thread had already
|
|
311
|
+
replaced the token -- in which case the caller should use that one rather
|
|
312
|
+
than mint again.
|
|
313
|
+
"""
|
|
314
|
+
with self._lock:
|
|
315
|
+
if only_if is not None and self._jwt != only_if:
|
|
316
|
+
return False
|
|
317
|
+
self._prev_jwt = self._jwt
|
|
318
|
+
self._replayed = False
|
|
319
|
+
self._jwt = None
|
|
320
|
+
self._exp = 0.0
|
|
321
|
+
self._refresh = None
|
|
322
|
+
return True
|
|
323
|
+
|
|
237
324
|
def _mint(self, params):
|
|
238
325
|
# Returns True on success. The refresh path is best-effort: ANY failure
|
|
239
326
|
# -- a non-200, a transport error, or a malformed/missing-token body --
|
|
@@ -259,8 +346,55 @@ class _TokenManager:
|
|
|
259
346
|
if isinstance(exc, TokenExchangeError):
|
|
260
347
|
raise
|
|
261
348
|
raise TokenExchangeError(f"token exchange failed: {exc!r}") from exc
|
|
349
|
+
# A mint that returns the token we already hold is the server saying it
|
|
350
|
+
# has nothing newer; `_refresh_margin` stops asking again until this one
|
|
351
|
+
# has actually expired.
|
|
352
|
+
self._replayed = token == self._jwt
|
|
262
353
|
self._jwt = token
|
|
263
|
-
|
|
354
|
+
# The JWT's own `exp` WINS when it is sooner than what `expires_in`
|
|
355
|
+
# promises. A mint that replays a cached token while still reporting the
|
|
356
|
+
# full TTL would otherwise push `_exp` past the token's real expiry, and
|
|
357
|
+
# each refresh pushes it further -- so the proactive `_exp - _LEEWAY`
|
|
358
|
+
# check never fires and an expired token goes on the wire. Observed in
|
|
359
|
+
# production: four consecutive mints returned the same `jti` with
|
|
360
|
+
# `expires_in=300` each time, while the token's true remaining life fell
|
|
361
|
+
# 300s -> 289s. Any load running longer than one token lifetime then
|
|
362
|
+
# fails at whatever it happens to be doing when the token dies.
|
|
363
|
+
#
|
|
364
|
+
# `min`, not "prefer the claim": a claim FURTHER out than `expires_in`
|
|
365
|
+
# would extend the client's trust past what the server offered, which is
|
|
366
|
+
# the opposite of the guarantee wanted here.
|
|
367
|
+
now = time.time()
|
|
368
|
+
by_expires_in = now + expires_in
|
|
369
|
+
claim = _exp_from_jwt(token)
|
|
370
|
+
if claim is not None and (claim > now or self._replayed):
|
|
371
|
+
self._exp = min(by_expires_in, claim)
|
|
372
|
+
else:
|
|
373
|
+
# No readable claim, or one already elapsed on a token we had NOT seen
|
|
374
|
+
# before. Two causes produce an elapsed claim and they want opposite
|
|
375
|
+
# answers, so `_replayed` -- set just above -- separates them:
|
|
376
|
+
#
|
|
377
|
+
# replayed the server keeps handing back one cached token, and its
|
|
378
|
+
# `exp` has finally passed. The token really is dead.
|
|
379
|
+
# Honour the claim: this manager accepted the same claim as
|
|
380
|
+
# FUTURE on an earlier mint, so the clock is not the
|
|
381
|
+
# explanation, and pretending otherwise would put `_exp` at
|
|
382
|
+
# `now + 300` on a corpse -- the pre-fix behaviour, leaving
|
|
383
|
+
# the 401 retry to pay a round trip that a proactive mint
|
|
384
|
+
# would have avoided.
|
|
385
|
+
#
|
|
386
|
+
# first seen the clocks disagree. The server validates `exp` against
|
|
387
|
+
# its own clock, so the token is fine and only this client
|
|
388
|
+
# disbelieves it. Honouring the claim would pin `_exp` in
|
|
389
|
+
# the past and run both grants on every request forever.
|
|
390
|
+
# `expires_in` is always in the future, which is what makes
|
|
391
|
+
# it the safe fallback.
|
|
392
|
+
#
|
|
393
|
+
# A skewed clock AND a replaying server together fall to the first
|
|
394
|
+
# branch, i.e. back to both grants per request. That union is the case
|
|
395
|
+
# being traded away, knowingly: it is rarer than either alone and the
|
|
396
|
+
# 401 retry still covers it.
|
|
397
|
+
self._exp = by_expires_in
|
|
264
398
|
self._refresh = data.get("refresh_token") or self._refresh
|
|
265
399
|
return True
|
|
266
400
|
|
|
@@ -2601,9 +2601,9 @@ class ConnectionsApi:
|
|
|
2601
2601
|
_headers: Optional[Dict[StrictStr, Any]] = None,
|
|
2602
2602
|
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
|
2603
2603
|
) -> LoadManagedTableResponse:
|
|
2604
|
-
"""Load managed table from upload or query result
|
|
2604
|
+
"""Load managed table from inline data, upload, or query result
|
|
2605
2605
|
|
|
2606
|
-
Publish data as the new contents of a managed table from one of
|
|
2606
|
+
Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously.
|
|
2607
2607
|
|
|
2608
2608
|
:param connection_id: Connection ID (required)
|
|
2609
2609
|
:type connection_id: str
|
|
@@ -2652,6 +2652,7 @@ class ConnectionsApi:
|
|
|
2652
2652
|
'400': "ApiErrorResponse",
|
|
2653
2653
|
'404': "ApiErrorResponse",
|
|
2654
2654
|
'409': "ApiErrorResponse",
|
|
2655
|
+
'413': "ApiErrorResponse",
|
|
2655
2656
|
}
|
|
2656
2657
|
response_data = self.api_client.call_api(
|
|
2657
2658
|
*_param,
|
|
@@ -2684,9 +2685,9 @@ class ConnectionsApi:
|
|
|
2684
2685
|
_headers: Optional[Dict[StrictStr, Any]] = None,
|
|
2685
2686
|
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
|
2686
2687
|
) -> ApiResponse[LoadManagedTableResponse]:
|
|
2687
|
-
"""Load managed table from upload or query result
|
|
2688
|
+
"""Load managed table from inline data, upload, or query result
|
|
2688
2689
|
|
|
2689
|
-
Publish data as the new contents of a managed table from one of
|
|
2690
|
+
Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously.
|
|
2690
2691
|
|
|
2691
2692
|
:param connection_id: Connection ID (required)
|
|
2692
2693
|
:type connection_id: str
|
|
@@ -2735,6 +2736,7 @@ class ConnectionsApi:
|
|
|
2735
2736
|
'400': "ApiErrorResponse",
|
|
2736
2737
|
'404': "ApiErrorResponse",
|
|
2737
2738
|
'409': "ApiErrorResponse",
|
|
2739
|
+
'413': "ApiErrorResponse",
|
|
2738
2740
|
}
|
|
2739
2741
|
response_data = self.api_client.call_api(
|
|
2740
2742
|
*_param,
|
|
@@ -2767,9 +2769,9 @@ class ConnectionsApi:
|
|
|
2767
2769
|
_headers: Optional[Dict[StrictStr, Any]] = None,
|
|
2768
2770
|
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
|
2769
2771
|
) -> RESTResponseType:
|
|
2770
|
-
"""Load managed table from upload or query result
|
|
2772
|
+
"""Load managed table from inline data, upload, or query result
|
|
2771
2773
|
|
|
2772
|
-
Publish data as the new contents of a managed table from one of
|
|
2774
|
+
Publish data as the new contents of a managed table from one of three sources — provide exactly one. With `data`, CSV text is sent inline in this request, up to 2 MiB; column types are detected from the data unless `columns` declares them, and a larger payload is rejected with 413 and the error code `INLINE_DATA_TOO_LARGE`, at which point the data should be uploaded and loaded by `upload_id` instead. With `upload_id`, a previously-uploaded file is published: CSV, JSON, and Parquet are supported; the format is auto-detected from the upload's `Content-Type` and file contents, or set explicitly via the `format` field. With `result_id`, a persisted query result is copied into the table, so the table keeps its data even after the result expires; a result can be loaded into any number of tables. If the target table (or its schema) has not been declared yet, it is created automatically as part of the load — declaring tables up front is optional. `mode` selects how the data is applied: `replace` overwrites the table's contents, `append` inserts the new rows on top of the existing data. Concurrent loads against the same upload return 409. For an upload or inline data, set `async` to run the load in the background and get back a job ID to poll; add `async_after_ms` to wait briefly for it to finish before falling back to a job ID. A `result_id` load runs synchronously.
|
|
2773
2775
|
|
|
2774
2776
|
:param connection_id: Connection ID (required)
|
|
2775
2777
|
:type connection_id: str
|
|
@@ -2818,6 +2820,7 @@ class ConnectionsApi:
|
|
|
2818
2820
|
'400': "ApiErrorResponse",
|
|
2819
2821
|
'404': "ApiErrorResponse",
|
|
2820
2822
|
'409': "ApiErrorResponse",
|
|
2823
|
+
'413': "ApiErrorResponse",
|
|
2821
2824
|
}
|
|
2822
2825
|
response_data = self.api_client.call_api(
|
|
2823
2826
|
*_param,
|