tango-python 1.10.0__tar.gz → 1.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. {tango_python-1.10.0 → tango_python-1.11.0}/CHANGELOG.md +6 -0
  2. {tango_python-1.10.0 → tango_python-1.11.0}/PKG-INFO +1 -1
  3. {tango_python-1.10.0 → tango_python-1.11.0}/docs/API_REFERENCE.md +31 -0
  4. {tango_python-1.10.0 → tango_python-1.11.0}/pyproject.toml +1 -1
  5. {tango_python-1.10.0 → tango_python-1.11.0}/tango/__init__.py +1 -1
  6. {tango_python-1.10.0 → tango_python-1.11.0}/tango/client.py +7 -0
  7. {tango_python-1.10.0 → tango_python-1.11.0}/tango/models.py +18 -0
  8. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/generated_overlay.py +27 -0
  9. tango_python-1.11.0/tests/test_opportunity_awards.py +136 -0
  10. {tango_python-1.10.0 → tango_python-1.11.0}/.gitignore +0 -0
  11. {tango_python-1.10.0 → tango_python-1.11.0}/LICENSE +0 -0
  12. {tango_python-1.10.0 → tango_python-1.11.0}/README.md +0 -0
  13. {tango_python-1.10.0 → tango_python-1.11.0}/docs/CLIENT.md +0 -0
  14. {tango_python-1.10.0 → tango_python-1.11.0}/docs/DYNAMIC_MODELS.md +0 -0
  15. {tango_python-1.10.0 → tango_python-1.11.0}/docs/ERRORS.md +0 -0
  16. {tango_python-1.10.0 → tango_python-1.11.0}/docs/PAGINATION.md +0 -0
  17. {tango_python-1.10.0 → tango_python-1.11.0}/docs/SHAPES.md +0 -0
  18. {tango_python-1.10.0 → tango_python-1.11.0}/docs/WEBHOOKS.md +0 -0
  19. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/README.md +0 -0
  20. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/check_filter_shape_conformance.py +0 -0
  21. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/check_shape_coverage.py +0 -0
  22. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/fetch_api_schema.py +0 -0
  23. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/generate_schemas_from_api.py +0 -0
  24. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/generate_shape_overlay.py +0 -0
  25. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/pr_review.py +0 -0
  26. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/probe_shape_types.py +0 -0
  27. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/refresh_contract.py +0 -0
  28. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/smoke_api_parity.py +0 -0
  29. {tango_python-1.10.0 → tango_python-1.11.0}/scripts/test_production.py +0 -0
  30. {tango_python-1.10.0 → tango_python-1.11.0}/tango/exceptions.py +0 -0
  31. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/__init__.py +0 -0
  32. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/explicit_schemas.py +0 -0
  33. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/factory.py +0 -0
  34. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/generator.py +0 -0
  35. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/models.py +0 -0
  36. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/parser.py +0 -0
  37. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/schema.py +0 -0
  38. {tango_python-1.10.0 → tango_python-1.11.0}/tango/shapes/types.py +0 -0
  39. {tango_python-1.10.0 → tango_python-1.11.0}/tango/webhooks/__init__.py +0 -0
  40. {tango_python-1.10.0 → tango_python-1.11.0}/tango/webhooks/cli.py +0 -0
  41. {tango_python-1.10.0 → tango_python-1.11.0}/tango/webhooks/receiver.py +0 -0
  42. {tango_python-1.10.0 → tango_python-1.11.0}/tango/webhooks/signing.py +0 -0
  43. {tango_python-1.10.0 → tango_python-1.11.0}/tango/webhooks/simulate.py +0 -0
  44. {tango_python-1.10.0 → tango_python-1.11.0}/tests/__init__.py +0 -0
  45. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
  46. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
  47. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
  48. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
  49. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
  50. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
  51. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
  52. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
  53. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
  54. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
  55. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
  56. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
  57. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
  58. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
  59. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
  60. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
  61. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
  62. {tango_python-1.10.0 → tango_python-1.11.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
  63. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  64. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
  65. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
  66. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
  67. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
  68. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
  69. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
  70. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
  71. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
  72. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
  73. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
  74. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
  75. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
  76. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
  77. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
  78. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
  79. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
  80. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
  81. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
  82. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
  83. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
  84. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
  85. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
  86. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_budget_flows +0 -0
  87. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
  88. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
  89. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
  90. {tango_python-1.10.0 → tango_python-1.11.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
  91. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
  92. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  93. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  94. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
  95. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
  96. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
  97. {tango_python-1.10.0 → tango_python-1.11.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
  98. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
  99. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
  100. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
  101. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
  102. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
  103. {tango_python-1.10.0 → tango_python-1.11.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
  104. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
  105. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
  106. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
  107. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
  108. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
  109. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
  110. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
  111. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
  112. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
  113. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
  114. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
  115. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
  116. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
  117. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
  118. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
  119. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
  120. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
  121. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
  122. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
  123. {tango_python-1.10.0 → tango_python-1.11.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
  124. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
  125. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
  126. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
  127. {tango_python-1.10.0 → tango_python-1.11.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
  128. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  129. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
  130. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
  131. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
  132. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
  133. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
  134. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
  135. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
  136. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
  137. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
  138. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
  139. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
  140. {tango_python-1.10.0 → tango_python-1.11.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
  141. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  142. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
  143. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
  144. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
  145. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
  146. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
  147. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
  148. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
  149. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
  150. {tango_python-1.10.0 → tango_python-1.11.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
  151. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
  152. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
  153. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
  154. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  155. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
  156. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  157. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  158. {tango_python-1.10.0 → tango_python-1.11.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
  159. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  160. {tango_python-1.10.0 → tango_python-1.11.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
  161. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  162. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
  163. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
  164. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
  165. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
  166. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
  167. {tango_python-1.10.0 → tango_python-1.11.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
  168. {tango_python-1.10.0 → tango_python-1.11.0}/tests/conftest.py +0 -0
  169. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/README.md +0 -0
  170. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/__init__.py +0 -0
  171. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/conftest.py +0 -0
  172. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_agencies_integration.py +0 -0
  173. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_contracts_integration.py +0 -0
  174. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_edge_cases_integration.py +0 -0
  175. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_entities_integration.py +0 -0
  176. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_forecasts_integration.py +0 -0
  177. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_grants_integration.py +0 -0
  178. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_itdashboard_integration.py +0 -0
  179. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_naics_integration.py +0 -0
  180. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_notices_integration.py +0 -0
  181. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_offices_integration.py +0 -0
  182. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_opportunities_integration.py +0 -0
  183. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_organizations_integration.py +0 -0
  184. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
  185. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_protests_integration.py +0 -0
  186. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_reference_data_integration.py +0 -0
  187. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_subawards_integration.py +0 -0
  188. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
  189. {tango_python-1.10.0 → tango_python-1.11.0}/tests/integration/validation.py +0 -0
  190. {tango_python-1.10.0 → tango_python-1.11.0}/tests/production/__init__.py +0 -0
  191. {tango_python-1.10.0 → tango_python-1.11.0}/tests/production/conftest.py +0 -0
  192. {tango_python-1.10.0 → tango_python-1.11.0}/tests/production/test_production_smoke.py +0 -0
  193. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_api_parity.py +0 -0
  194. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_client.py +0 -0
  195. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_contract_appeals.py +0 -0
  196. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_dibbs_exclusions_sbir.py +0 -0
  197. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_ebuy.py +0 -0
  198. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_federal_register.py +0 -0
  199. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_models.py +0 -0
  200. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_opportunity_attachments.py +0 -0
  201. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_organization_scope.py +0 -0
  202. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_shape_coverage_gate.py +0 -0
  203. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_shapes.py +0 -0
  204. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_sled.py +0 -0
  205. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_webhooks_cli.py +0 -0
  206. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_webhooks_receiver.py +0 -0
  207. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_webhooks_signing.py +0 -0
  208. {tango_python-1.10.0 → tango_python-1.11.0}/tests/test_webhooks_simulate.py +0 -0
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.11.0] - 2026-09-29
11
+
12
+ ### Added
13
+
14
+ - **Award fields on opportunities and notices** (Tango API 5.5.0) — new `awarded` / `awardee_uei` filters on `list_opportunities()`, plus award leaves and an `awards(...)` expand in the opportunity and notice shapes (see `docs/API_REFERENCE.md`).
15
+
10
16
  ## [1.10.0] - 2026-09-29
11
17
 
12
18
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tango-python
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: Python SDK for the Tango API
5
5
  Project-URL: Homepage, https://github.com/makegov/tango-python
6
6
  Project-URL: Documentation, https://docs.makegov.com/tango-python
@@ -868,6 +868,8 @@ opportunities = client.list_opportunities(
868
868
  # Filter parameters (all optional)
869
869
  active=None,
870
870
  agency=None,
871
+ awarded=None,
872
+ awardee_uei=None,
871
873
  first_notice_date_after=None,
872
874
  first_notice_date_before=None,
873
875
  last_notice_date_after=None,
@@ -894,6 +896,8 @@ opportunities = client.list_opportunities(
894
896
  **Filter Parameters:**
895
897
  - `active` - Filter by active status (bool)
896
898
  - `agency` - Filter by agency code
899
+ - `awarded` - Whether the opportunity has an award, either posted on it or linked to it (bool). Requires Tango API 5.5.0
900
+ - `awardee_uei` - Awardee UEI, case-insensitive; OR several with `|`. Requires Tango API 5.5.0
897
901
  - `first_notice_date_after` / `first_notice_date_before` - First notice date range
898
902
  - `last_notice_date_after` / `last_notice_date_before` - Last notice date range
899
903
  - `naics` - NAICS code
@@ -947,6 +951,33 @@ for attachment in opp["attachments"]:
947
951
  - A Free-plan request that names them gets the response without them, plus an entry in `meta.upgrade_hints`.
948
952
  - There is no filter on document role.
949
953
 
954
+ **Award fields:**
955
+
956
+ Tango API 5.5.0 adds award information to opportunities.
957
+ None of it is in the SDK's default shape, so name the fields you want.
958
+
959
+ ```python
960
+ opp = client.get_opportunity(
961
+ opportunity_id,
962
+ shape=(
963
+ "opportunity_id,title,awarded,award_date,award_amount,awardee,awardee_uei,"
964
+ "award_count,solicitation_opportunity_id,"
965
+ "awards(opportunity_id,notice_id,award_number,award_date,award_amount,awardee,awardee_uei)"
966
+ ),
967
+ )
968
+
969
+ awarded = client.list_opportunities(
970
+ awardee_uei="ABCDEF123456",
971
+ shape="opportunity_id,title,award_date,awardee",
972
+ )
973
+ ```
974
+
975
+ - `award_date`, `award_amount`, `awardee` and `awardee_uei` are filled only where SAM.gov posted an award notice; elsewhere they are null. `award_amount` is the text SAM.gov published, not a number.
976
+ - SAM.gov often posts an award as its own opportunity. When that award notice references its solicitation, the award's `solicitation_opportunity_id` points back to the solicitation, and the solicitation's `awards(...)` lists up to ten of the most recent linked awards; `award_count` is the full count. An award notice that does not reference its solicitation is not linked.
977
+ - `awarded` is true when the opportunity has an award of its own or has linked awards.
978
+ - The `awarded` and `awardee_uei` filters search every opportunity, not just active ones, so pass `active=True` to narrow to open opportunities.
979
+ - Notices accept `award_date`, `award_amount`, `awardee` and `awardee_uei` in `shape` too.
980
+
950
981
  ---
951
982
 
952
983
  ## Notices
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tango-python"
7
- version = "1.10.0"
7
+ version = "1.11.0"
8
8
  description = "Python SDK for the Tango API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -59,7 +59,7 @@ from .webhooks import (
59
59
  )
60
60
  from .webhooks.receiver import Delivery, WebhookReceiver
61
61
 
62
- __version__ = "1.10.0"
62
+ __version__ = "1.11.0"
63
63
  __all__ = [
64
64
  "TangoClient",
65
65
  "TangoAPIError",
@@ -2346,6 +2346,9 @@ class TangoClient:
2346
2346
  search: str | None = None,
2347
2347
  set_aside: str | None = None,
2348
2348
  solicitation_number: str | None = None,
2349
+ *,
2350
+ awarded: bool | None = None,
2351
+ awardee_uei: str | None = None,
2349
2352
  ) -> PaginatedResponse:
2350
2353
  """
2351
2354
  List contract opportunities/solicitations
@@ -2358,6 +2361,8 @@ class TangoClient:
2358
2361
  flat_lists: If True, flatten arrays using indexed keys
2359
2362
  active: Filter by active status
2360
2363
  agency: Agency filter
2364
+ awarded: Filter by whether the opportunity has an award, either posted on it or linked to it from a separate award notice. Searches every opportunity rather than only active ones. Requires Tango API 5.5.0
2365
+ awardee_uei: Filter by the awardee's UEI (case-insensitive). OR several with ``|``. Searches every opportunity rather than only active ones. Requires Tango API 5.5.0
2361
2366
  first_notice_date_after: First notice date after
2362
2367
  first_notice_date_before: First notice date before
2363
2368
  last_notice_date_after: Last notice date after
@@ -2387,6 +2392,8 @@ class TangoClient:
2387
2392
  for key, val in (
2388
2393
  ("active", active),
2389
2394
  ("agency", agency),
2395
+ ("awarded", awarded),
2396
+ ("awardee_uei", awardee_uei),
2390
2397
  ("first_notice_date_after", first_notice_date_after),
2391
2398
  ("first_notice_date_before", first_notice_date_before),
2392
2399
  ("last_notice_date_after", last_notice_date_after),
@@ -565,6 +565,10 @@ class Opportunity:
565
565
  Both must be named, e.g. ``attachments(name,url,doc_role,doc_role_alt)``, because ``attachments(*)`` does not include them.
566
566
  An attachment Tango has not classified omits both keys rather than returning null, so read them with ``.get()``.
567
567
  A Free-plan request that names them gets the response without them, plus an entry in ``meta.upgrade_hints``.
568
+
569
+ The award fields (``award_date``, ``award_amount``, ``awardee``, ``awardee_uei``) are filled only where SAM.gov posted an award notice, and ``award_amount`` is the text SAM.gov published, not a number.
570
+ SAM.gov often posts an award as its own opportunity; when that award notice references its solicitation, ``solicitation_opportunity_id`` points back to it, and the solicitation's ``awards(...)`` expand lists up to ten of the most recent linked awards, with ``award_count`` giving the full count.
571
+ An award notice that does not reference its solicitation is not linked.
568
572
  """
569
573
 
570
574
  opportunity_id: str
@@ -575,6 +579,15 @@ class Opportunity:
575
579
  active: bool | None = None
576
580
  naics_code: str | None = None
577
581
  psc_code: str | None = None
582
+ award_number: str | None = None
583
+ awarded: bool | None = None
584
+ award_date: date | None = None
585
+ award_amount: str | None = None
586
+ awardee: str | None = None
587
+ awardee_uei: str | None = None
588
+ award_count: int | None = None
589
+ solicitation_opportunity_id: str | None = None
590
+ awards: list[dict[str, Any]] | None = None
578
591
 
579
592
 
580
593
  @dataclass
@@ -593,6 +606,11 @@ class Notice:
593
606
  description: str | None = None
594
607
  posted_date: datetime | None = None
595
608
  naics_code: str | None = None
609
+ award_number: str | None = None
610
+ award_date: date | None = None
611
+ award_amount: str | None = None
612
+ awardee: str | None = None
613
+ awardee_uei: str | None = None
596
614
 
597
615
 
598
616
  @dataclass
@@ -213,6 +213,16 @@ AWARDS_SCHEMA: dict[str, FieldSchema] = {
213
213
  "transactions": FieldSchema(name="transactions", type=str, is_optional=True, is_list=False),
214
214
  }
215
215
 
216
+ AWARDS2_SCHEMA: dict[str, FieldSchema] = {
217
+ "award_amount": FieldSchema(name="award_amount", type=str, is_optional=True, is_list=False),
218
+ "award_date": FieldSchema(name="award_date", type=date, is_optional=True, is_list=False),
219
+ "award_number": FieldSchema(name="award_number", type=str, is_optional=True, is_list=False),
220
+ "awardee": FieldSchema(name="awardee", type=str, is_optional=True, is_list=False),
221
+ "awardee_uei": FieldSchema(name="awardee_uei", type=str, is_optional=True, is_list=False),
222
+ "notice_id": FieldSchema(name="notice_id", type=str, is_optional=True, is_list=False),
223
+ "opportunity_id": FieldSchema(name="opportunity_id", type=str, is_optional=True, is_list=False),
224
+ }
225
+
216
226
  BUDGET_APPROPRIATION_SCHEMA: dict[str, FieldSchema] = {
217
227
  "cgac": FieldSchema(name="cgac", type=int, is_optional=True, is_list=False),
218
228
  "fiscal_year": FieldSchema(name="fiscal_year", type=int, is_optional=True, is_list=False),
@@ -1112,6 +1122,7 @@ GENERATED_NESTED: dict[str, dict[str, FieldSchema]] = {
1112
1122
  "AwardingOffice": AWARDING_OFFICE_SCHEMA,
1113
1123
  "AwardingOffice2": AWARDING_OFFICE2_SCHEMA,
1114
1124
  "Awards": AWARDS_SCHEMA,
1125
+ "Awards2": AWARDS2_SCHEMA,
1115
1126
  "BudgetAppropriation": BUDGET_APPROPRIATION_SCHEMA,
1116
1127
  "BudgetSpending": BUDGET_SPENDING_SCHEMA,
1117
1128
  "Children": CHILDREN_SCHEMA,
@@ -2228,6 +2239,10 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2228
2239
  is_list=True,
2229
2240
  nested_model="Attachments",
2230
2241
  ),
2242
+ "award_amount": FieldSchema(name="award_amount", type=str, is_optional=True, is_list=False),
2243
+ "award_date": FieldSchema(name="award_date", type=date, is_optional=True, is_list=False),
2244
+ "awardee": FieldSchema(name="awardee", type=str, is_optional=True, is_list=False),
2245
+ "awardee_uei": FieldSchema(name="awardee_uei", type=str, is_optional=True, is_list=False),
2231
2246
  "meta": FieldSchema(
2232
2247
  name="meta", type=dict, is_optional=True, is_list=False, nested_model="Meta"
2233
2248
  ),
@@ -2476,6 +2491,15 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2476
2491
  is_list=False,
2477
2492
  nested_model="Attachments5",
2478
2493
  ),
2494
+ "award_amount": FieldSchema(name="award_amount", type=str, is_optional=True, is_list=False),
2495
+ "award_count": FieldSchema(name="award_count", type=int, is_optional=True, is_list=False),
2496
+ "award_date": FieldSchema(name="award_date", type=date, is_optional=True, is_list=False),
2497
+ "awarded": FieldSchema(name="awarded", type=bool, is_optional=True, is_list=False),
2498
+ "awardee": FieldSchema(name="awardee", type=str, is_optional=True, is_list=False),
2499
+ "awardee_uei": FieldSchema(name="awardee_uei", type=str, is_optional=True, is_list=False),
2500
+ "awards": FieldSchema(
2501
+ name="awards", type=dict, is_optional=True, is_list=True, nested_model="Awards2"
2502
+ ),
2479
2503
  "department": FieldSchema(
2480
2504
  name="department",
2481
2505
  type=dict,
@@ -2529,6 +2553,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2529
2553
  nested_model="CodeDescription",
2530
2554
  ),
2531
2555
  "snippet": FieldSchema(name="snippet", type=str, is_optional=True, is_list=False),
2556
+ "solicitation_opportunity_id": FieldSchema(
2557
+ name="solicitation_opportunity_id", type=str, is_optional=True, is_list=False
2558
+ ),
2532
2559
  },
2533
2560
  "Organization": {
2534
2561
  "aac_code": FieldSchema(name="aac_code", type=str, is_optional=True, is_list=False),
@@ -0,0 +1,136 @@
1
+ """Award fields on opportunities and notices, the `awards(...)` expand, and the `awarded` / `awardee_uei` filters."""
2
+
3
+ from datetime import date
4
+ from unittest.mock import Mock, patch
5
+
6
+ import pytest
7
+
8
+ from tango import TangoClient
9
+ from tango.models import Notice, Opportunity
10
+ from tango.shapes.parser import ShapeParser
11
+
12
+ AWARD_LEAVES = "award_date,award_amount,awardee,awardee_uei"
13
+ OPPORTUNITY_AWARD_SHAPE = (
14
+ f"opportunity_id,awarded,{AWARD_LEAVES},award_count,solicitation_opportunity_id,awards(*)"
15
+ )
16
+
17
+
18
+ def _mock(mock_request, payload):
19
+ response = Mock()
20
+ response.is_success = True
21
+ response.json.return_value = payload
22
+ response.content = b"{}"
23
+ mock_request.return_value = response
24
+
25
+
26
+ def _page(results):
27
+ return {"count": len(results), "next": None, "previous": None, "results": results}
28
+
29
+
30
+ class TestAwardShapes:
31
+ @pytest.mark.parametrize(
32
+ "shape",
33
+ [
34
+ OPPORTUNITY_AWARD_SHAPE,
35
+ "opportunity_id,awards(opportunity_id,notice_id,award_number,award_date,award_amount,awardee,awardee_uei)",
36
+ ],
37
+ )
38
+ def test_opportunity_award_shape_validates(self, shape):
39
+ parser = ShapeParser(cache_enabled=False)
40
+ parser.validate(parser.parse(shape), Opportunity)
41
+
42
+ def test_notice_award_shape_validates(self):
43
+ parser = ShapeParser(cache_enabled=False)
44
+ parser.validate(parser.parse(f"notice_id,award_number,{AWARD_LEAVES}"), Notice)
45
+
46
+
47
+ class TestAwardResponses:
48
+ @patch("tango.client.httpx.Client.request")
49
+ def test_get_opportunity_parses_award_fields_and_linked_awards(self, mock_request):
50
+ _mock(
51
+ mock_request,
52
+ {
53
+ "opportunity_id": "solicitation-1",
54
+ "awarded": True,
55
+ "award_date": None,
56
+ "award_amount": None,
57
+ "awardee": None,
58
+ "awardee_uei": None,
59
+ "award_count": 12,
60
+ "solicitation_opportunity_id": None,
61
+ "awards": [
62
+ {
63
+ "opportunity_id": "award-1",
64
+ "notice_id": "notice-1",
65
+ "award_number": "W912-26-C-0001",
66
+ "award_date": "2026-08-14",
67
+ "award_amount": "$1,250,000.00",
68
+ "awardee": "Example Corp",
69
+ "awardee_uei": "ABCDEF123456",
70
+ }
71
+ ],
72
+ },
73
+ )
74
+ row = TangoClient(api_key="k").get_opportunity(
75
+ "solicitation-1", shape=OPPORTUNITY_AWARD_SHAPE
76
+ )
77
+ assert row["awarded"] is True
78
+ assert row["award_count"] == 12
79
+ assert row["awards"][0]["award_number"] == "W912-26-C-0001"
80
+ assert row["awards"][0]["award_amount"] == "$1,250,000.00"
81
+ assert row["awards"][0]["awardee_uei"] == "ABCDEF123456"
82
+
83
+ @patch("tango.client.httpx.Client.request")
84
+ def test_list_notices_returns_award_fields(self, mock_request):
85
+ shape = f"notice_id,{AWARD_LEAVES}"
86
+ _mock(
87
+ mock_request,
88
+ _page(
89
+ [
90
+ {
91
+ "notice_id": "notice-1",
92
+ "award_date": "2026-08-14",
93
+ "award_amount": "1250000",
94
+ "awardee": "Example Corp",
95
+ "awardee_uei": "ABCDEF123456",
96
+ }
97
+ ]
98
+ ),
99
+ )
100
+ page = TangoClient(api_key="k").list_notices(shape=shape)
101
+ assert page.results[0]["award_date"] == date(2026, 8, 14)
102
+ assert page.results[0]["award_amount"] == "1250000"
103
+
104
+
105
+ class TestAwardFilters:
106
+ @patch("tango.client.httpx.Client.request")
107
+ def test_list_opportunities_sends_award_filters(self, mock_request):
108
+ _mock(mock_request, _page([]))
109
+ TangoClient(api_key="k").list_opportunities(
110
+ awarded=True, awardee_uei="ABCDEF123456|abcdef654321"
111
+ )
112
+ params = mock_request.call_args.kwargs["params"]
113
+ assert params["awarded"] is True
114
+ assert params["awardee_uei"] == "ABCDEF123456|abcdef654321"
115
+
116
+ @patch("tango.client.httpx.Client.request")
117
+ def test_list_opportunities_sends_awarded_false(self, mock_request):
118
+ _mock(mock_request, _page([]))
119
+ TangoClient(api_key="k").list_opportunities(awarded=False)
120
+ params = mock_request.call_args.kwargs["params"]
121
+ assert params["awarded"] is False
122
+ assert "awardee_uei" not in params
123
+
124
+ @patch("tango.client.httpx.Client.request")
125
+ def test_existing_positional_arguments_keep_their_meaning(self, mock_request):
126
+ _mock(mock_request, _page([]))
127
+ TangoClient(api_key="k").list_opportunities(
128
+ 1, 25, None, False, False, None, None, "2026-08-01"
129
+ )
130
+ params = mock_request.call_args.kwargs["params"]
131
+ assert params["first_notice_date_after"] == "2026-08-01"
132
+ assert "awarded" not in params
133
+
134
+ def test_award_filters_are_keyword_only(self):
135
+ with pytest.raises(TypeError):
136
+ TangoClient(api_key="k").list_opportunities(*([None] * 21), True)
File without changes
File without changes
File without changes