lusid-sdk 2.1.405__py3-none-any.whl → 2.1.537__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 (294) hide show
  1. lusid/__init__.py +142 -8
  2. lusid/api/__init__.py +2 -2
  3. lusid/api/abor_api.py +126 -133
  4. lusid/api/abor_configuration_api.py +46 -45
  5. lusid/api/address_key_definition_api.py +28 -27
  6. lusid/api/aggregation_api.py +37 -36
  7. lusid/api/allocations_api.py +39 -38
  8. lusid/api/amortisation_rule_sets_api.py +55 -54
  9. lusid/api/application_metadata_api.py +28 -27
  10. lusid/api/blocks_api.py +37 -36
  11. lusid/api/calendars_api.py +469 -111
  12. lusid/api/chart_of_accounts_api.py +454 -279
  13. lusid/api/complex_market_data_api.py +37 -36
  14. lusid/api/compliance_api.py +136 -135
  15. lusid/api/configuration_recipe_api.py +100 -99
  16. lusid/api/conventions_api.py +109 -108
  17. lusid/api/corporate_action_sources_api.py +82 -81
  18. lusid/api/counterparties_api.py +73 -72
  19. lusid/api/custom_entities_api.py +102 -101
  20. lusid/api/custom_entity_definitions_api.py +37 -36
  21. lusid/api/custom_entity_types_api.py +37 -36
  22. lusid/api/cut_label_definitions_api.py +46 -45
  23. lusid/api/data_types_api.py +248 -72
  24. lusid/api/derived_transaction_portfolios_api.py +19 -18
  25. lusid/api/entities_api.py +431 -46
  26. lusid/api/executions_api.py +37 -36
  27. lusid/api/fee_types_api.py +55 -54
  28. lusid/api/{fund_configurations_api.py → fund_configuration_api.py} +222 -46
  29. lusid/api/funds_api.py +337 -162
  30. lusid/api/group_reconciliations_api.py +2182 -63
  31. lusid/api/instrument_event_types_api.py +64 -63
  32. lusid/api/instrument_events_api.py +46 -45
  33. lusid/api/instruments_api.py +374 -186
  34. lusid/api/legacy_compliance_api.py +73 -72
  35. lusid/api/legal_entities_api.py +167 -166
  36. lusid/api/order_graph_api.py +32 -31
  37. lusid/api/order_instructions_api.py +37 -36
  38. lusid/api/order_management_api.py +251 -90
  39. lusid/api/orders_api.py +37 -36
  40. lusid/api/packages_api.py +37 -36
  41. lusid/api/participations_api.py +37 -36
  42. lusid/api/persons_api.py +163 -162
  43. lusid/api/placements_api.py +37 -36
  44. lusid/api/portfolio_groups_api.py +235 -234
  45. lusid/api/portfolios_api.py +307 -309
  46. lusid/api/property_definitions_api.py +100 -99
  47. lusid/api/queryable_keys_api.py +10 -9
  48. lusid/api/quotes_api.py +82 -81
  49. lusid/api/reconciliations_api.py +136 -135
  50. lusid/api/reference_lists_api.py +39 -38
  51. lusid/api/reference_portfolio_api.py +213 -36
  52. lusid/api/relation_definitions_api.py +28 -27
  53. lusid/api/relations_api.py +19 -18
  54. lusid/api/relationship_definitions_api.py +46 -45
  55. lusid/api/relationships_api.py +19 -18
  56. lusid/api/schemas_api.py +37 -36
  57. lusid/api/scopes_api.py +19 -18
  58. lusid/api/scripted_translation_api.py +73 -72
  59. lusid/api/search_api.py +37 -36
  60. lusid/api/sequences_api.py +37 -36
  61. lusid/api/staged_modifications_api.py +37 -36
  62. lusid/api/staging_rule_set_api.py +46 -45
  63. lusid/api/structured_result_data_api.py +82 -81
  64. lusid/api/system_configuration_api.py +64 -63
  65. lusid/api/tax_rule_sets_api.py +46 -45
  66. lusid/api/transaction_configuration_api.py +100 -99
  67. lusid/api/transaction_fees_api.py +46 -45
  68. lusid/api/transaction_portfolios_api.py +771 -328
  69. lusid/api/translation_api.py +19 -18
  70. lusid/api/workspace_api.py +181 -180
  71. lusid/api_client.py +26 -17
  72. lusid/configuration.py +87 -2
  73. lusid/extensions/api_client.py +25 -17
  74. lusid/extensions/api_client_factory.py +14 -5
  75. lusid/extensions/api_configuration.py +50 -1
  76. lusid/extensions/configuration_loaders.py +39 -11
  77. lusid/extensions/configuration_options.py +67 -0
  78. lusid/extensions/rest.py +78 -26
  79. lusid/extensions/retry.py +109 -37
  80. lusid/models/__init__.py +140 -6
  81. lusid/models/access_metadata_value.py +1 -1
  82. lusid/models/accounting_method.py +7 -0
  83. lusid/models/accumulation_event.py +3 -3
  84. lusid/models/address_key_list.py +3 -3
  85. lusid/models/amortisation_event.py +3 -3
  86. lusid/models/amount.py +69 -0
  87. lusid/models/applicable_instrument_event.py +7 -2
  88. lusid/models/asset_leg.py +1 -1
  89. lusid/models/basket.py +3 -3
  90. lusid/models/batch_update_user_review_for_comparison_result_request.py +81 -0
  91. lusid/models/batch_update_user_review_for_comparison_result_response.py +146 -0
  92. lusid/models/batch_upsert_dates_for_calendar_response.py +146 -0
  93. lusid/models/batch_upsert_portfolio_access_metadata_request.py +27 -17
  94. lusid/models/batch_upsert_portfolio_access_metadata_response.py +56 -16
  95. lusid/models/{metadata_key_value.py → batch_upsert_portfolio_access_metadata_response_item.py} +15 -9
  96. lusid/models/bond.py +3 -3
  97. lusid/models/bond_coupon_event.py +10 -5
  98. lusid/models/bond_default_event.py +3 -3
  99. lusid/models/bond_principal_event.py +10 -5
  100. lusid/models/bonus_issue_event.py +166 -0
  101. lusid/models/{component_rule.py → break_code_source.py} +17 -21
  102. lusid/models/call_on_intermediate_securities_event.py +139 -0
  103. lusid/models/cancel_order_and_move_remaining_result.py +84 -0
  104. lusid/models/cancel_orders_and_move_remaining_request.py +83 -0
  105. lusid/models/cancel_orders_and_move_remaining_response.py +153 -0
  106. lusid/models/cap_floor.py +3 -3
  107. lusid/models/capital_distribution_event.py +3 -3
  108. lusid/models/cash.py +3 -3
  109. lusid/models/cash_dividend_event.py +3 -3
  110. lusid/models/cash_flow_event.py +3 -3
  111. lusid/models/cash_perpetual.py +3 -3
  112. lusid/models/cds_credit_event.py +6 -6
  113. lusid/models/cds_index.py +3 -3
  114. lusid/models/cdx_credit_event.py +6 -6
  115. lusid/models/change_interval.py +123 -0
  116. lusid/models/change_interval_with_order_management_detail.py +3 -3
  117. lusid/models/close_event.py +3 -3
  118. lusid/models/comparison_attribute_value_pair.py +71 -0
  119. lusid/models/complex_bond.py +3 -3
  120. lusid/models/component_transaction.py +10 -3
  121. lusid/models/contract_for_difference.py +3 -3
  122. lusid/models/create_derived_transaction_portfolio_request.py +3 -3
  123. lusid/models/create_group_reconciliation_definition_request.py +113 -0
  124. lusid/models/create_staging_rule_set_request.py +1 -6
  125. lusid/models/create_transaction_portfolio_request.py +3 -3
  126. lusid/models/credit_default_swap.py +3 -3
  127. lusid/models/credit_premium_cash_flow_event.py +3 -3
  128. lusid/models/custodian_account_request.py +1 -1
  129. lusid/models/custom_entity_entity.py +146 -0
  130. lusid/models/custom_entity_response.py +7 -1
  131. lusid/models/decimal_list.py +3 -3
  132. lusid/models/delete_instrument_properties_response.py +7 -1
  133. lusid/models/delete_instrument_response.py +7 -1
  134. lusid/models/delete_instruments_response.py +22 -1
  135. lusid/models/deleted_entity_response.py +7 -1
  136. lusid/models/diary_entry_request.py +10 -1
  137. lusid/models/dividend_option_event.py +3 -3
  138. lusid/models/dividend_reinvestment_event.py +9 -4
  139. lusid/models/effective_range.py +71 -0
  140. lusid/models/equity.py +3 -3
  141. lusid/models/equity_option.py +22 -7
  142. lusid/models/equity_swap.py +3 -3
  143. lusid/models/exchange_traded_option.py +3 -3
  144. lusid/models/exchange_traded_option_contract_details.py +1 -1
  145. lusid/models/exercise_event.py +3 -3
  146. lusid/models/exotic_instrument.py +3 -3
  147. lusid/models/expiry_event.py +3 -3
  148. lusid/models/fee.py +17 -10
  149. lusid/models/fee_request.py +20 -13
  150. lusid/models/fee_type.py +4 -4
  151. lusid/models/fee_type_request.py +3 -3
  152. lusid/models/fixed_leg.py +3 -3
  153. lusid/models/fixed_schedule.py +3 -3
  154. lusid/models/flexible_loan.py +3 -3
  155. lusid/models/float_schedule.py +4 -4
  156. lusid/models/floating_leg.py +3 -3
  157. lusid/models/flow_conventions.py +7 -1
  158. lusid/models/forward_rate_agreement.py +3 -3
  159. lusid/models/fund_configuration.py +44 -17
  160. lusid/models/fund_configuration_request.py +31 -19
  161. lusid/models/fund_id_list.py +99 -0
  162. lusid/models/fund_share_class.py +23 -8
  163. lusid/models/funding_leg.py +3 -3
  164. lusid/models/future.py +3 -3
  165. lusid/models/future_expiry_event.py +3 -3
  166. lusid/models/fx_forward.py +3 -3
  167. lusid/models/fx_forward_settlement_event.py +3 -3
  168. lusid/models/fx_option.py +3 -3
  169. lusid/models/fx_swap.py +3 -3
  170. lusid/models/group_reconciliation_aggregate_attribute_rule.py +2 -2
  171. lusid/models/group_reconciliation_aggregate_attribute_values.py +86 -0
  172. lusid/models/group_reconciliation_aggregate_comparison_rule_operand.py +1 -1
  173. lusid/models/group_reconciliation_comparison_result.py +148 -0
  174. lusid/models/group_reconciliation_core_attribute_values.py +86 -0
  175. lusid/models/group_reconciliation_core_comparison_rule_operand.py +1 -1
  176. lusid/models/group_reconciliation_date_pair.py +81 -0
  177. lusid/models/group_reconciliation_dates.py +78 -0
  178. lusid/models/group_reconciliation_definition.py +136 -0
  179. lusid/models/group_reconciliation_definition_comparison_ruleset_ids.py +83 -0
  180. lusid/models/group_reconciliation_definition_currencies.py +71 -0
  181. lusid/models/group_reconciliation_definition_portfolio_entity_ids.py +86 -0
  182. lusid/models/group_reconciliation_definition_recipe_ids.py +78 -0
  183. lusid/models/group_reconciliation_instance_id.py +71 -0
  184. lusid/models/group_reconciliation_result_statuses.py +89 -0
  185. lusid/models/group_reconciliation_result_types.py +96 -0
  186. lusid/models/group_reconciliation_review_statuses.py +96 -0
  187. lusid/models/group_reconciliation_run_details.py +76 -0
  188. lusid/models/group_reconciliation_run_request.py +75 -0
  189. lusid/models/{metadata_key_value_response.py → group_reconciliation_run_response.py} +20 -29
  190. lusid/models/group_reconciliation_summary.py +121 -0
  191. lusid/models/group_reconciliation_user_review.py +112 -0
  192. lusid/models/group_reconciliation_user_review_add.py +88 -0
  193. lusid/models/group_reconciliation_user_review_break_code.py +80 -0
  194. lusid/models/group_reconciliation_user_review_comment.py +80 -0
  195. lusid/models/group_reconciliation_user_review_match_key.py +80 -0
  196. lusid/models/group_reconciliation_user_review_remove.py +88 -0
  197. lusid/models/holding_contributor.py +11 -4
  198. lusid/models/holding_ids_request.py +69 -0
  199. lusid/models/inflation_leg.py +3 -3
  200. lusid/models/inflation_linked_bond.py +3 -3
  201. lusid/models/inflation_swap.py +3 -3
  202. lusid/models/informational_error_event.py +3 -3
  203. lusid/models/informational_event.py +3 -3
  204. lusid/models/instrument.py +7 -1
  205. lusid/models/instrument_definition.py +8 -2
  206. lusid/models/instrument_event.py +17 -5
  207. lusid/models/instrument_event_holder.py +9 -1
  208. lusid/models/instrument_event_type.py +12 -0
  209. lusid/models/instrument_leg.py +3 -3
  210. lusid/models/instrument_list.py +3 -3
  211. lusid/models/instrument_type.py +2 -0
  212. lusid/models/interest_rate_swap.py +3 -3
  213. lusid/models/interest_rate_swaption.py +3 -3
  214. lusid/models/intermediate_securities_distribution_event.py +140 -0
  215. lusid/models/lapse_election.py +73 -0
  216. lusid/models/loan_facility.py +97 -0
  217. lusid/models/lusid_instrument.py +7 -5
  218. lusid/models/market_data_key_rule.py +5 -3
  219. lusid/models/market_data_specific_rule.py +5 -3
  220. lusid/models/mastered_instrument.py +139 -0
  221. lusid/models/maturity_event.py +3 -3
  222. lusid/models/mbs_coupon_event.py +102 -0
  223. lusid/models/mbs_interest_deferral_event.py +102 -0
  224. lusid/models/mbs_interest_shortfall_event.py +102 -0
  225. lusid/models/mbs_principal_event.py +102 -0
  226. lusid/models/mbs_principal_write_off_event.py +102 -0
  227. lusid/models/merger_event.py +22 -22
  228. lusid/models/new_instrument.py +1 -1
  229. lusid/models/open_event.py +3 -3
  230. lusid/models/option_exercise_cash_event.py +144 -0
  231. lusid/models/option_exercise_election.py +73 -0
  232. lusid/models/option_exercise_physical_event.py +149 -0
  233. lusid/models/output_transaction.py +9 -2
  234. lusid/models/paged_resource_list_of_group_reconciliation_comparison_result.py +113 -0
  235. lusid/models/paged_resource_list_of_group_reconciliation_comparison_ruleset.py +113 -0
  236. lusid/models/paged_resource_list_of_group_reconciliation_definition.py +113 -0
  237. lusid/models/portfolio.py +3 -3
  238. lusid/models/portfolio_details.py +3 -3
  239. lusid/models/portfolio_group_id_list.py +3 -3
  240. lusid/models/portfolio_id_list.py +3 -3
  241. lusid/models/portfolio_without_href.py +3 -3
  242. lusid/models/pricing_options.py +8 -2
  243. lusid/models/property_list.py +3 -3
  244. lusid/models/protection_payout_cash_flow_event.py +102 -0
  245. lusid/models/raw_vendor_event.py +3 -3
  246. lusid/models/reference_instrument.py +3 -3
  247. lusid/models/reference_list.py +6 -5
  248. lusid/models/reference_list_type.py +1 -0
  249. lusid/models/repo.py +3 -3
  250. lusid/models/reset_event.py +3 -3
  251. lusid/models/resource_list_of_change_interval.py +113 -0
  252. lusid/models/resource_list_of_output_transaction.py +113 -0
  253. lusid/models/return_zero_pv_options.py +69 -0
  254. lusid/models/reverse_stock_split_event.py +21 -7
  255. lusid/models/scrip_dividend_event.py +3 -3
  256. lusid/models/settlement_cycle.py +79 -0
  257. lusid/models/share_class_dealing_breakdown.py +3 -2
  258. lusid/models/share_class_details.py +18 -1
  259. lusid/models/simple_cash_flow_loan.py +3 -3
  260. lusid/models/simple_instrument.py +3 -3
  261. lusid/models/simple_rounding_convention.py +76 -0
  262. lusid/models/spin_off_event.py +3 -3
  263. lusid/models/staged_modification_effective_range.py +2 -2
  264. lusid/models/stock_dividend_event.py +20 -6
  265. lusid/models/stock_split_event.py +3 -3
  266. lusid/models/string_list.py +3 -3
  267. lusid/models/swap_cash_flow_event.py +3 -3
  268. lusid/models/swap_principal_event.py +3 -3
  269. lusid/models/target_tax_lot.py +23 -2
  270. lusid/models/target_tax_lot_request.py +23 -2
  271. lusid/models/tender_event.py +172 -0
  272. lusid/models/term_deposit.py +3 -3
  273. lusid/models/total_return_swap.py +4 -4
  274. lusid/models/transaction.py +9 -2
  275. lusid/models/transaction_date_windows.py +85 -0
  276. lusid/models/transaction_price.py +3 -3
  277. lusid/models/transaction_price_type.py +2 -0
  278. lusid/models/transaction_request.py +9 -2
  279. lusid/models/transition_event.py +3 -3
  280. lusid/models/trigger_event.py +3 -3
  281. lusid/models/update_fee_type_request.py +4 -4
  282. lusid/models/update_group_reconciliation_comparison_ruleset_request.py +91 -0
  283. lusid/models/update_group_reconciliation_definition_request.py +107 -0
  284. lusid/models/update_reference_data_request.py +87 -0
  285. lusid/models/update_staging_rule_set_request.py +1 -6
  286. lusid/models/upsert_custom_entities_response.py +20 -1
  287. lusid/models/upsert_reference_portfolio_constituent_properties_request.py +84 -0
  288. lusid/models/upsert_reference_portfolio_constituent_properties_response.py +115 -0
  289. lusid/models/valuation_point_data_query_parameters.py +3 -3
  290. lusid/models/valuation_point_data_response.py +8 -13
  291. lusid/rest.py +70 -20
  292. {lusid_sdk-2.1.405.dist-info → lusid_sdk-2.1.537.dist-info}/METADATA +118 -26
  293. {lusid_sdk-2.1.405.dist-info → lusid_sdk-2.1.537.dist-info}/RECORD +294 -226
  294. {lusid_sdk-2.1.405.dist-info → lusid_sdk-2.1.537.dist-info}/WHEEL +0 -0
