diff --git a/.hark/changes/2026-09-15_zacchua_add-stripe-suppress-notices.change.md b/.hark/changes/2026-09-15_zacchua_add-stripe-suppress-notices.change.md new file mode 100644 index 000000000..40261bc43 --- /dev/null +++ b/.hark/changes/2026-09-15_zacchua_add-stripe-suppress-notices.change.md @@ -0,0 +1,7 @@ +--- +title: Allow suppressing Stripe notices +pr_url: https://github.com/stripe/stripe-python/pull/1909 +semver_level: minor +--- + +Set the `STRIPE_SUPPRESS_NOTICES` environment variable to `true` to suppress Stripe notices in test and sandbox environments when not running under a detected AI agent. Notices remain enabled by default and continue to be shown to AI agents. diff --git a/stripe/_api_requestor.py b/stripe/_api_requestor.py index e0f22186d..37f2011db 100644 --- a/stripe/_api_requestor.py +++ b/stripe/_api_requestor.py @@ -72,12 +72,31 @@ _default_proxy: Optional[str] = None -def _maybe_emit_stripe_notice(rheaders: Mapping[str, str]) -> None: +def _maybe_emit_stripe_notice( + rheaders: Mapping[str, str], + environ: Optional[Mapping[str, str]] = None, +) -> None: notice = rheaders.get("Stripe-Notice") - if notice: - import warnings + if not notice: + return + + environ = os.environ if environ is None else environ + ai_agent = _APIRequestor._detect_ai_agent(environ) + if ( + not ai_agent + and environ.get("STRIPE_SUPPRESS_NOTICES", "").lower() == "true" + ): + return + + if not ai_agent: + notice += ( + "\nTo suppress Stripe notices in test and sandbox environments, " + "set the STRIPE_SUPPRESS_NOTICES environment variable to true." + ) + + import warnings - warnings.warn(notice) + warnings.warn(notice) def is_v2_delete_resp(method: str, api_mode: ApiMode) -> bool: diff --git a/tests/test_api_requestor.py b/tests/test_api_requestor.py index c49803c46..744930ed9 100644 --- a/tests/test_api_requestor.py +++ b/tests/test_api_requestor.py @@ -11,7 +11,11 @@ import stripe import io -from stripe._api_requestor import _api_encode, _APIRequestor +from stripe._api_requestor import ( + _api_encode, + _APIRequestor, + _maybe_emit_stripe_notice, +) from stripe._customer import Customer from stripe._request_options import RequestOptions from stripe._requestor_options import ( @@ -1077,6 +1081,55 @@ def test_stripe_notice_header_emits_warning( with pytest.warns(UserWarning, match="test notice value"): requestor.request("get", self.v1_path, {}, base_address="api") + def test_stripe_notice_tells_humans_how_to_suppress_notices(self): + with pytest.warns(UserWarning) as warning: + _maybe_emit_stripe_notice( + {"Stripe-Notice": "test notice value"}, {} + ) + + assert str(warning[0].message) == ( + "test notice value\n" + "To suppress Stripe notices in test and sandbox environments, " + "set the STRIPE_SUPPRESS_NOTICES environment variable to true." + ) + + @pytest.mark.parametrize("suppression_value", ["true", "TRUE"]) + def test_stripe_notice_can_be_suppressed_for_humans( + self, suppression_value + ): + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("error") + _maybe_emit_stripe_notice( + {"Stripe-Notice": "test notice value"}, + {"STRIPE_SUPPRESS_NOTICES": suppression_value}, + ) + + @pytest.mark.parametrize( + "suppression_value", ["", "false", "1", "invalid"] + ) + def test_stripe_notice_is_not_suppressed_for_other_values( + self, suppression_value + ): + with pytest.warns(UserWarning): + _maybe_emit_stripe_notice( + {"Stripe-Notice": "test notice value"}, + {"STRIPE_SUPPRESS_NOTICES": suppression_value}, + ) + + def test_stripe_notice_is_not_suppressed_for_ai_agents(self): + with pytest.warns(UserWarning) as warning: + _maybe_emit_stripe_notice( + {"Stripe-Notice": "test notice value"}, + { + "STRIPE_SUPPRESS_NOTICES": "true", + "CODEX_SANDBOX": "1", + }, + ) + + assert str(warning[0].message) == "test notice value" + @pytest.mark.anyio async def test_stripe_notice_header_emits_warning_async( self, requestor, http_client_mock