@typespec/http-client-python 0.28.2 → 0.29.0

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 (368) hide show
  1. package/dist/emitter/code-model.d.ts.map +1 -1
  2. package/dist/emitter/code-model.js +7 -6
  3. package/dist/emitter/code-model.js.map +1 -1
  4. package/dist/emitter/constants.d.ts +6 -0
  5. package/dist/emitter/constants.d.ts.map +1 -0
  6. package/dist/emitter/constants.js +27 -0
  7. package/dist/emitter/constants.js.map +1 -0
  8. package/dist/emitter/emitter.d.ts.map +1 -1
  9. package/dist/emitter/emitter.js +193 -119
  10. package/dist/emitter/emitter.js.map +1 -1
  11. package/dist/emitter/http.d.ts.map +1 -1
  12. package/dist/emitter/http.js +33 -1
  13. package/dist/emitter/http.js.map +1 -1
  14. package/dist/emitter/lib.d.ts +37 -3
  15. package/dist/emitter/lib.d.ts.map +1 -1
  16. package/dist/emitter/lib.js +17 -0
  17. package/dist/emitter/lib.js.map +1 -1
  18. package/dist/emitter/run-python3.js +1 -1
  19. package/dist/emitter/run-python3.js.map +1 -1
  20. package/dist/emitter/system-requirements.js +6 -4
  21. package/dist/emitter/system-requirements.js.map +1 -1
  22. package/dist/emitter/types.d.ts +2 -5
  23. package/dist/emitter/types.d.ts.map +1 -1
  24. package/dist/emitter/types.js +86 -63
  25. package/dist/emitter/types.js.map +1 -1
  26. package/dist/emitter/utils.d.ts.map +1 -1
  27. package/dist/emitter/utils.js +16 -17
  28. package/dist/emitter/utils.js.map +1 -1
  29. package/emitter/src/code-model.ts +7 -13
  30. package/emitter/src/constants.ts +27 -0
  31. package/emitter/src/emitter.ts +241 -123
  32. package/emitter/src/http.ts +29 -1
  33. package/emitter/src/lib.ts +24 -0
  34. package/emitter/src/run-python3.ts +1 -1
  35. package/emitter/src/system-requirements.ts +4 -4
  36. package/emitter/src/types.ts +102 -62
  37. package/emitter/src/utils.ts +16 -18
  38. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  39. package/emitter/test/utils.test.ts +3 -0
  40. package/emitter/tsconfig.build.json +2 -1
  41. package/emitter/tsconfig.json +2 -1
  42. package/eng/scripts/Build-Packages.ps1 +62 -37
  43. package/eng/scripts/Check-GitChanges.ps1 +30 -3
  44. package/eng/scripts/Functions.ps1 +54 -15
  45. package/eng/scripts/Generate.ps1 +42 -8
  46. package/eng/scripts/Generation.psm1 +44 -15
  47. package/eng/scripts/Initialize-Repository.ps1 +29 -11
  48. package/eng/scripts/Test-Packages.ps1 +47 -31
  49. package/eng/scripts/ci/config/eslint-ci.config.mjs +64 -0
  50. package/eng/scripts/ci/{mypy.ini → config/mypy.ini} +1 -1
  51. package/eng/scripts/ci/{pylintrc → config/pylintrc} +1 -1
  52. package/eng/scripts/ci/{pyrightconfig.json → config/pyrightconfig.json} +1 -1
  53. package/eng/scripts/ci/dev_requirements.txt +2 -1
  54. package/eng/scripts/ci/format.ts +232 -2
  55. package/eng/scripts/ci/lint.ts +223 -20
  56. package/eng/scripts/ci/regenerate-common.ts +411 -136
  57. package/eng/scripts/ci/regenerate.ts +247 -101
  58. package/eng/scripts/ci/run-tests.ts +507 -0
  59. package/eng/scripts/ci/run_apiview.py +22 -16
  60. package/eng/scripts/ci/run_mypy.py +34 -23
  61. package/eng/scripts/ci/run_pylint.py +27 -19
  62. package/eng/scripts/ci/run_pyright.py +9 -5
  63. package/eng/scripts/ci/run_sphinx_build.py +24 -9
  64. package/eng/scripts/ci/typecheck.ts +225 -0
  65. package/eng/scripts/ci/util.py +61 -25
  66. package/eng/scripts/ci/utils.ts +5 -5
  67. package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
  68. package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
  69. package/eng/scripts/setup/build.ts +1 -1
  70. package/eng/scripts/setup/build_pygen_wheel.py +4 -3
  71. package/eng/scripts/setup/install.py +4 -3
  72. package/eng/scripts/setup/install.ts +1 -1
  73. package/eng/scripts/setup/prepare.py +3 -2
  74. package/eng/scripts/setup/prepare.ts +1 -1
  75. package/eng/scripts/setup/run-python3.ts +1 -1
  76. package/eng/scripts/setup/run_batch.py +197 -0
  77. package/eng/scripts/setup/run_tsp.py +3 -3
  78. package/eng/scripts/setup/system-requirements.ts +8 -4
  79. package/eng/scripts/setup/venvtools.py +1 -1
  80. package/generator/build/lib/pygen/__init__.py +1 -3
  81. package/generator/build/lib/pygen/codegen/__init__.py +0 -1
  82. package/generator/build/lib/pygen/codegen/models/base.py +0 -1
  83. package/generator/build/lib/pygen/codegen/models/client.py +2 -1
  84. package/generator/build/lib/pygen/codegen/models/code_model.py +1 -0
  85. package/generator/build/lib/pygen/codegen/models/enum_type.py +0 -1
  86. package/generator/build/lib/pygen/codegen/models/model_type.py +1 -7
  87. package/generator/build/lib/pygen/codegen/models/parameter.py +16 -14
  88. package/generator/build/lib/pygen/codegen/serializers/__init__.py +27 -12
  89. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +12 -3
  90. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +6 -2
  91. package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +17 -4
  92. package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +17 -11
  93. package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +176 -71
  94. package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +11 -3
  95. package/generator/build/lib/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +1 -1
  96. package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +5 -1
  97. package/generator/build/lib/pygen/preprocess/__init__.py +37 -30
  98. package/generator/component-detection-pip-report.json +7 -7
  99. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  100. package/generator/pygen/__init__.py +1 -3
  101. package/generator/pygen/codegen/__init__.py +0 -1
  102. package/generator/pygen/codegen/models/base.py +0 -1
  103. package/generator/pygen/codegen/models/client.py +2 -1
  104. package/generator/pygen/codegen/models/code_model.py +1 -0
  105. package/generator/pygen/codegen/models/enum_type.py +0 -1
  106. package/generator/pygen/codegen/models/model_type.py +1 -7
  107. package/generator/pygen/codegen/models/parameter.py +16 -14
  108. package/generator/pygen/codegen/serializers/__init__.py +27 -12
  109. package/generator/pygen/codegen/serializers/builder_serializer.py +12 -3
  110. package/generator/pygen/codegen/serializers/general_serializer.py +6 -2
  111. package/generator/pygen/codegen/serializers/import_serializer.py +17 -4
  112. package/generator/pygen/codegen/serializers/parameter_serializer.py +17 -11
  113. package/generator/pygen/codegen/templates/model_base.py.jinja2 +176 -71
  114. package/generator/pygen/codegen/templates/packaging_templates/README.md.jinja2 +11 -3
  115. package/generator/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +1 -1
  116. package/generator/pygen/codegen/templates/serialization.py.jinja2 +5 -1
  117. package/generator/pygen/preprocess/__init__.py +37 -30
  118. package/generator/pygen.egg-info/PKG-INFO +1 -2
  119. package/generator/pygen.egg-info/requires.txt +1 -1
  120. package/generator/setup.py +1 -2
  121. package/package.json +63 -42
  122. package/eng/scripts/ci/run-ci.ts +0 -89
  123. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_commonproperties_async.py +0 -94
  124. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_largeheader_async.py +0 -30
  125. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_nonresource_async.py +0 -36
  126. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_operationtemplates_async.py +0 -205
  127. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_resource_async.py +0 -478
  128. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_access_async.py +0 -101
  129. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_header_async.py +0 -18
  130. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_path_async.py +0 -18
  131. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_query_async.py +0 -18
  132. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_default_value_async.py +0 -46
  133. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_initialization_async.py +0 -58
  134. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_location_async.py +0 -82
  135. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_deserialize_empty_string_as_null_async.py +0 -20
  136. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_flatten_async.py +0 -115
  137. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_hierrarchy_building_async.py +0 -53
  138. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_next_link_verb_async.py +0 -28
  139. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_override_async.py +0 -106
  140. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_usage_async.py +0 -38
  141. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_basic_async.py +0 -76
  142. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_rpc_async.py +0 -22
  143. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_standard_async.py +0 -39
  144. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_model_async.py +0 -33
  145. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_page_async.py +0 -80
  146. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_scalar_async.py +0 -41
  147. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_traits_async.py +0 -87
  148. package/generator/test/azure/mock_api_tests/asynctests/test_azure_encode_duration_async.py +0 -19
  149. package/generator/test/azure/mock_api_tests/asynctests/test_azure_example_basic_async.py +0 -30
  150. package/generator/test/azure/mock_api_tests/asynctests/test_azure_payload_pageable_async.py +0 -19
  151. package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_method_subscription_id_async.py +0 -248
  152. package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_multi_service_async.py +0 -110
  153. package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_multi_service_shared_models_async.py +0 -132
  154. package/generator/test/azure/mock_api_tests/asynctests/test_azure_special_headers_client_request_id_async.py +0 -30
  155. package/generator/test/azure/mock_api_tests/asynctests/test_azure_versioning_previewversion_async.py +0 -53
  156. package/generator/test/azure/mock_api_tests/asynctests/test_client_namespace_async.py +0 -34
  157. package/generator/test/azure/mock_api_tests/asynctests/test_client_naming_async.py +0 -69
  158. package/generator/test/azure/mock_api_tests/asynctests/test_client_naming_enum_conflict_async.py +0 -37
  159. package/generator/test/azure/mock_api_tests/asynctests/test_client_overload_async.py +0 -27
  160. package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_async.py +0 -64
  161. package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_clientoperationgroup_async.py +0 -31
  162. package/generator/test/azure/mock_api_tests/asynctests/test_encode_duration_async.py +0 -63
  163. package/generator/test/azure/mock_api_tests/asynctests/test_encode_numeric_async.py +0 -35
  164. package/generator/test/azure/mock_api_tests/asynctests/test_parameters_basic_async.py +0 -24
  165. package/generator/test/azure/mock_api_tests/asynctests/test_parameters_spread_async.py +0 -76
  166. package/generator/test/azure/mock_api_tests/asynctests/test_payload_content_negotiation_async.py +0 -37
  167. package/generator/test/azure/mock_api_tests/asynctests/test_payload_multipart_async.py +0 -216
  168. package/generator/test/azure/mock_api_tests/asynctests/test_resiliency_srv_driven_async.py +0 -130
  169. package/generator/test/azure/mock_api_tests/asynctests/test_serialization_encoded_name_json_async.py +0 -24
  170. package/generator/test/azure/mock_api_tests/asynctests/test_service_multi_service_async.py +0 -31
  171. package/generator/test/azure/mock_api_tests/asynctests/test_special_words_async.py +0 -71
  172. package/generator/test/azure/mock_api_tests/conftest.py +0 -207
  173. package/generator/test/azure/mock_api_tests/data/image.jpg +0 -0
  174. package/generator/test/azure/mock_api_tests/data/image.png +0 -0
  175. package/generator/test/azure/mock_api_tests/test_azure_arm_commonproperties.py +0 -89
  176. package/generator/test/azure/mock_api_tests/test_azure_arm_largeheader.py +0 -27
  177. package/generator/test/azure/mock_api_tests/test_azure_arm_nonresource.py +0 -34
  178. package/generator/test/azure/mock_api_tests/test_azure_arm_operationtemplates.py +0 -180
  179. package/generator/test/azure/mock_api_tests/test_azure_arm_resource.py +0 -436
  180. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_access.py +0 -92
  181. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_header.py +0 -17
  182. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_path.py +0 -17
  183. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_query.py +0 -17
  184. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_default_value.py +0 -42
  185. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_initialization.py +0 -51
  186. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_location.py +0 -70
  187. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_deserialize_empty_string_as_null.py +0 -19
  188. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_flatten.py +0 -104
  189. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_hierrarchy_building.py +0 -45
  190. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_next_link_verb.py +0 -27
  191. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_override.py +0 -102
  192. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_usage.py +0 -32
  193. package/generator/test/azure/mock_api_tests/test_azure_core_basic.py +0 -70
  194. package/generator/test/azure/mock_api_tests/test_azure_core_lro_rpc.py +0 -20
  195. package/generator/test/azure/mock_api_tests/test_azure_core_lro_standard.py +0 -32
  196. package/generator/test/azure/mock_api_tests/test_azure_core_model.py +0 -30
  197. package/generator/test/azure/mock_api_tests/test_azure_core_page.py +0 -71
  198. package/generator/test/azure/mock_api_tests/test_azure_core_scalar.py +0 -35
  199. package/generator/test/azure/mock_api_tests/test_azure_core_traits.py +0 -85
  200. package/generator/test/azure/mock_api_tests/test_azure_encode_duration.py +0 -18
  201. package/generator/test/azure/mock_api_tests/test_azure_example_basic.py +0 -29
  202. package/generator/test/azure/mock_api_tests/test_azure_payload_pageable.py +0 -18
  203. package/generator/test/azure/mock_api_tests/test_azure_resource_manager_method_subscription_id.py +0 -235
  204. package/generator/test/azure/mock_api_tests/test_azure_resource_manager_multi_service.py +0 -104
  205. package/generator/test/azure/mock_api_tests/test_azure_resource_manager_multi_service_shared_models.py +0 -128
  206. package/generator/test/azure/mock_api_tests/test_azure_special_headers_client_request_id.py +0 -29
  207. package/generator/test/azure/mock_api_tests/test_azure_versioning_previewversion.py +0 -50
  208. package/generator/test/azure/mock_api_tests/test_clear_output_folder.py +0 -23
  209. package/generator/test/azure/mock_api_tests/test_client_namespace.py +0 -32
  210. package/generator/test/azure/mock_api_tests/test_client_naming.py +0 -57
  211. package/generator/test/azure/mock_api_tests/test_client_naming_enum_conflict.py +0 -35
  212. package/generator/test/azure/mock_api_tests/test_client_overload.py +0 -25
  213. package/generator/test/azure/mock_api_tests/test_client_structure.py +0 -60
  214. package/generator/test/azure/mock_api_tests/test_client_structure_clientoperationgroup.py +0 -29
  215. package/generator/test/azure/mock_api_tests/test_encode_duration.py +0 -60
  216. package/generator/test/azure/mock_api_tests/test_encode_numeric.py +0 -31
  217. package/generator/test/azure/mock_api_tests/test_model_base_flatten_compatibility.py +0 -251
  218. package/generator/test/azure/mock_api_tests/test_parameters_basic.py +0 -22
  219. package/generator/test/azure/mock_api_tests/test_parameters_spread.py +0 -66
  220. package/generator/test/azure/mock_api_tests/test_payload_content_negotiation.py +0 -33
  221. package/generator/test/azure/mock_api_tests/test_payload_multipart.py +0 -198
  222. package/generator/test/azure/mock_api_tests/test_resiliency_srv_driven.py +0 -124
  223. package/generator/test/azure/mock_api_tests/test_serialization_encoded_name_json.py +0 -21
  224. package/generator/test/azure/mock_api_tests/test_service_multi_service.py +0 -29
  225. package/generator/test/azure/mock_api_tests/test_special_words.py +0 -63
  226. package/generator/test/azure/requirements.txt +0 -125
  227. package/generator/test/azure/tox.ini +0 -64
  228. package/generator/test/data/image.jpg +0 -0
  229. package/generator/test/data/image.png +0 -0
  230. package/generator/test/dev_requirements.txt +0 -8
  231. package/generator/test/generic_mock_api_tests/asynctests/test_authentication_async.py +0 -140
  232. package/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py +0 -139
  233. package/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py +0 -132
  234. package/generator/test/generic_mock_api_tests/asynctests/test_encode_datetime_async.py +0 -127
  235. package/generator/test/generic_mock_api_tests/asynctests/test_headasboolean_async.py +0 -35
  236. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_body_optionality_async.py +0 -30
  237. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_collection_format_async.py +0 -38
  238. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_path_async.py +0 -24
  239. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_query_async.py +0 -18
  240. package/generator/test/generic_mock_api_tests/asynctests/test_payload_json_merge_patch_async.py +0 -98
  241. package/generator/test/generic_mock_api_tests/asynctests/test_payload_media_type_async.py +0 -27
  242. package/generator/test/generic_mock_api_tests/asynctests/test_payload_pageable_async.py +0 -134
  243. package/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py +0 -150
  244. package/generator/test/generic_mock_api_tests/asynctests/test_response_status_code_range_async.py +0 -38
  245. package/generator/test/generic_mock_api_tests/asynctests/test_routes_async.py +0 -331
  246. package/generator/test/generic_mock_api_tests/asynctests/test_server_endpoint_not_defined_async.py +0 -18
  247. package/generator/test/generic_mock_api_tests/asynctests/test_server_path_multiple_async.py +0 -25
  248. package/generator/test/generic_mock_api_tests/asynctests/test_server_path_single_async.py +0 -18
  249. package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_not_versioned_async.py +0 -28
  250. package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_versioned_async.py +0 -34
  251. package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_conditional_request_async.py +0 -38
  252. package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_repeatability_async.py +0 -19
  253. package/generator/test/generic_mock_api_tests/asynctests/test_specs_documentation_async.py +0 -59
  254. package/generator/test/generic_mock_api_tests/asynctests/test_streaming_jsonl_async.py +0 -27
  255. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_array_async.py +0 -127
  256. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_dictionary_async.py +0 -98
  257. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_extensible_async.py +0 -25
  258. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_fixed_async.py +0 -27
  259. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_file_async.py +0 -59
  260. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_empty_async.py +0 -32
  261. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_enum_discriminator_async.py +0 -70
  262. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_nested_discriminator_async.py +0 -85
  263. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_not_discriminated_async.py +0 -34
  264. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_recursive_async.py +0 -34
  265. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_single_discriminator_async.py +0 -67
  266. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_usage_async.py +0 -32
  267. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_visibility_async.py +0 -47
  268. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_additionalproperties_async.py +0 -353
  269. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py +0 -110
  270. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_optional_async.py +0 -197
  271. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_valuetypes_async.py +0 -315
  272. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_scalar_async.py +0 -60
  273. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_union_async.py +0 -90
  274. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_added_async.py +0 -36
  275. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_made_optional_async.py +0 -21
  276. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_removed_async.py +0 -39
  277. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_renamed_from_async.py +0 -29
  278. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_return_type_changed_from_async.py +0 -18
  279. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_type_changed_from_async.py +0 -22
  280. package/generator/test/generic_mock_api_tests/conftest.py +0 -75
  281. package/generator/test/generic_mock_api_tests/data/image.jpg +0 -0
  282. package/generator/test/generic_mock_api_tests/data/image.png +0 -0
  283. package/generator/test/generic_mock_api_tests/test_authentication.py +0 -130
  284. package/generator/test/generic_mock_api_tests/test_encode_array.py +0 -126
  285. package/generator/test/generic_mock_api_tests/test_encode_bytes.py +0 -128
  286. package/generator/test/generic_mock_api_tests/test_encode_datetime.py +0 -123
  287. package/generator/test/generic_mock_api_tests/test_headasboolean.py +0 -33
  288. package/generator/test/generic_mock_api_tests/test_parameters_body_optionality.py +0 -27
  289. package/generator/test/generic_mock_api_tests/test_parameters_collection_format.py +0 -33
  290. package/generator/test/generic_mock_api_tests/test_parameters_path.py +0 -22
  291. package/generator/test/generic_mock_api_tests/test_parameters_query.py +0 -17
  292. package/generator/test/generic_mock_api_tests/test_payload_json_merge_patch.py +0 -93
  293. package/generator/test/generic_mock_api_tests/test_payload_media_type.py +0 -25
  294. package/generator/test/generic_mock_api_tests/test_payload_pageable.py +0 -99
  295. package/generator/test/generic_mock_api_tests/test_payload_xml.py +0 -135
  296. package/generator/test/generic_mock_api_tests/test_response_status_code_range.py +0 -36
  297. package/generator/test/generic_mock_api_tests/test_routes.py +0 -285
  298. package/generator/test/generic_mock_api_tests/test_server_endpoint_not_defined.py +0 -17
  299. package/generator/test/generic_mock_api_tests/test_server_path_multiple.py +0 -21
  300. package/generator/test/generic_mock_api_tests/test_server_path_single.py +0 -17
  301. package/generator/test/generic_mock_api_tests/test_server_versions_not_versioned.py +0 -25
  302. package/generator/test/generic_mock_api_tests/test_server_versions_versioned.py +0 -30
  303. package/generator/test/generic_mock_api_tests/test_special_headers_conditional_request.py +0 -34
  304. package/generator/test/generic_mock_api_tests/test_special_headers_repeatability.py +0 -18
  305. package/generator/test/generic_mock_api_tests/test_specs_documentation.py +0 -51
  306. package/generator/test/generic_mock_api_tests/test_streaming_jsonl.py +0 -25
  307. package/generator/test/generic_mock_api_tests/test_typetest_array.py +0 -111
  308. package/generator/test/generic_mock_api_tests/test_typetest_dictionary.py +0 -86
  309. package/generator/test/generic_mock_api_tests/test_typetest_enum_extensible.py +0 -23
  310. package/generator/test/generic_mock_api_tests/test_typetest_enum_fixed.py +0 -25
  311. package/generator/test/generic_mock_api_tests/test_typetest_file.py +0 -52
  312. package/generator/test/generic_mock_api_tests/test_typetest_model_empty.py +0 -29
  313. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_enum_discriminator.py +0 -58
  314. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_nested_discriminator.py +0 -79
  315. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_not_discriminated.py +0 -31
  316. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_recursive.py +0 -32
  317. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_single_discriminator.py +0 -60
  318. package/generator/test/generic_mock_api_tests/test_typetest_model_usage.py +0 -28
  319. package/generator/test/generic_mock_api_tests/test_typetest_model_visibility.py +0 -40
  320. package/generator/test/generic_mock_api_tests/test_typetest_property_additionalproperties.py +0 -314
  321. package/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py +0 -102
  322. package/generator/test/generic_mock_api_tests/test_typetest_property_optional.py +0 -174
  323. package/generator/test/generic_mock_api_tests/test_typetest_property_valuetypes.py +0 -286
  324. package/generator/test/generic_mock_api_tests/test_typetest_scalar.py +0 -53
  325. package/generator/test/generic_mock_api_tests/test_typetest_union.py +0 -80
  326. package/generator/test/generic_mock_api_tests/test_typetest_union_discriminated.py +0 -290
  327. package/generator/test/generic_mock_api_tests/test_versioning_added.py +0 -33
  328. package/generator/test/generic_mock_api_tests/test_versioning_made_optional.py +0 -20
  329. package/generator/test/generic_mock_api_tests/test_versioning_removed.py +0 -37
  330. package/generator/test/generic_mock_api_tests/test_versioning_renamed_from.py +0 -27
  331. package/generator/test/generic_mock_api_tests/test_versioning_return_type_changed_from.py +0 -17
  332. package/generator/test/generic_mock_api_tests/test_versioning_type_changed_from.py +0 -21
  333. package/generator/test/unbranded/mock_api_tests/asynctests/test_auth_flow_async.py +0 -19
  334. package/generator/test/unbranded/mock_api_tests/asynctests/test_encode_duration_async.py +0 -63
  335. package/generator/test/unbranded/mock_api_tests/asynctests/test_encode_numeric_async.py +0 -35
  336. package/generator/test/unbranded/mock_api_tests/asynctests/test_parameters_basic_async.py +0 -24
  337. package/generator/test/unbranded/mock_api_tests/asynctests/test_parameters_spread_async.py +0 -76
  338. package/generator/test/unbranded/mock_api_tests/asynctests/test_payload_content_negotiation_async.py +0 -37
  339. package/generator/test/unbranded/mock_api_tests/asynctests/test_payload_multipart_async.py +0 -218
  340. package/generator/test/unbranded/mock_api_tests/asynctests/test_serialization_encoded_name_json_async.py +0 -24
  341. package/generator/test/unbranded/mock_api_tests/asynctests/test_special_words_async.py +0 -73
  342. package/generator/test/unbranded/mock_api_tests/asynctests/test_unbranded_async.py +0 -24
  343. package/generator/test/unbranded/mock_api_tests/conftest.py +0 -91
  344. package/generator/test/unbranded/mock_api_tests/data/image.jpg +0 -0
  345. package/generator/test/unbranded/mock_api_tests/data/image.png +0 -0
  346. package/generator/test/unbranded/mock_api_tests/test_auth_flow.py +0 -17
  347. package/generator/test/unbranded/mock_api_tests/test_encode_duration.py +0 -60
  348. package/generator/test/unbranded/mock_api_tests/test_encode_numeric.py +0 -32
  349. package/generator/test/unbranded/mock_api_tests/test_parameters_basic.py +0 -22
  350. package/generator/test/unbranded/mock_api_tests/test_parameters_spread.py +0 -66
  351. package/generator/test/unbranded/mock_api_tests/test_payload_content_negotiation.py +0 -33
  352. package/generator/test/unbranded/mock_api_tests/test_payload_multipart.py +0 -200
  353. package/generator/test/unbranded/mock_api_tests/test_serialization_encoded_name_json.py +0 -22
  354. package/generator/test/unbranded/mock_api_tests/test_special_words.py +0 -66
  355. package/generator/test/unbranded/mock_api_tests/test_unbranded.py +0 -65
  356. package/generator/test/unbranded/requirements.txt +0 -69
  357. package/generator/test/unbranded/tox.ini +0 -64
  358. package/generator/test/unittests/conftest.py +0 -15
  359. package/generator/test/unittests/requirements.txt +0 -3
  360. package/generator/test/unittests/test_enums.py +0 -89
  361. package/generator/test/unittests/test_model_base_serialization.py +0 -4906
  362. package/generator/test/unittests/test_model_base_xml_serialization.py +0 -1239
  363. package/generator/test/unittests/test_name_converter.py +0 -35
  364. package/generator/test/unittests/test_optional_return_type.py +0 -268
  365. package/generator/test/unittests/test_parameter_ordering.py +0 -104
  366. package/generator/test/unittests/test_sort_schema.py +0 -93
  367. package/generator/test/unittests/tox.ini +0 -5
  368. /package/eng/scripts/ci/{pyproject.toml → config/pyproject.toml} +0 -0
@@ -0,0 +1,507 @@
1
+ /* eslint-disable no-console */
2
+ import { ChildProcess, spawn } from "child_process";
3
+ import fs from "fs";
4
+ import { cpus } from "os";
5
+ import { dirname, join } from "path";
6
+ import pc from "picocolors";
7
+ import { fileURLToPath } from "url";
8
+ import { parseArgs } from "util";
9
+
10
+ const root = join(dirname(fileURLToPath(import.meta.url)), "../../../");
11
+ const monorepoRoot = join(root, "../../");
12
+ const testsDir = join(root, "tests");
13
+
14
+ const argv = parseArgs({
15
+ args: process.argv.slice(2),
16
+ options: {
17
+ emitter: { type: "boolean", short: "e" },
18
+ generator: { type: "boolean", short: "g" },
19
+ flavor: { type: "string", short: "f", default: "all" },
20
+ env: { type: "string" },
21
+ jobs: { type: "string", short: "j" },
22
+ name: { type: "string", short: "n" },
23
+ quiet: { type: "boolean", short: "q", default: false },
24
+ help: { type: "boolean", short: "h", default: false },
25
+ },
26
+ });
27
+
28
+ if (argv.values.help) {
29
+ console.log(`
30
+ Usage: run-tests.ts [options]
31
+
32
+ Options:
33
+ -e, --emitter Run TypeScript emitter tests (vitest)
34
+ -g, --generator Run Python generator tests (tox)
35
+ -f, --flavor <azure|unbranded> SDK flavor to test (only applies to --generator)
36
+ If not specified, tests both flavors
37
+ --env <env1,env2,...> Specific tox environments to run
38
+ Available: test, lint, mypy, pyright, apiview, sphinx, docs, ci, unittest
39
+ -j, --jobs <n> Number of parallel jobs (default: CPU cores - 2)
40
+ -n, --name <pattern> Filter tests by name pattern
41
+ -q, --quiet Suppress test output (only show pass/fail summary)
42
+ -h, --help Show this help message
43
+
44
+ Environments (for --generator):
45
+ test Run pytest tests for generated packages
46
+ lint Run pylint on generated packages
47
+ mypy Run mypy type checking on generated packages
48
+ pyright Run pyright type checking on generated packages
49
+ apiview Run apiview validation on generated packages
50
+ sphinx Run sphinx docstring validation on generated packages
51
+ docs Run apiview + sphinx (split into parallel envs)
52
+ ci Run all checks (test + lint + mypy + pyright + apiview + sphinx)
53
+ unittest Run unit tests for pygen internals
54
+
55
+ Examples:
56
+ run-tests.ts # Run all tests (emitter + generator)
57
+ run-tests.ts --emitter # Run only emitter tests
58
+ run-tests.ts --generator # Run generator tests for all flavors
59
+ run-tests.ts --generator --flavor=azure # Run generator tests for azure only
60
+ run-tests.ts -g -f azure --env=test # Run pytest for azure only
61
+ run-tests.ts -g --env=lint # Run pylint for all flavors
62
+ run-tests.ts -g -f unbranded --env=mypy # Run mypy for unbranded only
63
+ `);
64
+ process.exit(0);
65
+ }
66
+
67
+ // Get Python venv path
68
+ function getVenvPython(): string {
69
+ const venvPath = join(root, "venv");
70
+ if (fs.existsSync(join(venvPath, "bin"))) {
71
+ return join(venvPath, "bin", "python");
72
+ } else if (fs.existsSync(join(venvPath, "Scripts"))) {
73
+ return join(venvPath, "Scripts", "python.exe");
74
+ }
75
+ throw new Error("Virtual environment not found. Run 'npm run install' first.");
76
+ }
77
+
78
+ interface ToxResult {
79
+ env: string;
80
+ success: boolean;
81
+ duration: number;
82
+ error?: string;
83
+ failedTests?: string[];
84
+ }
85
+
86
+ interface RunningTask {
87
+ env: string;
88
+ proc: ChildProcess;
89
+ promise: Promise<ToxResult>;
90
+ }
91
+
92
+ function extractFailedTests(output: string): string[] {
93
+ const lines = output.split("\n");
94
+ const failedTests: string[] = [];
95
+ let inSummary = false;
96
+
97
+ for (const line of lines) {
98
+ if (line.includes("short test summary info")) {
99
+ inSummary = true;
100
+ continue;
101
+ }
102
+ if (inSummary) {
103
+ const match = line.match(/^FAILED\s+(.+)/);
104
+ if (match) {
105
+ failedTests.push(match[1].trim());
106
+ } else if (line.match(/^={3,}/) || line.match(/^\d+ failed/)) {
107
+ break;
108
+ }
109
+ }
110
+ }
111
+
112
+ return failedTests;
113
+ }
114
+
115
+ function startToxEnv(env: string, pythonPath: string, name?: string): RunningTask {
116
+ const startTime = Date.now();
117
+ const toxIniPath = join(testsDir, "tox.ini");
118
+
119
+ // Build command
120
+ const args = ["-m", "tox", "-c", toxIniPath, "-e", env];
121
+ if (name) {
122
+ args.push("--", "-k", name);
123
+ }
124
+
125
+ console.log(`${pc.blue("[START]")} ${env}`);
126
+
127
+ const proc: ChildProcess = spawn(pythonPath, args, {
128
+ cwd: testsDir,
129
+ stdio: "pipe",
130
+ env: { ...process.env, FOLDER: env.split("-")[1] || "azure" },
131
+ });
132
+
133
+ let stderr = "";
134
+ let stdout = "";
135
+ if (proc.stdout) {
136
+ proc.stdout.on("data", (data) => {
137
+ const chunk = data.toString();
138
+ stdout += chunk;
139
+ if (!argv.values.quiet) {
140
+ process.stdout.write(chunk);
141
+ }
142
+ });
143
+ }
144
+ if (proc.stderr) {
145
+ proc.stderr.on("data", (data) => {
146
+ const chunk = data.toString();
147
+ stderr += chunk;
148
+ if (!argv.values.quiet) {
149
+ process.stderr.write(chunk);
150
+ }
151
+ });
152
+ }
153
+
154
+ const promise = new Promise<ToxResult>((resolve) => {
155
+ proc.on("close", (code) => {
156
+ const duration = (Date.now() - startTime) / 1000;
157
+ const success = code === 0;
158
+
159
+ if (success) {
160
+ console.log(`${pc.green("[PASS]")} ${env} (${duration.toFixed(1)}s)`);
161
+ } else {
162
+ console.log(`${pc.red("[FAIL]")} ${env} (${duration.toFixed(1)}s)`);
163
+ }
164
+
165
+ const failedTests = extractFailedTests(stdout);
166
+
167
+ resolve({
168
+ env,
169
+ success,
170
+ duration,
171
+ error: success ? undefined : stderr || `Exit code: ${code}`,
172
+ failedTests: failedTests.length > 0 ? failedTests : undefined,
173
+ });
174
+ });
175
+
176
+ proc.on("error", (err) => {
177
+ const duration = (Date.now() - startTime) / 1000;
178
+ console.log(`${pc.red("[ERROR]")} ${env}: ${err.message}`);
179
+ resolve({
180
+ env,
181
+ success: false,
182
+ duration,
183
+ error: err.message,
184
+ });
185
+ });
186
+ });
187
+
188
+ return { env, proc, promise };
189
+ }
190
+
191
+ function killTask(task: RunningTask): void {
192
+ try {
193
+ task.proc.kill("SIGTERM");
194
+ } catch {
195
+ // Process may have already exited
196
+ }
197
+ }
198
+
199
+ async function runParallel(
200
+ envs: string[],
201
+ pythonPath: string,
202
+ maxJobs: number,
203
+ name?: string,
204
+ ): Promise<ToxResult[]> {
205
+ const results: ToxResult[] = [];
206
+ const running: Map<string, RunningTask> = new Map();
207
+
208
+ for (const env of envs) {
209
+ // Wait if we're at max capacity
210
+ while (running.size >= maxJobs) {
211
+ const promises = Array.from(running.values()).map((t) => t.promise);
212
+ const completed = await Promise.race(promises);
213
+ results.push(completed);
214
+ running.delete(completed.env);
215
+
216
+ // Fail-fast: kill all running tasks and exit
217
+ if (!completed.success) {
218
+ console.log(pc.red(`\n[FAIL-FAST] ${completed.env} failed, killing remaining tasks...`));
219
+ for (const task of running.values()) {
220
+ killTask(task);
221
+ }
222
+ // Wait briefly for processes to terminate
223
+ await Promise.all(Array.from(running.values()).map((t) => t.promise));
224
+ return results;
225
+ }
226
+ }
227
+
228
+ // Start new task
229
+ const task = startToxEnv(env, pythonPath, name);
230
+ running.set(env, task);
231
+ }
232
+
233
+ // Wait for remaining tasks, checking for failures
234
+ while (running.size > 0) {
235
+ const promises = Array.from(running.values()).map((t) => t.promise);
236
+ const completed = await Promise.race(promises);
237
+ results.push(completed);
238
+ running.delete(completed.env);
239
+
240
+ // Fail-fast: kill all running tasks and exit
241
+ if (!completed.success) {
242
+ console.log(pc.red(`\n[FAIL-FAST] ${completed.env} failed, killing remaining tasks...`));
243
+ for (const task of running.values()) {
244
+ killTask(task);
245
+ }
246
+ // Wait briefly for processes to terminate
247
+ await Promise.all(Array.from(running.values()).map((t) => t.promise));
248
+ return results;
249
+ }
250
+ }
251
+
252
+ return results;
253
+ }
254
+
255
+ async function _runSequential(
256
+ envs: string[],
257
+ pythonPath: string,
258
+ name?: string,
259
+ ): Promise<ToxResult[]> {
260
+ const results: ToxResult[] = [];
261
+ for (const env of envs) {
262
+ const task = startToxEnv(env, pythonPath, name);
263
+ const result = await task.promise;
264
+ results.push(result);
265
+ }
266
+ return results;
267
+ }
268
+
269
+ function printSummary(results: ToxResult[]): void {
270
+ console.log("\n" + pc.bold("═".repeat(60)));
271
+ console.log(pc.bold(" Test Results Summary"));
272
+ console.log(pc.bold("═".repeat(60)) + "\n");
273
+
274
+ const passed = results.filter((r) => r.success);
275
+ const failed = results.filter((r) => !r.success);
276
+ const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
277
+
278
+ for (const result of results) {
279
+ const status = result.success ? pc.green("PASS") : pc.red("FAIL");
280
+ console.log(` ${status} ${result.env} (${result.duration.toFixed(1)}s)`);
281
+ }
282
+
283
+ console.log("\n" + "─".repeat(60));
284
+ console.log(
285
+ ` ${pc.green(`Passed: ${passed.length}`)} ` +
286
+ `${pc.red(`Failed: ${failed.length}`)} ` +
287
+ `Total: ${results.length} ` +
288
+ `Duration: ${totalDuration.toFixed(1)}s`,
289
+ );
290
+ console.log("─".repeat(60) + "\n");
291
+
292
+ if (failed.length > 0) {
293
+ console.log(pc.red("Failed environments:"));
294
+ for (const result of failed) {
295
+ console.log(` - ${result.env}`);
296
+ if (result.failedTests && result.failedTests.length > 0) {
297
+ for (const test of result.failedTests) {
298
+ console.log(` ${pc.red("FAILED")} ${test}`);
299
+ }
300
+ } else if (result.error && !argv.values.quiet) {
301
+ console.log(` ${result.error.split("\n").slice(0, 5).join("\n ")}`);
302
+ }
303
+ }
304
+ console.log();
305
+ }
306
+ }
307
+
308
+ async function runEmitterTests(): Promise<ToxResult> {
309
+ const startTime = Date.now();
310
+ console.log(`${pc.blue("[START]")} emitter (vitest)`);
311
+
312
+ // Add node_modules/.bin directories to PATH so vitest can be found
313
+ const pathSep = process.platform === "win32" ? ";" : ":";
314
+ const binPaths = [
315
+ join(root, "node_modules", ".bin"),
316
+ join(monorepoRoot, "node_modules", ".bin"),
317
+ ].join(pathSep);
318
+ const env = {
319
+ ...process.env,
320
+ PATH: `${binPaths}${pathSep}${process.env.PATH}`,
321
+ };
322
+
323
+ return new Promise((resolve) => {
324
+ const proc: ChildProcess = spawn("vitest", ["run", "-c", "./emitter/vitest.config.ts"], {
325
+ cwd: root,
326
+ stdio: !argv.values.quiet ? "inherit" : "pipe",
327
+ shell: true,
328
+ env,
329
+ });
330
+
331
+ let stderr = "";
332
+ if (argv.values.quiet && proc.stderr) {
333
+ proc.stderr.on("data", (data) => {
334
+ stderr += data.toString();
335
+ });
336
+ }
337
+
338
+ proc.on("close", (code) => {
339
+ const duration = (Date.now() - startTime) / 1000;
340
+ const success = code === 0;
341
+
342
+ if (success) {
343
+ console.log(`${pc.green("[PASS]")} emitter (vitest) (${duration.toFixed(1)}s)`);
344
+ } else {
345
+ console.log(`${pc.red("[FAIL]")} emitter (vitest) (${duration.toFixed(1)}s)`);
346
+ }
347
+
348
+ resolve({
349
+ env: "emitter (vitest)",
350
+ success,
351
+ duration,
352
+ error: success ? undefined : stderr || `Exit code: ${code}`,
353
+ });
354
+ });
355
+
356
+ proc.on("error", (err) => {
357
+ const duration = (Date.now() - startTime) / 1000;
358
+ console.log(`${pc.red("[ERROR]")} emitter (vitest): ${err.message}`);
359
+ resolve({
360
+ env: "emitter (vitest)",
361
+ success: false,
362
+ duration,
363
+ error: err.message,
364
+ });
365
+ });
366
+ });
367
+ }
368
+
369
+ async function main(): Promise<void> {
370
+ const startTime = Date.now();
371
+
372
+ const runEmitter = argv.values.emitter;
373
+ const runGenerator = argv.values.generator;
374
+ const runBoth = !runEmitter && !runGenerator;
375
+
376
+ const allResults: ToxResult[] = [];
377
+
378
+ // Header
379
+ console.log(pc.cyan("\n╔══════════════════════════════════════════════════════════╗"));
380
+ console.log(
381
+ pc.cyan("║") +
382
+ pc.bold(" TypeSpec Python SDK Generator Tests ") +
383
+ pc.cyan("║"),
384
+ );
385
+ console.log(pc.cyan("╚══════════════════════════════════════════════════════════╝") + "\n");
386
+
387
+ // Run emitter tests if requested
388
+ if (runEmitter || runBoth) {
389
+ console.log(`${pc.bold("=== Emitter Tests (TypeScript) ===")}\n`);
390
+ const emitterResult = await runEmitterTests();
391
+ allResults.push(emitterResult);
392
+ }
393
+
394
+ // Run generator tests if requested
395
+ if (runGenerator || runBoth) {
396
+ console.log(`\n${pc.bold("=== Generator Tests (Python) ===")}\n`);
397
+
398
+ // Determine flavors
399
+ const flavors = argv.values.flavor === "all" ? ["azure", "unbranded"] : [argv.values.flavor!];
400
+
401
+ // Determine environments
402
+ let baseEnvs: string[];
403
+ if (argv.values.env) {
404
+ baseEnvs = argv.values.env.split(",").map((e) => e.trim());
405
+ } else {
406
+ // Default: run test environments
407
+ baseEnvs = ["test"];
408
+ }
409
+
410
+ // Expand 'ci' and 'docs' into component environments for parallel execution
411
+ const expandedEnvs: string[] = [];
412
+ for (const env of baseEnvs) {
413
+ if (env === "ci") {
414
+ // All envs run in parallel — pre-built wheels make installs cheap
415
+ expandedEnvs.push("test", "lint", "mypy", "pyright", "apiview", "sphinx");
416
+ } else if (env === "docs") {
417
+ // Split docs into apiview + sphinx for parallelism
418
+ expandedEnvs.push("apiview", "sphinx");
419
+ } else {
420
+ expandedEnvs.push(env);
421
+ }
422
+ }
423
+
424
+ // Build full environment list
425
+ const envs: string[] = [];
426
+ for (const env of expandedEnvs) {
427
+ if (env === "unittest") {
428
+ envs.push("unittest");
429
+ } else {
430
+ for (const flavor of flavors) {
431
+ envs.push(`${env}-${flavor}`);
432
+ }
433
+ }
434
+ }
435
+
436
+ // Get Python path
437
+ let pythonPath: string;
438
+ try {
439
+ pythonPath = getVenvPython();
440
+ } catch (error) {
441
+ console.error(pc.red((error as Error).message));
442
+ process.exit(1);
443
+ }
444
+
445
+ const maxJobs = argv.values.jobs
446
+ ? parseInt(argv.values.jobs, 10)
447
+ : Math.max(2, cpus().length - 2);
448
+
449
+ console.log(` Flavors: ${flavors.join(", ")}`);
450
+ console.log(` Environments: ${envs.join(", ")}`);
451
+ console.log(` Jobs: ${maxJobs}`);
452
+ if (argv.values.name) {
453
+ console.log(` Filter: ${argv.values.name}`);
454
+ }
455
+ console.log();
456
+
457
+ // Pre-build wheels for each flavor so tox envs install from pre-built
458
+ // wheels instead of rebuilding from source (~2min build once vs ~2min × N envs)
459
+ console.log(pc.cyan("Pre-building wheels for all flavors..."));
460
+ const installScript = join(testsDir, "install_packages.py");
461
+ for (const flavor of flavors) {
462
+ const startTime = Date.now();
463
+ const proc = spawn(pythonPath, [installScript, "build", flavor, testsDir], {
464
+ cwd: testsDir,
465
+ stdio: "inherit",
466
+ });
467
+ await new Promise<void>((resolve) => {
468
+ proc.on("close", (code) => {
469
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
470
+ if (code === 0) {
471
+ console.log(`${pc.green("[PASS]")} wheel build ${flavor} (${duration}s)`);
472
+ } else {
473
+ console.log(
474
+ `${pc.yellow("[WARN]")} wheel build ${flavor} failed (${duration}s), tox envs will build from source`,
475
+ );
476
+ }
477
+ resolve();
478
+ });
479
+ });
480
+ }
481
+ console.log();
482
+
483
+ // Run all environments in parallel
484
+ // The mock server serves both azure and unbranded specs, so all tests can run together
485
+ console.log(pc.cyan("Running all environments in parallel..."));
486
+ const results = await runParallel(envs, pythonPath, maxJobs, argv.values.name);
487
+
488
+ allResults.push(...results);
489
+ }
490
+
491
+ // Print summary
492
+ printSummary(allResults);
493
+
494
+ const totalDuration = (Date.now() - startTime) / 1000;
495
+ console.log(`Total execution time: ${totalDuration.toFixed(1)}s\n`);
496
+
497
+ // Exit with appropriate code
498
+ const failed = allResults.filter((r) => !r.success);
499
+ if (failed.length > 0) {
500
+ process.exit(1);
501
+ }
502
+ }
503
+
504
+ main().catch((error) => {
505
+ console.error(`${pc.red("Unexpected error:")}`, error);
506
+ process.exit(1);
507
+ });
@@ -10,32 +10,38 @@
10
10
 
11
11
  import os
12
12
  import sys
13
- from subprocess import check_call, CalledProcessError
13
+ from subprocess import run, TimeoutExpired
14
14
  import logging
15
15
  from util import run_check
16
16
 
17
17
  logging.getLogger().setLevel(logging.INFO)
18
18
 
19
+ # Timeout for each apiview generation (seconds)
20
+ APIVIEW_TIMEOUT = 30
21
+
19
22
 
20
23
  def _single_dir_apiview(mod):
21
- loop = 0
22
- while True:
24
+ for attempt in range(2):
23
25
  try:
24
- check_call(
25
- [
26
- "apistubgen",
27
- "--pkg-path",
28
- str(mod.absolute()),
29
- ]
26
+ result = run(
27
+ ["apistubgen", "--pkg-path", str(mod.absolute())],
28
+ capture_output=True,
29
+ timeout=APIVIEW_TIMEOUT,
30
30
  )
31
- except CalledProcessError as e:
32
- if loop >= 2: # retry for maximum 3 times because sometimes the apistubgen has transient failure.
33
- logging.error("{} exited with apiview generation error {}".format(mod.stem, e.returncode))
31
+ if result.returncode == 0:
32
+ return True
33
+ if attempt == 1:
34
+ logging.error(f"{mod.stem} failed: {result.stderr.decode()[:200]}")
35
+ return False
36
+ except TimeoutExpired:
37
+ if attempt == 1:
38
+ logging.error(f"{mod.stem} timed out after {APIVIEW_TIMEOUT}s")
39
+ return False
40
+ except Exception as e:
41
+ if attempt == 1:
42
+ logging.error(f"{mod.stem} error: {e}")
34
43
  return False
35
- else:
36
- loop += 1
37
- continue
38
- return True
44
+ return False
39
45
 
40
46
 
41
47
  if __name__ == "__main__":
@@ -12,37 +12,48 @@ from subprocess import check_call, CalledProcessError
12
12
  import os
13
13
  import logging
14
14
  import sys
15
- from util import run_check
15
+ from util import run_check, get_package_namespace_dir
16
16
 
17
17
  logging.getLogger().setLevel(logging.INFO)
18
18
 
19
19
 
20
20
  def get_config_file_location():
21
- mypy_ini_path = os.path.join(os.getcwd(), "../../eng/scripts/ci/mypy.ini")
21
+ # When running from tests/ directory via tox
22
+ mypy_ini_path = os.path.join(os.getcwd(), "../eng/scripts/ci/config/mypy.ini")
22
23
  if os.path.exists(mypy_ini_path):
23
24
  return mypy_ini_path
24
- else:
25
- return os.path.join(os.getcwd(), "../../../eng/scripts/ci/mypy.ini")
26
-
27
-
28
- def _single_dir_mypy(mod):
29
- inner_class = next(d for d in mod.iterdir() if d.is_dir() and not str(d).endswith("egg-info"))
30
- try:
31
- check_call(
32
- [
33
- sys.executable,
34
- "-m",
35
- "mypy",
36
- "--config-file",
37
- get_config_file_location(),
38
- "--ignore-missing",
39
- str(inner_class.absolute()),
40
- ]
41
- )
25
+ # Fallback for running from different directories
26
+ return os.path.join(os.path.dirname(__file__), "config/mypy.ini")
27
+
28
+
29
+ def _single_dir_mypy(mod, retries=2):
30
+ inner_class = get_package_namespace_dir(mod)
31
+ if not inner_class:
32
+ logging.info(f"No package directory found in {mod}, skipping")
42
33
  return True
43
- except CalledProcessError as e:
44
- logging.error("{} exited with mypy error {}".format(inner_class.stem, e.returncode))
45
- return False
34
+ for attempt in range(1, retries + 2):
35
+ try:
36
+ check_call(
37
+ [
38
+ sys.executable,
39
+ "-m",
40
+ "mypy",
41
+ "--config-file",
42
+ get_config_file_location(),
43
+ "--ignore-missing",
44
+ str(inner_class.absolute()),
45
+ ]
46
+ )
47
+ return True
48
+ except CalledProcessError as e:
49
+ if attempt <= retries:
50
+ logging.warning(
51
+ "{} mypy attempt {} failed (exit {}), retrying...".format(inner_class.stem, attempt, e.returncode)
52
+ )
53
+ else:
54
+ logging.error("{} exited with mypy error {}".format(inner_class.stem, e.returncode))
55
+ return False
56
+ return False
46
57
 
47
58
 
48
59
  if __name__ == "__main__":
@@ -12,36 +12,44 @@ from subprocess import check_call, CalledProcessError
12
12
  import os
13
13
  import logging
14
14
  import sys
15
- from util import run_check
15
+ from util import run_check, get_package_namespace_dir
16
16
 
17
17
  logging.getLogger().setLevel(logging.INFO)
18
18
 
19
19
 
20
20
  def get_rfc_file_location():
21
- rfc_file_location = os.path.join(os.getcwd(), "../../../eng/scripts/ci/pylintrc")
21
+ # When running from tests/ directory via tox
22
+ rfc_file_location = os.path.join(os.getcwd(), "../eng/scripts/ci/config/pylintrc")
22
23
  if os.path.exists(rfc_file_location):
23
24
  return rfc_file_location
24
- else:
25
- return os.path.join(os.getcwd(), "../../../../eng/scripts/ci/pylintrc")
25
+ # Fallback for running from different directories
26
+ return os.path.join(os.path.dirname(__file__), "config/pylintrc")
26
27
 
27
28
 
28
29
  def _single_dir_pylint(mod):
29
- inner_class = next(d for d in mod.iterdir() if d.is_dir() and not str(d).endswith("egg-info"))
30
+ inner_class = get_package_namespace_dir(mod)
31
+ if not inner_class:
32
+ logging.info(f"No package directory found in {mod}, skipping")
33
+ return True
34
+ # Only load the Azure pylint guidelines checker plugin for azure packages.
35
+ # The plugin (azure-pylint-guidelines-checker) is only installed in the
36
+ # lint-azure tox environment and is not available for unbranded packages.
37
+ is_azure = "azure" in mod.parts
38
+ pylint_args = [
39
+ sys.executable,
40
+ "-m",
41
+ "pylint",
42
+ "--rcfile={}".format(get_rfc_file_location()),
43
+ "--evaluation=(max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention + info)/ statement) * 10)))",
44
+ "--output-format=parseable",
45
+ "--recursive=y",
46
+ "--py-version=3.10",
47
+ ]
48
+ if is_azure:
49
+ pylint_args.append("--load-plugins=pylint_guidelines_checker")
50
+ pylint_args.append(str(inner_class.absolute()))
30
51
  try:
31
- check_call(
32
- [
33
- sys.executable,
34
- "-m",
35
- "pylint",
36
- "--rcfile={}".format(get_rfc_file_location()),
37
- "--evaluation=(max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention + info)/ statement) * 10)))",
38
- "--load-plugins=pylint_guidelines_checker",
39
- "--output-format=parseable",
40
- "--recursive=y",
41
- "--py-version=3.9",
42
- str(inner_class.absolute()),
43
- ]
44
- )
52
+ check_call(pylint_args)
45
53
  return True
46
54
  except CalledProcessError as e:
47
55
  logging.error("{} exited with linting error {}".format(str(inner_class.absolute()), e.returncode))