@@ -0,0 +1,67 @@
1
+ from typing import Optional
2
+
3
+ class ConfigurationOptions:
4
+
5
+ def __init__(
6
+ self,
7
+ total_timeout_ms: Optional[int] = None,
8
+ connect_timeout_ms: Optional[int] = None,
9
+ read_timeout_ms: Optional[int] = None,
10
+ rate_limit_retries: Optional[int] = None
11
+ ):
12
+ self.total_timeout_ms = total_timeout_ms
13
+ self.connect_timeout_ms = connect_timeout_ms
14
+ self.read_timeout_ms = read_timeout_ms
15
+ self.rate_limit_retries = rate_limit_retries
16
+
17
+ @property
18
+ def total_timeout_ms(self):
19
+ return self.__total_timeout_ms
20
+
21
+ @total_timeout_ms.setter
22
+ def total_timeout_ms(self, value):
23
+ if value:
24
+ if not isinstance(value, int):
25
+ raise TypeError(f"total_timeout_ms must be type int but type '{type(value)}' used")
26
+ if value < 0:
27
+ raise ValueError(f"total_timeout_ms must be an integer greater than or equal to zero")
28
+ self.__total_timeout_ms = value
29
+
30
+ @property
31
+ def connect_timeout_ms(self):
32
+ return self.__connect_timeout_ms
33
+
34
+ @connect_timeout_ms.setter
35
+ def connect_timeout_ms(self, value):
36
+ if value:
37
+ if not isinstance(value, int):
38
+ raise TypeError(f"connect_timeout_ms must be type int but type '{type(value)}' used")
39
+ if value < 0:
40
+ raise ValueError(f"connect_timeout_ms must be an integer greater than or equal to zero")
41
+ self.__connect_timeout_ms = value
42
+
43
+ @property
44
+ def read_timeout_ms(self):
45
+ return self.__read_timeout_ms
46
+
47
+ @read_timeout_ms.setter
48
+ def read_timeout_ms(self, value):
49
+ if value:
50
+ if not isinstance(value, int):
51
+ raise TypeError(f"read_timeout_ms must be type int but type '{type(value)}' used")
52
+ if value < 0:
53
+ raise ValueError(f"read_timeout_ms must be an integer greater than or equal to zero")
54
+ self.__read_timeout_ms = value
55
+
56
+ @property
57
+ def rate_limit_retries(self):
58
+ return self.__rate_limit_retries
59
+
60
+ @rate_limit_retries.setter
61
+ def rate_limit_retries(self, value):
62
+ if value:
63
+ if not isinstance(value, int):
64
+ raise TypeError(f"rate_limit_retries must be type int but type '{type(value)}' used")
65
+ if value < 0:
66
+ raise ValueError(f"rate_limit_retries must be an integer greater than or equal to zero")
67
+ self.__rate_limit_retries = value
lusid/extensions/rest.py CHANGED
@@ -103,9 +103,15 @@ class RESTClientObject(object):
103
103
  **addition_pool_args
