tango-python 1.2.0__tar.gz → 1.3.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.
- tango_python-1.3.0/.github/workflows/lint.yml +110 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/CHANGELOG.md +107 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/PKG-INFO +1 -1
- {tango_python-1.2.0 → tango_python-1.3.0}/docs/WEBHOOKS.md +17 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/pyproject.toml +1 -1
- tango_python-1.3.0/scripts/README.md +114 -0
- tango_python-1.3.0/scripts/check_filter_shape_conformance.py +576 -0
- tango_python-1.3.0/scripts/check_shape_coverage.py +323 -0
- tango_python-1.3.0/scripts/generate_shape_overlay.py +301 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/scripts/pr_review.py +4 -3
- tango_python-1.3.0/scripts/probe_shape_types.py +170 -0
- tango_python-1.3.0/scripts/refresh_contract.py +103 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/__init__.py +13 -1
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/client.py +800 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/exceptions.py +18 -1
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/models.py +225 -0
- tango_python-1.3.0/tango/shapes/generated_overlay.py +1874 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/schema.py +16 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/production/test_production_smoke.py +20 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_client.py +49 -0
- tango_python-1.3.0/tests/test_dibbs_exclusions_sbir.py +203 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_shapes.py +1 -3
- tango_python-1.2.0/.github/workflows/lint.yml +0 -94
- tango_python-1.2.0/scripts/README.md +0 -100
- tango_python-1.2.0/scripts/check_filter_shape_conformance.py +0 -286
- {tango_python-1.2.0 → tango_python-1.3.0}/.github/workflows/docs-dispatch.yml +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/.github/workflows/publish.yml +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/.github/workflows/test.yml +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/.gitignore +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/LICENSE +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/README.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/docs/API_REFERENCE.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/docs/CLIENT.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/docs/DYNAMIC_MODELS.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/docs/ERRORS.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/docs/PAGINATION.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/docs/SHAPES.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/scripts/fetch_api_schema.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/scripts/generate_schemas_from_api.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/scripts/smoke_api_parity.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/scripts/test_production.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/__init__.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/explicit_schemas.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/factory.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/generator.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/models.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/parser.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/shapes/types.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/webhooks/__init__.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/webhooks/cli.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/webhooks/receiver.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/webhooks/signing.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tango/webhooks/simulate.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/__init__.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[detailed-key,piid,award_date,description,total_contract_value,obligated,fiscal_year,set_aside,recipient(display_name,uei),awarding_office(-),place_of_performa...ce114a3c47e2037aaa3c15d00b7031bd +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_budget_flows +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[comprehensive-uei,legal_business_name,dba_name,cage_code,business_types,primary_naics,naics_codes,psc_codes,email_address,entity_url,description,capabilities,ke...1603a7d52e211cf2b3bc7d32080238aa +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[detailed-id,source_system,external_id,title,description,anticipated_award_date,fiscal_year,naics_code,status,is_active] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[detailed-grant_id,opportunity_number,title,status(-),agency_code,description,last_updated,cfda_numbers(number,title),applicant_types(-),funding_categories(-)] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[minimal-uii,agency_name,bureau_name,investment_title,type_of_investment,part_of_it_portfolio,updated_time,url] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[detailed-notice_id,title,description,solicitation_number,posted_date,naics_code,set_aside,office(-),place_of_performance(-)] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[detailed-opportunity_id,title,description,solicitation_number,response_deadline,first_notice_date,last_notice_date,active,naics_code,psc_code,set_asid...23b6b4502ddd665b7184afcff6c6d8d9 +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[with_dockets-case_id,case_number,title,outcome,filed_date,dockets(docket_number,filed_date,outcome)] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[detailed-notice_id,title,description,solicitation_number,posted_date,naics_code,set_aside,office(-),place_of_performance(-)] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[detailed-opportunity_id,title,description,solicitation_number,response_deadline,first_notice_date,last_notice_date,active,naics_code,psc_code,set_aside,sam_url,office(-),place_of_performance(-)] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/conftest.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/README.md +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/__init__.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/conftest.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_agencies_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_contracts_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_edge_cases_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_entities_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_forecasts_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_grants_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_itdashboard_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_naics_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_notices_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_offices_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_opportunities_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_organizations_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_protests_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_reference_data_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_subawards_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/integration/validation.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/production/__init__.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/production/conftest.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_api_parity.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_models.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_webhooks_cli.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_webhooks_receiver.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_webhooks_signing.py +0 -0
- {tango_python-1.2.0 → tango_python-1.3.0}/tests/test_webhooks_simulate.py +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
name: Linting
|
|
2
|
+
|
|
3
|
+
# Lint gate runs on every PR and push to main.
|
|
4
|
+
#
|
|
5
|
+
# - ruff format + ruff check are HARD gates (block the PR).
|
|
6
|
+
# - mypy is a HARD gate: the package type-checks cleanly under strict mypy.
|
|
7
|
+
# (The earlier ~28-error burn-down is complete.)
|
|
8
|
+
# - The SDK filter/shape conformance check is a HARD gate: it runs against the
|
|
9
|
+
# vendored contract at contracts/filter_shape_contract.json on every run (no
|
|
10
|
+
# secrets needed, works on forks). A second, token-gated step compares the
|
|
11
|
+
# vendored contract against the tango repo's HEAD and emits a staleness
|
|
12
|
+
# notice (never a failure — tango HEAD may carry unreleased changes).
|
|
13
|
+
# Refresh the vendored contract with scripts/refresh_contract.py.
|
|
14
|
+
# - Reverse shape-coverage is a HARD gate: check_shape_coverage.py fails when
|
|
15
|
+
# Tango's shape trees expose a field/expand the SDK schema doesn't capture and
|
|
16
|
+
# it isn't in contracts/shape_coverage_baseline.json. Also offline (vendored
|
|
17
|
+
# contract), so it runs on forks. Burn the baseline down with the generator.
|
|
18
|
+
on:
|
|
19
|
+
workflow_dispatch:
|
|
20
|
+
push:
|
|
21
|
+
branches: [ main ]
|
|
22
|
+
pull_request:
|
|
23
|
+
branches: [ main ]
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
lint:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v6
|
|
31
|
+
|
|
32
|
+
- name: Install uv
|
|
33
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
34
|
+
with:
|
|
35
|
+
version: "latest"
|
|
36
|
+
|
|
37
|
+
- name: Set up Python
|
|
38
|
+
run: uv python install 3.12
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: uv sync --all-extras
|
|
42
|
+
|
|
43
|
+
- name: Check formatting with ruff
|
|
44
|
+
run: uv run ruff format --check tango/
|
|
45
|
+
|
|
46
|
+
- name: Lint with ruff
|
|
47
|
+
run: uv run ruff check tango/
|
|
48
|
+
|
|
49
|
+
- name: Type check with mypy
|
|
50
|
+
run: uv run mypy tango/
|
|
51
|
+
|
|
52
|
+
conformance:
|
|
53
|
+
# Hard gate against the vendored contract (contracts/filter_shape_contract.json).
|
|
54
|
+
# Runs unconditionally — no secrets required, so forks and tokenless runs
|
|
55
|
+
# get the full check instead of a silent skip.
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v6
|
|
60
|
+
|
|
61
|
+
- name: Install uv
|
|
62
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
63
|
+
with:
|
|
64
|
+
version: "latest"
|
|
65
|
+
|
|
66
|
+
- name: Set up Python
|
|
67
|
+
run: uv python install 3.12
|
|
68
|
+
|
|
69
|
+
- name: Install dependencies
|
|
70
|
+
run: uv sync --all-extras
|
|
71
|
+
|
|
72
|
+
- name: Check SDK filter/shape conformance (vendored contract)
|
|
73
|
+
run: uv run python scripts/check_filter_shape_conformance.py
|
|
74
|
+
|
|
75
|
+
- name: Check reverse shape coverage (Tango exposes -> SDK captures)
|
|
76
|
+
# Complements the conformance check with the OTHER direction: fails when
|
|
77
|
+
# Tango's shape trees expose a field/expand the SDK schema doesn't capture
|
|
78
|
+
# and it isn't in contracts/shape_coverage_baseline.json. Also offline
|
|
79
|
+
# against the vendored contract — no secrets, works on forks.
|
|
80
|
+
run: uv run python scripts/check_shape_coverage.py
|
|
81
|
+
|
|
82
|
+
# --- Staleness notice (best-effort, never fails the job) ---------------
|
|
83
|
+
- name: Determine token availability
|
|
84
|
+
id: gate
|
|
85
|
+
env:
|
|
86
|
+
TANGO_API_REPO_ACCESS_TOKEN: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}
|
|
87
|
+
run: |
|
|
88
|
+
if [ -n "$TANGO_API_REPO_ACCESS_TOKEN" ]; then
|
|
89
|
+
echo "ready=true" >> "$GITHUB_OUTPUT"
|
|
90
|
+
else
|
|
91
|
+
echo "ready=false" >> "$GITHUB_OUTPUT"
|
|
92
|
+
echo "::notice::Contract staleness check skipped — TANGO_API_REPO_ACCESS_TOKEN not configured."
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
- name: Checkout tango API repo (contract source)
|
|
96
|
+
if: steps.gate.outputs.ready == 'true'
|
|
97
|
+
uses: actions/checkout@v6
|
|
98
|
+
with:
|
|
99
|
+
repository: makegov/tango
|
|
100
|
+
path: tango-api
|
|
101
|
+
token: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}
|
|
102
|
+
|
|
103
|
+
- name: Compare vendored contract against tango HEAD
|
|
104
|
+
if: steps.gate.outputs.ready == 'true'
|
|
105
|
+
run: |
|
|
106
|
+
if ! diff -q contracts/filter_shape_contract.json tango-api/contracts/filter_shape_contract.json >/dev/null; then
|
|
107
|
+
echo "::warning::Vendored contract differs from makegov/tango HEAD. Refresh with: uv run python scripts/refresh_contract.py"
|
|
108
|
+
else
|
|
109
|
+
echo "Vendored contract matches makegov/tango HEAD."
|
|
110
|
+
fi
|
|
@@ -7,6 +7,113 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0] - 2026-07-19
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **DIBBS, exclusions, and SBIR/STTR endpoint support.** Six endpoint families
|
|
14
|
+
Tango shipped in v4.16–v4.18 had no SDK support at all — no models, no methods.
|
|
15
|
+
Added `list_dibbs_rfqs`/`get_dibbs_rfq`, `list_dibbs_rfps`/`get_dibbs_rfp`,
|
|
16
|
+
`list_dibbs_awards`/`get_dibbs_award`, `list_exclusions`/`get_exclusion`,
|
|
17
|
+
`list_sbir_topics`/`get_sbir_topic`, and
|
|
18
|
+
`list_sbir_solicitations`/`get_sbir_solicitation`, with all 85 filter params,
|
|
19
|
+
shape schemas, and `ShapeConfig` defaults. New models: `DibbsRfq`, `DibbsRfp`,
|
|
20
|
+
`DibbsAward`, `Exclusion`, `SbirTopic`, `SbirSolicitation`.
|
|
21
|
+
|
|
22
|
+
Two API behaviors are worth knowing. `is_open` (DIBBS) and
|
|
23
|
+
`is_currently_excluded` (exclusions) are derived at query time, so filter with
|
|
24
|
+
the `open` / `active` kwargs rather than shaping on those fields. And DIBBS
|
|
25
|
+
`total_contract_price` is the *order* total repeated on every line item —
|
|
26
|
+
never sum it across rows; deduplicate on award + delivery-order number first.
|
|
27
|
+
- **Reverse shape-coverage: the SDK now captures every field and expand the API
|
|
28
|
+
returns.** The conformance check only validated one direction — that the SDK's
|
|
29
|
+
shape constants reference *allowed* fields. Nothing checked the reverse, so the
|
|
30
|
+
hand-maintained `tango/shapes/explicit_schemas.py` had silently fallen ~200+
|
|
31
|
+
fields behind the API: 209 leaf fields, 41 whole nested expand branches, and 3
|
|
32
|
+
resources (naics, psc, mas_sins) the typed shape API could not request at all.
|
|
33
|
+
A new generated overlay (`tango/shapes/generated_overlay.py`, produced by
|
|
34
|
+
`scripts/generate_shape_overlay.py` and merged over the base by `SchemaRegistry`)
|
|
35
|
+
closes all of them, with types resolved from live-API sampling rather than
|
|
36
|
+
guessed. Notable now-shapeable data: contract/IDV acquisition attributes
|
|
37
|
+
(`fair_opportunity_limited_sources`, `subcontracting_plan`, …), `contracts.officers`
|
|
38
|
+
and `period_of_performance`, the full `organizations` hierarchy
|
|
39
|
+
(`obligation_rank`, `l1..l8_fh_key`, `budget_appropriation`, `children`/`parent`),
|
|
40
|
+
`otas`/`otidvs` `transactions`, and the deep `vehicles.awardees[.orders]` tree.
|
|
41
|
+
Code-object expands (`set_aside`, `award_type`, `idv_type`, …), previously
|
|
42
|
+
modeled inconsistently as `str`/bare `dict`, now uniformly resolve to
|
|
43
|
+
`{code, description}`.
|
|
44
|
+
- **Reverse shape-coverage gate.** `scripts/check_shape_coverage.py` walks Tango's
|
|
45
|
+
shape trees (from the vendored contract) against the SDK schemas and fails when
|
|
46
|
+
the SDK misses anything the API exposes and it isn't in
|
|
47
|
+
`contracts/shape_coverage_baseline.json`. Offline against the vendored contract —
|
|
48
|
+
no token, runs on forks — and wired into the `lint.yml` conformance job.
|
|
49
|
+
Regenerate the overlay with `scripts/generate_shape_overlay.py` (from the vendored
|
|
50
|
+
contract + `contracts/observed_shape_types.json`, no API key); refresh the type
|
|
51
|
+
observations with `scripts/probe_shape_types.py` (maintainer-run, needs a key).
|
|
52
|
+
- **Contract-first conformance system.** The canonical API filter/shape
|
|
53
|
+
contract is now vendored at `contracts/filter_shape_contract.json` (refresh
|
|
54
|
+
with the new `scripts/refresh_contract.py`), so the conformance check runs
|
|
55
|
+
out of the box — locally, in CI, and on forks — with no tango checkout or
|
|
56
|
+
access token. `scripts/check_filter_shape_conformance.py` gained three new
|
|
57
|
+
checks on top of filter coverage: **staleness** (an SDK filter argument the
|
|
58
|
+
API no longer accepts is an error — it would silently no-op), **types**
|
|
59
|
+
(each argument's annotation is validated against the contract's
|
|
60
|
+
`schema_version: 2` per-filter type metadata), and a **known-gaps baseline**
|
|
61
|
+
(`contracts/conformance_baseline.json`) that downgrades accepted missing
|
|
62
|
+
params from errors to warnings so backlog is tracked instead of silent. A
|
|
63
|
+
new `--suggest` flag prints ready-to-paste typed parameter scaffolds for
|
|
64
|
+
any missing filters. The first run against the current API surface found 7
|
|
65
|
+
real coverage gaps, now baselined: `key` on contracts/IDVs/OTAs/OTIDVs,
|
|
66
|
+
`cage` on entities, `id` on forecasts, and `opportunity_id` on
|
|
67
|
+
opportunities.
|
|
68
|
+
- `TangoValidationError` now exposes the API's structured validation details
|
|
69
|
+
directly: `.issues` (the list of `{"path": ..., "reason": ...}` entries the
|
|
70
|
+
server returns for shape errors) and `.available_fields` (the endpoint's
|
|
71
|
+
valid field set, when included). Both were previously reachable only by
|
|
72
|
+
digging through `.response_data`. ([#45](https://github.com/makegov/tango-python/issues/45))
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
- The CI conformance job (`lint.yml`) now runs unconditionally against the
|
|
76
|
+
vendored contract instead of silently skipping when
|
|
77
|
+
`TANGO_API_REPO_ACCESS_TOKEN` is absent; the token is only used for a
|
|
78
|
+
best-effort staleness notice comparing the vendored contract to tango HEAD.
|
|
79
|
+
`scripts/pr_review.py` likewise defaults its conformance step to the
|
|
80
|
+
vendored contract (override with `TANGO_CONTRACT_MANIFEST`).
|
|
81
|
+
- 400 error messages now name the rejected field(s) and reason when the API
|
|
82
|
+
returns structured `issues` — e.g.
|
|
83
|
+
`Invalid request parameters: Invalid shape: tradeoff_process (unknown_field)`
|
|
84
|
+
instead of just `Invalid request parameters: Invalid shape`. ([#45](https://github.com/makegov/tango-python/issues/45))
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
- **`docs/WEBHOOKS.md` listed only five of the eight webhook alert types.** It
|
|
88
|
+
was missing `alerts.exclusion.match`, `alerts.dibbs_rfq.match`, and
|
|
89
|
+
`alerts.dibbs_rfp.match`. No SDK change was needed — event types are served by
|
|
90
|
+
the API and never hardcoded, so the new types already worked — but the
|
|
91
|
+
hand-written list had gone stale. Also documents two things that surprise
|
|
92
|
+
people: DIBBS *awards* are not alertable, and nothing fires when a record
|
|
93
|
+
merely lapses (an exclusion hitting its termination date, or an RFQ/RFP
|
|
94
|
+
passing its close date, emits no event). A production smoke test now asserts
|
|
95
|
+
every live event type appears in the doc, so the list cannot silently fall
|
|
96
|
+
behind again.
|
|
97
|
+
- **Refreshed the vendored API contract, which had gone stale by seven
|
|
98
|
+
resources.** It tracked 25 resources against Tango's current 32, so the
|
|
99
|
+
conformance and coverage checks were validating against out-of-date truth and
|
|
100
|
+
could not see DIBBS, exclusions, SBIR, or `budget/accounts` at all. Refreshing
|
|
101
|
+
it also surfaced 72 additional fields on existing resources, now covered.
|
|
102
|
+
Nested routes are keyed with a slash (`budget/accounts`), which silently broke
|
|
103
|
+
the old `budget_accounts` mapping — both keys are now accepted.
|
|
104
|
+
- **`check_filter_shape_conformance.py` no longer reports false stale params.**
|
|
105
|
+
It resolved SDK arguments to API params only through an explicit
|
|
106
|
+
`api_param_mapping` dict, so methods that express the translation as tuple
|
|
107
|
+
tables — `("account_title__icontains", account_title)` and
|
|
108
|
+
`range_filters = (("apportioned", apportioned, apportioned_gte, ...))` — looked
|
|
109
|
+
like they exposed dozens of params the API rejects. They do not:
|
|
110
|
+
`list_budget_accounts` correctly sends `apportioned__gte`, `fiscal_year__lte`,
|
|
111
|
+
and `account_title__icontains`. The checker now understands both tuple forms
|
|
112
|
+
(in `Assign`, `AnnAssign`, and inline `for` iterables), with an explicit
|
|
113
|
+
`api_param_mapping` still taking precedence. This let `budget/accounts` be
|
|
114
|
+
conformance-checked for the first time; its genuinely missing lookup variants
|
|
115
|
+
(`agency_code__in`, `bureau_name__icontains`, …) are now baselined.
|
|
116
|
+
|
|
10
117
|
## [1.2.0] - 2026-06-05
|
|
11
118
|
|
|
12
119
|
### Added
|
|
@@ -84,8 +84,25 @@ tango webhooks list-event-types
|
|
|
84
84
|
# alerts.entity.match Entity matched a saved alert
|
|
85
85
|
# alerts.grant.match Grant matched a saved alert
|
|
86
86
|
# alerts.forecast.match Forecast matched a saved alert
|
|
87
|
+
# alerts.exclusion.match SAM.gov exclusion matched a saved alert
|
|
88
|
+
# alerts.dibbs_rfq.match DLA DIBBS RFQ matched a saved alert
|
|
89
|
+
# alerts.dibbs_rfp.match DLA DIBBS RFP matched a saved alert
|
|
87
90
|
```
|
|
88
91
|
|
|
92
|
+
This list is served by the API, so it is always current — the SDK does not hardcode
|
|
93
|
+
event types anywhere. Always trust `list-event-types` over any list written down
|
|
94
|
+
here.
|
|
95
|
+
|
|
96
|
+
Two things that surprise people:
|
|
97
|
+
|
|
98
|
+
- **DIBBS awards are not alertable.** There is no `alerts.dibbs_award.match`;
|
|
99
|
+
only RFQs and RFPs emit alerts.
|
|
100
|
+
- **Nothing fires when a record merely lapses.** An exclusion reaching its
|
|
101
|
+
termination date, or an RFQ/RFP passing its close date, emits no event.
|
|
102
|
+
Alerts fire on a record matching your saved search, not on the passage of
|
|
103
|
+
time. Poll with `list_exclusions(active=...)` / `list_dibbs_rfqs(open=...)`
|
|
104
|
+
if you need to observe expiry.
|
|
105
|
+
|
|
89
106
|
### 2. See what a payload looks like
|
|
90
107
|
|
|
91
108
|
```bash
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Development Scripts
|
|
2
|
+
|
|
3
|
+
This directory contains utility scripts used during development and maintenance of the tango-python library.
|
|
4
|
+
|
|
5
|
+
## Scripts
|
|
6
|
+
|
|
7
|
+
### Schema and API Tools
|
|
8
|
+
|
|
9
|
+
- **`fetch_api_schema.py`** - Fetches the OpenAPI schema from the Tango API and saves it locally
|
|
10
|
+
- **`generate_schemas_from_api.py`** - Generates schema definitions from the API reference (outputs to stdout)
|
|
11
|
+
|
|
12
|
+
### Testing and Validation
|
|
13
|
+
|
|
14
|
+
- **`test_production.py`** - Runs production API smoke tests against the live API
|
|
15
|
+
- **`pr_review.py`** - Runs configurable validation checks for PR review (linting, type checking, tests, conformance)
|
|
16
|
+
- **`check_filter_shape_conformance.py`** - Validates SDK filter and shape conformance (see [Filter and shape conformance](#filter-and-shape-conformance))
|
|
17
|
+
- **`check_shape_coverage.py`** - Reverse shape-coverage gate: fails when Tango's shape trees expose a field/expand the SDK schema doesn't capture and it isn't in `contracts/shape_coverage_baseline.json`. Offline against the vendored contract (no API key; runs on forks)
|
|
18
|
+
- **`generate_shape_overlay.py`** - Regenerates `tango/shapes/generated_overlay.py` (the schema additions that close the coverage gaps) from the vendored contract + `contracts/observed_shape_types.json`. No API key needed; `--report` prints the gaps without writing
|
|
19
|
+
- **`probe_shape_types.py`** - Samples the live API to derive ground-truth shape types (types + list-ness the contract can't express) into `contracts/observed_shape_types.json`. Maintainer-run; requires `TANGO_API_KEY`
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
These scripts are primarily for maintainers and are not part of the public API.
|
|
24
|
+
|
|
25
|
+
### Production API Testing
|
|
26
|
+
|
|
27
|
+
Run smoke tests against the production API:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Requires TANGO_API_KEY environment variable
|
|
31
|
+
uv run python scripts/test_production.py
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This runs fast smoke tests (~30-60 seconds) that validate core SDK functionality against the live production API.
|
|
35
|
+
|
|
36
|
+
### PR Review Validation
|
|
37
|
+
|
|
38
|
+
Run validation checks for PR review. **Automatically detects PR context** from GitHub Actions, GitHub CLI, or git branch.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Auto-detect PR and run validation (default: production mode)
|
|
42
|
+
uv run python scripts/pr_review.py
|
|
43
|
+
|
|
44
|
+
# Review a specific PR number
|
|
45
|
+
uv run python scripts/pr_review.py --pr-number 123
|
|
46
|
+
|
|
47
|
+
# Smoke tests only
|
|
48
|
+
uv run python scripts/pr_review.py --mode smoke
|
|
49
|
+
|
|
50
|
+
# Quick validation (linting + type checking, no tests)
|
|
51
|
+
uv run python scripts/pr_review.py --mode quick
|
|
52
|
+
|
|
53
|
+
# Full validation (all checks including all tests)
|
|
54
|
+
uv run python scripts/pr_review.py --mode full
|
|
55
|
+
|
|
56
|
+
# Check only changed files (auto-enabled when PR is detected)
|
|
57
|
+
uv run python scripts/pr_review.py --mode production --changed-files-only
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Validation Modes:**
|
|
61
|
+
- `smoke` - Production API smoke tests only
|
|
62
|
+
- `quick` - Linting + type checking (no tests)
|
|
63
|
+
- `full` - All checks (linting + type checking + filter/shape conformance + all tests)
|
|
64
|
+
- `production` - Linting + type checking + filter/shape conformance + production API smoke tests (default)
|
|
65
|
+
|
|
66
|
+
Both `full` and `production` run the [filter and shape conformance](#filter-and-shape-conformance) check against the vendored contract (`contracts/filter_shape_contract.json`, overridable via `TANGO_CONTRACT_MANIFEST`). If the contract is missing, that step is skipped with a warning.
|
|
67
|
+
|
|
68
|
+
**PR Detection:**
|
|
69
|
+
The script automatically detects PR information from:
|
|
70
|
+
- GitHub Actions environment variables (`GITHUB_EVENT_PATH`, `GITHUB_PR_NUMBER`)
|
|
71
|
+
- GitHub CLI (`gh pr view` - requires `gh auth login`)
|
|
72
|
+
- Current git branch
|
|
73
|
+
|
|
74
|
+
When a PR is detected, the script displays PR information and automatically:
|
|
75
|
+
- Detects the base branch from the PR
|
|
76
|
+
- Checks only changed files
|
|
77
|
+
- Shows PR URL in summary
|
|
78
|
+
|
|
79
|
+
### Filter and shape conformance
|
|
80
|
+
|
|
81
|
+
The SDK is validated against the canonical API contract — vendored at
|
|
82
|
+
`contracts/filter_shape_contract.json` and refreshed with
|
|
83
|
+
`scripts/refresh_contract.py` — for **filter conformance**, and against its own
|
|
84
|
+
schemas for **shape conformance**:
|
|
85
|
+
|
|
86
|
+
1. **Filter coverage** – Every list/get method exposes the filter parameters defined in the contract (explicit argument or `api_param_mapping`). Missing params are errors unless accepted in `contracts/conformance_baseline.json`, where each entry is tracked backlog (remove the entry in the same PR that adds the param). Methods that use `**kwargs` for filters are reported as warnings because their filter names cannot be verified.
|
|
87
|
+
2. **Staleness** – Every filter-like SDK argument resolves to a param the API actually accepts. A stale param silently no-ops for users, so this is always an error.
|
|
88
|
+
3. **Types** – With a `schema_version >= 2` contract, each SDK argument's annotation must be compatible with the contract value type (e.g. a `date` param must be `str`, a `boolean` must be `bool`).
|
|
89
|
+
4. **Shape conformance** – Every `ShapeConfig` constant (e.g. `CONTRACTS_MINIMAL`, `IDVS_MINIMAL`) parses correctly and only references fields that exist in the SDK’s explicit schemas for that model. Invalid or unknown fields in default shapes are reported as errors.
|
|
90
|
+
|
|
91
|
+
This script runs in CI on every push/PR (see [Lint workflow](.github/workflows/lint.yml)) against the vendored contract — no secrets or sibling checkout needed.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Full check (filter + shape) against the vendored contract.
|
|
95
|
+
uv run python scripts/check_filter_shape_conformance.py
|
|
96
|
+
|
|
97
|
+
# Ready-to-paste typed parameter scaffolds for any missing filters
|
|
98
|
+
uv run python scripts/check_filter_shape_conformance.py --suggest
|
|
99
|
+
|
|
100
|
+
# List resources that have no matching SDK method (for implementation checklist)
|
|
101
|
+
uv run python scripts/check_filter_shape_conformance.py --list-unmapped
|
|
102
|
+
|
|
103
|
+
# Refresh the vendored contract (sibling ../tango checkout, or GitHub via gh)
|
|
104
|
+
uv run python scripts/refresh_contract.py
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Output:** JSON with `manifest`, `errors`, and `warnings`. Exit code 1 if there are any errors (missing or stale filters, type mismatches, invalid shapes, or missing SDK methods for manifest resources).
|
|
108
|
+
|
|
109
|
+
**Local runs:** The vendored contract makes the full check work out of the box. To check against a different contract (e.g. an unreleased tango branch), pass `--manifest /path/to/filter_shape_contract.json` or set `TANGO_CONTRACT_MANIFEST` (honored by `pr_review.py`).
|
|
110
|
+
|
|
111
|
+
## Requirements
|
|
112
|
+
|
|
113
|
+
- `TANGO_API_KEY` environment variable (required for production API tests)
|
|
114
|
+
- All dependencies installed: `uv sync --all-extras`
|