lago-python-client 1.45.0__tar.gz → 1.46.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/PKG-INFO +1 -1
  2. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/base_client.py +10 -2
  3. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/client.py +59 -42
  4. lago_python_client-1.46.0/lago_python_client/exceptions.py +69 -0
  5. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/mixins.py +18 -0
  6. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/fee.py +2 -0
  7. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice_custom_section.py +11 -10
  8. lago_python_client-1.46.0/lago_python_client/services/rate_limit.py +126 -0
  9. lago_python_client-1.46.0/lago_python_client/services/request.py +122 -0
  10. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/services/response.py +10 -1
  11. lago_python_client-1.46.0/lago_python_client/version.py +1 -0
  12. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/PKG-INFO +1 -1
  13. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/SOURCES.txt +4 -0
  14. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallet_client.py +15 -0
  15. lago_python_client-1.46.0/tests/test_rate_limit.py +320 -0
  16. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_request_services.py +8 -10
  17. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_subscription_client.py +3 -0
  18. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_wallet_client.py +15 -0
  19. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_wallet_transaction_client.py +4 -0
  20. lago_python_client-1.45.0/lago_python_client/exceptions.py +0 -26
  21. lago_python_client-1.45.0/lago_python_client/services/request.py +0 -55
  22. lago_python_client-1.45.0/lago_python_client/version.py +0 -1
  23. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/LICENSE +0 -0
  24. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/README.md +0 -0
  25. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/__init__.py +0 -0
  26. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/activity_logs/__init__.py +0 -0
  27. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/activity_logs/clients.py +0 -0
  28. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/add_ons/__init__.py +0 -0
  29. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/add_ons/clients.py +0 -0
  30. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/api_logs/__init__.py +0 -0
  31. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/api_logs/clients.py +0 -0
  32. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/base_model.py +0 -0
  33. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billable_metrics/__init__.py +0 -0
  34. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billable_metrics/clients.py +0 -0
  35. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billing_entities/__init__.py +0 -0
  36. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/billing_entities/clients.py +0 -0
  37. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/coupons/__init__.py +0 -0
  38. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/coupons/clients.py +0 -0
  39. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/credit_notes/__init__.py +0 -0
  40. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/credit_notes/clients.py +0 -0
  41. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/__init__.py +0 -0
  42. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/applied_coupons_client.py +0 -0
  43. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/clients.py +0 -0
  44. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/credit_notes_client.py +0 -0
  45. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/invoices_client.py +0 -0
  46. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/payment_methods_client.py +0 -0
  47. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/payment_requests_client.py +0 -0
  48. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/payments_client.py +0 -0
  49. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/subscriptions_client.py +0 -0
  50. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallet_client.py +0 -0
  51. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets/__init__.py +0 -0
  52. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets/alert_client.py +0 -0
  53. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets/metadata_client.py +0 -0
  54. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/customers/wallets_client.py +0 -0
  55. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/events/__init__.py +0 -0
  56. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/events/clients.py +0 -0
  57. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/fees/__init__.py +0 -0
  58. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/fees/clients.py +0 -0
  59. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/functools_ext.py +0 -0
  60. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/gross_revenues/__init__.py +0 -0
  61. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/gross_revenues/clients.py +0 -0
  62. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoice_collections/__init__.py +0 -0
  63. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoice_collections/clients.py +0 -0
  64. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoiced_usages/__init__.py +0 -0
  65. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoiced_usages/clients.py +0 -0
  66. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoices/__init__.py +0 -0
  67. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/invoices/clients.py +0 -0
  68. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/__init__.py +0 -0
  69. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/activity_log.py +0 -0
  70. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/add_on.py +0 -0
  71. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/alert.py +0 -0
  72. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/api_log.py +0 -0
  73. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/applied_coupon.py +0 -0
  74. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/billable_metric.py +0 -0
  75. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/billing_entity.py +0 -0
  76. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/billing_period.py +0 -0
  77. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/charge.py +0 -0
  78. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/coupon.py +0 -0
  79. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/credit.py +0 -0
  80. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/credit_note.py +0 -0
  81. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/customer.py +0 -0
  82. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/customer_projected_usage.py +0 -0
  83. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/customer_usage.py +0 -0
  84. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/error_detail.py +0 -0
  85. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/event.py +0 -0
  86. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/fixed_charge.py +0 -0
  87. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/gross_revenue.py +0 -0
  88. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice.py +0 -0
  89. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice_collection.py +0 -0
  90. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoice_item.py +0 -0
  91. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/invoiced_usage.py +0 -0
  92. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/lifetime_usage.py +0 -0
  93. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/minimum_commitment.py +0 -0
  94. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/mrr.py +0 -0
  95. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/organization.py +0 -0
  96. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/overdue_balance.py +0 -0
  97. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment.py +0 -0
  98. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment_method.py +0 -0
  99. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment_receipt.py +0 -0
  100. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/payment_request.py +0 -0
  101. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/plan.py +0 -0
  102. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/subscription.py +0 -0
  103. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/tax.py +0 -0
  104. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/usage.py +0 -0
  105. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/usage_threshold.py +0 -0
  106. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/wallet.py +0 -0
  107. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/wallet_transaction.py +0 -0
  108. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/models/webhook_endpoint.py +0 -0
  109. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/mrrs/__init__.py +0 -0
  110. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/mrrs/clients.py +0 -0
  111. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/organizations/__init__.py +0 -0
  112. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/organizations/clients.py +0 -0
  113. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/overdue_balances/__init__.py +0 -0
  114. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/overdue_balances/clients.py +0 -0
  115. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_receipts/__init__.py +0 -0
  116. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_receipts/clients.py +0 -0
  117. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_requests/__init__.py +0 -0
  118. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payment_requests/clients.py +0 -0
  119. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payments/__init__.py +0 -0
  120. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/payments/clients.py +0 -0
  121. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/__init__.py +0 -0
  122. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/charges_client.py +0 -0
  123. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/clients.py +0 -0
  124. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/plans/fixed_charges_client.py +0 -0
  125. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/py.typed +0 -0
  126. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/services/__init__.py +0 -0
  127. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/services/json.py +0 -0
  128. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/__init__.py +0 -0
  129. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/charges_client.py +0 -0
  130. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/clients.py +0 -0
  131. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/subscriptions/fixed_charges_client.py +0 -0
  132. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/taxes/__init__.py +0 -0
  133. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/taxes/clients.py +0 -0
  134. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/usages/__init__.py +0 -0
  135. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/usages/clients.py +0 -0
  136. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/wallets/__init__.py +0 -0
  137. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/wallets/clients.py +0 -0
  138. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhook_endpoints/__init__.py +0 -0
  139. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhook_endpoints/clients.py +0 -0
  140. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhooks/__init__.py +0 -0
  141. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client/webhooks/clients.py +0 -0
  142. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/dependency_links.txt +0 -0
  143. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/requires.txt +0 -0
  144. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/lago_python_client.egg-info/top_level.txt +0 -0
  145. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/pyproject.toml +0 -0
  146. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/setup.cfg +0 -0
  147. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/__init__.py +0 -0
  148. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_activity_logs_client.py +0 -0
  149. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_add_on_client.py +0 -0
  150. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_api_logs_client.py +0 -0
  151. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_applied_coupon_client.py +0 -0
  152. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_billable_metric_client.py +0 -0
  153. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_billing_entity_client.py +0 -0
  154. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_client.py +0 -0
  155. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_coupon_client.py +0 -0
  156. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_credit_note_client.py +0 -0
  157. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_applied_coupons_client.py +0 -0
  158. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_client.py +0 -0
  159. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_credit_notes_client.py +0 -0
  160. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_invoices_client.py +0 -0
  161. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_payment_methods_client.py +0 -0
  162. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_payment_requests_client.py +0 -0
  163. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_payments_client.py +0 -0
  164. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_sbscriptions_client.py +0 -0
  165. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallet_alert_client.py +0 -0
  166. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallet_metadata_client.py +0 -0
  167. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_customer_wallets_client.py +0 -0
  168. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_event_client.py +0 -0
  169. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_fee_client.py +0 -0
  170. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_functools_ext.py +0 -0
  171. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_gross_revenue_client.py +0 -0
  172. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_invoice_client.py +0 -0
  173. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_invoice_collection_client.py +0 -0
  174. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_invoiced_usage_client.py +0 -0
  175. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_json_services.py +0 -0
  176. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_mrr_client.py +0 -0
  177. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_organization_client.py +0 -0
  178. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_overdue_balance_client.py +0 -0
  179. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_payment_client.py +0 -0
  180. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_payment_receipt_client.py +0 -0
  181. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_payment_request_client.py +0 -0
  182. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_plan_client.py +0 -0
  183. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_response_services.py +0 -0
  184. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_tax_client.py +0 -0
  185. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_usage_client.py +0 -0
  186. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_webhook_client.py +0 -0
  187. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/test_webhook_endpoint_client.py +0 -0
  188. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/utils/__init__.py +0 -0
  189. {lago_python_client-1.45.0 → lago_python_client-1.46.0}/tests/utils/mixin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lago-python-client