104
104
  )
105
105
 
106
+ self.timeout = self.get_timeout(
107
+ total=configuration.timeouts.total_timeout_ms / 1000.0,
108
+ connect=configuration.timeouts.connect_timeout_ms / 1000.0,
109
+ read=configuration.timeouts.read_timeout_ms / 1000.0,
110
+ )
111
+
106
112
  def request(self, method, url, query_params=None, headers=None,
107
113
  body=None, post_params=None, _preload_content=True,
108
- _request_timeout=None):
114
+ _request_timeout=None, opts=None):
109
115
  """Perform requests.
110
116
 
111
117
  :param method: http request method
@@ -119,10 +125,9 @@ class RESTClientObject(object):
119
125
  :param _preload_content: if False, the urllib3.HTTPResponse object will
120
126
  be returned without reading/decoding response
121
127
  data. Default is True.
122
- :param _request_timeout: timeout setting for this request. If one
123
- number provided, it will be total request
124
- timeout. It can also be a pair (tuple) of
125
- (connection, read) timeouts.
128
+ :param _request_timeout: Timeout setting. Do not use - use the opts parameter instead
129
+ :param opts: Configuration options for this request
130
+ :type opts: ConfigurationOptions, optional
126
131
  """
127
132
  method = method.upper()
128
133
  assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT',
