From f292a2152af539923adb157f26329558bd27bcc8 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Tue, 3 Nov 2026 08:47:56 -0800 Subject: [PATCH] ref(api): Remove unreachable blank branch from empty number fields EmptyDecimalField and EmptyIntegerField both convert a blank string to None in run_validation, which DRF calls before to_internal_value. The blank check in their to_internal_value overrides could not be reached through DRF, but #129614 typed it by casting None to Decimal and int, so both overrides claimed a non-null result while holding a None branch. Dropping the overrides leaves run_validation (typed `Decimal | None` / `int | None`) as the only blank path, and to_internal_value inherits DRF's honest `Decimal` / `int` contract. MissedMarginField now uses that contract instead of `Any -> Any`. --- src/sentry/api/fields/empty_decimal.py | 6 ------ src/sentry/api/fields/empty_integer.py | 7 ------- src/sentry/monitors/validators.py | 4 ++-- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/sentry/api/fields/empty_decimal.py b/src/sentry/api/fields/empty_decimal.py index 35c7ab2344d3..955f71f38fb2 100644 --- a/src/sentry/api/fields/empty_decimal.py +++ b/src/sentry/api/fields/empty_decimal.py @@ -1,5 +1,4 @@ from decimal import Decimal -from typing import cast from rest_framework import serializers from rest_framework.fields import empty @@ -12,11 +11,6 @@ class EmptyDecimalField(serializers.DecimalField): cases, so this restores it. """ - def to_internal_value(self, data: int | float | str | Decimal) -> Decimal: - if data == "": - return cast(Decimal, None) - return super().to_internal_value(data) - def run_validation(self, data: object = empty) -> Decimal | None: if data == "": return None diff --git a/src/sentry/api/fields/empty_integer.py b/src/sentry/api/fields/empty_integer.py index 63cdcb85424e..f798ce9cd7ba 100644 --- a/src/sentry/api/fields/empty_integer.py +++ b/src/sentry/api/fields/empty_integer.py @@ -1,5 +1,3 @@ -from typing import cast - from rest_framework import serializers from rest_framework.fields import empty @@ -11,11 +9,6 @@ class EmptyIntegerField(serializers.IntegerField): cases, so this restores it. """ - def to_internal_value(self, data: int | float | str) -> int: - if data == "": - return cast(int, None) - return super().to_internal_value(data) - def run_validation(self, data: object = empty) -> int | None: if data == "": return None diff --git a/src/sentry/monitors/validators.py b/src/sentry/monitors/validators.py index 930ea9265100..d16ea61bba99 100644 --- a/src/sentry/monitors/validators.py +++ b/src/sentry/monitors/validators.py @@ -131,8 +131,8 @@ def get_value(self, dictionary: Any) -> Any: class MissedMarginField(EmptyIntegerField): - def to_internal_value(self, value: Any) -> Any: - value = super().to_internal_value(value) + def to_internal_value(self, data: float | int | str) -> int: + value = super().to_internal_value(data) # XXX(epurkhiser): As part of GH-56526 we changed the minimum value # allowed for the checkin_margin to 1 from 0. Some monitors may still