3
- Version: 1.45.0
3
+ Version: 1.46.0
4
4
  Summary: Lago Python API Client
5
5
  Home-page: https://github.com/getlago/lago-python-client
6
6
  Author: Lovro Colic
@@ -1,16 +1,24 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Type
2
+ from typing import Optional, Type
3
3
 
4
4
  from lago_python_client.base_model import BaseModel
5
+ from lago_python_client.services.rate_limit import RateLimitRetryConfig
5
6
 
6
7
 
7
8
  class BaseClient(ABC):
8
9
  """The base class used for each collection client."""
9
10
 
10
- def __init__(self, base_url: str, api_key: str, base_ingest_url: str = ""):
11
+ def __init__(
12
+ self,
13
+ base_url: str,
14
+ api_key: str,
15
+ base_ingest_url: str = "",
16
+ rate_limit_retry_config: Optional[RateLimitRetryConfig] = None,
17
+ ):
11
18
  self.base_url = base_url
12
19
  self.api_key = api_key
13
20
  self.base_ingest_url = base_ingest_url
21
+ self.rate_limit_retry_config = rate_limit_retry_config or RateLimitRetryConfig()
14
22
 
15
23
  @property
16
24
  @classmethod
@@ -32,6 +32,7 @@ from .payments.clients import PaymentClient
32
32
  from .plans.charges_client import PlanChargesClient