@@ -139,14 +144,45 @@ class RESTClientObject(object):
139
144
  # so reset query_params to empty dict
140
145
  query_params = {}
141
146
 
147
+ # _request_timeout param cannot be removed for backwards compatability
148
+ # values from opts override values from _request_timeout
149
+ # try to get values from opts first, then _request_timeout, then self.timeout, else set to None
150
+ # timeout = _request_timeout or self.timeout
142
151
  timeout = None
143
- if _request_timeout:
144
- if isinstance(_request_timeout, (int,float)): # noqa: E501,F821
145
- timeout = urllib3.Timeout(total=_request_timeout)
146
- elif (isinstance(_request_timeout, tuple) and
147
- len(_request_timeout) == 2):
148
- timeout = urllib3.Timeout(
149
- connect=_request_timeout[0], read=_request_timeout[1])
152
+ opts_total_timeout = opts.total_timeout_ms / 1000.0 if opts and opts.total_timeout_ms != None else None
153
+ opts_connect_timeout = opts.connect_timeout_ms / 1000.0 if opts and opts.connect_timeout_ms != None else None
154
+ opts_read_timeout = opts.read_timeout_ms / 1000.0 if opts and opts.read_timeout_ms != None else None
155
+ if not _request_timeout:
156
+ timeout = self.get_timeout(
157
+ total=opts_total_timeout if opts_total_timeout != None
158
+ else self.timeout.total,
159
+ connect=opts_connect_timeout if opts_connect_timeout != None
160
+ else self.timeout._connect,
161
+ read=opts_read_timeout if opts_read_timeout != None
162
+ else self.timeout._read)
163
+ elif isinstance(_request_timeout, urllib3.Timeout):
164
+ timeout = self.get_timeout(
165
+ total=opts_total_timeout if opts_total_timeout != None
166
+ else _request_timeout.total if _request_timeout.total != None
167
+ else self.timeout.total,
168
+ connect=opts_connect_timeout if opts_connect_timeout != None
169
+ else _request_timeout._connect if _request_timeout._connect != None
170
+ else self.timeout._connect,
171
+ read=opts_read_timeout if opts_read_timeout != None
172
+ else _request_timeout._read if _request_timeout._read != None
173
+ else self.timeout._read)
174
+ elif isinstance(_request_timeout, (int, float)):
175
+ timeout = self.get_timeout(
176
+ total=opts_total_timeout if opts_total_timeout != None else _request_timeout,
177
+ connect=opts_connect_timeout if opts_connect_timeout != None else self.timeout._connect,
178
+ read=opts_read_timeout if opts_read_timeout != None else self.timeout._read)
179
+ elif (isinstance(_request_timeout, tuple) and len(_request_timeout) == 2):
180
+ timeout = self.get_timeout(
181
+ total=opts_total_timeout if opts_total_timeout != None else self.timeout.total,
182
+ connect=opts_connect_timeout if opts_connect_timeout != None else _request_timeout[0],
183
+ read=opts_read_timeout if opts_read_timeout != None else _request_timeout[1])
184
+ else:
185
+ raise f"unexpected type '{type(_request_timeout)}' for _request_timeout"
150
186
 
151
187
  try:
152
188
  # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
@@ -235,66 +271,82 @@ class RESTClientObject(object):
235
271
  return r
236
272
 
237
273
  def get_request(self, url, headers=None, query_params=None, _preload_content=True,
238
- _request_timeout=None):
274
+ _request_timeout=None, opts=None):
239
275
  return self.request("GET", url,
240
276
  headers=headers,
241
277
  _preload_content=_preload_content,
242
278
  _request_timeout=_request_timeout,
243
- query_params=query_params)
279
+ query_params=query_params,
280
+ opts=opts)
244
281
 
245
282
  def head_request(self, url, headers=None, query_params=None, _preload_content=True,
246
- _request_timeout=None):
283
+ _request_timeout=None, opts=None):
247
284
  return self.request("HEAD", url,
248
285
  headers=headers,
249
286
  _preload_content=_preload_content,
250
287
  _request_timeout=_request_timeout,
251
- query_params=query_params)
288
+ query_params=query_params,
289
+ opts=opts)
252
290
 
253
291
  def options_request(self, url, headers=None, query_params=None, post_params=None,
254
- body=None, _preload_content=True, _request_timeout=None):
292
+ body=None, _preload_content=True, _request_timeout=None, opts=None):
255
293
  return self.request("OPTIONS", url,
256
294
  headers=headers,
257
295
  query_params=query_params,
258
296
  post_params=post_params,
259
297
  _preload_content=_preload_content,
260
298
  _request_timeout=_request_timeout,
261
- body=body)
299
+ body=body,
300
+ opts=opts)
262
301
 
263
302
  def delete_request(self, url, headers=None, query_params=None, body=None,
264
- _preload_content=True, _request_timeout=None):
303
+ _preload_content=True, _request_timeout=None, opts=None):
265
304
  return self.request("DELETE", url,
266
305
  headers=headers,
267
306
  query_params=query_params,
268
307
  _preload_content=_preload_content,
269
308
  _request_timeout=_request_timeout,
270
- body=body)
309
+ body=body,
310
+ opts=opts)
271
311
 
