• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AlexandreDecan / portion / 8322112955

26 Feb 2024 08:26AM UTC coverage: 98.159%. Remained the same
8322112955

push

github

AlexandreDecan
Drop support for 3.7 + minor changes in doc

3 of 3 new or added lines in 2 files covered. (100.0%)

2 existing lines in 2 files now uncovered.

693 of 706 relevant lines covered (98.16%)

4.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.77
/portion/io.py
1
import re
5✔
2

3
from .const import Bound, inf
5✔
4
from .interval import Interval
5✔
5

6

7
def from_string(
5✔
8
    string,
9
    conv,
10
    *,
11
    bound=r".+?",
12
    disj=r" ?\| ?",
13
    sep=r", ?",
14
    left_open=r"\(",
15
    left_closed=r"\[",
16
    right_open=r"\)",
17
    right_closed=r"\]",
18
    pinf=r"\+inf",
19
    ninf=r"-inf",
20
    klass=Interval,
21
):
22
    """
23
    Parse given string and create an Interval instance.
24
    A converter function has to be provided to convert a bound (as string) to a value.
25
    This function raises a ValueError if given string cannot be parsed to an interval.
26

27
    :param string: string to parse.
28
    :param conv: function to convert a bound (as string) to an object.
29
    :param bound: regex pattern for a value.
30
    :param disj: regex pattern for disjunctive operator (default matches '|' and ' | ').
31
    :param sep: regex pattern for bound separator (default matches ',').
32
    :param left_open: regex pattern for left open boundary (default matches '(').
33
    :param left_closed: regex pattern for left closed boundary (default
34
        matches '[').
35
    :param right_open: regex pattern for right open boundary (default matches ')').
36
    :param right_closed: regex pattern for right closed boundary (default
37
        matches ']').
38
    :param pinf: regex pattern for positive infinity (default matches '+inf').
39
    :param ninf: regex pattern for negative infinity (default matches '-inf').
40
    :param klass: class to use for creating intervals (default to Interval).
41
    :return: an interval.
42
    """
43

44
    re_left_boundary = r"(?P<left>{}|{})".format(left_open, left_closed)
5✔
45
    re_right_boundary = r"(?P<right>{}|{})".format(right_open, right_closed)
5✔
46
    re_bounds = r"(?P<lower>{bound})({sep}(?P<upper>{bound}))?".format(
5✔
47
        bound=bound, sep=sep
48
    )
49
    re_interval = r"{}(|{}){}".format(re_left_boundary, re_bounds, re_right_boundary)
5✔
50

51
    intervals = []
5✔
52
    has_more = True
5✔
53
    source = string
5✔
54

55
    def _convert(bound):
5✔
56
        if re.match(pinf, bound):
5✔
57
            return inf
5✔
58
        elif re.match(ninf, bound):
5✔
59
            return -inf
5✔
60
        else:
61
            return conv(bound)
5✔
62

63
    while has_more:
5✔
64
        match = re.match(re_interval, string)
5✔
65
        if match is None:
5✔
66
            raise ValueError('"{}" cannot be parsed to an interval.'.format(source))
5✔
67

68
        # Parse atomic interval
69
        group = match.groupdict()
5✔
70

71
        left = (
5✔
72
            Bound.CLOSED if re.match(left_closed + "$", group["left"]) else Bound.OPEN
73
        )
74
        right = (
5✔
75
            Bound.CLOSED if re.match(right_closed + "$", group["right"]) else Bound.OPEN
76
        )
77
        lower = group.get("lower", None)
5✔
78
        upper = group.get("upper", None)
5✔
79
        lower = _convert(lower) if lower is not None else inf
5✔
80
        upper = _convert(upper) if upper is not None else lower
5✔
81

82
        intervals.append(klass.from_atomic(left, lower, upper, right))
5✔
83
        string = string[match.end() :]
5✔
84

85
        # Are there more atomic intervals?
86
        if len(string) > 0:
5✔
87
            match = re.match(disj, string)
5✔
88
            if match is None:
5✔
UNCOV
89
                raise ValueError('"{}" cannot be parsed to an interval.'.format(source))
×
90
            else:
91
                string = string[match.end() :]
5✔
92
        else:
93
            has_more = False
5✔
94

95
    return klass(*intervals)