33
33
  from .plans.clients import PlanClient
34
34
  from .plans.fixed_charges_client import PlanFixedChargesClient
35
+ from .services.rate_limit import RateLimitRetryConfig
35
36
  from .subscriptions.charges_client import SubscriptionChargesClient
36
37
  from .subscriptions.clients import SubscriptionClient
37
38
  from .subscriptions.fixed_charges_client import SubscriptionFixedChargesClient
@@ -53,12 +54,22 @@ class Client:
53
54
  API_PATH: Final[str] = "api/v1/"
54
55
 
55
56
  def __init__(
56
- self, api_key: str = "", api_url: str = "", use_ingest_service: bool = False, ingest_api_url: str = ""
57
+ self,
58
+ api_key: str = "",
59
+ api_url: str = "",
60
+ use_ingest_service: bool = False,
61
+ ingest_api_url: str = "",
62
+ max_retries: int = 3,
63
+ retry_on_rate_limit: bool = True,
57
64
  ) -> None:
58
65
  self.api_key: str = api_key
59
66
  self.api_url: str = api_url
60
67
  self.use_ingest_service: bool = use_ingest_service
61
68
  self.ingest_api_url: str = ingest_api_url
69
+ self.rate_limit_retry_config: RateLimitRetryConfig = RateLimitRetryConfig(
70
+ max_retries=max_retries,
71
+ retry_on_rate_limit=retry_on_rate_limit,
72
+ )
62
73
 
63
74
  @property
64
75
  def base_api_url(self) -> str:
@@ -76,166 +87,172 @@ class Client:
76
87
 
77
88
  return urljoin(ingest_url, Client.API_PATH)
78
89
 
90
+ def _create_client(self, client_class, base_url, api_key, base_ingest_url=""):
91
+ """Create a client instance with rate limit config."""
92
+ if base_ingest_url:
93
+ return client_class(base_url, api_key, base_ingest_url, self.rate_limit_retry_config)
94
+ return client_class(base_url, api_key, rate_limit_retry_config=self.rate_limit_retry_config)
95
+
79
96
  @callable_cached_property
80
97
  def add_ons(self) -> AddOnClient:
81
- return AddOnClient(self.base_api_url, self.api_key)
98
+ return self._create_client(AddOnClient, self.base_api_url, self.api_key)
82
99
 
83
100
  @callable_cached_property
84
101
  def billable_metrics(self) -> BillableMetricClient:
85
- return BillableMetricClient(self.base_api_url, self.api_key)
102
+ return self._create_client(BillableMetricClient, self.base_api_url, self.api_key)
86
103
 
87
104
  @callable_cached_property
88
105
  def billing_entities(self) -> BillingEntityClient:
89
- return BillingEntityClient(self.base_api_url, self.api_key)
106
+ return self._create_client(BillingEntityClient, self.base_api_url, self.api_key)
90
107
 
91
108
  @callable_cached_property
92
109
  def coupons(self) -> CouponClient:
93
- return CouponClient(self.base_api_url, self.api_key)
110
+ return self._create_client(CouponClient, self.base_api_url, self.api_key)
94
111
 
95
112
  @callable_cached_property
96
113
  def applied_coupons(self) -> AppliedCouponClient:
97
- return AppliedCouponClient(self.base_api_url, self.api_key)
114
+ return self._create_client(AppliedCouponClient, self.base_api_url, self.api_key)
98
115
 
99
116
  @callable_cached_property
100
117
  def credit_notes(self) -> CreditNoteClient:
101
- return CreditNoteClient(self.base_api_url, self.api_key)
118
+ return self._create_client(CreditNoteClient, self.base_api_url, self.api_key)
102
119
 
103
120
  @callable_cached_property
104
121
  def customers(self) -> CustomerClient:
105
- return CustomerClient(self.base_api_url, self.api_key)
122
+ return self._create_client(CustomerClient, self.base_api_url, self.api_key)
106
123
 
107
124
  @callable_cached_property
108
125
  def customer_applied_coupons(self) -> CustomerAppliedCouponsClient:
109
- return CustomerAppliedCouponsClient(self.base_api_url, self.api_key)
126
+ return self._create_client(CustomerAppliedCouponsClient, self.base_api_url, self.api_key)
110
127
 