272
312
  def post_request(self, url, headers=None, query_params=None, post_params=None,
273
- body=None, _preload_content=True, _request_timeout=None):
313
+ body=None, _preload_content=True, _request_timeout=None, opts=None):
274
314
  return self.request("POST", url,
275
315
  headers=headers,
276
316
  query_params=query_params,
277
317
  post_params=post_params,
278
318
  _preload_content=_preload_content,
279
319
  _request_timeout=_request_timeout,
280
- body=body)
320
+ body=body,
321
+ opts=opts)
281
322
 
282
323
  def put_request(self, url, headers=None, query_params=None, post_params=None,
283
- body=None, _preload_content=True, _request_timeout=None):
324
+ body=None, _preload_content=True, _request_timeout=None, opts=None):
284
325
  return self.request("PUT", url,
285
326
  headers=headers,
286
327
  query_params=query_params,
287
328
  post_params=post_params,
288
329
  _preload_content=_preload_content,
289
330
  _request_timeout=_request_timeout,
290
- body=body)
331
+ body=body,
332
+ opts=opts)
291
333
 
292
334
  def patch_request(self, url, headers=None, query_params=None, post_params=None,
293
- body=None, _preload_content=True, _request_timeout=None):
335
+ body=None, _preload_content=True, _request_timeout=None, opts=None):
294
336
  return self.request("PATCH", url,
295
337
  headers=headers,
296
338
  query_params=query_params,
297
339
  post_params=post_params,
298
340
  _preload_content=_preload_content,
299
341
  _request_timeout=_request_timeout,
300
- body=body)
342
+ body=body,
343
+ opts=opts)
344
+
345
+ def get_timeout(self, total: int, connect: int, read: int):
346
+ # zero is used in the sdk config to explicitly set to infinity (and None is used to indicate the value has not been set)
347
+ # but zero is not an allowed value for urllib3.Timeout so change any zeros to Nones
348
+ return urllib3.Timeout(
349
+ total=total if total != 0 else None,
350
+ connect=connect if connect != 0 else None,
351
+ read=read if read != 0 else None,
352
+ )
lusid/extensions/retry.py CHANGED
@@ -1,19 +1,33 @@
1
1
  import time
2
- from typing import overload
2
+ from typing import Optional
3
3
 
4
- from lusid import ApiException
5
4
  import asyncio
6
5
 
6
+ from lusid.configuration import Configuration
7
+ from lusid.exceptions import ApiException
8
+
7
9
 
8
10
  class RetryingRestWrapper:
9
11
  """Wrapper for HTTP requests
10
12
  Which retries on failure
11
13
  And waits the amount of time specified in the Retry After header.
12
14
  """
13
- def __init__(self, rest_object, retries: int = 3):
15
+ def __init__(
16
+ self,
17
+ rest_object,
18
+ retries: int = Configuration.DEFAULT_RETRIES,
19
+ rate_limit_retries: int = Configuration.DEFAULT_RATE_LIMIT_RETRIES
20
+ ):
14
21
  if not isinstance(retries, int):
15
- raise ValueError(f"retries should be an int, found {type(self.retries)}")
22
+ raise TypeError(f"retries should be an int, found {type(retries)}")
23
+ if retries < 0:
24
+ raise ValueError(f"retries should be greater than or equal to zero but was '{retries}'")
25
+ if not isinstance(rate_limit_retries, int):
26
+ raise TypeError(f"rate_limit_retries should be an int, found {type(rate_limit_retries)}")
27
+ if rate_limit_retries < 0:
28
+ raise ValueError(f"rate_limit_retries should be greater than or equal to zero but was '{rate_limit_retries}'")
16
29
  self.retries: int = retries
30
+ self.rate_limit_retries: int = rate_limit_retries
17
31
  self.rest_object = rest_object
18
32
 
19
33
  def request(
@@ -26,9 +40,12 @@ class RetryingRestWrapper:
26
40
  post_params=None,
27
41
  _preload_content=True,
28
42
  _request_timeout=None,
43
+ opts=None,
29
44
  ):
30
- tries = 0
31
- while tries < self.retries + 1:
45
+ retries_count = 0
46
+ rate_limit_retries_count = 0
47
+
48
+ while True:
32
49
  try:
33
50
  return self.rest_object.request(
34
51
  method,
@@ -39,17 +56,26 @@ class RetryingRestWrapper:
39
56
  post_params,
40
57
  _preload_content,
41
58
  _request_timeout,
59
+ opts,
42
60
  )
43
61
  except ApiException as ex:
44
-
45
- retry_after = ex.headers.get("Retry-After")
46
62
 
47
- # have done max number of retries
48
- if tries >= self.retries:
49
- raise
63
+ if ex.status == 429 and ((opts != None and opts.rate_limit_retries != None) or self.rate_limit_retries != None):
64
+ # check for limit of rate limit retries
65
+ limit = opts.rate_limit_retries if (opts and opts.rate_limit_retries != None) else self.rate_limit_retries
66
+ if rate_limit_retries_count >= limit:
67
+ raise
68
+ rate_limit_retries_count += 1
69
+ else:
70
+ # check for limit of all other retries
71
+ if retries_count >= self.retries:
72
+ raise
73
+ retries_count += 1
74
+
75
+ retry_after = ex.headers.get("Retry-After")
50
76
 
51
77
  # try after delay
52
- elif retry_after is not None:
78
+ if retry_after is not None:
53
79
  if not isinstance(retry_after, float):
54
80
  try:
55
81
  retry_after = float(retry_after)
@@ -61,7 +87,6 @@ class RetryingRestWrapper:
61
87
  # no retry header
62
88
  else:
63
89
  raise
64
- tries += 1
65
90
 
66
91
  def get_request(
67
92
  self,
@@ -70,12 +95,14 @@ class RetryingRestWrapper:
70
95
  query_params=None,
71
96
  _preload_content=True,
72
97
  _request_timeout=None,
98
+ opts=None,
73
99
  ):
