diff --git a/.hark/changes/2026-09-18_zacchua_remove-unneeded-python-helpers.change.md b/.hark/changes/2026-09-18_zacchua_remove-unneeded-python-helpers.change.md new file mode 100644 index 000000000..f32d38f75 --- /dev/null +++ b/.hark/changes/2026-09-18_zacchua_remove-unneeded-python-helpers.change.md @@ -0,0 +1,9 @@ +--- +title: Remove unneeded Python resource helpers +pr_url: https://github.com/stripe/stripe-python/pull/1922 +semver_level: major +jira_tickets_closed: +- DEVSDK-1704 +--- + +Removed the `SingletonAPIResource` and `nested_resource_class_methods` infrastructure helpers. Concrete resource methods continue to work without changes. Users who imported either helper directly should follow the v16 migration guide. diff --git a/.hark/migration-guides/v16.md b/.hark/migration-guides/v16.md index d1d065a40..d1dd1e35e 100644 --- a/.hark/migration-guides/v16.md +++ b/.hark/migration-guides/v16.md @@ -20,3 +20,40 @@ response = stripe_object.request("get", "/v1/example") client = stripe.StripeClient("sk_test_...") response = client.raw_request("get", "/v1/example") ``` + +## Resource infrastructure helpers have been removed + +The `SingletonAPIResource` base class and `nested_resource_class_methods` +decorator have been removed. These were implementation helpers used by +generated resource classes. Generated singleton and nested-resource methods +continue to work as before, so most users do not need to make any changes. + +If a custom resource subclasses `SingletonAPIResource`, subclass `APIResource` +and define `instance_url()` directly instead: + +```python +# Before +from stripe import SingletonAPIResource + + +class MySingleton(SingletonAPIResource["MySingleton"]): + @classmethod + def class_url(cls): + return "/v1/my_singleton" + + +# After +from stripe import APIResource + + +class MySingleton(APIResource["MySingleton"]): + @classmethod + def class_url(cls): + return "/v1/my_singleton" + + def instance_url(self): + return self.class_url() +``` + +If custom code uses `nested_resource_class_methods`, remove the decorator and +define the required nested-resource methods directly on the class. diff --git a/stripe/__init__.py b/stripe/__init__.py index b1e4ffa26..b00819616 100644 --- a/stripe/__init__.py +++ b/stripe/__init__.py @@ -365,9 +365,6 @@ def set_app_info( from stripe._login_link import LoginLink as LoginLink from stripe._mandate import Mandate as Mandate from stripe._mandate_service import MandateService as MandateService - from stripe._nested_resource_class_methods import ( - nested_resource_class_methods as nested_resource_class_methods, - ) from stripe._oauth import OAuth as OAuth from stripe._oauth_service import OAuthService as OAuthService from stripe._payment_attempt_record import ( @@ -469,9 +466,6 @@ def set_app_info( ShippingRateService as ShippingRateService, ) from stripe._sigma_service import SigmaService as SigmaService - from stripe._singleton_api_resource import ( - SingletonAPIResource as SingletonAPIResource, - ) from stripe._source import Source as Source from stripe._source_mandate_notification import ( SourceMandateNotification as SourceMandateNotification, @@ -764,10 +758,6 @@ def set_app_info( "LoginLink": ("stripe._login_link", False), "Mandate": ("stripe._mandate", False), "MandateService": ("stripe._mandate_service", False), - "nested_resource_class_methods": ( - "stripe._nested_resource_class_methods", - False, - ), "OAuth": ("stripe._oauth", False), "OAuthService": ("stripe._oauth_service", False), "PaymentAttemptRecord": ("stripe._payment_attempt_record", False), @@ -847,7 +837,6 @@ def set_app_info( "ShippingRate": ("stripe._shipping_rate", False), "ShippingRateService": ("stripe._shipping_rate_service", False), "SigmaService": ("stripe._sigma_service", False), - "SingletonAPIResource": ("stripe._singleton_api_resource", False), "Source": ("stripe._source", False), "SourceMandateNotification": ( "stripe._source_mandate_notification", diff --git a/stripe/_account.py b/stripe/_account.py index 649080c72..6226f362d 100644 --- a/stripe/_account.py +++ b/stripe/_account.py @@ -5,7 +5,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._oauth import OAuth from stripe._person import Person from stripe._stripe_object import StripeObject, UntypedStripeObject @@ -71,10 +70,6 @@ from stripe.params._account_unreject_params import AccountUnrejectParams -@nested_resource_class_methods("capability") -@nested_resource_class_methods("external_account") -@nested_resource_class_methods("login_link") -@nested_resource_class_methods("person") class Account( CreateableAPIResource["Account"], DeletableAPIResource["Account"], diff --git a/stripe/_application_fee.py b/stripe/_application_fee.py index a1db92e4e..e502465e4 100644 --- a/stripe/_application_fee.py +++ b/stripe/_application_fee.py @@ -3,7 +3,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._stripe_object import StripeObject from stripe._util import class_method_variant, sanitize_id from typing import ClassVar, Optional, Union, cast, overload @@ -38,7 +37,6 @@ ) -@nested_resource_class_methods("refund") class ApplicationFee(ListableAPIResource["ApplicationFee"]): OBJECT_NAME: ClassVar[Literal["application_fee"]] = "application_fee" diff --git a/stripe/_balance.py b/stripe/_balance.py index e92b86349..42d9c3d58 100644 --- a/stripe/_balance.py +++ b/stripe/_balance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # File generated from our OpenAPI spec -from stripe._singleton_api_resource import SingletonAPIResource +from stripe._api_resource import APIResource from stripe._stripe_object import StripeObject from typing import ClassVar, List, Optional from typing_extensions import Literal, Unpack, TYPE_CHECKING @@ -9,7 +9,7 @@ from stripe.params._balance_retrieve_params import BalanceRetrieveParams -class Balance(SingletonAPIResource["Balance"]): +class Balance(APIResource["Balance"]): """ This is an object representing your Stripe balance. You can retrieve it to see the balance currently on your Stripe account. @@ -307,6 +307,9 @@ async def retrieve_async( def class_url(cls): return "/v1/balance" + def instance_url(self): + return self.class_url() + _inner_class_types = { "available": Available, "connect_reserved": ConnectReserved, diff --git a/stripe/_balance_settings.py b/stripe/_balance_settings.py index 7e3da5f17..5df7df2f9 100644 --- a/stripe/_balance_settings.py +++ b/stripe/_balance_settings.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # File generated from our OpenAPI spec -from stripe._singleton_api_resource import SingletonAPIResource from stripe._stripe_object import StripeObject, UntypedStripeObject from stripe._updateable_api_resource import UpdateableAPIResource from typing import ClassVar, List, Optional, Union, cast @@ -15,10 +14,7 @@ ) -class BalanceSettings( - SingletonAPIResource["BalanceSettings"], - UpdateableAPIResource["BalanceSettings"], -): +class BalanceSettings(UpdateableAPIResource["BalanceSettings"]): """ Options for customizing account balances and payout settings for a Stripe platform's connected accounts. """ @@ -211,4 +207,7 @@ async def retrieve_async( def class_url(cls): return "/v1/balance_settings" + def instance_url(self): + return self.class_url() + _inner_class_types = {"payments": Payments} diff --git a/stripe/_charge.py b/stripe/_charge.py index f46178727..a3392ddf7 100644 --- a/stripe/_charge.py +++ b/stripe/_charge.py @@ -4,7 +4,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._search_result_object import SearchResultObject from stripe._searchable_api_resource import SearchableAPIResource from stripe._stripe_object import StripeObject, UntypedStripeObject @@ -51,7 +50,6 @@ from stripe.params._charge_search_params import ChargeSearchParams -@nested_resource_class_methods("refund") class Charge( CreateableAPIResource["Charge"], ListableAPIResource["Charge"], diff --git a/stripe/_credit_note.py b/stripe/_credit_note.py index 3eb6a8657..bec64ff7e 100644 --- a/stripe/_credit_note.py +++ b/stripe/_credit_note.py @@ -4,7 +4,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._stripe_object import StripeObject, UntypedStripeObject from stripe._updateable_api_resource import UpdateableAPIResource from stripe._util import class_method_variant, sanitize_id @@ -43,7 +42,6 @@ ) -@nested_resource_class_methods("line") class CreditNote( CreateableAPIResource["CreditNote"], ListableAPIResource["CreditNote"], diff --git a/stripe/_customer.py b/stripe/_customer.py index 9ef7186a0..231376a70 100644 --- a/stripe/_customer.py +++ b/stripe/_customer.py @@ -5,7 +5,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._search_result_object import SearchResultObject from stripe._searchable_api_resource import SearchableAPIResource from stripe._stripe_object import StripeObject, UntypedStripeObject @@ -114,10 +113,6 @@ from stripe.test_helpers._test_clock import TestClock -@nested_resource_class_methods("balance_transaction") -@nested_resource_class_methods("cash_balance_transaction") -@nested_resource_class_methods("source") -@nested_resource_class_methods("tax_id") class Customer( CreateableAPIResource["Customer"], DeletableAPIResource["Customer"], diff --git a/stripe/_invoice.py b/stripe/_invoice.py index 113ef5f88..71cb228e8 100644 --- a/stripe/_invoice.py +++ b/stripe/_invoice.py @@ -5,7 +5,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._search_result_object import SearchResultObject from stripe._searchable_api_resource import SearchableAPIResource from stripe._stripe_object import StripeObject, UntypedStripeObject @@ -79,7 +78,6 @@ from stripe.test_helpers._test_clock import TestClock -@nested_resource_class_methods("line") class Invoice( CreateableAPIResource["Invoice"], DeletableAPIResource["Invoice"], diff --git a/stripe/_nested_resource_class_methods.py b/stripe/_nested_resource_class_methods.py deleted file mode 100644 index 5eeea6c4d..000000000 --- a/stripe/_nested_resource_class_methods.py +++ /dev/null @@ -1,118 +0,0 @@ -from typing import List, Optional -from urllib.parse import quote_plus - -from stripe._api_resource import APIResource - - -# TODO(major): 1704. Remove this. It is no longer used except for "nested_resource_url" and "nested_resource_request", -# which are unnecessary and deprecated and should also be removed. -def nested_resource_class_methods( - resource: str, - path: Optional[str] = None, - operations: Optional[List[str]] = None, - resource_plural: Optional[str] = None, -): - if resource_plural is None: - resource_plural = "%ss" % resource - if path is None: - path = resource_plural - - def wrapper(cls): - def nested_resource_url(cls, id, nested_id=None): - url = "%s/%s/%s" % ( - cls.class_url(), - quote_plus(id), - quote_plus(path), - ) - if nested_id is not None: - url += "/%s" % quote_plus(nested_id) - return url - - resource_url_method = "%ss_url" % resource - setattr(cls, resource_url_method, classmethod(nested_resource_url)) - - def nested_resource_request(cls, method, url, **params): - return APIResource._static_request( - method, - url, - params=params, - ) - - resource_request_method = "%ss_request" % resource - setattr( - cls, resource_request_method, classmethod(nested_resource_request) - ) - - if operations is None: - return cls - - for operation in operations: - if operation == "create": - - def create_nested_resource(cls, id, **params): - url = getattr(cls, resource_url_method)(id) - return getattr(cls, resource_request_method)( - "post", url, **params - ) - - create_method = "create_%s" % resource - setattr( - cls, create_method, classmethod(create_nested_resource) - ) - - elif operation == "retrieve": - - def retrieve_nested_resource(cls, id, nested_id, **params): - url = getattr(cls, resource_url_method)(id, nested_id) - return getattr(cls, resource_request_method)( - "get", url, **params - ) - - retrieve_method = "retrieve_%s" % resource - setattr( - cls, retrieve_method, classmethod(retrieve_nested_resource) - ) - - elif operation == "update": - - def modify_nested_resource(cls, id, nested_id, **params): - url = getattr(cls, resource_url_method)(id, nested_id) - return getattr(cls, resource_request_method)( - "post", url, **params - ) - - modify_method = "modify_%s" % resource - setattr( - cls, modify_method, classmethod(modify_nested_resource) - ) - - elif operation == "delete": - - def delete_nested_resource(cls, id, nested_id, **params): - url = getattr(cls, resource_url_method)(id, nested_id) - return getattr(cls, resource_request_method)( - "delete", url, **params - ) - - delete_method = "delete_%s" % resource - setattr( - cls, delete_method, classmethod(delete_nested_resource) - ) - - elif operation == "list": - - def list_nested_resources(cls, id, **params): - url = getattr(cls, resource_url_method)(id) - return getattr(cls, resource_request_method)( - "get", url, **params - ) - - list_method = "list_%s" % resource_plural - setattr(cls, list_method, classmethod(list_nested_resources)) - - else: - raise ValueError("Unknown operation: %s" % operation) - - return cls - - return wrapper diff --git a/stripe/_payment_intent.py b/stripe/_payment_intent.py index dcaa81981..c0443957a 100644 --- a/stripe/_payment_intent.py +++ b/stripe/_payment_intent.py @@ -4,7 +4,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._search_result_object import SearchResultObject from stripe._searchable_api_resource import SearchableAPIResource from stripe._stripe_object import StripeObject, UntypedStripeObject @@ -75,7 +74,6 @@ ) -@nested_resource_class_methods("amount_details_line_item") class PaymentIntent( CreateableAPIResource["PaymentIntent"], ListableAPIResource["PaymentIntent"], diff --git a/stripe/_product.py b/stripe/_product.py index e63587b5b..9b1b2c833 100644 --- a/stripe/_product.py +++ b/stripe/_product.py @@ -5,7 +5,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._search_result_object import SearchResultObject from stripe._searchable_api_resource import SearchableAPIResource from stripe._stripe_object import StripeObject, UntypedStripeObject @@ -47,7 +46,6 @@ from stripe.params._product_search_params import ProductSearchParams -@nested_resource_class_methods("feature") class Product( CreateableAPIResource["Product"], DeletableAPIResource["Product"], diff --git a/stripe/_singleton_api_resource.py b/stripe/_singleton_api_resource.py deleted file mode 100644 index b64b9b3a2..000000000 --- a/stripe/_singleton_api_resource.py +++ /dev/null @@ -1,29 +0,0 @@ -from stripe._api_resource import APIResource - -from typing import TypeVar -from stripe._stripe_object import StripeObject - -T = TypeVar("T", bound=StripeObject) - -# TODO(major): 1704 - Inline into Tax.Settings and Balance, and remove this class. - - -class SingletonAPIResource(APIResource[T]): - @classmethod - def retrieve(cls, **params) -> T: - return super(SingletonAPIResource, cls).retrieve(None, **params) - - @classmethod - def class_url(cls): - if cls == SingletonAPIResource: - raise NotImplementedError( - "SingletonAPIResource is an abstract class. You should " - "perform actions on its subclasses (e.g. Balance)" - ) - # Namespaces are separated in object names with periods (.) and in URLs - # with forward slashes (/), so replace the former with the latter. - base = cls.OBJECT_NAME.replace(".", "/") - return "/v1/%s" % (base,) - - def instance_url(self): - return self.class_url() diff --git a/stripe/_transfer.py b/stripe/_transfer.py index 5d9bfd668..6a2876c5d 100644 --- a/stripe/_transfer.py +++ b/stripe/_transfer.py @@ -4,7 +4,6 @@ from stripe._expandable_field import ExpandableField from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._stripe_object import UntypedStripeObject from stripe._updateable_api_resource import UpdateableAPIResource from stripe._util import sanitize_id @@ -34,7 +33,6 @@ ) -@nested_resource_class_methods("reversal") class Transfer( CreateableAPIResource["Transfer"], ListableAPIResource["Transfer"], diff --git a/stripe/billing/_credit_balance_summary.py b/stripe/billing/_credit_balance_summary.py index c87a468b2..4f7d3252d 100644 --- a/stripe/billing/_credit_balance_summary.py +++ b/stripe/billing/_credit_balance_summary.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # File generated from our OpenAPI spec +from stripe._api_resource import APIResource from stripe._expandable_field import ExpandableField -from stripe._singleton_api_resource import SingletonAPIResource from stripe._stripe_object import StripeObject from typing import ClassVar, List, Optional from typing_extensions import Literal, Unpack, TYPE_CHECKING @@ -13,7 +13,7 @@ ) -class CreditBalanceSummary(SingletonAPIResource["CreditBalanceSummary"]): +class CreditBalanceSummary(APIResource["CreditBalanceSummary"]): """ Indicates the billing credit balance for billing credits granted to a customer. """ @@ -119,4 +119,7 @@ async def retrieve_async( def class_url(cls): return "/v1/billing/credit_balance_summary" + def instance_url(self): + return self.class_url() + _inner_class_types = {"balances": Balance} diff --git a/stripe/billing/_meter.py b/stripe/billing/_meter.py index 4f27637b2..a31d78f46 100644 --- a/stripe/billing/_meter.py +++ b/stripe/billing/_meter.py @@ -3,7 +3,6 @@ from stripe._createable_api_resource import CreateableAPIResource from stripe._list_object import ListObject from stripe._listable_api_resource import ListableAPIResource -from stripe._nested_resource_class_methods import nested_resource_class_methods from stripe._stripe_object import StripeObject from stripe._updateable_api_resource import UpdateableAPIResource from stripe._util import class_method_variant, sanitize_id @@ -29,7 +28,6 @@ ) -@nested_resource_class_methods("event_summary") class Meter( CreateableAPIResource["Meter"], ListableAPIResource["Meter"], diff --git a/stripe/tax/_settings.py b/stripe/tax/_settings.py index c180373d6..4ec011b68 100644 --- a/stripe/tax/_settings.py +++ b/stripe/tax/_settings.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # File generated from our OpenAPI spec -from stripe._singleton_api_resource import SingletonAPIResource from stripe._stripe_object import StripeObject from stripe._updateable_api_resource import UpdateableAPIResource from typing import ClassVar, List, Optional, Union, cast @@ -13,10 +12,7 @@ ) -class Settings( - SingletonAPIResource["Settings"], - UpdateableAPIResource["Settings"], -): +class Settings(UpdateableAPIResource["Settings"]): """ You can use Tax `Settings` to manage configurations used by Stripe Tax calculations. @@ -162,6 +158,9 @@ async def retrieve_async( def class_url(cls): return "/v1/tax/settings" + def instance_url(self): + return self.class_url() + _inner_class_types = { "defaults": Defaults, "head_office": HeadOffice, diff --git a/tests/api_resources/abstract/test_nested_resource_class_methods.py b/tests/api_resources/abstract/test_nested_resource_class_methods.py deleted file mode 100644 index a21c8ba0f..000000000 --- a/tests/api_resources/abstract/test_nested_resource_class_methods.py +++ /dev/null @@ -1,78 +0,0 @@ -from stripe._nested_resource_class_methods import nested_resource_class_methods -from stripe._api_resource import APIResource - - -class TestNestedResourceClassMethods(object): - @nested_resource_class_methods( - "nested", operations=["create", "retrieve", "update", "delete", "list"] - ) - class MainResource(APIResource): - OBJECT_NAME = "mainresource" - - def test_create_nested(self, http_client_mock): - http_client_mock.stub_request( - "post", - path="/v1/mainresources/id/nesteds", - rbody='{"id": "nested_id", "object": "nested", "foo": "bar"}', - ) - nested_resource = self.MainResource.create_nested("id", foo="bar") - http_client_mock.assert_requested( - "post", path="/v1/mainresources/id/nesteds", post_data="foo=bar" - ) - assert nested_resource.foo == "bar" - - def test_retrieve_nested(self, http_client_mock): - http_client_mock.stub_request( - "get", - path="/v1/mainresources/id/nesteds/nested_id", - rbody='{"id": "nested_id", "object": "nested", "foo": "bar"}', - ) - nested_resource = self.MainResource.retrieve_nested("id", "nested_id") - http_client_mock.assert_requested( - "get", - path="/v1/mainresources/id/nesteds/nested_id", - query_string="", - ) - assert nested_resource.foo == "bar" - - def test_modify_nested(self, http_client_mock): - http_client_mock.stub_request( - "post", - path="/v1/mainresources/id/nesteds/nested_id", - rbody='{"id": "nested_id", "object": "nested", "foo": "baz"}', - ) - nested_resource = self.MainResource.modify_nested( - "id", "nested_id", foo="baz" - ) - http_client_mock.assert_requested( - "post", - path="/v1/mainresources/id/nesteds/nested_id", - post_data="foo=baz", - ) - assert nested_resource.foo == "baz" - - def test_delete_nested(self, http_client_mock): - http_client_mock.stub_request( - "delete", - path="/v1/mainresources/id/nesteds/nested_id", - rbody='{"id": "nested_id", "object": "nested", "deleted": true}', - ) - nested_resource = self.MainResource.delete_nested("id", "nested_id") - http_client_mock.assert_requested( - "delete", - path="/v1/mainresources/id/nesteds/nested_id", - query_string="", - ) - assert nested_resource.deleted is True - - def test_list_nesteds(self, http_client_mock): - http_client_mock.stub_request( - "get", - path="/v1/mainresources/id/nesteds", - rbody='{"object": "list", "data": []}', - ) - nested_resource = self.MainResource.list_nesteds("id") - http_client_mock.assert_requested( - "get", path="/v1/mainresources/id/nesteds", query_string="" - ) - assert isinstance(nested_resource.data, list) diff --git a/tests/api_resources/abstract/test_singleton_api_resource.py b/tests/api_resources/abstract/test_singleton_api_resource.py deleted file mode 100644 index d9a194af2..000000000 --- a/tests/api_resources/abstract/test_singleton_api_resource.py +++ /dev/null @@ -1,22 +0,0 @@ -from stripe._singleton_api_resource import SingletonAPIResource - - -class TestSingletonAPIResource(object): - class MySingleton(SingletonAPIResource): - OBJECT_NAME = "mysingleton" - - def test_retrieve(self, http_client_mock): - http_client_mock.stub_request( - "get", - path="/v1/mysingleton", - rbody='{"single": "ton"}', - rheaders={"request-id": "req_id"}, - ) - - res = self.MySingleton.retrieve() - - http_client_mock.assert_requested("get", path="/v1/mysingleton") - assert res.single == "ton" - - assert res.last_response is not None - assert res.last_response.request_id == "req_id" diff --git a/tests/api_resources/test_singleton_resources.py b/tests/api_resources/test_singleton_resources.py new file mode 100644 index 000000000..4df96ccde --- /dev/null +++ b/tests/api_resources/test_singleton_resources.py @@ -0,0 +1,41 @@ +import pytest +from typing_extensions import Type + +import stripe +from stripe._api_resource import APIResource + + +SINGLETON_RESOURCES = [ + (stripe.Balance, "/v1/balance"), + (stripe.BalanceSettings, "/v1/balance_settings"), + ( + stripe.billing.CreditBalanceSummary, + "/v1/billing/credit_balance_summary", + ), + (stripe.tax.Settings, "/v1/tax/settings"), +] + + +@pytest.mark.parametrize(("resource_class", "path"), SINGLETON_RESOURCES) +def test_singleton_resource_retrieve( + resource_class: Type[APIResource], path: str, http_client_mock +) -> None: + http_client_mock.stub_request("get", path=path, rbody="{}") + + resource = resource_class.retrieve() + + assert resource.instance_url() == path + http_client_mock.assert_requested("get", path=path) + + +@pytest.mark.anyio +@pytest.mark.parametrize(("resource_class", "path"), SINGLETON_RESOURCES) +async def test_singleton_resource_retrieve_async( + resource_class: Type[APIResource], path: str, http_client_mock +) -> None: + http_client_mock.stub_request("get", path=path, rbody="{}") + + resource = await resource_class.retrieve_async() + + assert resource.instance_url() == path + http_client_mock.assert_requested("get", path=path) diff --git a/tests/test_exports.py b/tests/test_exports.py index e9c3a04cd..fb78f9941 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -58,7 +58,6 @@ def test_can_import_event_notification_members() -> None: def test_can_import_abstract() -> None: from stripe import ( APIResource, # pyright: ignore[reportUnusedImport] - SingletonAPIResource, # pyright: ignore[reportUnusedImport] CreateableAPIResource, # pyright: ignore[reportUnusedImport] UpdateableAPIResource, # pyright: ignore[reportUnusedImport] DeletableAPIResource, # pyright: ignore[reportUnusedImport] @@ -67,7 +66,6 @@ def test_can_import_abstract() -> None: VerifyMixin, # pyright: ignore[reportUnusedImport] APIResourceTestHelpers, # pyright: ignore[reportUnusedImport] custom_method, # pyright: ignore[reportDeprecated, reportUnusedImport] - nested_resource_class_methods, # pyright: ignore[reportUnusedImport] )