111
128
  @callable_cached_property
112
129
  def customer_credit_notes(self) -> CustomerCreditNotesClient:
113
- return CustomerCreditNotesClient(self.base_api_url, self.api_key)
130
+ return self._create_client(CustomerCreditNotesClient, self.base_api_url, self.api_key)
114
131
 
115
132
  @callable_cached_property
116
133
  def customer_invoices(self) -> CustomerInvoicesClient:
117
- return CustomerInvoicesClient(self.base_api_url, self.api_key)
134
+ return self._create_client(CustomerInvoicesClient, self.base_api_url, self.api_key)
118
135
 
119
136
  @callable_cached_property
120
137
  def customer_payment_methods(self) -> CustomerPaymentMethodsClient:
121
- return CustomerPaymentMethodsClient(self.base_api_url, self.api_key)
138
+ return self._create_client(CustomerPaymentMethodsClient, self.base_api_url, self.api_key)
122
139
 
123
140
  @callable_cached_property
124
141
  def customer_payments(self) -> CustomerPaymentsClient:
125
- return CustomerPaymentsClient(self.base_api_url, self.api_key)
142
+ return self._create_client(CustomerPaymentsClient, self.base_api_url, self.api_key)
126
143
 
127
144
  @callable_cached_property
128
145
  def customer_payment_requests(self) -> CustomerPaymentRequestsClient:
129
- return CustomerPaymentRequestsClient(self.base_api_url, self.api_key)
146
+ return self._create_client(CustomerPaymentRequestsClient, self.base_api_url, self.api_key)
130
147
 
131
148
  @callable_cached_property
132
149
  def customer_subscriptions(self) -> CustomerSubscriptionsClient:
133
- return CustomerSubscriptionsClient(self.base_api_url, self.api_key)
150
+ return self._create_client(CustomerSubscriptionsClient, self.base_api_url, self.api_key)
134
151
 
135
152
  @callable_cached_property
136
153
  def customer_wallets(self) -> CustomerWalletsClient:
137
- return CustomerWalletsClient(self.base_api_url, self.api_key)
154
+ return self._create_client(CustomerWalletsClient, self.base_api_url, self.api_key)
138
155
 
139
156
  @callable_cached_property
140
157
  def events(self) -> EventClient:
141
- return EventClient(self.base_api_url, self.api_key, self.base_ingest_api_url)
158
+ return self._create_client(EventClient, self.base_api_url, self.api_key, self.base_ingest_api_url)
142
159
 
143
160
  @callable_cached_property
144
161
  def fees(self) -> FeeClient:
145
- return FeeClient(self.base_api_url, self.api_key)
162
+ return self._create_client(FeeClient, self.base_api_url, self.api_key)
146
163
 
147
164
  @callable_cached_property
148
165
  def gross_revenues(self) -> GrossRevenueClient:
149
- return GrossRevenueClient(self.base_api_url, self.api_key)
166
+ return self._create_client(GrossRevenueClient, self.base_api_url, self.api_key)
150
167
 
151
168
  @callable_cached_property
152
169
  def invoices(self) -> InvoiceClient:
153
- return InvoiceClient(self.base_api_url, self.api_key)
170
+ return self._create_client(InvoiceClient, self.base_api_url, self.api_key)
154
171
 
155
172
  @callable_cached_property
156
173
  def invoiced_usages(self) -> InvoicedUsageClient:
157
- return InvoicedUsageClient(self.base_api_url, self.api_key)
174
+ return self._create_client(InvoicedUsageClient, self.base_api_url, self.api_key)
158
175
 
159
176
  @callable_cached_property
160
177
  def mrrs(self) -> MrrClient:
161
- return MrrClient(self.base_api_url, self.api_key)
178
+ return self._create_client(MrrClient, self.base_api_url, self.api_key)
162
179
 
163
180
  @callable_cached_property
164
181
  def organizations(self) -> OrganizationClient:
165
- return OrganizationClient(self.base_api_url, self.api_key)
182
+ return self._create_client(OrganizationClient, self.base_api_url, self.api_key)
166
183
 
167
184
  @callable_cached_property
168
185
  def overdue_balances(self) -> OverdueBalanceClient:
169
- return OverdueBalanceClient(self.base_api_url, self.api_key)
186
+ return self._create_client(OverdueBalanceClient, self.base_api_url, self.api_key)
170
187
 
171
188
  @callable_cached_property
172
189
  def payment_receipts(self) -> PaymentReceiptClient:
173
- return PaymentReceiptClient(self.base_api_url, self.api_key)
190
+ return self._create_client(PaymentReceiptClient, self.base_api_url, self.api_key)
174
191
 
175
192
  @callable_cached_property
176
193
  def payment_requests(self) -> PaymentRequestClient:
177
- return PaymentRequestClient(self.base_api_url, self.api_key)
194
+ return self._create_client(PaymentRequestClient, self.base_api_url, self.api_key)
178
195
 
179
196
  @callable_cached_property
180
197
  def payments(self) -> PaymentClient:
181
- return PaymentClient(self.base_api_url, self.api_key)
198
+ return self._create_client(PaymentClient, self.base_api_url, self.api_key)
182
199
 
183
200
  @callable_cached_property
184
201
  def invoice_collections(self) -> InvoiceCollectionClient:
185
- return InvoiceCollectionClient(self.base_api_url, self.api_key)
202
+ return self._create_client(InvoiceCollectionClient, self.base_api_url, self.api_key)
186
203
 
187
204
  @callable_cached_property
188
205
  def plans(self) -> PlanClient:
189
- return PlanClient(self.base_api_url, self.api_key)
206
+ return self._create_client(PlanClient, self.base_api_url, self.api_key)
190
207
 
191
208
  @callable_cached_property
192
209
  def plan_charges(self) -> PlanChargesClient:
193
- return PlanChargesClient(self.base_api_url, self.api_key)
210
+ return self._create_client(PlanChargesClient, self.base_api_url, self.api_key)
194
211
 
195
212
  @callable_cached_property
196
213
  def plan_fixed_charges(self) -> PlanFixedChargesClient:
197
- return PlanFixedChargesClient(self.base_api_url, self.api_key)
214
+ return self._create_client(PlanFixedChargesClient, self.base_api_url, self.api_key)
198
215
 
199
216
  @callable_cached_property
200
217
  def subscriptions(self) -> SubscriptionClient:
201
- return SubscriptionClient(self.base_api_url, self.api_key)
218
+ return self._create_client(SubscriptionClient, self.base_api_url, self.api_key)
202
219
 
203
220
  @callable_cached_property
204
221
  def subscription_charges(self) -> SubscriptionChargesClient:
205
- return SubscriptionChargesClient(self.base_api_url, self.api_key)
222
+ return self._create_client(SubscriptionChargesClient, self.base_api_url, self.api_key)
206
223
 
207
224
  @callable_cached_property
208
225
  def subscription_fixed_charges(self) -> SubscriptionFixedChargesClient:
209
- return SubscriptionFixedChargesClient(self.base_api_url, self.api_key)
226
+ return self._create_client(SubscriptionFixedChargesClient, self.base_api_url, self.api_key)
210
227
 
211
228
  @callable_cached_property
212
229
  def taxes(self) -> TaxClient:
213
- return TaxClient(self.base_api_url, self.api_key)
230
+ return self._create_client(TaxClient, self.base_api_url, self.api_key)
214
231
 
215
232
  @callable_cached_property
216
233
  def usages(self) -> UsageClient:
217
- return UsageClient(self.base_api_url, self.api_key)
234
+ return self._create_client(UsageClient, self.base_api_url, self.api_key)
218
235
 
219
236
  @callable_cached_property
220
237
  def wallets(self) -> WalletClient:
221
- return WalletClient(self.base_api_url, self.api_key)
238
+ return self._create_client(WalletClient, self.base_api_url, self.api_key)
222
239
 
223
240
  @callable_cached_property
224
241
  def wallet_transactions(self) -> WalletTransactionClient:
225
- return WalletTransactionClient(self.base_api_url, self.api_key)
242
+ return self._create_client(WalletTransactionClient, self.base_api_url, self.api_key)
226
243
 
227
244
  @callable_cached_property
228
245
  def webhooks(self) -> WebhookClient:
229
- return WebhookClient(self.base_api_url, self.api_key)
246
+ return self._create_client(WebhookClient, self.base_api_url, self.api_key)
230
247
 
231
248
  @callable_cached_property
232
249
  def webhook_endpoints(self) -> WebhookEndpointClient:
233
- return WebhookEndpointClient(self.base_api_url, self.api_key)
250
+ return self._create_client(WebhookEndpointClient, self.base_api_url, self.api_key)
234
251
 
235
252
  @callable_cached_property
236
253
  def activity_logs(self) -> ActivityLogClient:
237
- return ActivityLogClient(self.base_api_url, self.api_key)
254
+ return self._create_client(ActivityLogClient, self.base_api_url, self.api_key)
238
255
 
239
256
  @callable_cached_property
240
257
  def api_logs(self) -> ApiLogClient:
241
- return ApiLogClient(self.base_api_url, self.api_key)
258
+ return self._create_client(ApiLogClient, self.base_api_url, self.api_key)
@@ -0,0 +1,69 @@
1
+ from collections.abc import MutableMapping
2
+ from http import HTTPStatus
3
+ from typing import Any, Optional
4
+
5
+
6
+ class LagoApiError(Exception):
7
+ def __init__(
8
+ self,
9
+ status_code: int,
10
+ url: Optional[str],
11
+ response: Any,
12
+ detail: Optional[str] = None,
13
+ headers: Optional[MutableMapping[str, str]] = None,
14
+ ) -> None:
15
+ if detail is None:
16
+ detail = HTTPStatus(status_code).phrase
17
+ self.status_code = status_code
18
+ self.url = url
19
+ self.response = response
20
+ self.detail = detail
21
+ self.headers = headers
22
+ super().__init__(self.response)
23
+
24
+ def __repr__(self) -> str:
25
+ class_name = self.__class__.__name__
26
+ return f"{class_name}(response={self.response!r}"
27
+
28
+
29
+ class LagoRateLimitError(LagoApiError):
30
+ """Exception raised when rate limit is exceeded (HTTP 429)."""
31
+
32
+ def __init__(
33
+ self,
34
+ status_code: int,
35
+ url: Optional[str],
36
+ response: Any,
37
+ detail: Optional[str] = None,
38
+ headers: Optional[MutableMapping[str, str]] = None,
39
+ ) -> None:
40
+ super().__init__(
41
+ status_code=status_code,
42
+ url=url,
43
+ response=response,
44
+ detail=detail,
45
+ headers=headers,
46
+ )
47
+ # Parse rate limit headers
48
+ self.limit: Optional[int] = None
49
+ self.remaining: Optional[int] = None
50
+ self.reset: Optional[int] = None
51
+
52
+ if headers:
53
+ try:
54
+ self.limit = int(headers["x-ratelimit-limit"])
55
+ except (KeyError, ValueError, TypeError):
56
+ pass
57
+ try:
58
+ self.remaining = int(headers["x-ratelimit-remaining"])
59
+ except (KeyError, ValueError, TypeError):
60
+ pass
61
+ try:
62
+ self.reset = int(headers["x-ratelimit-reset"])
63
+ except (KeyError, ValueError, TypeError):
64
+ pass
65
+
66
+ def __repr__(self) -> str:
67
+ return (
68
+ f"LagoRateLimitError(limit={self.limit}, remaining={self.remaining}, reset={self.reset}, url={self.url!r})"
69
+ )
@@ -10,6 +10,7 @@ except ImportError: # Python 3.7
10
10
  from collections.abc import Mapping
11
11
 
12
12
  from lago_python_client.base_model import BaseModel
13
+ from lago_python_client.services.rate_limit import RateLimitRetryConfig
13
14
 
14
15
  from .services.json import to_json
15
16
  from .services.request import (
@@ -47,6 +48,8 @@ class _ClientMixin(Protocol[_PM_co]):
47
48
  def api_key(self) -> str: ...
48
49
  @property
49
50
  def base_url(self) -> str: ...
51
+ @property
52
+ def rate_limit_retry_config(self) -> RateLimitRetryConfig: ...
50
53
 
51
54
 
52
55
  class CreateCommandMixin(Generic[_M]):
@@ -71,6 +74,7 @@ class CreateCommandMixin(Generic[_M]):
71
74
  ),
72
75
  headers=make_headers(api_key=self.api_key),
73
76
  timeout=timeout,
77
+ rate_limit_retry_config=self.rate_limit_retry_config,
74
78
  )
75
79
 
76
80
  # Process response data
@@ -105,6 +109,7 @@ class DestroyCommandMixin(Generic[_M]):
105
109
  ),
106
110
  headers=make_headers(api_key=self.api_key),
107
111
  timeout=timeout,
112
+ rate_limit_retry_config=self.rate_limit_retry_config,
108
113
  )
109
114
 
110
115
  # Process response data
@@ -134,6 +139,7 @@ class FindAllCommandMixin(Generic[_M]):
134
139
  ),
135
140
  headers=make_headers(api_key=self.api_key),
136
141
  timeout=timeout,
142
+ rate_limit_retry_config=self.rate_limit_retry_config,
137
143
  )
138
144
 
139
145
  # Process response data
@@ -167,6 +173,7 @@ class FindAllChildrenCommandMixin(Generic[_M]):
167
173
  ),
168
174
  query_pairs=options,
169
175
  ),
176
+ rate_limit_retry_config=self.rate_limit_retry_config,
170
177
  )
171
178
 
172
179
  return prepare_index_response(
@@ -200,6 +207,7 @@ class FindChildCommandMixin(Generic[_M]):
200
207
  query_pairs=options,
201
208
  ),
202
209
  headers=make_headers(api_key=self.api_key),
210
+ rate_limit_retry_config=self.rate_limit_retry_config,
203
211
  )
204
212
 
205
213
  return prepare_object_response(
@@ -232,6 +240,7 @@ class CreateChildCommandMixin(Generic[_M]):
232
240
  ),
233
241
  content=to_json({self.ROOT_NAME: input_object.dict(exclude_none=True)}),
234
242
  headers=make_headers(api_key=self.api_key),
243
+ rate_limit_retry_config=self.rate_limit_retry_config,
235
244
  )
236
245
 
237
246
  return prepare_object_response(
@@ -266,6 +275,7 @@ class UpdateChildCommandMixin(Generic[_M]):
266
275
  ),
267
276
  content=to_json({self.ROOT_NAME: input_object.dict(exclude_none=True)}),
268
277
  headers=make_headers(api_key=self.api_key),
278
+ rate_limit_retry_config=self.rate_limit_retry_config,
269
279
  )
270
280
 
271
281
  return prepare_object_response(
@@ -298,6 +308,7 @@ class DestroyChildCommandMixin(Generic[_M]):
298
308
  query_pairs=options,
299
309
  ),
300
310
  headers=make_headers(api_key=self.api_key),
311
+ rate_limit_retry_config=self.rate_limit_retry_config,
301
312
  )
302
313
 
303
314
  return prepare_object_response(
@@ -327,6 +338,7 @@ class FindCommandMixin(Generic[_M]):
327
338
  ),
328
339
  headers=make_headers(api_key=self.api_key),
329
340
  timeout=timeout,
341
+ rate_limit_retry_config=self.rate_limit_retry_config,
330
342
  )
331
343
 
332
344
  # Process response data
@@ -359,6 +371,7 @@ class UpdateCommandMixin(Generic[_M]):
359
371
  ),
360
372
  headers=make_headers(api_key=self.api_key),
361
373
  timeout=timeout,
374
+ rate_limit_retry_config=self.rate_limit_retry_config,
362
375
  )
363
376
 
364
377
  # Process response data
@@ -389,6 +402,7 @@ class NestedCreateCommandMixin(Generic[_M]):
389
402
  ),
390
403
  headers=make_headers(api_key=self.api_key),
391
404
  timeout=timeout,
405
+ rate_limit_retry_config=self.rate_limit_retry_config,
392
406
  )
393
407
 
394
408
  # Process response data
@@ -426,6 +440,7 @@ class NestedUpdateCommandMixin(Generic[_M]):
426
440
  ),
427
441
  headers=make_headers(api_key=self.api_key),
428
442
  timeout=timeout,
443
+ rate_limit_retry_config=self.rate_limit_retry_config,
429
444
  )
430
445
 
431
446
  # Process response data
@@ -454,6 +469,7 @@ class NestedDestroyCommandMixin(Generic[_M]):
454
469
  ),
455
470
  headers=make_headers(api_key=self.api_key),
456
471
  timeout=timeout,
472
+ rate_limit_retry_config=self.rate_limit_retry_config,
457
473
  )
458
474
 
459
475
  # Process response data
@@ -482,6 +498,7 @@ class NestedFindCommandMixin(Generic[_M]):
482
498
  ),
483
499
  headers=make_headers(api_key=self.api_key),
484
500
  timeout=timeout,
501
+ rate_limit_retry_config=self.rate_limit_retry_config,
485
502
  )
486
503
 
487
504
  # Process response data
@@ -510,6 +527,7 @@ class NestedFindAllCommandMixin(Generic[_M]):
510
527
  ),
511
528
  headers=make_headers(api_key=self.api_key),
512
529
  timeout=timeout,
530
+ rate_limit_retry_config=self.rate_limit_retry_config,
513
531
  )
514
532
 
515
533
  # Process response data
@@ -53,6 +53,8 @@ class FeeResponse(BaseResponseModel):
53
53
  total_amount_cents: Optional[int]
54
54
  unit_amount_cents: Optional[int] # deprecated
55
55
  precise_unit_amount: Optional[str]
56
+ sub_total_excluding_taxes_amount_cents: Optional[int]
57
+ sub_total_excluding_taxes_precise_amount_cents: Optional[str]
56
58
  precise_amount: Optional[str]
57
59
  precise_total_amount: Optional[str]
58
60
  taxes_precise_amount: Optional[str]
@@ -10,16 +10,6 @@ class InvoiceCustomSectionInput(BaseModel):
10
10
  invoice_custom_section_codes: Optional[List[str]]
11
11
 
12
12
 
13
- class AppliedInvoiceCustomSection(BaseResponseModel):
14
- lago_id: Optional[str]
15
- invoice_custom_section_id: Optional[str]
16
- created_at: Optional[str]
17
-
18
-
19
- class AppliedInvoiceCustomSections(BaseResponseModel):
20
- __root__: List[AppliedInvoiceCustomSection]
21
-
22
-
23
13
  class InvoiceCustomSectionResponse(BaseResponseModel):
24
14
  lago_id: Optional[str]
25
15
  code: Optional[str]
@@ -30,5 +20,16 @@ class InvoiceCustomSectionResponse(BaseResponseModel):
30
20
  applied_to_organization: Optional[bool] # Deprecated
31
21
 
32
22
 
23
+ class AppliedInvoiceCustomSection(BaseResponseModel):
24
+ lago_id: Optional[str]
25
+ invoice_custom_section_id: Optional[str]
26
+ created_at: Optional[str]
27
+ invoice_custom_section: Optional[InvoiceCustomSectionResponse]
28
+
29
+
30
+ class AppliedInvoiceCustomSections(BaseResponseModel):
31
+ __root__: List[AppliedInvoiceCustomSection]
32
+
33
+
33
34
  class InvoiceCustomSectionsResponseList(BaseResponseModel):
34
35
  __root__: List[InvoiceCustomSectionResponse]
@@ -0,0 +1,126 @@
1
+ import time
2
+ from typing import TYPE_CHECKING, Optional
3
+
4
+ if TYPE_CHECKING:
5
+ from httpx import Response
6
+
7
+ from ..exceptions import LagoRateLimitError
8
+
9
+
10
+ class RateLimitRetryConfig:
11
+ """Configuration for rate limit retry behavior."""
12
+
13
+ def __init__(
14
+ self,
15
+ max_retries: int = 3,
16
+ retry_on_rate_limit: bool = True,
17
+ base_backoff_seconds: float = 1.0,
18
+ backoff_multiplier: float = 2.0,
19
+ max_retry_delay: float = 20.0,
20
+ ):
21
+ """
22
+ Initialize rate limit retry configuration.
23
+
24
+ Args:
25
+ max_retries: Maximum number of retry attempts (0 means no retries).
26
+ retry_on_rate_limit: Whether to automatically retry on 429 responses.
27
+ base_backoff_seconds: Initial backoff duration in seconds for exponential backoff.
28
+ backoff_multiplier: Multiplier for exponential backoff between retries.
29
+ max_retry_delay: Maximum delay in seconds before a retry (default: 20).
30
+ """
31
+ self.max_retries = max_retries
32
+ self.retry_on_rate_limit = retry_on_rate_limit
33
+ self.base_backoff_seconds = base_backoff_seconds
34
+ self.backoff_multiplier = backoff_multiplier
35
+ self.max_retry_delay = max_retry_delay
36
+
37
+ def calculate_backoff(self, retry_attempt: int) -> float:
38
+ """
39
+ Calculate backoff duration for exponential backoff strategy.
40
+
41
+ Args:
42
+ retry_attempt: Current retry attempt number (0-based).
43
+
44
+ Returns:
45
+ Duration in seconds to wait before retrying.
46
+ """
47
+ return self.base_backoff_seconds * (self.backoff_multiplier**retry_attempt)
48
+
49
+
50
+ def handle_rate_limit_response(
51
+ response: "Response",
52
+ config: RateLimitRetryConfig,
53
+ retry_attempt: int,
54
+ ) -> float:
55
+ """
56
+ Determine if and how long to wait before retrying a rate-limited request.
57
+
58
+ Args:
59
+ response: The HTTP response object (should be 429).
60
+ config: Rate limit retry configuration.
61
+ retry_attempt: Current retry attempt number (0-based).
62
+
63
+ Returns:
64
+ Duration in seconds to wait before retrying, or raises LagoRateLimitError.
65
+
66
+ Raises:
67
+ LagoRateLimitError: If retry is not configured or max retries exceeded.
68
+ """
69
+ # Check if retries are enabled
70
+ if not config.retry_on_rate_limit:
71
+ raise LagoRateLimitError(
72
+ status_code=response.status_code,
73
+ url=str(response.request.url),
74
+ response=None,
75
+ headers=response.headers,
76
+ )
77
+
78
+ # Check if we've exceeded max retries
79
+ if retry_attempt >= config.max_retries:
80
+ raise LagoRateLimitError(
81
+ status_code=response.status_code,
82
+ url=str(response.request.url),
83
+ response=None,
84
+ headers=response.headers,
85
+ )
86
+
87
+ # Try to get reset time from header
88
+ reset_seconds: Optional[int] = None
89
+ if "x-ratelimit-reset" in response.headers:
90
+ try:
91
+ reset_seconds = int(response.headers["x-ratelimit-reset"])
92
+ except (ValueError, TypeError):
93
+ pass
94
+
95
+ # Use header value if available, otherwise use exponential backoff
96
+ if reset_seconds is not None:
97
+ delay = float(reset_seconds)
98
+ else:
99
+ delay = config.calculate_backoff(retry_attempt)
100
+
101
+ # Cap at max_retry_delay
102
+ return min(delay, config.max_retry_delay)
103
+
104
+
105
+ def is_rate_limit_response(response: "Response") -> bool:
106
+ """
107
+ Check if a response indicates rate limit exceeded.
108
+
109
+ Args:
110
+ response: The HTTP response to check.
111
+
112
+ Returns:
113
+ True if response is a 429 Too Many Requests, False otherwise.
114
+ """
115
+ return response.status_code == 429
116
+
117
+
118
+ def wait_for_retry(duration: float) -> None:
119
+ """
120
+ Sleep for the specified duration before retrying.
121
+
122
+ Args:
123
+ duration: Number of seconds to wait.
124
+ """
125
+ if duration > 0:
126
+ time.sleep(duration)