74
100
  return self.request("GET", url,
75
101
  headers=headers,
76
102
  _preload_content=_preload_content,
77
103
  _request_timeout=_request_timeout,
78
- query_params=query_params)
104
+ query_params=query_params,
105
+ opts=opts)
79
106
 
80
107
  def head_request(
81
108
  self,
@@ -84,12 +111,14 @@ class RetryingRestWrapper:
84
111
  query_params=None,
85
112
  _preload_content=True,
86
113
  _request_timeout=None,
114
+ opts=None,
87
115
  ):
88
116
  return self.request("HEAD", url,
89
117
  headers=headers,
90
118
  _preload_content=_preload_content,
91
119
  _request_timeout=_request_timeout,
92
- query_params=query_params)
120
+ query_params=query_params,
121
+ opts=opts)
93
122
 
94
123
  def options_request(
95
124
  self,
@@ -100,6 +129,7 @@ class RetryingRestWrapper:
100
129
  body=None,
101
130
  _preload_content=True,
102
131
  _request_timeout=None,
132
+ opts=None,
103
133
  ):
104
134
  return self.request("OPTIONS", url,
105
135
  headers=headers,
@@ -107,7 +137,8 @@ class RetryingRestWrapper:
107
137
  post_params=post_params,
108
138
  _preload_content=_preload_content,
109
139
  _request_timeout=_request_timeout,
110
- body=body)
140
+ body=body,
141
+ opts=opts)
111
142
 
112
143
  def delete_request(
113
144
  self,
@@ -117,13 +148,15 @@ class RetryingRestWrapper:
117
148
  body=None,
118
149
  _preload_content=True,
119
150
  _request_timeout=None,
151
+ opts=None,
120
152
  ):
121
153
  return self.request("DELETE", url,
122
154
  headers=headers,
123
155
  query_params=query_params,
124
156
  _preload_content=_preload_content,
125
157
  _request_timeout=_request_timeout,
126
- body=body)
158
+ body=body,
159
+ opts=opts)
127
160
 
128
161
  def post_request(
129
162
  self,
@@ -134,6 +167,7 @@ class RetryingRestWrapper:
134
167
  body=None,
135
168
  _preload_content=True,
136
169
  _request_timeout=None,
170
+ opts=None,
137
171
  ):
138
172
  return self.request("POST", url,
139
173
  headers=headers,
@@ -141,7 +175,8 @@ class RetryingRestWrapper:
141
175
  post_params=post_params,
142
176
  _preload_content=_preload_content,
143
177
  _request_timeout=_request_timeout,
144
- body=body)
178
+ body=body,
179
+ opts=opts)
145
180
 
146
181
  def put_request(
147
182
  self,
@@ -152,6 +187,7 @@ class RetryingRestWrapper:
152
187
  body=None,
153
188
  _preload_content=True,
154
189
  _request_timeout=None,
190
+ opts=None,
155
191
  ):
156
192
  return self.request("PUT", url,
157
193
  headers=headers,
@@ -159,7 +195,8 @@ class RetryingRestWrapper:
159
195
  post_params=post_params,
160
196
  _preload_content=_preload_content,
161
197
  _request_timeout=_request_timeout,
162
- body=body)
198
+ body=body,
199
+ opts=opts)
163
200
 
164
201
  def patch_request(
165
202
  self,
@@ -170,6 +207,7 @@ class RetryingRestWrapper:
170
207
  body=None,
171
208
  _preload_content=True,
172
209
  _request_timeout=None,
210
+ opts=None,
173
211
  ):
174
212
  return self.request("PATCH", url,
175
213
  headers=headers,
@@ -177,7 +215,8 @@ class RetryingRestWrapper:
177
215
  post_params=post_params,
178
216
  _preload_content=_preload_content,
179
217
  _request_timeout=_request_timeout,
180
- body=body)
218
+ body=body,
219
+ opts=opts)
181
220
 
182
221
 
183
222
  class RetryingRestWrapperAsync:
@@ -185,10 +224,17 @@ class RetryingRestWrapperAsync:
185
224
  Which retries on failure
186
225
  And waits the amount of time specified in the Retry After header.
