From 96545ffb741486ba15ff5a808520b4a46b6c85fe Mon Sep 17 00:00:00 2001 From: Praniket Walavalkar Date: Mon, 22 Dec 2025 22:55:43 -0800 Subject: [PATCH] Fix DeprecationWarning when encoding StripeObject metadata (fixes #1651) (#1687) * Fix DeprecationWarning when encoding StripeObject metadata (fixes #1651) * Suppress deprecation warnings during internal encoding to fix metadata updates * Use getattr for id access to avoid stripe_id deprecation warning * Handle None id values correctly in encoding * Simplify to check hasattr(value, id) directly --------- Co-authored-by: Ramya Rao <100975018+ramya-stripe@users.noreply.github.com> --- stripe/_encode.py | 4 +- tests/test_encode.py | 96 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 tests/test_encode.py diff --git a/stripe/_encode.py b/stripe/_encode.py index 228edfdc6..3d7e68ef1 100644 --- a/stripe/_encode.py +++ b/stripe/_encode.py @@ -31,8 +31,8 @@ def _api_encode(data) -> Generator[Tuple[str, Any], None, None]: for key, value in data.items(): if value is None: continue - elif hasattr(value, "stripe_id"): - yield (key, value.stripe_id) + elif hasattr(value, "id"): + yield (key, getattr(value, "id")) elif isinstance(value, list) or isinstance(value, tuple): for i, sv in enumerate(value): # Always use indexed format for arrays diff --git a/tests/test_encode.py b/tests/test_encode.py new file mode 100644 index 000000000..6aabc5c55 --- /dev/null +++ b/tests/test_encode.py @@ -0,0 +1,96 @@ +import warnings + +from stripe._stripe_object import StripeObject +from stripe._encode import _api_encode + + +class TestApiEncode: + def test_encode_stripe_object_without_id_no_deprecation_warning(self): + """ + Test that encoding a StripeObject without an id (like metadata) + does not trigger a deprecation warning. + Regression test for issue #1651. + """ + metadata = StripeObject() + metadata["key1"] = "value1" + metadata["key2"] = "value2" + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = list(_api_encode({"metadata": metadata})) + + # Check no deprecation warnings were raised + deprecation_warnings = [ + warning + for warning in w + if issubclass(warning.category, DeprecationWarning) + ] + assert len(deprecation_warnings) == 0, ( + f"Expected no deprecation warnings, but got {len(deprecation_warnings)}" + ) + + # Verify the metadata was encoded correctly as nested dict + assert result == [ + ("metadata[key1]", "value1"), + ("metadata[key2]", "value2"), + ] + + def test_encode_stripe_object_with_id_extracts_id(self): + """ + Test that encoding a StripeObject with an id (like a customer reference) + correctly extracts just the id value. + """ + customer = StripeObject() + customer["id"] = "cus_123" + customer["name"] = "Test Customer" + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = list(_api_encode({"customer": customer})) + + # Check no deprecation warnings were raised + deprecation_warnings = [ + warning + for warning in w + if issubclass(warning.category, DeprecationWarning) + ] + assert len(deprecation_warnings) == 0, ( + f"Expected no deprecation warnings, but got {len(deprecation_warnings)}" + ) + + # Should encode to just the ID + assert result == [("customer", "cus_123")] + + def test_encode_regular_dict(self): + """Test that regular dicts are encoded as nested dicts.""" + regular_dict = {"key1": "value1", "key2": "value2"} + result = list(_api_encode({"data": regular_dict})) + + assert result == [ + ("data[key1]", "value1"), + ("data[key2]", "value2"), + ] + + def test_encode_none_value_skipped(self): + """Test that None values are skipped during encoding.""" + result = list(_api_encode({"field": None})) + assert result == [] + + def test_encode_string_value(self): + """Test that string values are encoded directly.""" + result = list(_api_encode({"name": "John Doe"})) + assert result == [("name", "John Doe")] + + def test_encode_boolean_value(self): + """Test that boolean values are encoded as lowercase strings.""" + result = list(_api_encode({"active": True, "deleted": False})) + assert result == [("active", "true"), ("deleted", "false")] + + def test_encode_list_value(self): + """Test that list values are encoded with indexed keys.""" + result = list(_api_encode({"items": ["item1", "item2", "item3"]})) + assert result == [ + ("items[0]", "item1"), + ("items[1]", "item2"), + ("items[2]", "item3"), + ]