diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index 8e7ba91381..ccc299fc25 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -34,10 +34,9 @@ _POSSIBLE_VALIDATE_ON_VALUES = {"blur", "changed", "submitted"} """Set literal with the legal values for the type `InputValidationOn`.""" - _RESTRICT_TYPES = { - "integer": r"[-+]?\d*", - "number": r"[-+]?\d*\.?\d*[eE]?[-+]?\d*", + "integer": r"[-+]?(?:\d*|\d+_)*", + "number": r"[-+]?(?:\d*|\d+_)*\.?(?:\d*|\d+_)*(?:\d[eE]?[-+]?(?:\d*|\d+_)*)?", "text": None, } InputType = Literal["integer", "number", "text"] diff --git a/tests/input/test_input_restrict.py b/tests/input/test_input_restrict.py index 12100811ac..48d9d50ee7 100644 --- a/tests/input/test_input_restrict.py +++ b/tests/input/test_input_restrict.py @@ -8,23 +8,35 @@ def test_input_number_type(): - """Test number type regex.""" + """Test number type regex, value should be number or the prefix of a valid number""" number = _RESTRICT_TYPES["number"] assert re.fullmatch(number, "0") assert re.fullmatch(number, "0.") assert re.fullmatch(number, ".") + assert re.fullmatch(number, "-") + assert re.fullmatch(number, "+") assert re.fullmatch(number, ".0") assert re.fullmatch(number, "1.1") + assert re.fullmatch(number, "1_") + assert re.fullmatch(number, "1_2") + assert re.fullmatch(number, "-000_123_456.78e01_234") assert re.fullmatch(number, "1e1") + assert re.fullmatch(number, "1") + assert re.fullmatch(number, "1.") + assert re.fullmatch(number, "1.2") assert re.fullmatch(number, "1.2e") - assert re.fullmatch(number, "1.2e10") - assert re.fullmatch(number, "1.2E10") assert re.fullmatch(number, "1.2e-") + assert re.fullmatch(number, "1.2e-1") assert re.fullmatch(number, "1.2e-10") + assert re.fullmatch(number, "1.2E10") assert not re.fullmatch(number, "1.2e10e") + assert not re.fullmatch(number, "-000_123_456.78e01_234.") + assert not re.fullmatch(number, "e") # float("e23") is not valid assert not re.fullmatch(number, "1f2") assert not re.fullmatch(number, "inf") assert not re.fullmatch(number, "nan") + assert not re.fullmatch(number, "-inf") + def test_input_integer_type(): @@ -38,6 +50,13 @@ def test_input_integer_type(): assert re.fullmatch(integer, "+") assert re.fullmatch(integer, "-1") assert re.fullmatch(integer, "+2") + assert re.fullmatch(integer, "+0") + assert re.fullmatch(integer, "+0_") + assert re.fullmatch(integer, "+0_1") + assert re.fullmatch(integer, "+0_12") + assert re.fullmatch(integer, "+0_123") + assert not re.fullmatch(integer, "+_123") + assert not re.fullmatch(integer, "123.") assert not re.fullmatch(integer, "+2e") assert not re.fullmatch(integer, "foo") @@ -125,9 +144,9 @@ def compose(self) -> ComposeResult: integer_input.focus() await pilot.press("a") - assert integer_input.value == "" - + assert not integer_input.value await pilot.press("-") + assert integer_input.value == "-" assert integer_input.is_valid is False await pilot.press("1")