tango-python 0.3.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. {tango_python-0.3.0 → tango_python-0.4.1}/.github/workflows/lint.yml +9 -0
  2. {tango_python-0.3.0 → tango_python-0.4.1}/.gitignore +2 -0
  3. {tango_python-0.3.0 → tango_python-0.4.1}/CHANGELOG.md +13 -0
  4. {tango_python-0.3.0 → tango_python-0.4.1}/PKG-INFO +13 -6
  5. {tango_python-0.3.0 → tango_python-0.4.1}/README.md +12 -5
  6. tango_python-0.4.1/ROADMAP.md +22 -0
  7. {tango_python-0.3.0 → tango_python-0.4.1}/docs/API_REFERENCE.md +45 -1
  8. {tango_python-0.3.0 → tango_python-0.4.1}/docs/DEVELOPERS.md +27 -0
  9. {tango_python-0.3.0 → tango_python-0.4.1}/docs/SHAPES.md +30 -2
  10. {tango_python-0.3.0 → tango_python-0.4.1}/pyproject.toml +1 -1
  11. tango_python-0.4.1/scripts/README.md +100 -0
  12. tango_python-0.4.1/scripts/check_filter_shape_conformance.py +272 -0
  13. {tango_python-0.3.0 → tango_python-0.4.1}/scripts/pr_review.py +22 -0
  14. {tango_python-0.3.0 → tango_python-0.4.1}/tango/__init__.py +3 -1
  15. {tango_python-0.3.0 → tango_python-0.4.1}/tango/client.py +904 -160
  16. {tango_python-0.3.0 → tango_python-0.4.1}/tango/models.py +79 -0
  17. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/explicit_schemas.py +114 -0
  18. tango_python-0.4.1/tests/cassettes/TestAssistanceIntegration.test_list_assistance +121 -0
  19. tango_python-0.4.1/tests/cassettes/TestNaicsIntegration.test_list_naics +82 -0
  20. tango_python-0.4.1/tests/cassettes/TestOTAsIntegration.test_get_ota +306 -0
  21. tango_python-0.4.1/tests/cassettes/TestOTAsIntegration.test_list_otas +170 -0
  22. tango_python-0.4.1/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +310 -0
  23. tango_python-0.4.1/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +176 -0
  24. tango_python-0.4.1/tests/cassettes/TestOfficesIntegration.test_get_office +78 -0
  25. tango_python-0.4.1/tests/cassettes/TestOfficesIntegration.test_list_offices +96 -0
  26. tango_python-0.4.1/tests/cassettes/TestOrganizationsIntegration.test_get_organization +152 -0
  27. tango_python-0.4.1/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +83 -0
  28. tango_python-0.4.1/tests/cassettes/TestSubawardsIntegration.test_list_subawards +95 -0
  29. tango_python-0.4.1/tests/integration/test_assistance_integration.py +46 -0
  30. tango_python-0.4.1/tests/integration/test_naics_integration.py +36 -0
  31. tango_python-0.4.1/tests/integration/test_offices_integration.py +60 -0
  32. tango_python-0.4.1/tests/integration/test_organizations_integration.py +57 -0
  33. tango_python-0.4.1/tests/integration/test_otas_otidvs_integration.py +100 -0
  34. tango_python-0.4.1/tests/integration/test_subawards_integration.py +43 -0
  35. tango_python-0.4.1/tests/production/test_production_smoke.py +719 -0
  36. {tango_python-0.3.0 → tango_python-0.4.1}/tests/test_client.py +233 -11
  37. {tango_python-0.3.0 → tango_python-0.4.1}/uv.lock +1 -1
  38. tango_python-0.3.0/ROADMAP.md +0 -29
  39. tango_python-0.3.0/scripts/README.md +0 -76
  40. tango_python-0.3.0/tests/production/test_production_smoke.py +0 -314
  41. {tango_python-0.3.0 → tango_python-0.4.1}/.env.example +0 -0
  42. {tango_python-0.3.0 → tango_python-0.4.1}/.github/workflows/publish.yml +0 -0
  43. {tango_python-0.3.0 → tango_python-0.4.1}/.github/workflows/test.yml +0 -0
  44. {tango_python-0.3.0 → tango_python-0.4.1}/LICENSE +0 -0
  45. {tango_python-0.3.0 → tango_python-0.4.1}/docs/quick_start.ipynb +0 -0
  46. {tango_python-0.3.0 → tango_python-0.4.1}/scripts/fetch_api_schema.py +0 -0
  47. {tango_python-0.3.0 → tango_python-0.4.1}/scripts/generate_schemas_from_api.py +0 -0
  48. {tango_python-0.3.0 → tango_python-0.4.1}/scripts/test_production.py +0 -0
  49. {tango_python-0.3.0 → tango_python-0.4.1}/tango/exceptions.py +0 -0
  50. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/__init__.py +0 -0
  51. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/factory.py +0 -0
  52. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/generator.py +0 -0
  53. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/models.py +0 -0
  54. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/parser.py +0 -0
  55. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/schema.py +0 -0
  56. {tango_python-0.3.0 → tango_python-0.4.1}/tango/shapes/types.py +0 -0
  57. {tango_python-0.3.0 → tango_python-0.4.1}/tests/__init__.py +0 -0
  58. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
  59. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
  60. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
  61. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
  62. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
  63. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
  64. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
  65. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
  66. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
  67. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
  68. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
  69. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
  70. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
  71. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
  72. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
  73. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
  74. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
  75. {tango_python-0.3.0 → tango_python-0.4.1}/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
  76. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  77. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
  78. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
  79. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
  80. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
  81. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
  82. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
  83. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
  84. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
  85. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
  86. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
  87. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
  88. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
  89. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
  90. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
  91. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
  92. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
  93. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
  94. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
  95. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
  96. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
  97. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
  98. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
  99. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
  100. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
  101. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
  102. {tango_python-0.3.0 → tango_python-0.4.1}/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...95fcff7efcf320ecc846393dd484321d +0 -0
  103. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
  104. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  105. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  106. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
  107. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
  108. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
  109. {tango_python-0.3.0 → tango_python-0.4.1}/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
  110. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
  111. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
  112. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
  113. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
  114. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
  115. {tango_python-0.3.0 → tango_python-0.4.1}/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
  116. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
  117. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestIDVsIntegration.test_get_idv_summary +0 -0
  118. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
  119. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
  120. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
  121. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestIDVsIntegration.test_list_idv_summary_awards +0 -0
  122. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
  123. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
  124. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
  125. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
  126. {tango_python-0.3.0 → tango_python-0.4.1}/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
  127. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  128. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
  129. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
  130. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
  131. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
  132. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
  133. {tango_python-0.3.0 → tango_python-0.4.1}/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
  134. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  135. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
  136. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestProductionSmokeWithCassettes.test_contract_cursor_pagination +0 -0
  137. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
  138. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  139. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
  140. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  141. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  142. {tango_python-0.3.0 → tango_python-0.4.1}/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
  143. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  144. {tango_python-0.3.0 → tango_python-0.4.1}/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
  145. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  146. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
  147. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
  148. {tango_python-0.3.0 → tango_python-0.4.1}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
  149. {tango_python-0.3.0 → tango_python-0.4.1}/tests/conftest.py +0 -0
  150. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/README.md +0 -0
  151. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/__init__.py +0 -0
  152. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/conftest.py +0 -0
  153. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_agencies_integration.py +0 -0
  154. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_contracts_integration.py +0 -0
  155. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_edge_cases_integration.py +0 -0
  156. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_entities_integration.py +0 -0
  157. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_forecasts_integration.py +0 -0
  158. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_grants_integration.py +0 -0
  159. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_notices_integration.py +0 -0
  160. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_opportunities_integration.py +0 -0
  161. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_reference_data_integration.py +0 -0
  162. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/test_vehicles_idvs_integration.py +0 -0
  163. {tango_python-0.3.0 → tango_python-0.4.1}/tests/integration/validation.py +0 -0
  164. {tango_python-0.3.0 → tango_python-0.4.1}/tests/production/__init__.py +0 -0
  165. {tango_python-0.3.0 → tango_python-0.4.1}/tests/production/conftest.py +0 -0
  166. {tango_python-0.3.0 → tango_python-0.4.1}/tests/test_models.py +0 -0
  167. {tango_python-0.3.0 → tango_python-0.4.1}/tests/test_shapes.py +0 -0
@@ -12,6 +12,12 @@ jobs:
12
12
 
13
13
  steps:
14
14
  - uses: actions/checkout@v4
15
+
16
+ - name: Checkout tango API repo (manifest source)
17
+ uses: actions/checkout@v4
18
+ with:
19
+ repository: makegov/tango
20
+ path: tango-api
15
21
 
16
22
  - name: Install uv
17
23
  uses: astral-sh/setup-uv@v4
@@ -29,3 +35,6 @@ jobs:
29
35
 
30
36
  - name: Lint with ruff
31
37
  run: uv run ruff check tango/
38
+
39
+ - name: Check SDK filter/shape conformance
40
+ run: uv run python scripts/check_filter_shape_conformance.py --manifest tango-api/contracts/filter_shape_contract.json
@@ -148,3 +148,5 @@ Thumbs.db
148
148
  # Other
149
149
  yoni/
150
150
  .cursor/*
151
+
152
+ examples/
@@ -7,9 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.1] - 2026-03-03
11
+
12
+ ### Added
13
+ - GSA eLibrary contracts: `list_gsa_elibrary_contracts`, `get_gsa_elibrary_contract` with shaping and filter params (`contract_number`, `key`, `piid`, `schedule`, `search`, `sin`, `uei`).
14
+
15
+ ### Changed
16
+ - Conformance: replaced `**kwargs`/`**filters` with explicit filter parameters on `list_contracts`, `list_idvs`, `list_entities`, `list_forecasts`, `list_grants`, `list_notices`, `list_opportunities` for full filter/shape conformance. Backward compatibility preserved for `list_contracts(filters=SearchFilters(...))`.
17
+
18
+ ## [0.4.0] - 2026-02-24
19
+
10
20
  ### Added
21
+ - Offices, Organizations, OTAs, OTIDVs, Subawards, NAICS, and Assistance endpoints.
22
+ - Filter/shape conformance tooling and documentation.
11
23
 
12
24
  ### Changed
25
+ - CI lint workflow runs filter/shape conformance when the manifest is available.
13
26
 
14
27
  ## [0.3.0] - 2026-02-09
15
28
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tango-python
3
- Version: 0.3.0
3
+ Version: 0.4.1
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
@@ -487,8 +487,10 @@ tango-python/
487
487
  │ └── quick_start.ipynb # Interactive quick start
488
488
  ├── scripts/ # Utility scripts
489
489
  │ ├── README.md
490
+ │ ├── check_filter_shape_conformance.py # Filter + shape conformance (CI)
490
491
  │ ├── fetch_api_schema.py
491
- └── generate_schemas_from_api.py
492
+ ├── generate_schemas_from_api.py
493
+ │ └── pr_review.py # PR validation (lint, types, tests, conformance)
492
494
  ├── pyproject.toml # Project configuration
493
495
  ├── uv.lock # Dependency lock file
494
496
  ├── LICENSE # MIT License
@@ -526,7 +528,12 @@ Contributions are welcome! Please feel free to submit a Pull Request.
526
528
 
527
529
  1. Fork the repository
528
530
  2. Create your feature branch (`git checkout -b feature/amazing-feature`)
529
- 3. Run tests (`uv run pytest`)
530
- 4. Commit your changes (`git commit -m 'Add amazing feature'`)
531
- 5. Push to the branch (`git push origin feature/amazing-feature`)
532
- 6. Open a Pull Request
531
+ 3. Run lint and format: `uv run ruff format tango/ && uv run ruff check tango/`
532
+ 4. Run type checking: `uv run mypy tango/`
533
+ 5. Run tests: `uv run pytest`
534
+ 6. (Optional) Run [filter and shape conformance](scripts/README.md#filter-and-shape-conformance) if you have the tango API manifest; CI will run it on push/PR
535
+ 7. Commit your changes (`git commit -m 'Add amazing feature'`)
536
+ 8. Push to the branch (`git push origin feature/amazing-feature`)
537
+ 9. Open a Pull Request
538
+
539
+ For a single command that runs formatting, linting, type checking, and tests (and conformance when the manifest is present), use: `uv run python scripts/pr_review.py --mode full`
@@ -432,8 +432,10 @@ tango-python/
432
432
  │ └── quick_start.ipynb # Interactive quick start
433
433
  ├── scripts/ # Utility scripts
434
434
  │ ├── README.md
435
+ │ ├── check_filter_shape_conformance.py # Filter + shape conformance (CI)
435
436
  │ ├── fetch_api_schema.py
436
- └── generate_schemas_from_api.py
437
+ ├── generate_schemas_from_api.py
438
+ │ └── pr_review.py # PR validation (lint, types, tests, conformance)
437
439
  ├── pyproject.toml # Project configuration
438
440
  ├── uv.lock # Dependency lock file
439
441
  ├── LICENSE # MIT License
@@ -471,7 +473,12 @@ Contributions are welcome! Please feel free to submit a Pull Request.
471
473
 
472
474
  1. Fork the repository
473
475
  2. Create your feature branch (`git checkout -b feature/amazing-feature`)
474
- 3. Run tests (`uv run pytest`)
475
- 4. Commit your changes (`git commit -m 'Add amazing feature'`)
476
- 5. Push to the branch (`git push origin feature/amazing-feature`)
477
- 6. Open a Pull Request
476
+ 3. Run lint and format: `uv run ruff format tango/ && uv run ruff check tango/`
477
+ 4. Run type checking: `uv run mypy tango/`
478
+ 5. Run tests: `uv run pytest`
479
+ 6. (Optional) Run [filter and shape conformance](scripts/README.md#filter-and-shape-conformance) if you have the tango API manifest; CI will run it on push/PR
480
+ 7. Commit your changes (`git commit -m 'Add amazing feature'`)
481
+ 8. Push to the branch (`git push origin feature/amazing-feature`)
482
+ 9. Open a Pull Request
483
+
484
+ For a single command that runs formatting, linting, type checking, and tests (and conformance when the manifest is present), use: `uv run python scripts/pr_review.py --mode full`
@@ -0,0 +1,22 @@
1
+ # ROADMAP
2
+
3
+ ## Now
4
+
5
+ - [X] Align existing API to the SDK
6
+ - [ ] Better Filter DX
7
+ - [ ] Dataclasses for search validation and typing
8
+ - [ ] Docs improvements
9
+ - [ ] Document each endpoint separately to allow for easier information
10
+ - [ ] Some more targeted examples
11
+
12
+ ## Next
13
+
14
+ - Better support for MCPs
15
+
16
+ ## Later
17
+
18
+ -
19
+
20
+ ## Maybe Someday
21
+
22
+ - CLI support
@@ -17,6 +17,7 @@ Complete reference for all Tango Python SDK methods and functionality.
17
17
  - [Business Types](#business-types)
18
18
  - [Webhooks](#webhooks)
19
19
  - [Response Objects](#response-objects)
20
+ - [ShapeConfig (predefined shapes)](#shapeconfig-predefined-shapes)
20
21
  - [Error Handling](#error-handling)
21
22
 
22
23
  ## Client Initialization
@@ -904,6 +905,48 @@ print(f"Total collected: {len(all_results)} results")
904
905
 
905
906
  ---
906
907
 
908
+ ## ShapeConfig (predefined shapes)
909
+
910
+ The SDK provides predefined shape strings as constants on `ShapeConfig`. Use them as the `shape` argument for list/get methods when you want a consistent, validated set of fields without building a custom shape string.
911
+
912
+ ```python
913
+ from tango import TangoClient, ShapeConfig
914
+
915
+ client = TangoClient()
916
+
917
+ # List methods default to the minimal shape when shape is omitted
918
+ contracts = client.list_contracts(limit=10) # uses CONTRACTS_MINIMAL
919
+
920
+ # Or pass the constant explicitly
921
+ contracts = client.list_contracts(shape=ShapeConfig.CONTRACTS_MINIMAL, limit=10)
922
+ entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE)
923
+ ```
924
+
925
+ **Available constants (by resource):**
926
+
927
+ | Constant | Used by | Description |
928
+ |----------|---------|-------------|
929
+ | `CONTRACTS_MINIMAL` | `list_contracts`, `search_contracts` | key, piid, award_date, recipient(display_name), description, total_contract_value |
930
+ | `ENTITIES_MINIMAL` | `list_entities` | uei, legal_business_name, cage_code, business_types |
931
+ | `ENTITIES_COMPREHENSIVE` | `get_entity` | Full entity profile (addresses, naics, psc, obligations, etc.) |
932
+ | `FORECASTS_MINIMAL` | `list_forecasts` | id, title, anticipated_award_date, fiscal_year, naics_code, status |
933
+ | `OPPORTUNITIES_MINIMAL` | `list_opportunities` | opportunity_id, title, solicitation_number, response_deadline, active |
934
+ | `NOTICES_MINIMAL` | `list_notices` | notice_id, title, solicitation_number, posted_date |
935
+ | `GRANTS_MINIMAL` | `list_grants` | grant_id, opportunity_number, title, status(*), agency_code |
936
+ | `IDVS_MINIMAL` | `list_idvs`, `list_vehicle_awardees` | key, piid, award_date, recipient(display_name,uei), description, total_contract_value, obligated, idv_type |
937
+ | `IDVS_COMPREHENSIVE` | `get_idv` | Full IDV with offices, place_of_performance, competition, transactions, etc. |
938
+ | `VEHICLES_MINIMAL` | `list_vehicles` | uuid, solicitation_identifier, organization_id, awardee_count, order_count, vehicle_obligations, vehicle_contracts_value, solicitation_title, solicitation_date |
939
+ | `VEHICLES_COMPREHENSIVE` | `get_vehicle` | Full vehicle with competition_details, fiscal_year, set_aside, etc. |
940
+ | `VEHICLE_AWARDEES_MINIMAL` | `list_vehicle_awardees` | uuid, key, piid, award_date, title, order_count, idv_obligations, idv_contracts_value, recipient(display_name,uei) |
941
+ | `ORGANIZATIONS_MINIMAL` | `list_organizations`, `list_organization_offices` | key, fh_key, name, level, type, short_name |
942
+ | `OTAS_MINIMAL` | `list_otas` | key, piid, award_date, recipient(display_name,uei), description, total_contract_value, obligated |
943
+ | `OTIDVS_MINIMAL` | `list_otidvs` | key, piid, award_date, recipient(display_name,uei), description, total_contract_value, obligated, idv_type |
944
+ | `SUBAWARDS_MINIMAL` | `list_subawards` | award_key, prime_recipient(uei,display_name), subaward_recipient(uei,display_name) |
945
+
946
+ All predefined shapes are validated at SDK release time (see [Developer Guide](DEVELOPERS.md#sdk-conformance-maintainers)). For custom shapes, see the [Shaping Guide](SHAPES.md).
947
+
948
+ ---
949
+
907
950
  ## Error Handling
908
951
 
909
952
  The SDK provides specific exception types for different error scenarios.
@@ -1094,7 +1137,8 @@ client = TangoClient()
1094
1137
 
1095
1138
  ## Additional Resources
1096
1139
 
1097
- - [Shaping Guide](SHAPES.md) - Comprehensive guide to response shaping
1140
+ - [Shaping Guide](SHAPES.md) - Response shaping syntax, examples, and field reference
1141
+ - [Developer Guide](DEVELOPERS.md) - Dynamic models, predefined shapes, and SDK conformance (maintainers)
1098
1142
  - [Quick Start](quick_start.ipynb) - Interactive notebook with examples
1099
1143
  - [GitHub Repository](https://github.com/makegov/tango-python) - Source code and examples
1100
1144
  - [Tango API Documentation](https://tango.makegov.com/docs) - Full API documentation
@@ -12,6 +12,7 @@ The Tango SDK uses dynamic models that generate runtime types matching the exact
12
12
  - [Type Hints and IDE Support](#type-hints-and-ide-support)
13
13
  - [Performance Considerations](#performance-considerations)
14
14
  - [Troubleshooting](#troubleshooting)
15
+ - [SDK conformance (maintainers)](#sdk-conformance-maintainers)
15
16
 
16
17
  ## Overview
17
18
 
@@ -623,6 +624,32 @@ If you encounter issues:
623
624
  3. See [examples/](../examples/) for working code samples
624
625
  4. Contact support at [tango@makegov.com](mailto:tango@makegov.com)
625
626
 
627
+ ## SDK conformance (maintainers)
628
+
629
+ The SDK is kept in sync with the Tango API and its own shape schemas via two conformance checks. Both run in CI on every push and PR (see [Lint workflow](../.github/workflows/lint.yml)) and can be run locally with [scripts/check_filter_shape_conformance.py](../scripts/check_filter_shape_conformance.py).
630
+
631
+ ### Filter conformance
632
+
633
+ - **What it checks:** Every list/get endpoint in the canonical manifest (from the [tango](https://github.com/makegov/tango) API repo) has a matching SDK method that exposes the manifest’s filter parameters—either as explicit arguments or via the method’s `api_param_mapping`.
634
+ - **Why it matters:** Ensures the SDK supports all query filters the API exposes for each resource, so users can filter without relying on undocumented `**kwargs`.
635
+ - **Warnings:** Methods that take filters only via `**kwargs` are reported as warnings (filter names cannot be verified against the manifest).
636
+
637
+ ### Shape conformance
638
+
639
+ - **What it checks:** Every predefined shape in `ShapeConfig` (e.g. `CONTRACTS_MINIMAL`, `IDVS_MINIMAL`, `GRANTS_MINIMAL`) is parsed and validated against the SDK’s explicit schemas in `tango/shapes/explicit_schemas.py`. Each shape must only reference fields that exist for that model (including nested fields).
640
+ - **Why it matters:** Ensures default shapes never reference invalid or renamed fields, so default list/get behavior stays valid after schema or API changes.
641
+ - **Errors:** Parse failures or invalid field names in any `ShapeConfig` constant are reported as errors and fail the check.
642
+
643
+ ### Running the conformance check
644
+
645
+ - **In CI:** The [Lint workflow](../.github/workflows/lint.yml) runs the full check automatically (it has access to the manifest). No setup needed for push/PR.
646
+ - **Locally:** You need the manifest file to run the script. If you have it (e.g. a path to `filter_shape_contract.json` from the [tango](https://github.com/makegov/tango) repo—wherever you keep that repo—or from a colleague), run:
647
+ ```bash
648
+ uv run python scripts/check_filter_shape_conformance.py --manifest /path/to/filter_shape_contract.json
649
+ ```
650
+ If you don’t have the manifest, CI will still run the full check on your branch; shape conformance is included whenever the script runs.
651
+ - **Output:** JSON with `errors` and `warnings`. Exit code 1 if there are any errors. See [scripts/README.md](../scripts/README.md#filter-and-shape-conformance) for full usage and `--list-missing`.
652
+
626
653
  ## Next Steps
627
654
 
628
655
  - Read the [API Reference](API_REFERENCE.md) for detailed class and method documentation
@@ -2,6 +2,8 @@
2
2
 
3
3
  Response shaping lets you control which fields the API returns, making your requests faster and more efficient. Instead of receiving hundreds of fields you don't need, you specify exactly what you want.
4
4
 
5
+ **See also:** [API Reference](API_REFERENCE.md) for method parameters and [ShapeConfig (predefined shapes)](API_REFERENCE.md#shapeconfig-predefined-shapes); [Developer Guide](DEVELOPERS.md) for dynamic models and maintainer conformance.
6
+
5
7
  ## Why Use Response Shaping?
6
8
 
7
9
  **Performance Benefits:**
@@ -27,11 +29,36 @@ contracts = client.list_contracts(
27
29
  shape="key,piid,recipient(display_name),total_contract_value"
28
30
  )
29
31
 
32
+ # Or use a predefined shape constant
33
+ from tango import ShapeConfig
34
+ contracts = client.list_contracts(shape=ShapeConfig.CONTRACTS_MINIMAL, limit=10)
35
+
30
36
  # Access the data
31
37
  for contract in contracts.results:
32
38
  print(f"{contract['piid']}: {contract['recipient']['display_name']}")
33
39
  ```
34
40
 
41
+ ## Predefined shapes (ShapeConfig)
42
+
43
+ Instead of writing shape strings by hand, you can use the SDK’s predefined constants. Each list/get method has a default minimal shape when you omit `shape`; you can also pass a constant explicitly.
44
+
45
+ ```python
46
+ from tango import TangoClient, ShapeConfig
47
+
48
+ client = TangoClient()
49
+
50
+ # These are equivalent (list_contracts defaults to CONTRACTS_MINIMAL)
51
+ contracts = client.list_contracts(limit=10)
52
+ contracts = client.list_contracts(shape=ShapeConfig.CONTRACTS_MINIMAL, limit=10)
53
+
54
+ # Other resources
55
+ entities = client.list_entities(shape=ShapeConfig.ENTITIES_MINIMAL)
56
+ idvs = client.list_idvs(shape=ShapeConfig.IDVS_MINIMAL)
57
+ grants = client.list_grants(shape=ShapeConfig.GRANTS_MINIMAL)
58
+ ```
59
+
60
+ **Available constants:** Contracts (`CONTRACTS_MINIMAL`), Entities (`ENTITIES_MINIMAL`, `ENTITIES_COMPREHENSIVE`), Forecasts, Opportunities, Notices, Grants, IDVs, Vehicles, Organizations, OTAs, OTIDVs, Subawards. See [API Reference – ShapeConfig](API_REFERENCE.md#shapeconfig-predefined-shapes) for the full table and which method uses which constant.
61
+
35
62
  ## Basic Shaping
36
63
 
37
64
  ### Simple Fields
@@ -455,5 +482,6 @@ display_name = contract.get('recipient', {}).get('display_name', 'Unknown')
455
482
  - **Define patterns** - Create reusable shapes for your common queries
456
483
 
457
484
  For more help, see:
458
- - [Quick Start Guide](quick_start.ipynb) - Interactive examples
459
- - [API Reference](API_REFERENCE.md) - Complete field listings
485
+ - [API Reference](API_REFERENCE.md) - Method parameters, [ShapeConfig table](API_REFERENCE.md#shapeconfig-predefined-shapes), and field context
486
+ - [Developer Guide](DEVELOPERS.md) - Dynamic models, predefined shapes in depth, and SDK conformance (for maintainers)
487
+ - [Quick Start Guide](quick_start.ipynb) - Interactive examples
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tango-python"
7
- version = "0.3.0"
7
+ version = "0.4.1"
8
8
  description = "Python SDK for the Tango API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -0,0 +1,100 @@
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
+
18
+ ## Usage
19
+
20
+ These scripts are primarily for maintainers and are not part of the public API.
21
+
22
+ ### Production API Testing
23
+
24
+ Run smoke tests against the production API:
25
+
26
+ ```bash
27
+ # Requires TANGO_API_KEY environment variable
28
+ uv run python scripts/test_production.py
29
+ ```
30
+
31
+ This runs fast smoke tests (~30-60 seconds) that validate core SDK functionality against the live production API.
32
+
33
+ ### PR Review Validation
34
+
35
+ Run validation checks for PR review. **Automatically detects PR context** from GitHub Actions, GitHub CLI, or git branch.
36
+
37
+ ```bash
38
+ # Auto-detect PR and run validation (default: production mode)
39
+ uv run python scripts/pr_review.py
40
+
41
+ # Review a specific PR number
42
+ uv run python scripts/pr_review.py --pr-number 123
43
+
44
+ # Smoke tests only
45
+ uv run python scripts/pr_review.py --mode smoke
46
+
47
+ # Quick validation (linting + type checking, no tests)
48
+ uv run python scripts/pr_review.py --mode quick
49
+
50
+ # Full validation (all checks including all tests)
51
+ uv run python scripts/pr_review.py --mode full
52
+
53
+ # Check only changed files (auto-enabled when PR is detected)
54
+ uv run python scripts/pr_review.py --mode production --changed-files-only
55
+ ```
56
+
57
+ **Validation Modes:**
58
+ - `smoke` - Production API smoke tests only
59
+ - `quick` - Linting + type checking (no tests)
60
+ - `full` - All checks (linting + type checking + filter/shape conformance + all tests)
61
+ - `production` - Linting + type checking + filter/shape conformance + production API smoke tests (default)
62
+
63
+ When the conformance manifest is present (`tango-api/contracts/filter_shape_contract.json` or `TANGO_CONTRACT_MANIFEST`), both `full` and `production` run the [filter and shape conformance](#filter-and-shape-conformance) check. If the manifest is missing, that step is skipped with a warning.
64
+
65
+ **PR Detection:**
66
+ The script automatically detects PR information from:
67
+ - GitHub Actions environment variables (`GITHUB_EVENT_PATH`, `GITHUB_PR_NUMBER`)
68
+ - GitHub CLI (`gh pr view` - requires `gh auth login`)
69
+ - Current git branch
70
+
71
+ When a PR is detected, the script displays PR information and automatically:
72
+ - Detects the base branch from the PR
73
+ - Checks only changed files
74
+ - Shows PR URL in summary
75
+
76
+ ### Filter and shape conformance
77
+
78
+ The SDK is validated against a canonical manifest (from the [tango](https://github.com/makegov/tango) API repo) for **filter conformance** and against its own schemas for **shape conformance**:
79
+
80
+ 1. **Filter conformance** – Ensures every list/get method exposes the filter parameters defined in the manifest (e.g. `award_date`, `naics_code`, `agency`). Methods that use `**kwargs` for filters are reported as warnings because their filter names cannot be verified.
81
+ 2. **Shape conformance** – Ensures 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.
82
+
83
+ This script runs in CI on every push/PR (see [Lint workflow](.github/workflows/lint.yml)). The manifest file is produced by the tango API repo and must be available for the full check.
84
+
85
+ ```bash
86
+ # Full check (filter + shape). Requires manifest from tango API repo.
87
+ uv run python scripts/check_filter_shape_conformance.py --manifest tango-api/contracts/filter_shape_contract.json
88
+
89
+ # List resources that have no matching SDK method (for implementation checklist)
90
+ uv run python scripts/check_filter_shape_conformance.py --manifest tango-api/contracts/filter_shape_contract.json --list-missing
91
+ ```
92
+
93
+ **Output:** JSON with `manifest`, `errors`, and `warnings`. Exit code 1 if there are any errors (missing filters, invalid shapes, or missing SDK methods for manifest resources).
94
+
95
+ **Local runs:** To run the full check locally, you need a copy of `filter_shape_contract.json` (from the [tango](https://github.com/makegov/tango) repo’s `contracts/` directory—wherever you keep that repo). Pass it with `--manifest` or set `TANGO_CONTRACT_MANIFEST`. The script runs both filter and shape conformance; shape conformance validates all `ShapeConfig` defaults against `tango/shapes/explicit_schemas.py`.
96
+
97
+ ## Requirements
98
+
99
+ - `TANGO_API_KEY` environment variable (required for production API tests)
100
+ - All dependencies installed: `uv sync --all-extras`