5✔
96

97

98
def to_string(
5✔
99
    interval,
100
    conv=repr,
101
    *,
102
    disj=" | ",
103
    sep=",",
104
    left_open="(",
105
    left_closed="[",
106
    right_open=")",
107
    right_closed="]",
108
    pinf="+inf",
109
    ninf="-inf",
110
):
111
    """
112
    Export given interval to string.
113

114
    :param interval: an interval.
115
    :param conv: function that is used to represent a bound (default is `repr`).
116
    :param disj: string representing disjunctive operator (default is ' | ').
117
    :param sep: string representing bound separator (default is ',').
118
    :param left_open: string representing left open boundary (default is '(').
119
    :param left_closed: string representing left closed boundary (default is '[').
120
    :param right_open: string representing right open boundary (default is ')').
121
    :param right_closed: string representing right closed boundary (default is ']').
122
    :param pinf: string representing a positive infinity (default is '+inf').
123
    :param ninf: string representing a negative infinity (default is '-inf').
124
    :return: a string representation for given interval.
125
    """
126
    if interval.empty:
5✔
127
        return left_open + right_open
5✔
128

129
    def _convert(bound):
5✔
130
        if bound == inf:
5✔
131
            return pinf
5✔
132
        elif bound == -inf:
5✔
133
            return ninf
5✔
134
        else:
135
            return conv(bound)
5✔
136

137
    exported_intervals = []
5✔
138
    for item in interval:
5✔
139
        left = left_open if item.left == Bound.OPEN else left_closed
5✔
140
        right = right_open if item.right == Bound.OPEN else right_closed
5✔
141

142
        lower = _convert(item.lower)
5✔
143
        upper = _convert(item.upper)
5✔
144

145
        if item.lower == item.upper:
5✔
146
            exported_intervals.append(left + lower + right)
5✔
147
        else:
148
            exported_intervals.append(left + lower + sep + upper + right)
5✔
149

150
    return disj.join(exported_intervals)
5✔
151

152

153
def from_data(
5✔
154
    data, conv=None, *, pinf=float("inf"), ninf=float("-inf"), klass=Interval
155
):
156
    """
157
    Import an interval from a list of 4-uples (left, lower, upper, right).
158

159
    :param data: a list of 4-uples (left, lower, upper, right).
160
    :param conv: function to convert bound values, default to identity.
161
    :param pinf: value used to represent positive infinity.
162
    :param ninf: value used to represent negative infinity.
163
    :param klass: class to use for creating intervals (default to Interval).
164
    :return: an interval.
165
    """
166
    intervals = []
5✔
167
    conv = (lambda v: v) if conv is None else conv
5✔
168

169
    def _convert(bound):
5✔
170
        if bound == pinf:
5✔
171
            return inf
5✔
172
        elif bound == ninf:
5✔
173
            return -inf
5✔
174
        else:
175
            return conv(bound)
5✔
176

177
    for item in data:
5✔
178
        left, lower, upper, right = item
5✔
179
        intervals.append(
5✔
180
            klass.from_atomic(
181
                Bound(left),
182
                _convert(lower),
183
                _convert(upper),
184
                Bound(right),
185
            )
186
        )
187
    return klass(*intervals)
5✔
188

189

190
def to_data(interval, conv=None, *, pinf=float("inf"), ninf=float("-inf")):
5✔
191
    """
192
    Export given interval to a list of 4-uples (left, lower, upper, right).
193

194
    :param interval: an interval.
195
    :param conv: function to convert bound values, default to identity.
196
    :param pinf: value used to encode positive infinity.
197
    :param ninf: value used to encode negative infinity.
198
    :return: a list of 4-uples (left, lower, upper, right)
199
    """
200
    conv = (lambda v: v) if conv is None else conv
5✔
201

202
    data = []
5✔
203

204
    def _convert(bound):
5✔
205
        if bound == inf:
5✔
206
            return pinf
5✔
207
        elif bound == -inf:
5✔
208
            return ninf
5✔
209
        else:
210
            return conv(bound)
5✔
211

212
    for item in interval:
5✔
213
        data.append(
5✔
214
            (
215
                item.left.value,
216
                _convert(item.lower),
217
                _convert(item.upper),
218
                item.right.value,
219
            )
220
        )
221
    return data
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc