tango-python 1.3.0__tar.gz → 1.4.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 → tango_python-1.4.0}/CHANGELOG.md +20 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/PKG-INFO +1 -1
- {tango_python-1.3.0 → tango_python-1.4.0}/pyproject.toml +1 -1
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/check_filter_shape_conformance.py +38 -13
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/check_shape_coverage.py +35 -1
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/__init__.py +1 -1
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/client.py +11 -1
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/generated_overlay.py +601 -45
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_client.py +88 -0
- tango_python-1.4.0/tests/test_shape_coverage_gate.py +108 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/.github/workflows/docs-dispatch.yml +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/.github/workflows/lint.yml +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/.github/workflows/publish.yml +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/.github/workflows/test.yml +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/.gitignore +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/LICENSE +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/README.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/docs/API_REFERENCE.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/docs/CLIENT.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/docs/DYNAMIC_MODELS.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/docs/ERRORS.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/docs/PAGINATION.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/docs/SHAPES.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/docs/WEBHOOKS.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/README.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/fetch_api_schema.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/generate_schemas_from_api.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/generate_shape_overlay.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/pr_review.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/probe_shape_types.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/refresh_contract.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/smoke_api_parity.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/scripts/test_production.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/exceptions.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/models.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/__init__.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/explicit_schemas.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/factory.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/generator.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/models.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/parser.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/schema.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/shapes/types.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/webhooks/__init__.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/webhooks/cli.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/webhooks/receiver.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/webhooks/signing.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tango/webhooks/simulate.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/__init__.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_budget_flows +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.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.3.0 → tango_python-1.4.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/conftest.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/README.md +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/__init__.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/conftest.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_agencies_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_contracts_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_edge_cases_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_entities_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_forecasts_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_grants_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_itdashboard_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_naics_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_notices_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_offices_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_opportunities_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_organizations_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_protests_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_reference_data_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_subawards_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/integration/validation.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/production/__init__.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/production/conftest.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/production/test_production_smoke.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_api_parity.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_dibbs_exclusions_sbir.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_models.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_shapes.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_webhooks_cli.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_webhooks_receiver.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_webhooks_signing.py +0 -0
- {tango_python-1.3.0 → tango_python-1.4.0}/tests/test_webhooks_simulate.py +0 -0
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.4.0] - 2026-07-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `list_protests()` now accepts `naics_code`, filtering protests by the solicitation's NAICS code. The API has always supported it (verified live: 15,027 protests unfiltered, 12 for `541519`, 2 for `336411`), but `protests` was absent from the conformance checker's resource-to-method map, so the gap was never reported.
|
|
14
|
+
- `list_psc()` now accepts `has_awards`, restricting results to codes with contract award history (2,526 PSC codes unfiltered, 2,243 with awards). `False` is sent explicitly rather than dropped, since the API distinguishes the two. This param has always worked but was invisible to the contract until [makegov/tango#2948](https://github.com/makegov/tango/pull/2948) added the `view_handled_params` declaration for params a viewset reads straight from `query_params`. That PR also fixed a truthiness bug where `has_awards=false` filtered identically to `true`, and is deployed — verified live at 2,526 for `False` and 2,243 for `True`.
|
|
15
|
+
|
|
16
|
+
One caveat that is not an SDK issue: `/api/psc/` responses are cached for 30 days per query-param key, so any `?has_awards=` key requested *before* that deploy still serves the old awards-only result until its entry expires or is invalidated.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **The reverse shape-coverage gate was blind to seven resources, and the SDK's schemas had silently drifted behind four of them.** The gate skipped any resource whose contract shape tree was falsy (`if not shape: continue`). Tango published `"shape": null` for `entities`, `opportunities`, `notices`, `protests`, `itdashboard`, `events`, and `news` — five of them because its contract generator crashed on tier-aware viewsets ([makegov/tango#2944](https://github.com/makegov/tango/pull/2944)) — so the gate reported full coverage while checking nothing for them. Re-vendoring the fixed contract surfaced **69 real gaps** (36 fields, 20 expands, 13 flattened expands) across entities, notices, protests, and itdashboard, all now closed by a regenerated overlay.
|
|
20
|
+
|
|
21
|
+
The user-visible consequence was worse than missing schema entries. Because `ShapeParser` validates client-side, the SDK *rejected* 13 fields the API accepts, before ever issuing a request — `list_entities(shape="display_name")` raised `ShapeValidationError` against an endpoint that returns 200 for it. Affected: `Entity`'s `display_name`, `entity_type`, `entity_structure`, `organization_structure`, `profit_structure`, `purpose_of_registration`, `country_of_incorporation`, `past_performance`, `registered`, `sba_business_types`, and `Protest`'s `decisions`, `resolved_agency`, `resolved_protester`. All 13 now validate. Note that `registered` and `sba_business_types` regressed *in 1.3.0* — they validated under 1.1.1 — so the overlay generation had narrowed `Entity` while nothing was watching.
|
|
22
|
+
|
|
23
|
+
The gate no longer skips silently: a resource declaring `shape_supported: true` with no shape tree is now a hard finding (`contract_missing_shape`) reported as an upstream contract defect rather than an SDK one. Contracts predating `schema_version: 2` omit the key, where a null tree stays genuinely ambiguous and skipping remains correct.
|
|
24
|
+
- **Seven resources with working SDK methods were never conformance-checked at all.** `RESOURCE_TO_METHOD` omitted `protests`, `psc`, `mas_sins`, `departments`, `business_types`, and `assistance_listings` entirely, and mapped `offices` to `None` as "not yet implemented" even though `list_offices` exists. Their filter coverage had never been validated. Wiring them up found one real gap (`protests.naics_code`, above); the other six were already complete. `events` and `news` stay mapped to `None` — genuinely absent from the SDK, now with a note explaining that `list_webhook_event_types` is unrelated.
|
|
25
|
+
- **`refresh_contract.py` + `probe_shape_types.py` re-run against the corrected contract.** `contracts/observed_shape_types.json` gained live-sampled types for the five newly-visible resources (entities alone contributes 136 observed paths), so the regenerated overlay resolves their types from real API responses rather than name heuristics.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- `CONTRACT_OMITTED_PARAMS` now warns when an entry is no longer needed, mirroring how baseline entries are burned down, and the list is now **empty** — which is the goal state. It briefly held `mas_sins: search`, added because the API applies it (330 results unfiltered, 0 for a nonsense term, 32 for `office`) while the contract recorded `filter_params: []`, so the checker would otherwise have flagged a working parameter as a silent no-op and invited its removal. Both that entry and `budget/accounts: search` were retired by the new warning once [makegov/tango#2944](https://github.com/makegov/tango/pull/2944) and [#2948](https://github.com/makegov/tango/pull/2948) taught the contract generator to publish them. The carve-out mechanism stays for the next such case.
|
|
29
|
+
|
|
10
30
|
## [1.3.0] - 2026-07-19
|
|
11
31
|
|
|
12
32
|
### Added
|
|
@@ -74,21 +74,35 @@ RESOURCE_TO_METHOD: dict[str, str | None] = {
|
|
|
74
74
|
"exclusions": "list_exclusions",
|
|
75
75
|
"sbir/topics": "list_sbir_topics",
|
|
76
76
|
"sbir/solicitations": "list_sbir_solicitations",
|
|
77
|
-
|
|
78
|
-
"offices":
|
|
77
|
+
"protests": "list_protests",
|
|
78
|
+
"offices": "list_offices",
|
|
79
|
+
"psc": "list_psc",
|
|
80
|
+
"mas_sins": "list_mas_sins",
|
|
81
|
+
"departments": "list_departments",
|
|
82
|
+
"business_types": "list_business_types",
|
|
83
|
+
"assistance_listings": "list_assistance_listings",
|
|
84
|
+
# Genuinely absent from the SDK. Both are content endpoints with no shaping
|
|
85
|
+
# and no list method; `list_webhook_event_types` is unrelated to `events`.
|
|
86
|
+
"events": None,
|
|
87
|
+
"news": None,
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
# Params the API genuinely accepts but the contract omits, so they would read as
|
|
82
|
-
# stale (a silent no-op) when they in fact work. Tango surfaces
|
|
83
|
-
# search params for filtersets
|
|
84
|
-
#
|
|
91
|
+
# stale (a silent no-op) when they in fact work. Tango's generator surfaces
|
|
92
|
+
# view-handled search params only for filtersets declaring `min_length_params`
|
|
93
|
+
# and DRF `SearchFilter` backends declaring `search_fields`; a resource that
|
|
94
|
+
# implements search another way is absent from the contract even though the
|
|
95
|
+
# endpoint honors it.
|
|
96
|
+
#
|
|
85
97
|
# Each entry must be verified against the live API before being added, and
|
|
86
|
-
# removed once Tango's
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
# removed once Tango's generator surfaces it — `check_conformance` warns when an
|
|
99
|
+
# entry is no longer needed, so this list cannot quietly rot.
|
|
100
|
+
#
|
|
101
|
+
# Currently empty, and that is the goal state. Two entries have come and gone:
|
|
102
|
+
# budget/accounts `search` (published by makegov/tango#2944's SearchFilter
|
|
103
|
+
# extraction) and mas_sins `search` (published by makegov/tango#2948's
|
|
104
|
+
# `view_handled_params` declaration). Both were retired by the warning above.
|
|
105
|
+
CONTRACT_OMITTED_PARAMS: dict[str, frozenset[str]] = {}
|
|
92
106
|
|
|
93
107
|
# SDK-level conveniences that never correspond to API filter params.
|
|
94
108
|
# `sort`/`order` compose into the API's `ordering`; `filters` is the legacy
|
|
@@ -395,11 +409,22 @@ def run_check(manifest_path: Path) -> tuple[list[str], list[str]]:
|
|
|
395
409
|
)
|
|
396
410
|
|
|
397
411
|
# Direction 2 — staleness: SDK sends it, API no longer accepts it.
|
|
412
|
+
omitted = CONTRACT_OMITTED_PARAMS.get(resource_name, frozenset())
|
|
413
|
+
|
|
414
|
+
# A carve-out is only justified while the contract still omits the param.
|
|
415
|
+
# Once Tango publishes it, the entry hides real staleness — warn so it
|
|
416
|
+
# gets removed, mirroring how baseline entries are burned down above.
|
|
417
|
+
redundant = sorted(omitted & runtime_filters)
|
|
418
|
+
if redundant:
|
|
419
|
+
warnings.append(
|
|
420
|
+
f"{resource_name}: CONTRACT_OMITTED_PARAMS entries no longer needed "
|
|
421
|
+
f"(the contract now publishes them): {', '.join(redundant)}"
|
|
422
|
+
)
|
|
423
|
+
|
|
398
424
|
stale = sorted(
|
|
399
425
|
f"{arg} (sends `{api_param}`)" if arg != api_param else arg
|
|
400
426
|
for arg, api_param in exposed.items()
|
|
401
|
-
if api_param not in runtime_filters
|
|
402
|
-
and api_param not in CONTRACT_OMITTED_PARAMS.get(resource_name, frozenset())
|
|
427
|
+
if api_param not in runtime_filters and api_param not in omitted
|
|
403
428
|
)
|
|
404
429
|
if stale:
|
|
405
430
|
errors.append(
|
|
@@ -171,8 +171,29 @@ def collect_gaps(contract: dict[str, Any], registry: Any, models: Any) -> list[d
|
|
|
171
171
|
walk(resource, child_path, enode, child_schema)
|
|
172
172
|
|
|
173
173
|
for rkey, r in contract.get("resources", {}).items():
|
|
174
|
-
|
|
174
|
+
runtime = r.get("runtime") or {}
|
|
175
|
+
shape = runtime.get("shape")
|
|
175
176
|
if not shape:
|
|
177
|
+
# A null tree used to be an unconditional skip, which made this gate
|
|
178
|
+
# blind to exactly the resources it most needed to check. Tango
|
|
179
|
+
# published null for entities/opportunities/notices/protests/
|
|
180
|
+
# itdashboard because its generator crashed on tier-aware viewsets
|
|
181
|
+
# (makegov/tango#2944), and this gate reported full coverage while
|
|
182
|
+
# the SDK's Entity and Protest schemas drifted 13 fields behind.
|
|
183
|
+
#
|
|
184
|
+
# Contracts at schema_version >= 2 declare shape_supported, so a
|
|
185
|
+
# null tree on a shaping resource is now a hard finding. Older
|
|
186
|
+
# contracts omit the key; there `None` is genuinely ambiguous and
|
|
187
|
+
# skipping stays the only safe read.
|
|
188
|
+
if runtime.get("shape_supported"):
|
|
189
|
+
findings.append(
|
|
190
|
+
{
|
|
191
|
+
"kind": "contract_missing_shape",
|
|
192
|
+
"resource": rkey,
|
|
193
|
+
"path": "(root)",
|
|
194
|
+
"name": runtime.get("shape_error") or "no shape tree published",
|
|
195
|
+
}
|
|
196
|
+
)
|
|
176
197
|
continue
|
|
177
198
|
model_name = RESOURCE_TO_MODEL.get(rkey)
|
|
178
199
|
model = getattr(models, model_name, None) if model_name else None
|
|
@@ -305,6 +326,19 @@ def main() -> int:
|
|
|
305
326
|
print("\nNo new shape-coverage drift. ✓")
|
|
306
327
|
return 0
|
|
307
328
|
|
|
329
|
+
contract_gaps = [f for f in new if f["kind"] == "contract_missing_shape"]
|
|
330
|
+
if contract_gaps:
|
|
331
|
+
print(
|
|
332
|
+
"\n*** CONTRACT DEFECT — these resources support shaping but publish no shape tree ***"
|
|
333
|
+
)
|
|
334
|
+
_print_grouped("RESOURCES WITH NO SHAPE TREE", contract_gaps)
|
|
335
|
+
print(
|
|
336
|
+
"\n This is an upstream problem, not an SDK one: the vendored contract understates\n"
|
|
337
|
+
" the API, so coverage cannot be checked for these resources at all. Refresh the\n"
|
|
338
|
+
" contract (scripts/refresh_contract.py); if it persists, the generator is failing\n"
|
|
339
|
+
" to extract them — see makegov/tango contracts/README.md."
|
|
340
|
+
)
|
|
341
|
+
|
|
308
342
|
print("\n*** NEW shape-coverage drift (Tango exposes these; the SDK schema does not) ***")
|
|
309
343
|
_print_grouped("MISSING FIELDS", [f for f in new if f["kind"] == "missing_field"])
|
|
310
344
|
_print_grouped("MISSING EXPANDS", [f for f in new if f["kind"] == "missing_expand"])
|
|
@@ -2540,6 +2540,7 @@ class TangoClient:
|
|
|
2540
2540
|
filed_date_before: str | None = None,
|
|
2541
2541
|
decision_date_after: str | None = None,
|
|
2542
2542
|
decision_date_before: str | None = None,
|
|
2543
|
+
naics_code: str | None = None,
|
|
2543
2544
|
search: str | None = None,
|
|
2544
2545
|
) -> PaginatedResponse:
|
|
2545
2546
|
"""
|
|
@@ -2569,6 +2570,7 @@ class TangoClient:
|
|
|
2569
2570
|
filed_date_before: Filed date on or before
|
|
2570
2571
|
decision_date_after: Decision date on or after
|
|
2571
2572
|
decision_date_before: Decision date on or before
|
|
2573
|
+
naics_code: Filter by the solicitation's NAICS code (e.g. 541519)
|
|
2572
2574
|
search: Full-text search over protest searchable fields
|
|
2573
2575
|
"""
|
|
2574
2576
|
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
@@ -2594,6 +2596,7 @@ class TangoClient:
|
|
|
2594
2596
|
("filed_date_before", filed_date_before),
|
|
2595
2597
|
("decision_date_after", decision_date_after),
|
|
2596
2598
|
("decision_date_before", decision_date_before),
|
|
2599
|
+
("naics_code", naics_code),
|
|
2597
2600
|
("search", search),
|
|
2598
2601
|
):
|
|
2599
2602
|
if val is not None:
|
|
@@ -4445,8 +4448,13 @@ class TangoClient:
|
|
|
4445
4448
|
shape: str | None = None,
|
|
4446
4449
|
flat: bool = False,
|
|
4447
4450
|
flat_lists: bool = False,
|
|
4451
|
+
has_awards: bool | None = None,
|
|
4448
4452
|
) -> PaginatedResponse[dict[str, Any]]:
|
|
4449
|
-
"""List Product Service Codes (`/api/psc/`).
|
|
4453
|
+
"""List Product Service Codes (`/api/psc/`).
|
|
4454
|
+
|
|
4455
|
+
Args:
|
|
4456
|
+
has_awards: When True, return only codes with contract award history.
|
|
4457
|
+
"""
|
|
4450
4458
|
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
4451
4459
|
if shape:
|
|
4452
4460
|
params["shape"] = shape
|
|
@@ -4454,6 +4462,8 @@ class TangoClient:
|
|
|
4454
4462
|
params["flat"] = "true"
|
|
4455
4463
|
if flat_lists:
|
|
4456
4464
|
params["flat_lists"] = "true"
|
|
4465
|
+
if has_awards is not None:
|
|
4466
|
+
params["has_awards"] = "true" if has_awards else "false"
|
|
4457
4467
|
data = self._get("/api/psc/", params)
|
|
4458
4468
|
return PaginatedResponse(
|
|
4459
4469
|
count=int(data.get("count", 0)),
|