mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
* Update Validation Test cases until not passing * update Number and Integer validators * Upgrade Number and Integer type regex to allow underscore, augment tests * Force CHANGELOG update to prevent merge issues * Update CHANGELOG.md for this PR --------- Co-authored-by: Darren Burns <darrenb900@gmail.com>
This commit is contained in:
14
CHANGELOG.md
14
CHANGELOG.md
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Input validation for floats and integers accept embedded underscores, e.g., "1_234_567" is valid. https://github.com/Textualize/textual/pull/4784
|
||||
|
||||
### Changed
|
||||
|
||||
- Input validation for integers no longer accepts scientific notation like '1.5e2'; must be castable to int. https://github.com/Textualize/textual/pull/4784
|
||||
|
||||
### Fixed
|
||||
|
||||
- Input validation of floats no longer accepts NaN (not a number). https://github.com/Textualize/textual/pull/4784
|
||||
|
||||
## [0.79.1] - 2024-08-31
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -294,7 +294,7 @@ class Number(Validator):
|
||||
except ValueError:
|
||||
return ValidationResult.failure([Number.NotANumber(self, value)])
|
||||
|
||||
if float_value in {math.nan, math.inf, -math.inf}:
|
||||
if math.isnan(float_value) or math.isinf(float_value):
|
||||
return ValidationResult.failure([Number.NotANumber(self, value)])
|
||||
|
||||
if not self._validate_range(float_value):
|
||||
@@ -354,10 +354,10 @@ class Integer(Number):
|
||||
return number_validation_result
|
||||
|
||||
# We know it's a number, but is that number an integer?
|
||||
is_integer = float(value).is_integer()
|
||||
if not is_integer:
|
||||
try:
|
||||
int_value = int(value)
|
||||
except ValueError:
|
||||
return ValidationResult.failure([Integer.NotAnInteger(self, value)])
|
||||
|
||||
return self.success()
|
||||
|
||||
def describe_failure(self, failure: Failure) -> str | None:
|
||||
|
||||
@@ -34,10 +34,9 @@ InputValidationOn = Literal["blur", "changed", "submitted"]
|
||||
_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"]
|
||||
|
||||
@@ -8,23 +8,35 @@ from textual.widgets._input import _RESTRICT_TYPES
|
||||
|
||||
|
||||
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 @@ async def test_restrict_type():
|
||||
|
||||
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")
|
||||
|
||||
@@ -114,6 +114,12 @@ def test_Failure_description_describe_and_description_inside_validate():
|
||||
("99", 100, 200, False), # valid number but not in range
|
||||
("201", 100, 200, False), # valid number but not in range
|
||||
("1.23e4", 0, 50000, True), # valid scientific notation within range
|
||||
("inf", None, None, False), # infinity never valid
|
||||
("nan", None, None, False), # nan never valid
|
||||
("-inf", None, None, False), # nan never valid
|
||||
("-4", 0, 5, False), # valid negative number, out of range with zero
|
||||
("2", -3, 0, False), # valid number out of range with zero
|
||||
("-2", -3, 0, True), # negative in range
|
||||
],
|
||||
)
|
||||
def test_Number_validate(value, minimum, maximum, expected_result):
|
||||
@@ -154,7 +160,11 @@ def test_Regex_validate(regex, value, expected_result):
|
||||
("123", 100, 200, True), # valid integer within range
|
||||
("99", 100, 200, False), # valid integer but not in range
|
||||
("201", 100, 200, False), # valid integer but not in range
|
||||
("1.23e4", None, None, True), # valid integer in scientific notation
|
||||
("1.23e4", None, None, False), # valid scientific notation, even resolving to an integer, is not valid
|
||||
("123.", None, None, False), # periods not valid in integers
|
||||
("123_456", None, None, True), # underscores are valid python
|
||||
("_123_456", None, None, False), # leading underscores are not valid python
|
||||
("-123", -123, -123, True), # valid negative number in minimal range
|
||||
],
|
||||
)
|
||||
def test_Integer_validate(value, minimum, maximum, expected_result):
|
||||
|
||||
Reference in New Issue
Block a user