lago-python-client 1.45.0__tar.gz → 1.47.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 (190) hide show
  1. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/PKG-INFO +1 -1
  2. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/base_client.py +10 -2
  3. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/client.py +62 -42
  4. lago_python_client-1.47.0/lago_python_client/exceptions.py +69 -0
  5. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/mixins.py +18 -0
  6. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/fee.py +2 -0
  7. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/invoice_custom_section.py +11 -10
  8. lago_python_client-1.47.0/lago_python_client/observability.py +67 -0
  9. lago_python_client-1.47.0/lago_python_client/services/rate_limit.py +235 -0
  10. lago_python_client-1.47.0/lago_python_client/services/request.py +131 -0
  11. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/services/response.py +10 -1
  12. lago_python_client-1.47.0/lago_python_client/version.py +1 -0
  13. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client.egg-info/PKG-INFO +1 -1
  14. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client.egg-info/SOURCES.txt +6 -0
  15. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_wallet_client.py +15 -0
  16. lago_python_client-1.47.0/tests/test_rate_limit.py +602 -0
  17. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_request_services.py +8 -10
  18. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_subscription_client.py +3 -0
  19. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_wallet_client.py +15 -0
  20. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_wallet_transaction_client.py +4 -0
  21. lago_python_client-1.45.0/lago_python_client/exceptions.py +0 -26
  22. lago_python_client-1.45.0/lago_python_client/services/request.py +0 -55
  23. lago_python_client-1.45.0/lago_python_client/version.py +0 -1
  24. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/LICENSE +0 -0
  25. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/README.md +0 -0
  26. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/__init__.py +0 -0
  27. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/activity_logs/__init__.py +0 -0
  28. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/activity_logs/clients.py +0 -0
  29. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/add_ons/__init__.py +0 -0
  30. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/add_ons/clients.py +0 -0
  31. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/api_logs/__init__.py +0 -0
  32. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/api_logs/clients.py +0 -0
  33. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/base_model.py +0 -0
  34. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/billable_metrics/__init__.py +0 -0
  35. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/billable_metrics/clients.py +0 -0
  36. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/billing_entities/__init__.py +0 -0
  37. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/billing_entities/clients.py +0 -0
  38. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/coupons/__init__.py +0 -0
  39. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/coupons/clients.py +0 -0
  40. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/credit_notes/__init__.py +0 -0
  41. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/credit_notes/clients.py +0 -0
  42. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/__init__.py +0 -0
  43. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/applied_coupons_client.py +0 -0
  44. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/clients.py +0 -0
  45. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/credit_notes_client.py +0 -0
  46. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/invoices_client.py +0 -0
  47. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/payment_methods_client.py +0 -0
  48. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/payment_requests_client.py +0 -0
  49. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/payments_client.py +0 -0
  50. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/subscriptions_client.py +0 -0
  51. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/wallet_client.py +0 -0
  52. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/wallets/__init__.py +0 -0
  53. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/wallets/alert_client.py +0 -0
  54. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/wallets/metadata_client.py +0 -0
  55. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/customers/wallets_client.py +0 -0
  56. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/events/__init__.py +0 -0
  57. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/events/clients.py +0 -0
  58. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/fees/__init__.py +0 -0
  59. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/fees/clients.py +0 -0
  60. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/functools_ext.py +0 -0
  61. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/gross_revenues/__init__.py +0 -0
  62. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/gross_revenues/clients.py +0 -0
  63. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/invoice_collections/__init__.py +0 -0
  64. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/invoice_collections/clients.py +0 -0
  65. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/invoiced_usages/__init__.py +0 -0
  66. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/invoiced_usages/clients.py +0 -0
  67. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/invoices/__init__.py +0 -0
  68. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/invoices/clients.py +0 -0
  69. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/__init__.py +0 -0
  70. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/activity_log.py +0 -0
  71. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/add_on.py +0 -0
  72. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/alert.py +0 -0
  73. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/api_log.py +0 -0
  74. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/applied_coupon.py +0 -0
  75. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/billable_metric.py +0 -0
  76. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/billing_entity.py +0 -0
  77. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/billing_period.py +0 -0
  78. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/charge.py +0 -0
  79. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/coupon.py +0 -0
  80. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/credit.py +0 -0
  81. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/credit_note.py +0 -0
  82. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/customer.py +0 -0
  83. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/customer_projected_usage.py +0 -0
  84. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/customer_usage.py +0 -0
  85. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/error_detail.py +0 -0
  86. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/event.py +0 -0
  87. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/fixed_charge.py +0 -0
  88. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/gross_revenue.py +0 -0
  89. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/invoice.py +0 -0
  90. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/invoice_collection.py +0 -0
  91. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/invoice_item.py +0 -0
  92. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/invoiced_usage.py +0 -0
  93. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/lifetime_usage.py +0 -0
  94. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/minimum_commitment.py +0 -0
  95. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/mrr.py +0 -0
  96. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/organization.py +0 -0
  97. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/overdue_balance.py +0 -0
  98. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/payment.py +0 -0
  99. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/payment_method.py +0 -0
  100. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/payment_receipt.py +0 -0
  101. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/payment_request.py +0 -0
  102. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/plan.py +0 -0
  103. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/subscription.py +0 -0
  104. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/tax.py +0 -0
  105. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/usage.py +0 -0
  106. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/usage_threshold.py +0 -0
  107. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/wallet.py +0 -0
  108. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/wallet_transaction.py +0 -0
  109. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/models/webhook_endpoint.py +0 -0
  110. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/mrrs/__init__.py +0 -0
  111. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/mrrs/clients.py +0 -0
  112. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/organizations/__init__.py +0 -0
  113. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/organizations/clients.py +0 -0
  114. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/overdue_balances/__init__.py +0 -0
  115. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/overdue_balances/clients.py +0 -0
  116. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/payment_receipts/__init__.py +0 -0
  117. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/payment_receipts/clients.py +0 -0
  118. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/payment_requests/__init__.py +0 -0
  119. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/payment_requests/clients.py +0 -0
  120. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/payments/__init__.py +0 -0
  121. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/payments/clients.py +0 -0
  122. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/plans/__init__.py +0 -0
  123. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/plans/charges_client.py +0 -0
  124. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/plans/clients.py +0 -0
  125. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/plans/fixed_charges_client.py +0 -0
  126. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/py.typed +0 -0
  127. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/services/__init__.py +0 -0
  128. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/services/json.py +0 -0
  129. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/subscriptions/__init__.py +0 -0
  130. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/subscriptions/charges_client.py +0 -0
  131. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/subscriptions/clients.py +0 -0
  132. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/subscriptions/fixed_charges_client.py +0 -0
  133. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/taxes/__init__.py +0 -0
  134. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/taxes/clients.py +0 -0
  135. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/usages/__init__.py +0 -0
  136. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/usages/clients.py +0 -0
  137. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/wallets/__init__.py +0 -0
  138. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/wallets/clients.py +0 -0
  139. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/webhook_endpoints/__init__.py +0 -0
  140. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/webhook_endpoints/clients.py +0 -0
  141. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/webhooks/__init__.py +0 -0
  142. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client/webhooks/clients.py +0 -0
  143. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client.egg-info/dependency_links.txt +0 -0
  144. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client.egg-info/requires.txt +0 -0
  145. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/lago_python_client.egg-info/top_level.txt +0 -0
  146. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/pyproject.toml +0 -0
  147. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/setup.cfg +0 -0
  148. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/__init__.py +0 -0
  149. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_activity_logs_client.py +0 -0
  150. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_add_on_client.py +0 -0
  151. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_api_logs_client.py +0 -0
  152. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_applied_coupon_client.py +0 -0
  153. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_billable_metric_client.py +0 -0
  154. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_billing_entity_client.py +0 -0
  155. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_client.py +0 -0
  156. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_coupon_client.py +0 -0
  157. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_credit_note_client.py +0 -0
  158. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_applied_coupons_client.py +0 -0
  159. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_client.py +0 -0
  160. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_credit_notes_client.py +0 -0
  161. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_invoices_client.py +0 -0
  162. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_payment_methods_client.py +0 -0
  163. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_payment_requests_client.py +0 -0
  164. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_payments_client.py +0 -0
  165. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_sbscriptions_client.py +0 -0
  166. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_wallet_alert_client.py +0 -0
  167. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_wallet_metadata_client.py +0 -0
  168. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_customer_wallets_client.py +0 -0
  169. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_event_client.py +0 -0
  170. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_fee_client.py +0 -0
  171. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_functools_ext.py +0 -0
  172. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_gross_revenue_client.py +0 -0
  173. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_invoice_client.py +0 -0
  174. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_invoice_collection_client.py +0 -0
  175. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_invoiced_usage_client.py +0 -0
  176. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_json_services.py +0 -0
  177. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_mrr_client.py +0 -0
  178. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_organization_client.py +0 -0
  179. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_overdue_balance_client.py +0 -0
  180. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_payment_client.py +0 -0
  181. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_payment_receipt_client.py +0 -0
  182. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_payment_request_client.py +0 -0
  183. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_plan_client.py +0 -0
  184. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_response_services.py +0 -0
  185. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_tax_client.py +0 -0
  186. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_usage_client.py +0 -0
  187. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_webhook_client.py +0 -0
  188. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/test_webhook_endpoint_client.py +0 -0
  189. {lago_python_client-1.45.0 → lago_python_client-1.47.0}/tests/utils/__init__.py +0 -0
  190. {lago_python_client-1.45.0 → lago_python_client-1.47.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.47.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
@@ -1,3 +1,4 @@
1
+ from typing import Optional
1
2
  from urllib.parse import urljoin
2
3
 
3
4
  from .activity_logs.clients import ActivityLogClient
@@ -32,6 +33,7 @@ from .payments.clients import PaymentClient
32
33
  from .plans.charges_client import PlanChargesClient
33
34
  from .plans.clients import PlanClient
34
35
  from .plans.fixed_charges_client import PlanFixedChargesClient
36
+ from .services.rate_limit import RateLimitCallback, RateLimitRetryConfig
35
37
  from .subscriptions.charges_client import SubscriptionChargesClient
36
38
  from .subscriptions.clients import SubscriptionClient
37
39
  from .subscriptions.fixed_charges_client import SubscriptionFixedChargesClient
@@ -53,12 +55,24 @@ class Client:
53
55
  API_PATH: Final[str] = "api/v1/"
54
56
 
55
57
  def __init__(
56
- self, api_key: str = "", api_url: str = "", use_ingest_service: bool = False, ingest_api_url: str = ""
58
+ self,
59
+ api_key: str = "",
60
+ api_url: str = "",
61
+ use_ingest_service: bool = False,
62
+ ingest_api_url: str = "",
63
+ max_retries: int = 3,
64
+ retry_on_rate_limit: bool = True,
65
+ on_rate_limit_info: Optional[RateLimitCallback] = None,
57
66
  ) -> None:
58
67
  self.api_key: str = api_key
59
68
  self.api_url: str = api_url
60
69
  self.use_ingest_service: bool = use_ingest_service
61
70
  self.ingest_api_url: str = ingest_api_url
71
+ self.rate_limit_retry_config: RateLimitRetryConfig = RateLimitRetryConfig(
72
+ max_retries=max_retries,
73
+ retry_on_rate_limit=retry_on_rate_limit,
74
+ on_rate_limit_info=on_rate_limit_info,
75
+ )
62
76
 
63
77
  @property
64
78
  def base_api_url(self) -> str:
@@ -76,166 +90,172 @@ class Client:
76
90
 
77
91
  return urljoin(ingest_url, Client.API_PATH)
78
92
 
93
+ def _create_client(self, client_class, base_url, api_key, base_ingest_url=""):
94
+ """Create a client instance with rate limit config."""
95
+ if base_ingest_url:
96
+ return client_class(base_url, api_key, base_ingest_url, self.rate_limit_retry_config)
97
+ return client_class(base_url, api_key, rate_limit_retry_config=self.rate_limit_retry_config)
98
+
79
99
  @callable_cached_property
80
100
  def add_ons(self) -> AddOnClient:
81
- return AddOnClient(self.base_api_url, self.api_key)
101
+ return self._create_client(AddOnClient, self.base_api_url, self.api_key)
82
102
 
83
103
  @callable_cached_property
84
104
  def billable_metrics(self) -> BillableMetricClient:
85
- return BillableMetricClient(self.base_api_url, self.api_key)
105
+ return self._create_client(BillableMetricClient, self.base_api_url, self.api_key)
86
106
 
87
107
  @callable_cached_property
88
108
  def billing_entities(self) -> BillingEntityClient:
89
- return BillingEntityClient(self.base_api_url, self.api_key)
109
+ return self._create_client(BillingEntityClient, self.base_api_url, self.api_key)
90
110
 
91
111
  @callable_cached_property
92
112
  def coupons(self) -> CouponClient:
93
- return CouponClient(self.base_api_url, self.api_key)
113
+ return self._create_client(CouponClient, self.base_api_url, self.api_key)
94
114
 
95
115
  @callable_cached_property
96
116
  def applied_coupons(self) -> AppliedCouponClient:
97
- return AppliedCouponClient(self.base_api_url, self.api_key)
117
+ return self._create_client(AppliedCouponClient, self.base_api_url, self.api_key)
98
118
 
99
119
  @callable_cached_property
100
120
  def credit_notes(self) -> CreditNoteClient:
101
- return CreditNoteClient(self.base_api_url, self.api_key)
121
+ return self._create_client(CreditNoteClient, self.base_api_url, self.api_key)
102
122
 
103
123
  @callable_cached_property
104
124
  def customers(self) -> CustomerClient:
105
- return CustomerClient(self.base_api_url, self.api_key)
125
+ return self._create_client(CustomerClient, self.base_api_url, self.api_key)
106
126
 
107
127
  @callable_cached_property
108
128
  def customer_applied_coupons(self) -> CustomerAppliedCouponsClient:
109
- return CustomerAppliedCouponsClient(self.base_api_url, self.api_key)
129
+ return self._create_client(CustomerAppliedCouponsClient, self.base_api_url, self.api_key)
110
130
 
111
131
  @callable_cached_property
112
132
  def customer_credit_notes(self) -> CustomerCreditNotesClient:
113
- return CustomerCreditNotesClient(self.base_api_url, self.api_key)
133
+ return self._create_client(CustomerCreditNotesClient, self.base_api_url, self.api_key)
114
134
 
115
135
  @callable_cached_property
116
136
  def customer_invoices(self) -> CustomerInvoicesClient:
117
- return CustomerInvoicesClient(self.base_api_url, self.api_key)
137
+ return self._create_client(CustomerInvoicesClient, self.base_api_url, self.api_key)
118
138
 
119
139
  @callable_cached_property
120
140
  def customer_payment_methods(self) -> CustomerPaymentMethodsClient:
121
- return CustomerPaymentMethodsClient(self.base_api_url, self.api_key)
141
+ return self._create_client(CustomerPaymentMethodsClient, self.base_api_url, self.api_key)
122
142
 
123
143
  @callable_cached_property
124
144
  def customer_payments(self) -> CustomerPaymentsClient:
125
- return CustomerPaymentsClient(self.base_api_url, self.api_key)
145
+ return self._create_client(CustomerPaymentsClient, self.base_api_url, self.api_key)
126
146
 
127
147
  @callable_cached_property
128
148
  def customer_payment_requests(self) -> CustomerPaymentRequestsClient:
129
- return CustomerPaymentRequestsClient(self.base_api_url, self.api_key)
149
+ return self._create_client(CustomerPaymentRequestsClient, self.base_api_url, self.api_key)
130
150
 
131
151
  @callable_cached_property
132
152
  def customer_subscriptions(self) -> CustomerSubscriptionsClient:
133
- return CustomerSubscriptionsClient(self.base_api_url, self.api_key)
153
+ return self._create_client(CustomerSubscriptionsClient, self.base_api_url, self.api_key)
134
154
 
135
155
  @callable_cached_property
136
156
  def customer_wallets(self) -> CustomerWalletsClient:
137
- return CustomerWalletsClient(self.base_api_url, self.api_key)
157
+ return self._create_client(CustomerWalletsClient, self.base_api_url, self.api_key)
138
158
 
139
159
  @callable_cached_property
140
160
  def events(self) -> EventClient:
141
- return EventClient(self.base_api_url, self.api_key, self.base_ingest_api_url)
161
+ return self._create_client(EventClient, self.base_api_url, self.api_key, self.base_ingest_api_url)
142
162
 
143
163
  @callable_cached_property
144
164
  def fees(self) -> FeeClient:
145
- return FeeClient(self.base_api_url, self.api_key)
165
+ return self._create_client(FeeClient, self.base_api_url, self.api_key)
146
166
 
147
167
  @callable_cached_property
148
168
  def gross_revenues(self) -> GrossRevenueClient:
149
- return GrossRevenueClient(self.base_api_url, self.api_key)
169
+ return self._create_client(GrossRevenueClient, self.base_api_url, self.api_key)
150
170
 
151
171
  @callable_cached_property
152
172
  def invoices(self) -> InvoiceClient:
153
- return InvoiceClient(self.base_api_url, self.api_key)
173
+ return self._create_client(InvoiceClient, self.base_api_url, self.api_key)
154
174
 
155
175
  @callable_cached_property
156
176
  def invoiced_usages(self) -> InvoicedUsageClient:
157
- return InvoicedUsageClient(self.base_api_url, self.api_key)
177
+ return self._create_client(InvoicedUsageClient, self.base_api_url, self.api_key)
158
178
 
159
179
  @callable_cached_property
160
180
  def mrrs(self) -> MrrClient:
161
- return MrrClient(self.base_api_url, self.api_key)
181
+ return self._create_client(MrrClient, self.base_api_url, self.api_key)
162
182
 
163
183
  @callable_cached_property
164
184
  def organizations(self) -> OrganizationClient:
165
- return OrganizationClient(self.base_api_url, self.api_key)
185
+ return self._create_client(OrganizationClient, self.base_api_url, self.api_key)
166
186
 
167
187
  @callable_cached_property
168
188
  def overdue_balances(self) -> OverdueBalanceClient:
169
- return OverdueBalanceClient(self.base_api_url, self.api_key)
189
+ return self._create_client(OverdueBalanceClient, self.base_api_url, self.api_key)
170
190
 
171
191
  @callable_cached_property
172
192
  def payment_receipts(self) -> PaymentReceiptClient:
173
- return PaymentReceiptClient(self.base_api_url, self.api_key)
193
+ return self._create_client(PaymentReceiptClient, self.base_api_url, self.api_key)
174
194
 
175
195
  @callable_cached_property
176
196
  def payment_requests(self) -> PaymentRequestClient:
177
- return PaymentRequestClient(self.base_api_url, self.api_key)
197
+ return self._create_client(PaymentRequestClient, self.base_api_url, self.api_key)
178
198
 
179
199
  @callable_cached_property
180
200
  def payments(self) -> PaymentClient:
181
- return PaymentClient(self.base_api_url, self.api_key)
201
+ return self._create_client(PaymentClient, self.base_api_url, self.api_key)
182
202
 
183
203
  @callable_cached_property
184
204
  def invoice_collections(self) -> InvoiceCollectionClient:
185
- return InvoiceCollectionClient(self.base_api_url, self.api_key)
205
+ return self._create_client(InvoiceCollectionClient, self.base_api_url, self.api_key)
186
206
 
187
207
  @callable_cached_property
188
208
  def plans(self) -> PlanClient:
189
- return PlanClient(self.base_api_url, self.api_key)
209
+ return self._create_client(PlanClient, self.base_api_url, self.api_key)
190
210
 
191
211
  @callable_cached_property
192
212
  def plan_charges(self) -> PlanChargesClient:
193
- return PlanChargesClient(self.base_api_url, self.api_key)
213
+ return self._create_client(PlanChargesClient, self.base_api_url, self.api_key)
194
214
 
195
215
  @callable_cached_property
196
216
  def plan_fixed_charges(self) -> PlanFixedChargesClient:
197
- return PlanFixedChargesClient(self.base_api_url, self.api_key)
217
+ return self._create_client(PlanFixedChargesClient, self.base_api_url, self.api_key)
198
218
 
199
219
  @callable_cached_property
200
220
  def subscriptions(self) -> SubscriptionClient:
201
- return SubscriptionClient(self.base_api_url, self.api_key)
221
+ return self._create_client(SubscriptionClient, self.base_api_url, self.api_key)
202
222
 
203
223
  @callable_cached_property
204
224
  def subscription_charges(self) -> SubscriptionChargesClient:
205
- return SubscriptionChargesClient(self.base_api_url, self.api_key)
225
+ return self._create_client(SubscriptionChargesClient, self.base_api_url, self.api_key)
206
226
 
207
227
  @callable_cached_property
208
228
  def subscription_fixed_charges(self) -> SubscriptionFixedChargesClient:
209
- return SubscriptionFixedChargesClient(self.base_api_url, self.api_key)
229
+ return self._create_client(SubscriptionFixedChargesClient, self.base_api_url, self.api_key)
210
230
 
211
231
  @callable_cached_property
212
232
  def taxes(self) -> TaxClient:
213
- return TaxClient(self.base_api_url, self.api_key)
233
+ return self._create_client(TaxClient, self.base_api_url, self.api_key)
214
234
 
215
235
  @callable_cached_property
216
236
  def usages(self) -> UsageClient:
217
- return UsageClient(self.base_api_url, self.api_key)
237
+ return self._create_client(UsageClient, self.base_api_url, self.api_key)
218
238
 
219
239
  @callable_cached_property
220
240
  def wallets(self) -> WalletClient:
221
- return WalletClient(self.base_api_url, self.api_key)
241
+ return self._create_client(WalletClient, self.base_api_url, self.api_key)
222
242
 
223
243
  @callable_cached_property
224
244
  def wallet_transactions(self) -> WalletTransactionClient:
225
- return WalletTransactionClient(self.base_api_url, self.api_key)
245
+ return self._create_client(WalletTransactionClient, self.base_api_url, self.api_key)
226
246
 
227
247
  @callable_cached_property
228
248
  def webhooks(self) -> WebhookClient:
229
- return WebhookClient(self.base_api_url, self.api_key)
249
+ return self._create_client(WebhookClient, self.base_api_url, self.api_key)
230
250
 
231
251
  @callable_cached_property
232
252
  def webhook_endpoints(self) -> WebhookEndpointClient:
233
- return WebhookEndpointClient(self.base_api_url, self.api_key)
253
+ return self._create_client(WebhookEndpointClient, self.base_api_url, self.api_key)
234
254
 
235
255
  @callable_cached_property
236
256
  def activity_logs(self) -> ActivityLogClient:
237
- return ActivityLogClient(self.base_api_url, self.api_key)
257
+ return self._create_client(ActivityLogClient, self.base_api_url, self.api_key)
238
258
 
239
259
  @callable_cached_property
240
260
  def api_logs(self) -> ApiLogClient:
241
- return ApiLogClient(self.base_api_url, self.api_key)
261
+ 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,67 @@
1
+ """
2
+ Optional observability helpers for Lago rate limits.
3
+
4
+ Provides ready-to-use ``on_rate_limit_info`` callbacks so callers can opt into
5
+ rate limit observability without writing one themselves.
6
+
7
+ Example:
8
+ from lago_python_client import Client
9
+ from lago_python_client.observability import LoggingRateLimitObserver
10
+
11
+ client = Client(
12
+ api_key="...",
13
+ on_rate_limit_info=LoggingRateLimitObserver(),
14
+ )
15
+ """
16
+
17
+ import logging
18
+ from typing import Optional, Sequence
19
+
20
+ from .services.rate_limit import RateLimitInfo
21
+
22
+ DEFAULT_THRESHOLDS: Sequence[float] = (0.80, 0.90, 0.95)
23
+
24
+
25
+ class LoggingRateLimitObserver:
26
+ """
27
+ Emits a log line each time rate limit usage crosses a configured threshold.
28
+
29
+ Stateless across calls: every response that lands above any threshold logs.
30
+ If you want one-shot crossings, wrap it with your own state.
31
+
32
+ Args:
33
+ thresholds: Usage fractions (0.0 - 1.0) that should produce a log line.
34
+ Defaults to 80%, 90%, 95%.
35
+ logger: Logger to emit on. Defaults to ``lago_python_client.rate_limit``.
36
+ level: Log level used when a threshold is crossed. Defaults to WARNING.
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ thresholds: Sequence[float] = DEFAULT_THRESHOLDS,
42
+ logger: Optional[logging.Logger] = None,
43
+ level: int = logging.WARNING,
44
+ ) -> None:
45
+ # Sort descending so we report the highest matching threshold first.
46
+ self.thresholds = tuple(sorted(thresholds, reverse=True))
47
+ self.logger = logger or logging.getLogger("lago_python_client.rate_limit")
48
+ self.level = level
49
+
50
+ def __call__(self, info: RateLimitInfo) -> None:
51
+ pct = info.usage_pct
52
+ if pct is None:
53
+ return
54
+
55
+ for threshold in self.thresholds:
56
+ if pct >= threshold:
57
+ self.logger.log(
58
+ self.level,
59
+ "Lago rate limit at %.0f%% (limit=%s, remaining=%s, reset=%ss, %s %s)",
60
+ pct * 100,
61
+ info.limit,
62
+ info.remaining,
63
+ info.reset,
64
+ info.method,
65
+ info.url,
66
+ )
67
+ return