From e49898c64d7ac55a9f0d05fc9b855f971f56918c Mon Sep 17 00:00:00 2001 From: Alexandre Decan Date: Sat, 28 Jan 2023 10:20:51 +0100 Subject: [PATCH] Bypass #75 --- CHANGELOG.md | 4 ++ portion/func.py | 25 ++++++++--- tests/test_interval.py | 95 +++++++++++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c23daae..aa7eb6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Speed up lookups in `IntervalDict` for non-interval keys. - Drop official support for Python 3.6. +### Fixed + - Infinite recursion in `iterate` with `Interval` subclasses (see [#75](https://github.com/AlexandreDecan/portion/issues/75)). + + ## 2.3.0 (2022-08-31) diff --git a/portion/func.py b/portion/func.py index d14cbce..fc6b5bc 100644 --- a/portion/func.py +++ b/portion/func.py @@ -1,8 +1,7 @@ import operator from functools import partial -from .const import inf -from .interval import singleton +from .const import Bound, inf def iterate(interval, step, *, base=None, reverse=False): @@ -37,8 +36,22 @@ def iterate(interval, step, *, base=None, reverse=False): def base(x): return x - exclude = operator.lt if not reverse else operator.gt - include = operator.le if not reverse else operator.ge + if not reverse: + + def exclude(v, i): + return v < i.lower or (i.left is Bound.OPEN and v <= i.lower) + + def include(v, i): + return v < i.upper or (i.right is Bound.CLOSED and v <= i.upper) + + else: + + def exclude(v, i): + return v > i.upper or (i.right is Bound.OPEN and v >= i.upper) + + def include(v, i): + return v > i.lower or (i.left is Bound.CLOSED and v >= i.lower) + step = step if callable(step) else partial(operator.add, step) value = base(interval.lower if not reverse else interval.upper) @@ -48,9 +61,9 @@ def base(x): for i in interval if not reverse else reversed(interval): value = base(i.lower if not reverse else i.upper) - while exclude(singleton(value), i): + while exclude(value, i): value = step(value) - while include(singleton(value), i): + while include(value, i): yield value value = step(value) diff --git a/tests/test_interval.py b/tests/test_interval.py index 70a568e..30e39bb 100644 --- a/tests/test_interval.py +++ b/tests/test_interval.py @@ -393,34 +393,73 @@ def test_with_empty_and_infinities(self): assert not (P.closedopen(0, P.inf) >= P.empty()) def test_with_values(self): - assert 0 < P.closed(1, 2) - assert not (0 < P.closed(-1, 1)) - assert not (0 < P.closed(0, 1)) - assert 0 < P.open(0, 1) - # assert 0 <= P.closed(1, 2) - # assert 0 <= P.open(0, 1) - # assert 0 <= P.closed(-1, 1) - # assert not (0 <= P.closed(-2, -1)) - # assert not (0 <= P.open(-1, 0)) - - assert P.closed(1, 2) > 0 - assert not (P.closed(-1, 1) > 0) - assert not (P.closed(0, 1) > 0) - assert P.open(0, 1) > 0 - assert P.closed(1, 2) >= 0 - assert P.open(0, 1) >= 0 - assert not (P.closed(-1, 1) >= 0) - assert not (P.closed(-2, -1) >= 0) - assert not (P.open(-1, 0) >= 0) - - assert not (0 < P.empty()) - # assert not (0 <= P.empty()) - assert not (0 > P.empty()) - # assert not (0 >= P.empty()) - assert not (P.empty() < 0) - assert not (P.empty() <= 0) - assert not (P.empty() > 0) - assert not (P.empty() >= 0) + with pytest.deprecated_call(): + assert 0 < P.closed(1, 2) + + with pytest.deprecated_call(): + assert not (0 < P.closed(-1, 1)) + + with pytest.deprecated_call(): + assert not (0 < P.closed(0, 1)) + + with pytest.deprecated_call(): + assert 0 < P.open(0, 1) + # assert 0 <= P.closed(1, 2) + # assert 0 <= P.open(0, 1) + # assert 0 <= P.closed(-1, 1) + # assert not (0 <= P.closed(-2, -1)) + # assert not (0 <= P.open(-1, 0)) + + with pytest.deprecated_call(): + assert P.closed(1, 2) > 0 + + with pytest.deprecated_call(): + assert not (P.closed(-1, 1) > 0) + + with pytest.deprecated_call(): + assert not (P.closed(0, 1) > 0) + + with pytest.deprecated_call(): + assert P.open(0, 1) > 0 + + with pytest.deprecated_call(): + assert P.closed(1, 2) >= 0 + + with pytest.deprecated_call(): + assert P.open(0, 1) >= 0 + + with pytest.deprecated_call(): + assert not (P.closed(-1, 1) >= 0) + + with pytest.deprecated_call(): + assert not (P.closed(-2, -1) >= 0) + + with pytest.deprecated_call(): + assert not (P.open(-1, 0) >= 0) + + with pytest.deprecated_call(): + assert not (0 < P.empty()) + + with pytest.deprecated_call(): + assert not (0 <= P.empty()) + + with pytest.deprecated_call(): + assert not (0 > P.empty()) + + with pytest.deprecated_call(): + assert not (0 >= P.empty()) + + with pytest.deprecated_call(): + assert not (P.empty() < 0) + + with pytest.deprecated_call(): + assert not (P.empty() <= 0) + + with pytest.deprecated_call(): + assert not (P.empty() > 0) + + with pytest.deprecated_call(): + assert not (P.empty() >= 0) class TestIntervalContainment: