satvu 0.2.0__py3-none-any.whl

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 (352) hide show
  1. builder/__init__.py +8 -0
  2. builder/__main__.py +12 -0
  3. builder/ast_generator.py +555 -0
  4. builder/build.py +457 -0
  5. builder/config.py +32 -0
  6. builder/jinja_filters/__init__.py +3 -0
  7. builder/jinja_filters/to_pydantic_model_field.py +35 -0
  8. builder/load.py +154 -0
  9. builder/openapi_preprocessor.py +86 -0
  10. builder/patches.py +769 -0
  11. builder/schema_utils.py +249 -0
  12. builder/streaming_detector.py +214 -0
  13. builder/streaming_post_processor.py +121 -0
  14. builder/templates/endpoint_module.py.jinja +325 -0
  15. builder/templates/macros/return_annotation.jinja +46 -0
  16. builder/templates/macros/test_error.jinja +80 -0
  17. builder/templates/macros/test_helpers.jinja +93 -0
  18. builder/templates/macros/test_no_content.jinja +31 -0
  19. builder/templates/macros/test_success.jinja +50 -0
  20. builder/templates/model.py.jinja +54 -0
  21. builder/templates/models_init.py.jinja +23 -0
  22. builder/templates/property_templates/list_property.py.jinja +55 -0
  23. builder/templates/property_templates/model_property.py.jinja +38 -0
  24. builder/templates/property_templates/union_property.py.jinja +97 -0
  25. builder/templates/test_module.py.jinja +114 -0
  26. builder/templates/test_schemas.py.jinja +80 -0
  27. builder/test_generator.py +465 -0
  28. satvu/__init__.py +11 -0
  29. satvu/auth.py +248 -0
  30. satvu/auth_test.py +415 -0
  31. satvu/core.py +306 -0
  32. satvu/core_retry_test.py +242 -0
  33. satvu/core_streaming_test.py +268 -0
  34. satvu/core_test.py +588 -0
  35. satvu/http/__init__.py +130 -0
  36. satvu/http/errors.py +499 -0
  37. satvu/http/httpx_adapter.py +300 -0
  38. satvu/http/httpx_adapter_test.py +236 -0
  39. satvu/http/protocol.py +132 -0
  40. satvu/http/requests_adapter.py +298 -0
  41. satvu/http/requests_adapter_test.py +284 -0
  42. satvu/http/stdlib_adapter.py +356 -0
  43. satvu/http/stdlib_adapter_test.py +208 -0
  44. satvu/http/urllib3_adapter.py +351 -0
  45. satvu/http/urllib3_adapter_test.py +282 -0
  46. satvu/py.typed +0 -0
  47. satvu/result.py +351 -0
  48. satvu/sdk.py +161 -0
  49. satvu/services/catalog/__init__.py +1 -0
  50. satvu/services/catalog/api.py +576 -0
  51. satvu/services/catalog/api_test.py +1165 -0
  52. satvu/services/catalog/models/__init__.py +159 -0
  53. satvu/services/catalog/models/and_or_expression.py +40 -0
  54. satvu/services/catalog/models/and_or_expression_op.py +9 -0
  55. satvu/services/catalog/models/api_error.py +21 -0
  56. satvu/services/catalog/models/arithmetic_expression.py +25 -0
  57. satvu/services/catalog/models/arithmetic_expression_op.py +14 -0
  58. satvu/services/catalog/models/asset.py +43 -0
  59. satvu/services/catalog/models/asset_bands.py +62 -0
  60. satvu/services/catalog/models/asset_bands_nodata_type_1.py +10 -0
  61. satvu/services/catalog/models/asset_bands_statistics.py +45 -0
  62. satvu/services/catalog/models/bbox_literal.py +16 -0
  63. satvu/services/catalog/models/binary_comparison_predicate.py +36 -0
  64. satvu/services/catalog/models/binary_comparison_predicate_op.py +13 -0
  65. satvu/services/catalog/models/catalog.py +51 -0
  66. satvu/services/catalog/models/collection.py +64 -0
  67. satvu/services/catalog/models/collections.py +28 -0
  68. satvu/services/catalog/models/conformance.py +18 -0
  69. satvu/services/catalog/models/conformance_response_429.py +18 -0
  70. satvu/services/catalog/models/cql_2_queryables_schema.py +28 -0
  71. satvu/services/catalog/models/data_type_of_the_band.py +23 -0
  72. satvu/services/catalog/models/date_instant.py +14 -0
  73. satvu/services/catalog/models/error.py +30 -0
  74. satvu/services/catalog/models/extent.py +31 -0
  75. satvu/services/catalog/models/feature.py +89 -0
  76. satvu/services/catalog/models/feature_collection.py +40 -0
  77. satvu/services/catalog/models/feature_properties.py +109 -0
  78. satvu/services/catalog/models/geo_json_geometry_collection.py +38 -0
  79. satvu/services/catalog/models/geo_json_geometry_collection_type.py +8 -0
  80. satvu/services/catalog/models/geo_json_line_string.py +22 -0
  81. satvu/services/catalog/models/geo_json_line_string_type.py +8 -0
  82. satvu/services/catalog/models/geo_json_multi_line_string.py +24 -0
  83. satvu/services/catalog/models/geo_json_multi_line_string_type.py +8 -0
  84. satvu/services/catalog/models/geo_json_multi_point.py +22 -0
  85. satvu/services/catalog/models/geo_json_multi_point_type.py +8 -0
  86. satvu/services/catalog/models/geo_json_multi_polygon.py +24 -0
  87. satvu/services/catalog/models/geo_json_multi_polygon_type.py +8 -0
  88. satvu/services/catalog/models/geo_json_point.py +22 -0
  89. satvu/services/catalog/models/geo_json_point_type.py +8 -0
  90. satvu/services/catalog/models/geo_json_polygon.py +24 -0
  91. satvu/services/catalog/models/geo_json_polygon_type.py +8 -0
  92. satvu/services/catalog/models/geojson_crs.py +20 -0
  93. satvu/services/catalog/models/geojson_geometry.py +32 -0
  94. satvu/services/catalog/models/get_collection_response_429.py +18 -0
  95. satvu/services/catalog/models/get_collections_response_429.py +18 -0
  96. satvu/services/catalog/models/get_item_collection_response_429.py +18 -0
  97. satvu/services/catalog/models/get_item_response_429.py +18 -0
  98. satvu/services/catalog/models/get_search_response_429.py +18 -0
  99. satvu/services/catalog/models/http_error.py +22 -0
  100. satvu/services/catalog/models/is_between_predicate.py +26 -0
  101. satvu/services/catalog/models/is_between_predicate_op.py +8 -0
  102. satvu/services/catalog/models/is_in_list_predicate.py +48 -0
  103. satvu/services/catalog/models/is_in_list_predicate_op.py +8 -0
  104. satvu/services/catalog/models/is_like_predicate.py +23 -0
  105. satvu/services/catalog/models/is_like_predicate_op.py +8 -0
  106. satvu/services/catalog/models/is_null_predicate.py +68 -0
  107. satvu/services/catalog/models/is_null_predicate_op.py +8 -0
  108. satvu/services/catalog/models/landing_page_response_429.py +18 -0
  109. satvu/services/catalog/models/link.py +54 -0
  110. satvu/services/catalog/models/not_expression.py +40 -0
  111. satvu/services/catalog/models/not_expression_op.py +8 -0
  112. satvu/services/catalog/models/pixel_sampling_in_the_band.py +9 -0
  113. satvu/services/catalog/models/post_search_input.py +111 -0
  114. satvu/services/catalog/models/post_search_response_429.py +18 -0
  115. satvu/services/catalog/models/property_ref.py +14 -0
  116. satvu/services/catalog/models/queryable_property.py +22 -0
  117. satvu/services/catalog/models/queryables_response_429.py +18 -0
  118. satvu/services/catalog/models/search_response.py +52 -0
  119. satvu/services/catalog/models/sort_by_element.py +22 -0
  120. satvu/services/catalog/models/spatial_extent.py +19 -0
  121. satvu/services/catalog/models/stac_geometry.py +35 -0
  122. satvu/services/catalog/models/temporal_extent.py +21 -0
  123. satvu/services/catalog/models/timestamp_instant.py +14 -0
  124. satvu/services/catalog/test_schemas.py +48120 -0
  125. satvu/services/conftest.py +57 -0
  126. satvu/services/cos/__init__.py +1 -0
  127. satvu/services/cos/api.py +572 -0
  128. satvu/services/cos/api_test.py +1298 -0
  129. satvu/services/cos/models/__init__.py +89 -0
  130. satvu/services/cos/models/collection.py +9 -0
  131. satvu/services/cos/models/feature_collection_order.py +64 -0
  132. satvu/services/cos/models/feature_order.py +39 -0
  133. satvu/services/cos/models/geojson_polygon.py +20 -0
  134. satvu/services/cos/models/http_exception_response.py +14 -0
  135. satvu/services/cos/models/http_validation_error.py +21 -0
  136. satvu/services/cos/models/link.py +59 -0
  137. satvu/services/cos/models/method.py +9 -0
  138. satvu/services/cos/models/order.py +36 -0
  139. satvu/services/cos/models/order_download_url.py +23 -0
  140. satvu/services/cos/models/order_edit_payload.py +17 -0
  141. satvu/services/cos/models/order_item_download_url.py +23 -0
  142. satvu/services/cos/models/order_item_price.py +22 -0
  143. satvu/services/cos/models/order_page.py +40 -0
  144. satvu/services/cos/models/order_price.py +41 -0
  145. satvu/services/cos/models/order_pricing.py +44 -0
  146. satvu/services/cos/models/order_submission_payload.py +27 -0
  147. satvu/services/cos/models/point_geometry.py +22 -0
  148. satvu/services/cos/models/polygon.py +20 -0
  149. satvu/services/cos/models/polygon_1.py +20 -0
  150. satvu/services/cos/models/polygon_geometry.py +24 -0
  151. satvu/services/cos/models/price_information.py +50 -0
  152. satvu/services/cos/models/price_request.py +28 -0
  153. satvu/services/cos/models/primary_format.py +9 -0
  154. satvu/services/cos/models/reseller_feature_collection_order.py +70 -0
  155. satvu/services/cos/models/reseller_order_price.py +49 -0
  156. satvu/services/cos/models/reseller_price_request.py +36 -0
  157. satvu/services/cos/models/reseller_submission_order_payload.py +35 -0
  158. satvu/services/cos/models/response_context.py +25 -0
  159. satvu/services/cos/models/satvu_filter.py +9 -0
  160. satvu/services/cos/models/search_request.py +26 -0
  161. satvu/services/cos/models/stac_metadata.py +42 -0
  162. satvu/services/cos/models/stac_properties_v4.py +83 -0
  163. satvu/services/cos/models/stac_properties_v7.py +155 -0
  164. satvu/services/cos/models/stac_properties_v9.py +161 -0
  165. satvu/services/cos/models/validation_error.py +18 -0
  166. satvu/services/cos/test_schemas.py +73873 -0
  167. satvu/services/id/__init__.py +1 -0
  168. satvu/services/id/api.py +554 -0
  169. satvu/services/id/api_test.py +1523 -0
  170. satvu/services/id/models/__init__.py +81 -0
  171. satvu/services/id/models/client_credentials.py +16 -0
  172. satvu/services/id/models/client_id.py +14 -0
  173. satvu/services/id/models/core_webhook.py +29 -0
  174. satvu/services/id/models/create_webhook_response.py +56 -0
  175. satvu/services/id/models/edit_webhook_payload.py +34 -0
  176. satvu/services/id/models/error_response.py +14 -0
  177. satvu/services/id/models/http_validation_error.py +21 -0
  178. satvu/services/id/models/link.py +22 -0
  179. satvu/services/id/models/list_response_context.py +24 -0
  180. satvu/services/id/models/list_webhook_response.py +29 -0
  181. satvu/services/id/models/notification_category.py +8 -0
  182. satvu/services/id/models/notification_config.py +24 -0
  183. satvu/services/id/models/notification_description.py +20 -0
  184. satvu/services/id/models/notification_settings.py +26 -0
  185. satvu/services/id/models/notification_update.py +31 -0
  186. satvu/services/id/models/post_webhook_response.py +50 -0
  187. satvu/services/id/models/reseller_notification_category.py +9 -0
  188. satvu/services/id/models/reseller_notification_config.py +22 -0
  189. satvu/services/id/models/reseller_notification_description.py +22 -0
  190. satvu/services/id/models/reseller_notification_update.py +32 -0
  191. satvu/services/id/models/reseller_webhook_event.py +8 -0
  192. satvu/services/id/models/test_webhook_response.py +47 -0
  193. satvu/services/id/models/topic.py +9 -0
  194. satvu/services/id/models/user_info.py +33 -0
  195. satvu/services/id/models/user_metadata.py +27 -0
  196. satvu/services/id/models/user_settings.py +28 -0
  197. satvu/services/id/models/validation_error.py +18 -0
  198. satvu/services/id/models/verbose_notification.py +33 -0
  199. satvu/services/id/models/webhook_event.py +8 -0
  200. satvu/services/id/models/webhook_failure_title.py +10 -0
  201. satvu/services/id/models/webhook_response.py +44 -0
  202. satvu/services/id/models/webhook_result.py +40 -0
  203. satvu/services/id/test_schemas.py +38179 -0
  204. satvu/services/otm/__init__.py +1 -0
  205. satvu/services/otm/api.py +866 -0
  206. satvu/services/otm/api_test.py +2822 -0
  207. satvu/services/otm/models/__init__.py +217 -0
  208. satvu/services/otm/models/asset.py +22 -0
  209. satvu/services/otm/models/assured_feasibility_fields.py +24 -0
  210. satvu/services/otm/models/assured_feasibility_fields_with_addons.py +38 -0
  211. satvu/services/otm/models/assured_feasibility_response_feature.py +38 -0
  212. satvu/services/otm/models/assured_feasibility_response_properties.py +87 -0
  213. satvu/services/otm/models/assured_order_request.py +21 -0
  214. satvu/services/otm/models/assured_order_request_properties.py +40 -0
  215. satvu/services/otm/models/assured_stored_feasibility_request_properties.py +44 -0
  216. satvu/services/otm/models/collection.py +9 -0
  217. satvu/services/otm/models/collections.py +10 -0
  218. satvu/services/otm/models/day_night_mode.py +10 -0
  219. satvu/services/otm/models/direction.py +9 -0
  220. satvu/services/otm/models/edit_order_payload.py +35 -0
  221. satvu/services/otm/models/edit_order_properties.py +84 -0
  222. satvu/services/otm/models/error_response.py +17 -0
  223. satvu/services/otm/models/extra_ignore_assured_feasibility_response_properties.py +86 -0
  224. satvu/services/otm/models/feasibility_request.py +31 -0
  225. satvu/services/otm/models/feasibility_request_status.py +12 -0
  226. satvu/services/otm/models/feasibility_response.py +52 -0
  227. satvu/services/otm/models/filter_.py +9 -0
  228. satvu/services/otm/models/filter_fields.py +24 -0
  229. satvu/services/otm/models/full_well_capacitance.py +9 -0
  230. satvu/services/otm/models/geometry_collection.py +42 -0
  231. satvu/services/otm/models/get_assured_order_properties.py +111 -0
  232. satvu/services/otm/models/get_order_response.py +44 -0
  233. satvu/services/otm/models/get_standard_order_properties.py +109 -0
  234. satvu/services/otm/models/http_validation_error.py +21 -0
  235. satvu/services/otm/models/line_string.py +23 -0
  236. satvu/services/otm/models/link.py +54 -0
  237. satvu/services/otm/models/list_stored_orders_response.py +36 -0
  238. satvu/services/otm/models/modify_feasibility_request.py +42 -0
  239. satvu/services/otm/models/modify_feasibility_request_properties.py +52 -0
  240. satvu/services/otm/models/multi_line_string.py +25 -0
  241. satvu/services/otm/models/multi_point.py +23 -0
  242. satvu/services/otm/models/multi_polygon.py +25 -0
  243. satvu/services/otm/models/order_item_download_url.py +23 -0
  244. satvu/services/otm/models/order_modification_price.py +36 -0
  245. satvu/services/otm/models/order_price.py +44 -0
  246. satvu/services/otm/models/order_status.py +15 -0
  247. satvu/services/otm/models/outage.py +29 -0
  248. satvu/services/otm/models/point.py +21 -0
  249. satvu/services/otm/models/point_geometry.py +22 -0
  250. satvu/services/otm/models/polygon.py +23 -0
  251. satvu/services/otm/models/polygon_geometry.py +24 -0
  252. satvu/services/otm/models/price.py +56 -0
  253. satvu/services/otm/models/price_information.py +65 -0
  254. satvu/services/otm/models/price_request.py +37 -0
  255. satvu/services/otm/models/primary_format.py +9 -0
  256. satvu/services/otm/models/readout_mode.py +9 -0
  257. satvu/services/otm/models/request_method.py +9 -0
  258. satvu/services/otm/models/reseller_assured_order_request.py +27 -0
  259. satvu/services/otm/models/reseller_get_order_response.py +48 -0
  260. satvu/services/otm/models/reseller_search_response_feature_assured_order_request.py +59 -0
  261. satvu/services/otm/models/reseller_search_response_feature_standard_order_request.py +59 -0
  262. satvu/services/otm/models/reseller_standard_order_request.py +34 -0
  263. satvu/services/otm/models/reseller_stored_order_response.py +54 -0
  264. satvu/services/otm/models/response_context.py +25 -0
  265. satvu/services/otm/models/search_assured_order_properties.py +111 -0
  266. satvu/services/otm/models/search_request.py +92 -0
  267. satvu/services/otm/models/search_response.py +77 -0
  268. satvu/services/otm/models/search_response_feature_assured_feasibility_request.py +54 -0
  269. satvu/services/otm/models/search_response_feature_assured_feasibility_response.py +54 -0
  270. satvu/services/otm/models/search_response_feature_assured_order_request.py +55 -0
  271. satvu/services/otm/models/search_response_feature_standard_feasibility_request.py +54 -0
  272. satvu/services/otm/models/search_response_feature_standard_feasibility_response.py +54 -0
  273. satvu/services/otm/models/search_response_feature_standard_order_request.py +55 -0
  274. satvu/services/otm/models/search_standard_order_properties.py +109 -0
  275. satvu/services/otm/models/sort_entities.py +21 -0
  276. satvu/services/otm/models/sortable_field.py +8 -0
  277. satvu/services/otm/models/stac_feature.py +66 -0
  278. satvu/services/otm/models/standard_feasibility_response_feature.py +36 -0
  279. satvu/services/otm/models/standard_feasibility_response_properties.py +100 -0
  280. satvu/services/otm/models/standard_order_fields_with_addons.py +65 -0
  281. satvu/services/otm/models/standard_order_request.py +29 -0
  282. satvu/services/otm/models/standard_order_request_properties.py +71 -0
  283. satvu/services/otm/models/standard_price_request_properties.py +65 -0
  284. satvu/services/otm/models/standard_request_properties.py +51 -0
  285. satvu/services/otm/models/standard_stored_feasibility_request_properties.py +70 -0
  286. satvu/services/otm/models/stored_assured_order_request_properties.py +90 -0
  287. satvu/services/otm/models/stored_feasibility_feature_collection.py +36 -0
  288. satvu/services/otm/models/stored_feasibility_request.py +50 -0
  289. satvu/services/otm/models/stored_order_response.py +50 -0
  290. satvu/services/otm/models/stored_standard_order_request_properties.py +88 -0
  291. satvu/services/otm/models/validation_error.py +18 -0
  292. satvu/services/otm/models/validation_error_detail.py +27 -0
  293. satvu/services/otm/models/validation_error_response.py +23 -0
  294. satvu/services/otm/test_schemas.py +444886 -0
  295. satvu/services/policy/__init__.py +1 -0
  296. satvu/services/policy/api.py +117 -0
  297. satvu/services/policy/api_test.py +264 -0
  298. satvu/services/policy/models/__init__.py +39 -0
  299. satvu/services/policy/models/civil_date.py +21 -0
  300. satvu/services/policy/models/contracts_addon.py +25 -0
  301. satvu/services/policy/models/contracts_addon_option.py +34 -0
  302. satvu/services/policy/models/contracts_contract_with_products.py +74 -0
  303. satvu/services/policy/models/contracts_geometry.py +23 -0
  304. satvu/services/policy/models/contracts_product.py +18 -0
  305. satvu/services/policy/models/list_active_contracts_input.py +14 -0
  306. satvu/services/policy/models/router_active_contracts_response.py +27 -0
  307. satvu/services/policy/models/router_http_error.py +22 -0
  308. satvu/services/policy/models/terms_user_terms_accepted.py +20 -0
  309. satvu/services/policy/models/user_acceptance_terms_input.py +18 -0
  310. satvu/services/policy/test_schemas.py +2061 -0
  311. satvu/services/reseller/__init__.py +1 -0
  312. satvu/services/reseller/api.py +418 -0
  313. satvu/services/reseller/api_test.py +532 -0
  314. satvu/services/reseller/models/__init__.py +59 -0
  315. satvu/services/reseller/models/company_address.py +28 -0
  316. satvu/services/reseller/models/company_search.py +28 -0
  317. satvu/services/reseller/models/company_search_fields.py +9 -0
  318. satvu/services/reseller/models/country_code.py +242 -0
  319. satvu/services/reseller/models/create_user.py +34 -0
  320. satvu/services/reseller/models/create_user_response.py +68 -0
  321. satvu/services/reseller/models/get_companies.py +36 -0
  322. satvu/services/reseller/models/get_company.py +46 -0
  323. satvu/services/reseller/models/get_user.py +72 -0
  324. satvu/services/reseller/models/get_users.py +36 -0
  325. satvu/services/reseller/models/http_validation_error.py +21 -0
  326. satvu/services/reseller/models/kyc_status.py +10 -0
  327. satvu/services/reseller/models/link.py +52 -0
  328. satvu/services/reseller/models/match_type.py +9 -0
  329. satvu/services/reseller/models/request_method.py +8 -0
  330. satvu/services/reseller/models/response_context.py +25 -0
  331. satvu/services/reseller/models/search_companies.py +39 -0
  332. satvu/services/reseller/models/search_users.py +37 -0
  333. satvu/services/reseller/models/user_search.py +28 -0
  334. satvu/services/reseller/models/user_search_fields.py +11 -0
  335. satvu/services/reseller/models/validation_error.py +18 -0
  336. satvu/services/reseller/test_schemas.py +15639 -0
  337. satvu/services/wallet/__init__.py +1 -0
  338. satvu/services/wallet/api.py +103 -0
  339. satvu/services/wallet/api_test.py +196 -0
  340. satvu/services/wallet/models/__init__.py +25 -0
  341. satvu/services/wallet/models/batch_balance_response.py +18 -0
  342. satvu/services/wallet/models/credit_balance_response.py +29 -0
  343. satvu/services/wallet/models/http_validation_error.py +21 -0
  344. satvu/services/wallet/models/validation_error.py +18 -0
  345. satvu/services/wallet/test_schemas.py +687 -0
  346. satvu/shared/__init__.py +0 -0
  347. satvu/shared/parsing.py +136 -0
  348. satvu/shared/parsing_test.py +387 -0
  349. satvu-0.2.0.dist-info/METADATA +107 -0
  350. satvu-0.2.0.dist-info/RECORD +352 -0
  351. satvu-0.2.0.dist-info/WHEEL +4 -0
  352. satvu-0.2.0.dist-info/licenses/LICENSE +21 -0
builder/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ Builder package for SatVu API SDK.
3
+
4
+ This package generates Python SDK code from OpenAPI specifications.
5
+ """
6
+
7
+ # Import patches to apply monkey-patches to openapi-python-client
8
+ import builder.patches # noqa: F401
builder/__main__.py ADDED
@@ -0,0 +1,12 @@
1
+ from argparse import ArgumentParser
2
+
3
+ from builder.build import build
4
+ from builder.config import CMD_ARGS
5
+
6
+ parser = ArgumentParser(__package__, description="OpenAPI SDK builder")
7
+ parser.add_argument("api", choices=CMD_ARGS.keys())
8
+ parser.add_argument("--cached", action="store_true", default=False)
9
+
10
+ args = parser.parse_args()
11
+
12
+ build(api_id=args.api, use_cached=args.cached)
@@ -0,0 +1,555 @@
1
+ """AST-based code generation for streaming download methods.
2
+
3
+ This module provides robust, type-safe code generation using Python's AST module
4
+ instead of fragile string templates. Guarantees syntactically correct Python code.
5
+ """
6
+
7
+ import ast
8
+
9
+ from builder.streaming_detector import StreamingEndpointConfig
10
+
11
+
12
+ class ASTMethodBuilder:
13
+ """Builder for generating streaming method AST nodes."""
14
+
15
+ def __init__(self, config: StreamingEndpointConfig):
16
+ """
17
+ Initialize builder with streaming endpoint configuration.
18
+
19
+ Args:
20
+ config: Configuration for the streaming endpoint
21
+ """
22
+ self.config = config
23
+
24
+ def build_method(self) -> ast.FunctionDef:
25
+ """
26
+ Build complete streaming method as AST node.
27
+
28
+ Returns:
29
+ AST FunctionDef node for the streaming method
30
+ """
31
+ # Path params are always required (used in URL formatting)
32
+ path_params = self.config.path_params
33
+
34
+ # Query params are all optional (added to params dict with defaults)
35
+ query_params = self.config.query_params
36
+
37
+ # Build function signature
38
+ args = self._build_arguments(path_params, query_params)
39
+
40
+ # Build function body
41
+ body = self._build_body(path_params, query_params)
42
+
43
+ # Build docstring
44
+ docstring_node = self._build_docstring(path_params, query_params)
45
+
46
+ # Combine docstring + body
47
+ full_body = [docstring_node] + body
48
+
49
+ # Build return annotation
50
+ return_annotation = self._build_return_annotation()
51
+
52
+ return ast.FunctionDef(
53
+ name=self.config.stream_method,
54
+ args=args,
55
+ body=full_body,
56
+ decorator_list=[],
57
+ returns=return_annotation,
58
+ lineno=0,
59
+ )
60
+
61
+ def _build_arguments(
62
+ self,
63
+ path_params: list[tuple[str, str]],
64
+ query_params: list[tuple[str, str]],
65
+ ) -> ast.arguments:
66
+ """Build function arguments with defaults."""
67
+ # Start with self
68
+ args = [ast.arg(arg="self", annotation=None)]
69
+
70
+ # Add path params (required positional - contract_id, order_id, etc.)
71
+ for name, type_ in path_params:
72
+ args.append(ast.arg(arg=name, annotation=parse_type_annotation(type_)))
73
+
74
+ # Add output_path (required positional)
75
+ args.append(
76
+ ast.arg(
77
+ arg="output_path",
78
+ annotation=ast.BinOp(
79
+ left=ast.Name(id="Path", ctx=ast.Load()),
80
+ op=ast.BitOr(),
81
+ right=ast.Name(id="str", ctx=ast.Load()),
82
+ ),
83
+ )
84
+ )
85
+
86
+ # Add query params as keyword-only with None defaults
87
+ positional_args = []
88
+ kw_args = []
89
+ kw_defaults: list[ast.expr | None] = []
90
+
91
+ for name, type_ in query_params:
92
+ kw_args.append(
93
+ ast.arg(
94
+ arg=name,
95
+ annotation=parse_type_annotation(type_),
96
+ )
97
+ )
98
+ # All query params default to None
99
+ kw_defaults.append(ast.Constant(value=None))
100
+
101
+ # Add standard streaming params (chunk_size, progress_callback, timeout)
102
+ kw_args.extend(
103
+ [
104
+ ast.arg(
105
+ arg="chunk_size", annotation=ast.Name(id="int", ctx=ast.Load())
106
+ ),
107
+ ast.arg(
108
+ arg="progress_callback",
109
+ annotation=ast.BinOp(
110
+ left=ast.Subscript(
111
+ value=ast.Name(id="Callable", ctx=ast.Load()),
112
+ slice=ast.Tuple(
113
+ elts=[
114
+ ast.List(
115
+ elts=[
116
+ ast.Name(id="int", ctx=ast.Load()),
117
+ ast.BinOp(
118
+ left=ast.Name(id="int", ctx=ast.Load()),
119
+ op=ast.BitOr(),
120
+ right=ast.Constant(value=None),
121
+ ),
122
+ ],
123
+ ctx=ast.Load(),
124
+ ),
125
+ ast.Constant(value=None),
126
+ ],
127
+ ctx=ast.Load(),
128
+ ),
129
+ ctx=ast.Load(),
130
+ ),
131
+ op=ast.BitOr(),
132
+ right=ast.Constant(value=None),
133
+ ),
134
+ ),
135
+ ast.arg(
136
+ arg="timeout",
137
+ annotation=ast.BinOp(
138
+ left=ast.Name(id="int", ctx=ast.Load()),
139
+ op=ast.BitOr(),
140
+ right=ast.Constant(value=None),
141
+ ),
142
+ ),
143
+ ]
144
+ )
145
+ kw_defaults.extend(
146
+ [
147
+ ast.Constant(value=self.config.default_chunk_size),
148
+ ast.Constant(value=None), # progress_callback default
149
+ ast.Constant(value=None), # timeout default
150
+ ]
151
+ )
152
+
153
+ return ast.arguments(
154
+ posonlyargs=positional_args,
155
+ args=args,
156
+ kwonlyargs=kw_args,
157
+ kw_defaults=kw_defaults,
158
+ defaults=[],
159
+ )
160
+
161
+ def _build_body(
162
+ self,
163
+ path_params: list[tuple[str, str]],
164
+ query_params: list[tuple[str, str]],
165
+ ) -> list[ast.stmt]:
166
+ """Build method body statements."""
167
+ body: list[ast.stmt] = []
168
+
169
+ # 1. Build params dict (redirect + all query params)
170
+ params_dict_keys: list[ast.expr | None] = [ast.Constant(value="redirect")]
171
+ params_dict_values: list[ast.expr] = [ast.Constant(value=True)]
172
+
173
+ # Add all query params to params dict
174
+ for name, _ in query_params:
175
+ params_dict_keys.append(ast.Constant(value=name))
176
+ params_dict_values.append(ast.Name(id=name, ctx=ast.Load()))
177
+
178
+ body.append(
179
+ ast.Assign(
180
+ targets=[ast.Name(id="params", ctx=ast.Store())],
181
+ value=ast.Dict(keys=params_dict_keys, values=params_dict_values),
182
+ )
183
+ )
184
+
185
+ # 2. Build URL format args (only path params)
186
+ url_format_keywords = [
187
+ ast.keyword(arg=name, value=ast.Name(id=name, ctx=ast.Load()))
188
+ for name, _ in path_params
189
+ ]
190
+
191
+ # 3. Call self.make_request()
192
+ body.append(
193
+ ast.Assign(
194
+ targets=[ast.Name(id="result", ctx=ast.Store())],
195
+ value=ast.Call(
196
+ func=ast.Attribute(
197
+ value=ast.Name(id="self", ctx=ast.Load()),
198
+ attr="make_request",
199
+ ctx=ast.Load(),
200
+ ),
201
+ args=[],
202
+ keywords=[
203
+ ast.keyword(arg="method", value=ast.Constant(value="get")),
204
+ ast.keyword(
205
+ arg="url",
206
+ value=ast.Call(
207
+ func=ast.Attribute(
208
+ value=ast.Constant(value=self.config.url_pattern),
209
+ attr="format",
210
+ ctx=ast.Load(),
211
+ ),
212
+ args=[],
213
+ keywords=url_format_keywords,
214
+ ),
215
+ ),
216
+ ast.keyword(
217
+ arg="params", value=ast.Name(id="params", ctx=ast.Load())
218
+ ),
219
+ ast.keyword(
220
+ arg="follow_redirects", value=ast.Constant(value=True)
221
+ ),
222
+ ast.keyword(
223
+ arg="timeout", value=ast.Name(id="timeout", ctx=ast.Load())
224
+ ),
225
+ ],
226
+ ),
227
+ )
228
+ )
229
+
230
+ # 4. Error check: if is_err(result): return ResultErr(result.error())
231
+ # We use the is_err() type guard function (not the method) so pyright can
232
+ # narrow the type and know result.error() is valid
233
+ body.append(
234
+ ast.If(
235
+ test=ast.Call(
236
+ func=ast.Name(id="is_err", ctx=ast.Load()),
237
+ args=[ast.Name(id="result", ctx=ast.Load())],
238
+ keywords=[],
239
+ ),
240
+ body=[
241
+ ast.Return(
242
+ value=ast.Call(
243
+ func=ast.Name(id="ResultErr", ctx=ast.Load()),
244
+ args=[
245
+ ast.Call(
246
+ func=ast.Attribute(
247
+ value=ast.Name(id="result", ctx=ast.Load()),
248
+ attr="error",
249
+ ctx=ast.Load(),
250
+ ),
251
+ args=[],
252
+ keywords=[],
253
+ )
254
+ ],
255
+ keywords=[],
256
+ )
257
+ )
258
+ ],
259
+ orelse=[],
260
+ )
261
+ )
262
+
263
+ # 5. Unwrap response
264
+ body.append(
265
+ ast.Assign(
266
+ targets=[ast.Name(id="response", ctx=ast.Store())],
267
+ value=ast.Call(
268
+ func=ast.Attribute(
269
+ value=ast.Name(id="result", ctx=ast.Load()),
270
+ attr="unwrap",
271
+ ctx=ast.Load(),
272
+ ),
273
+ args=[],
274
+ keywords=[],
275
+ ),
276
+ )
277
+ )
278
+
279
+ # 6. Call self.stream_to_file()
280
+ body.append(
281
+ ast.Assign(
282
+ targets=[ast.Name(id="downloaded_path", ctx=ast.Store())],
283
+ value=ast.Call(
284
+ func=ast.Attribute(
285
+ value=ast.Name(id="self", ctx=ast.Load()),
286
+ attr="stream_to_file",
287
+ ctx=ast.Load(),
288
+ ),
289
+ args=[],
290
+ keywords=[
291
+ ast.keyword(
292
+ arg="response",
293
+ value=ast.Name(id="response", ctx=ast.Load()),
294
+ ),
295
+ ast.keyword(
296
+ arg="output_path",
297
+ value=ast.Name(id="output_path", ctx=ast.Load()),
298
+ ),
299
+ ast.keyword(
300
+ arg="chunk_size",
301
+ value=ast.Name(id="chunk_size", ctx=ast.Load()),
302
+ ),
303
+ ast.keyword(
304
+ arg="progress_callback",
305
+ value=ast.Name(id="progress_callback", ctx=ast.Load()),
306
+ ),
307
+ ],
308
+ ),
309
+ )
310
+ )
311
+
312
+ # 7. Return ResultOk(downloaded_path)
313
+ body.append(
314
+ ast.Return(
315
+ value=ast.Call(
316
+ func=ast.Name(id="ResultOk", ctx=ast.Load()),
317
+ args=[ast.Name(id="downloaded_path", ctx=ast.Load())],
318
+ keywords=[],
319
+ )
320
+ )
321
+ )
322
+
323
+ return body
324
+
325
+ def _build_docstring(
326
+ self,
327
+ path_params: list[tuple[str, str]],
328
+ query_params: list[tuple[str, str]],
329
+ ) -> ast.Expr:
330
+ """Build docstring as AST Expr node."""
331
+
332
+ # Build sections
333
+ sections = [
334
+ # Summary
335
+ [self.config.docstring],
336
+ # Description
337
+ [
338
+ "Downloads directly to disk using streaming, avoiding loading",
339
+ "the entire file into memory. Ideal for large files (1GB+).",
340
+ ],
341
+ # Args
342
+ ["Args:"]
343
+ + [
344
+ f" {name} ({type_}): {_get_param_description(name)}"
345
+ for name, type_ in path_params
346
+ ]
347
+ + [" output_path (Path | str): Where to save the downloaded file."]
348
+ + [
349
+ f" {name} ({type_}): {_get_param_description(name)}"
350
+ for name, type_ in query_params
351
+ ]
352
+ + [
353
+ f" chunk_size (int): Bytes per chunk (default: {self.config.default_chunk_size}). Use 64KB+ for faster downloads.",
354
+ " progress_callback: Optional callback for download progress tracking.",
355
+ " Signature: callback(bytes_downloaded: int, total_bytes: int | None)",
356
+ " timeout: Optional request timeout in seconds. Overrides the instance timeout.",
357
+ ],
358
+ # Returns
359
+ [
360
+ "Returns:",
361
+ " Result[Path, HttpError]: Ok(Path) on success, Err(HttpError) on failure",
362
+ ],
363
+ ]
364
+
365
+ # Flatten sections with blank lines between them
366
+ parts = []
367
+ for i, section in enumerate(sections):
368
+ parts.extend(section)
369
+ if i < len(sections) - 1: # Add blank line between sections
370
+ parts.append("")
371
+
372
+ return ast.Expr(value=ast.Constant(value="\n".join(parts)))
373
+
374
+ def _build_return_annotation(self) -> ast.Subscript:
375
+ """Build Result[Path, HttpError] return annotation."""
376
+ return ast.Subscript(
377
+ value=ast.Name(id="Result", ctx=ast.Load()),
378
+ slice=ast.Tuple(
379
+ elts=[
380
+ ast.Name(id="Path", ctx=ast.Load()),
381
+ ast.Name(id="HttpError", ctx=ast.Load()),
382
+ ],
383
+ ctx=ast.Load(),
384
+ ),
385
+ ctx=ast.Load(),
386
+ )
387
+
388
+
389
+ def parse_type_annotation(type_str: str) -> ast.expr:
390
+ """
391
+ Parse type annotation string into AST expression.
392
+
393
+ Handles common patterns:
394
+ - UUID → ast.Name('UUID')
395
+ - str → ast.Name('str')
396
+ - list[Foo] → ast.Subscript(...)
397
+ - Union[A, B] → ast.BinOp with BitOr
398
+ - None | int → ast.BinOp with BitOr
399
+
400
+ Args:
401
+ type_str: Type annotation as string (e.g., 'UUID', 'list[str]')
402
+
403
+ Returns:
404
+ AST expression node representing the type
405
+ """
406
+ # Use ast.parse to handle complex type annotations
407
+ # Wrap in a function signature to parse the type annotation
408
+ try:
409
+ module = ast.parse(f"def f(x: {type_str}): pass")
410
+ func = module.body[0]
411
+ if not isinstance(func, ast.FunctionDef):
412
+ return ast.Name(id=type_str, ctx=ast.Load())
413
+ annotation = func.args.args[0].annotation
414
+ if annotation is None:
415
+ return ast.Name(id=type_str, ctx=ast.Load())
416
+ return annotation
417
+ except (SyntaxError, IndexError):
418
+ # Fallback to simple Name node if parsing fails
419
+ return ast.Name(id=type_str, ctx=ast.Load())
420
+
421
+
422
+ def _get_param_description(param_name: str) -> str:
423
+ """Get a generic description for a parameter."""
424
+ descriptions = {
425
+ "contract_id": "The contract ID",
426
+ "order_id": "The order ID",
427
+ "item_id": "The item ID",
428
+ "collections": "Optional subset of collections to download",
429
+ "primary_formats": "Optional file format(s) to download",
430
+ }
431
+ return descriptions.get(param_name, "Parameter")
432
+
433
+
434
+ def generate_streaming_method(config: StreamingEndpointConfig) -> str:
435
+ """
436
+ Generate streaming method code from config using AST.
437
+
438
+ Args:
439
+ config: Streaming endpoint configuration
440
+
441
+ Returns:
442
+ Generated method code as string
443
+ """
444
+ builder = ASTMethodBuilder(config)
445
+ method_node = builder.build_method()
446
+
447
+ # Fix missing line numbers and column offsets
448
+ ast.fix_missing_locations(method_node)
449
+
450
+ # Convert AST to code string
451
+ code = ast.unparse(method_node)
452
+
453
+ return code
454
+
455
+
456
+ def add_imports_to_ast(
457
+ tree: ast.Module, imports_to_add: dict[str, list[tuple[str, str | None]]]
458
+ ) -> ast.Module:
459
+ """
460
+ Add missing imports to AST module.
461
+
462
+ Args:
463
+ tree: Existing module AST
464
+ imports_to_add: Dict mapping module names to list of (name, alias) tuples
465
+ e.g., {'pathlib': [('Path', None)],
466
+ 'satvu.result': [('Result', None), ('Ok', 'ResultOk')]}
467
+
468
+ Returns:
469
+ Modified AST module with imports added
470
+ """
471
+ # Find where to insert imports (after existing imports)
472
+ insert_idx = 0
473
+ for i, node in enumerate(tree.body):
474
+ if isinstance(node, ast.Import | ast.ImportFrom):
475
+ insert_idx = i + 1
476
+
477
+ # Check what imports already exist (including aliases)
478
+ existing_imports: dict[str, set[tuple[str, str | None]]] = {}
479
+ for node in tree.body:
480
+ if isinstance(node, ast.ImportFrom) and node.module:
481
+ if node.module not in existing_imports:
482
+ existing_imports[node.module] = set()
483
+ for alias in node.names:
484
+ if alias.name != "*":
485
+ existing_imports[node.module].add((alias.name, alias.asname))
486
+
487
+ # Build new import nodes for missing imports
488
+ new_imports = []
489
+ for module, names_with_aliases in imports_to_add.items():
490
+ existing = existing_imports.get(module, set())
491
+ missing = [
492
+ (name, alias)
493
+ for name, alias in names_with_aliases
494
+ if (name, alias) not in existing
495
+ ]
496
+
497
+ if missing:
498
+ # Create ImportFrom node
499
+ import_node = ast.ImportFrom(
500
+ module=module,
501
+ names=[ast.alias(name=name, asname=alias) for name, alias in missing],
502
+ level=0,
503
+ )
504
+ new_imports.append(import_node)
505
+
506
+ # Insert new imports
507
+ tree.body[insert_idx:insert_idx] = new_imports
508
+
509
+ return tree
510
+
511
+
512
+ def insert_method_after_base(
513
+ tree: ast.Module, base_method: str, new_method_code: str
514
+ ) -> ast.Module:
515
+ """
516
+ Insert new method after base method in module AST.
517
+
518
+ Args:
519
+ tree: Module AST
520
+ base_method: Name of base method to insert after
521
+ new_method_code: Generated method code as string
522
+
523
+ Returns:
524
+ Modified AST module with new method inserted
525
+ """
526
+ # Parse the new method code
527
+ new_method_tree = ast.parse(new_method_code)
528
+ new_method_node = new_method_tree.body[0]
529
+ if not isinstance(new_method_node, ast.FunctionDef):
530
+ return tree # Safety check - should never happen with our generated code
531
+
532
+ # Find the class definition (should be first class in module)
533
+ class_node = None
534
+ for node in tree.body:
535
+ if isinstance(node, ast.ClassDef):
536
+ class_node = node
537
+ break
538
+
539
+ if not class_node:
540
+ return tree
541
+
542
+ # Find base method in class
543
+ base_method_idx = None
544
+ for i, node in enumerate(class_node.body):
545
+ if isinstance(node, ast.FunctionDef) and node.name == base_method:
546
+ base_method_idx = i
547
+ break
548
+
549
+ if base_method_idx is None:
550
+ return tree
551
+
552
+ # Insert new method after base method
553
+ class_node.body.insert(base_method_idx + 1, new_method_node)
554
+
555
+ return tree