Skip to content

Commit e182afe

Browse files
authored
add ndigits to round dunder method (#5019)
* add ndigits to round dunder method * add integration test
1 parent 346e848 commit e182afe

File tree

3 files changed

+46
-13
lines changed

3 files changed

+46
-13
lines changed

reflex/vars/base.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3317,10 +3317,15 @@ def __get__(
33173317

33183318
@overload
33193319
def __get__(
3320-
self: Field[int]
3321-
| Field[float]
3320+
self: Field[int] | Field[int | None],
3321+
instance: None,
3322+
owner: Any,
3323+
) -> NumberVar[int]: ...
3324+
3325+
@overload
3326+
def __get__(
3327+
self: Field[float]
33223328
| Field[int | float]
3323-
| Field[int | None]
33243329
| Field[float | None]
33253330
| Field[int | float | None],
33263331
instance: None,

reflex/vars/number.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
overload,
1717
)
1818

19+
from typing_extensions import TypeVar as TypeVarExt
20+
1921
from reflex.constants.base import Dirs
2022
from reflex.utils.exceptions import (
2123
PrimitiveUnserializableToJSONError,
@@ -35,7 +37,9 @@
3537
var_operation_return,
3638
)
3739

38-
NUMBER_T = TypeVar("NUMBER_T", int, float, bool)
40+
NUMBER_T = TypeVarExt(
41+
"NUMBER_T", bound=(int | float), default=(int | float), covariant=True
42+
)
3943

4044
if TYPE_CHECKING:
4145
from .sequence import ArrayVar
@@ -313,13 +317,19 @@ def __pos__(self) -> NumberVar:
313317
"""
314318
return self
315319

316-
def __round__(self):
320+
def __round__(self, ndigits: int | NumberVar = 0) -> NumberVar:
317321
"""Round the number.
318322
323+
Args:
324+
ndigits: The number of digits to round.
325+
319326
Returns:
320327
The number round operation.
321328
"""
322-
return number_round_operation(self)
329+
if not isinstance(ndigits, NUMBER_TYPES):
330+
raise_unsupported_operand_types("round", (type(self), type(ndigits)))
331+
332+
return number_round_operation(self, +ndigits)
323333

324334
def __ceil__(self):
325335
"""Ceil the number.
@@ -653,16 +663,23 @@ def number_exponent_operation(lhs: NumberVar, rhs: NumberVar):
653663

654664

655665
@var_operation
656-
def number_round_operation(value: NumberVar):
666+
def number_round_operation(value: NumberVar, ndigits: NumberVar | int):
657667
"""Round the number.
658668
659669
Args:
660670
value: The number.
671+
ndigits: The number of digits.
661672
662673
Returns:
663674
The number round operation.
664675
"""
665-
return var_operation_return(js_expression=f"Math.round({value})", var_type=int)
676+
if (isinstance(ndigits, LiteralNumberVar) and ndigits._var_value == 0) or (
677+
isinstance(ndigits, int) and ndigits == 0
678+
):
679+
return var_operation_return(js_expression=f"Math.round({value})", var_type=int)
680+
return var_operation_return(
681+
js_expression=f"(+{value}.toFixed({ndigits}))", var_type=float
682+
)
666683

667684

668685
@var_operation

tests/integration/test_var_operations.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class VarOperationState(rx.State):
3131
int_var3: rx.Field[int] = rx.field(7)
3232
float_var1: rx.Field[float] = rx.field(10.5)
3333
float_var2: rx.Field[float] = rx.field(5.5)
34+
long_float: rx.Field[float] = rx.field(13212312312.1231231)
3435
list1: rx.Field[list] = rx.field([1, 2])
3536
list2: rx.Field[list] = rx.field([3, 4])
3637
list3: rx.Field[list] = rx.field(["first", "second", "third"])
@@ -718,25 +719,33 @@ def index():
718719
),
719720
# ObjectVar
720721
rx.box(
721-
rx.text(VarOperationState.obj.name),
722+
rx.text.span(VarOperationState.obj.name),
722723
id="obj_name",
723724
),
724725
rx.box(
725-
rx.text(VarOperationState.obj.optional_none),
726+
rx.text.span(VarOperationState.obj.optional_none),
726727
id="obj_optional_none",
727728
),
728729
rx.box(
729-
rx.text(VarOperationState.obj.optional_str),
730+
rx.text.span(VarOperationState.obj.optional_str),
730731
id="obj_optional_str",
731732
),
732733
rx.box(
733-
rx.text(VarOperationState.obj.get("optional_none")),
734+
rx.text.span(VarOperationState.obj.get("optional_none")),
734735
id="obj_optional_none_get_none",
735736
),
736737
rx.box(
737-
rx.text(VarOperationState.obj.get("optional_none", "foo")),
738+
rx.text.span(VarOperationState.obj.get("optional_none", "foo")),
738739
id="obj_optional_none_get_foo",
739740
),
741+
rx.box(
742+
rx.text.span(round(VarOperationState.long_float)),
743+
id="float_round",
744+
),
745+
rx.box(
746+
rx.text.span(round(VarOperationState.long_float, 2)),
747+
id="float_round_2",
748+
),
740749
)
741750

742751

@@ -965,6 +974,8 @@ def test_var_operations(driver, var_operations: AppHarness):
965974
("obj_optional_str", "hello"),
966975
("obj_optional_none_get_none", ""),
967976
("obj_optional_none_get_foo", "foo"),
977+
("float_round", "13212312312"),
978+
("float_round_2", "13212312312.12"),
968979
]
969980

970981
for tag, expected in tests:

0 commit comments

Comments
 (0)