diff --git a/rangy/parse.py b/rangy/parse.py index 7a230f2..289c17e 100644 --- a/rangy/parse.py +++ b/rangy/parse.py @@ -1,10 +1,12 @@ import re from rangy.exceptions import ParseRangeError -from rangy import ConverterRegistry, Rangy -from .const import SPECIAL_CHARS +from .registry import ConverterRegistry +from .const import SPECIAL_CHARS, INFINITY def _split(as_squence): + if None in as_squence: + raise ParseRangeError("Invalid range tuple/list") if len(as_squence) == 1: # this is valid, as it # indicates a single value range @@ -28,7 +30,6 @@ def _nomalize_str(range_str): Returns: A tuples of parts of the range string. """ - # FIX: Remove brackets FIRST, then split. range_str = re.sub(r'^[\[\(]|[\]\)]$', '', range_str.strip()) # Remove brackets range_str = re.split(r'[\s,;|-]+', range_str) # split return tuple(part.strip() for part in range_str if part.strip()) # Remove empty strings @@ -45,6 +46,7 @@ def _normalize_to_sequence(range_input): Raises: ParseRangeError: If the input is invalid or cannot be normalized. """ + from rangy import Rangy if isinstance(range_input, Rangy): range_input = range_input.copy().values @@ -86,18 +88,24 @@ def parse_range(range_input): start, end = _normalize_to_sequence(range_input) try: - if not isinstance(start, str): + if start == '*': + parsed_start = 0 + elif start == '+': + parsed_start = 1 + elif not isinstance(start, str): converter = ConverterRegistry.get(start) - parsed_start = converter(start) # or converter.to_number(start) depending on your Converter interface. + parsed_start = converter(start) else: parsed_start = _convert_string_part(start) - if not isinstance(end, str): + if end == '*' or end == '+': + parsed_end = INFINITY + elif not isinstance(end, str): converter = ConverterRegistry.get(end) - parsed_end = converter(end) # or converter.to_number(end) + parsed_end = converter(end) else: parsed_end = _convert_string_part(end) return parsed_start, parsed_end - except (KeyError, ValueError, TypeError) as e: # Handle converter and TypeRegistry errors. + except (KeyError, ValueError, TypeError) as e: raise ParseRangeError(f"Error parsing range: {e}") from e diff --git a/rangy/rangy.py b/rangy/rangy.py index 1a481d8..c7a97fd 100644 --- a/rangy/rangy.py +++ b/rangy/rangy.py @@ -6,7 +6,7 @@ RangyType = Union[int, str] - +from .parse import parse_range as new_parse def _parse(self, rangy) -> Tuple[Union[int, float], Union[int, float]]: @@ -142,7 +142,7 @@ class Rangy: _max (int): The maximum range value. _rangy_type (int): The type of range. """ - def __init__(self, range: Union[int, str, Tuple[int, int]], parse_func: callable = _parse): + def __init__(self, range: Union[int, str, Tuple[int, int]], parse_func: callable = new_parse): """ Initializes a Rangy instance. @@ -157,7 +157,7 @@ def __init__(self, range: Union[int, str, Tuple[int, int]], parse_func: callable self._max = range._max self._type = range._type else: - self._min, self._max = parse_func(self, range) + self._min, self._max = parse_func(range) self._type = self._determine_type() def _determine_type(self) -> int: @@ -167,9 +167,9 @@ def _determine_type(self) -> int: Returns: int: The rangy type, one of rangy_EXACT, rangy_RANGE, rangy_ANY, or rangy_AT_LEAST_ONE. """ - if self._min == 0 and self._max == INFINITY: + if self._min == 0 and self._max == float(INFINITY): return ANY - elif self._min == 1 and self._max == INFINITY: + elif self._min == 1 and self._max == float(INFINITY): return AT_LEAST_ONE elif self._min == self._max: return EXACT diff --git a/tests/test_comparison.py b/tests/test_comparison.py index f3a1030..5585510 100644 --- a/tests/test_comparison.py +++ b/tests/test_comparison.py @@ -79,15 +79,15 @@ def test_ge(count, other, expected): var_count = Rangy(count) assert (var_count >= other) == expected -@pytest.mark.parametrize("count", [ - (-1, 3), - (3, -1), - (-1, -3), +@pytest.mark.parametrize("count, expected", [ + ((-1, 3), (-1, 3)), + ((3, -1), (-1, 3)), + ((-1, -3), (-3, -1)), ], ids=[ "negative_min", "negative_max", "negative_both" ]) -def test_negative(count): - with pytest.raises(ValueError): - Rangy(count) \ No newline at end of file +def test_negative(count, expected): + var_count = Rangy(count) + assert var_count.values == expected \ No newline at end of file diff --git a/tests/test_contains.py b/tests/test_contains.py index f661f5e..acae851 100644 --- a/tests/test_contains.py +++ b/tests/test_contains.py @@ -1,6 +1,7 @@ import pytest from rangy import Rangy +from rangy.exceptions import ParseRangeError @pytest.mark.parametrize("count, item, expected", [ @@ -32,5 +33,5 @@ def test_contains(count, item, expected): "none_max" ]) def test_invalid_tuple(count): - with pytest.raises(ValueError): + with pytest.raises(ParseRangeError): Rangy(count) \ No newline at end of file diff --git a/tests/test_parse.py b/tests/test_parse.py index 641c159..8c5271f 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,6 +1,7 @@ import pytest from rangy import INFINITY, _parse +from rangy.exceptions import ParseRangeError @pytest.mark.parametrize("count, expected", [ @@ -59,5 +60,5 @@ def test_parse(count, expected): "invalid_range_semicolon" ]) def test_parse_invalid(count): - with pytest.raises(ValueError): + with pytest.raises(ParseRangeError): _parse(None, count) \ No newline at end of file