187
226
  """
188
- def __init__(self, rest_object, retries: int = 3):
227
+ def __init__(self, rest_object, retries: int = 3, rate_limit_retries: Optional[int] = Configuration.DEFAULT_RATE_LIMIT_RETRIES):
189
228
  if not isinstance(retries, int):
190
- raise ValueError(f"retries should be an int, found {type(self.retries)}")
229
+ raise TypeError(f"retries should be an int, found {type(retries)}")
230
+ if retries < 0:
231
+ raise ValueError(f"retries should be greater than or equal to zero but was '{retries}'")
232
+ if not isinstance(rate_limit_retries, int):
233
+ raise TypeError(f"rate_limit_retries should be an int, found {type(rate_limit_retries)}")
234
+ if rate_limit_retries < 0:
235
+ raise ValueError(f"rate_limit_retries should be greater than or equal to zero but was '{rate_limit_retries}'")
191
236
  self.retries: int = retries
237
+ self.rate_limit_retries: Optional[int] = rate_limit_retries
192
238
  self.rest_object = rest_object
193
239
 
194
240
  async def close(self):
@@ -204,9 +250,12 @@ class RetryingRestWrapperAsync:
204
250
  post_params=None,
205
251
  _preload_content=True,
206
252
  _request_timeout=None,
253
+ opts=None,
207
254
  ):
208
- tries = 0
209
- while tries < self.retries + 1:
255
+ retries_count = 0
256
+ rate_limit_retries_count = 0
257
+
258
+ while True:
210
259
  try:
211
260
  return await self.rest_object.request(
212
261
  method,
@@ -217,16 +266,26 @@ class RetryingRestWrapperAsync:
217
266
  post_params,
218
267
  _preload_content,
219
268
  _request_timeout,
269
+ opts
220
270
  )
221
271
  except ApiException as ex:
222
- retry_after = ex.headers.get("Retry-After")
223
272
 
224
- # have done max number of retries
225
- if tries >= self.retries:
226
- raise
273
+ if ex.status == 429 and ((opts and opts.rate_limit_retries != None) or self.rate_limit_retries != None):
274
+ # check for limit of rate limit retries
275
+ limit = opts.rate_limit_retries if (opts and opts.rate_limit_retries != None) else self.rate_limit_retries
276
+ if rate_limit_retries_count >= limit:
277
+ raise
278
+ rate_limit_retries_count += 1
279
+ else:
280
+ # check for limit of all other retries
281
+ if retries_count >= self.retries:
282
+ raise
283
+ retries_count += 1
284
+
285
+ retry_after = ex.headers.get("Retry-After")
227
286
 
228
287
  # try after delay
229
- elif retry_after is not None:
288
+ if retry_after is not None:
230
289
  if not isinstance(retry_after, float):
231
290
  try:
232
291
  retry_after = float(retry_after)
@@ -238,7 +297,6 @@ class RetryingRestWrapperAsync:
238
297
  # no retry header
239
298
  else:
240
299
  raise
241
- tries += 1
242
300
 
243
301
  async def get_request(
244
302
  self,
@@ -247,12 +305,14 @@ class RetryingRestWrapperAsync:
247
305
  query_params=None,
248
306
  _preload_content=True,
249
307
  _request_timeout=None,
308
+ opts=None,
250
309
  ):
251
310
  return (await self.request("GET", url,
252
311
  headers=headers,
253
312
  _preload_content=_preload_content,
254
313
  _request_timeout=_request_timeout,
255
- query_params=query_params))
314
+ query_params=query_params,
315
+ opts=opts))
256
316
 
257
317
  async def head_request(
258
318
  self,
@@ -261,12 +321,14 @@ class RetryingRestWrapperAsync:
261
321
  query_params=None,
262
322
  _preload_content=True,
263
323
  _request_timeout=None,
324
+ opts=None,
264
325
  ):
265
326
  return (await self.request("HEAD", url,
266
327
  headers=headers,
267
328
  _preload_content=_preload_content,
268
329
  _request_timeout=_request_timeout,
269
- query_params=query_params))
330
+ query_params=query_params,
331
+ opts=opts))
270
332
 
271
333
  async def options_request(
272
334
  self,
@@ -277,6 +339,7 @@ class RetryingRestWrapperAsync:
277
339
  body=None,
278
340
  _preload_content=True,
279
341
  _request_timeout=None,
342
+ opts=None,
280
343
  ):
281
344
  return (await self.request("OPTIONS", url,
282
345
  headers=headers,
@@ -284,7 +347,8 @@ class RetryingRestWrapperAsync:
284
347
  post_params=post_params,
285
348
  _preload_content=_preload_content,
286
349
  _request_timeout=_request_timeout,
287
- body=body))
350
+ body=body,
351
+ opts=opts))
288
352
 
289
353
  async def delete_request(
290
354
  self,
@@ -294,13 +358,15 @@ class RetryingRestWrapperAsync:
294
358
  body=None,
295
359
  _preload_content=True,
296
360
  _request_timeout=None,
361
+ opts=None,
297
362
  ):
298
363
  return (await self.request("DELETE", url,
299
364
  headers=headers,
300
365
  query_params=query_params,
301
366
  _preload_content=_preload_content,
302
367
  _request_timeout=_request_timeout,
303
- body=body))
368
+ body=body,
369
+ opts=opts))
304
370
 
305
371
  async def post_request(
306
372
  self,
@@ -311,6 +377,7 @@ class RetryingRestWrapperAsync:
311
377
  body=None,
312
378
  _preload_content=True,
313
379
  _request_timeout=None,
380
+ opts=None,
314
381
  ):
315
382
  return (await self.request("POST", url,
316
383
  headers=headers,
@@ -318,7 +385,8 @@ class RetryingRestWrapperAsync:
318
385
  post_params=post_params,
319
386
  _preload_content=_preload_content,
320
387
  _request_timeout=_request_timeout,
321
- body=body))
388
+ body=body,
389
+ opts=opts))
322
390
 
323
391
  async def put_request(
324
392
  self,
@@ -329,6 +397,7 @@ class RetryingRestWrapperAsync:
329
397
  body=None,
330
398
  _preload_content=True,
331
399
  _request_timeout=None,
400
+ opts=None,
332
401
  ):
333
402
  return (await self.request("PUT", url,
334
403
  headers=headers,
@@ -336,7 +405,8 @@ class RetryingRestWrapperAsync:
336
405
  post_params=post_params,
337
406
  _preload_content=_preload_content,
338
407
  _request_timeout=_request_timeout,
339
- body=body))
408
+ body=body,
409
+ opts=opts))
340
410
 
341
411
  async def patch_request(
342
412
  self,
@@ -347,6 +417,7 @@ class RetryingRestWrapperAsync:
347
417
  body=None,
348
418
  _preload_content=True,
349
419
  _request_timeout=None,
420
+ opts=None,
350
421
  ):
351
422
  return (await self.request("PATCH", url,
352
423
  headers=headers,
@@ -354,4 +425,5 @@ class RetryingRestWrapperAsync:
354
425
  post_params=post_params,
355
426
  _preload_content=_preload_content,
356
427
  _request_timeout=_request_timeout,
357
- body=body))
428
+ body=body,
429
+ opts=opts))