Skip to content

Commit 6614836

Browse files
authored
Merge pull request #30 from bbc/jamesba-unionwithnothing
Fixed bug when taking union with an empty range
2 parents b680c28 + 98d3e62 commit 6614836

4 files changed

Lines changed: 111 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# mediatimestamp Changelog
22

3+
## 1.5.2
4+
- Fixed bug in taking unions with empty ranges
5+
36
## 1.5.1
47
- Fixed bug in `TimeOffset.to_count` (both versions) that caused
58
incorrect results when rounding down when the denominator of the

mediatimestamp/immutable.py

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,39 +1026,49 @@ def intersect_with(self, tr):
10261026

10271027
def starts_inside_timerange(self, other):
10281028
"""Returns true if the start of this timerange is located inside the other."""
1029-
return ((self.bounded_before() and self.start in other and
1030-
(not (other.bounded_after() and self.start == other.end and not self.includes_start()))) or
1031-
(self.bounded_before() and other.bounded_before() and self.start == other.start and
1032-
(not (self.includes_start() and not other.includes_start()))) or
1033-
(not self.bounded_before() and not other.bounded_before()))
1029+
return (not self.is_empty() and
1030+
not other.is_empty() and
1031+
((self.bounded_before() and self.start in other and
1032+
(not (other.bounded_after() and self.start == other.end and not self.includes_start()))) or
1033+
(self.bounded_before() and other.bounded_before() and self.start == other.start and
1034+
(not (self.includes_start() and not other.includes_start()))) or
1035+
(not self.bounded_before() and not other.bounded_before())))
10341036

10351037
def ends_inside_timerange(self, other):
10361038
"""Returns true if the end of this timerange is located inside the other."""
1037-
return ((self.bounded_after() and self.end in other and
1038-
(not (other.bounded_before() and self.end == other.start and not self.includes_end()))) or
1039-
(self.bounded_after() and other.bounded_after() and self.end == other.end and
1040-
(not (self.includes_end() and not other.includes_end()))) or
1041-
(not self.bounded_after() and not other.bounded_after()))
1039+
return (not self.is_empty() and
1040+
not other.is_empty() and
1041+
((self.bounded_after() and self.end in other and
1042+
(not (other.bounded_before() and self.end == other.start and not self.includes_end()))) or
1043+
(self.bounded_after() and other.bounded_after() and self.end == other.end and
1044+
(not (self.includes_end() and not other.includes_end()))) or
1045+
(not self.bounded_after() and not other.bounded_after())))
10421046

10431047
def is_earlier_than_timerange(self, other):
10441048
"""Returns true if this timerange ends earlier than the start of the other."""
1045-
return (other.bounded_before() and
1049+
return (not self.is_empty() and
1050+
not other.is_empty() and
1051+
other.bounded_before() and
10461052
self.bounded_after() and
10471053
(self.end < other.start or
10481054
(self.end == other.start and
10491055
not (self.includes_end() and other.includes_start()))))
10501056

10511057
def is_later_than_timerange(self, other):
10521058
"""Returns true if this timerange starts later than the end of the other."""
1053-
return (other.bounded_after() and
1059+
return (not self.is_empty() and
1060+
not other.is_empty() and
1061+
other.bounded_after() and
10541062
self.bounded_before() and
10551063
(self.start > other.end or
10561064
(self.start == other.end and
10571065
not (self.includes_start() and other.includes_end()))))
10581066

10591067
def starts_earlier_than_timerange(self, other):
10601068
"""Returns true if this timerange starts earlier than the start of the other."""
1061-
return (other.bounded_before() and
1069+
return (not self.is_empty() and
1070+
not other.is_empty() and
1071+
other.bounded_before() and
10621072
(not self.bounded_before() or
10631073
(self.start < other.start or
10641074
(self.start == other.start and
@@ -1067,23 +1077,29 @@ def starts_earlier_than_timerange(self, other):
10671077

10681078
def starts_later_than_timerange(self, other):
10691079
"""Returns true if this timerange starts later than the start of the other."""
1070-
return (self.bounded_before() and
1080+
return (not self.is_empty() and
1081+
not other.is_empty() and
1082+
self.bounded_before() and
10711083
(not other.bounded_before() or
10721084
(self.start > other.start or
10731085
(self.start == other.start and
10741086
(not self.includes_start() and other.includes_start())))))
10751087

10761088
def ends_earlier_than_timerange(self, other):
10771089
"""Returns true if this timerange ends earlier than the end of the other."""
1078-
return (self.bounded_after() and
1090+
return (not self.is_empty() and
1091+
not other.is_empty() and
1092+
self.bounded_after() and
10791093
(not other.bounded_after() or
10801094
(self.end < other.end or
10811095
(self.end == other.end and
10821096
(not self.includes_end() and other.includes_end())))))
10831097

10841098
def ends_later_than_timerange(self, other):
10851099
"""Returns true if this timerange ends later than the end of the other."""
1086-
return (other.bounded_after() and
1100+
return (not self.is_empty() and
1101+
not other.is_empty() and
1102+
other.bounded_after() and
10871103
(not self.bounded_after() or
10881104
(self.end > other.end or
10891105
(self.end == other.end and
@@ -1110,6 +1126,12 @@ def union_with_timerange(self, other):
11101126
if not self.is_contiguous_with_timerange(other):
11111127
raise ValueError("Timeranges {} and {} are not contiguous, so cannot take the union.".format(self, other))
11121128

1129+
if self.is_empty():
1130+
return other
1131+
1132+
if other.is_empty():
1133+
return self
1134+
11131135
inclusivity = TimeRange.EXCLUSIVE
11141136
if self.start == other.start:
11151137
start = self.start

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
# Basic metadata
2020
name = 'mediatimestamp'
21-
version = '1.5.1'
21+
version = '1.5.2'
2222
description = 'A timestamp library for high precision nanosecond timestamps'
2323
url = 'https://github.com/bbc/rd-apmm-python-lib-mediatimestamp'
2424
author = 'James P. Weaver'

tests/test_immutable.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,33 @@ def test_comparisons(self):
13781378
(False, False, True, False, True, False, True, False, False, True)),
13791379
(TimeRange.from_str("[0:0_5:0)"), TimeRange.from_str("(5:0_10:0)"),
13801380
(False, False, True, False, True, False, True, False, False, False)),
1381+
1382+
(TimeRange.never(), TimeRange.from_str("_"),
1383+
(False, False, False, False, False, False, False, False, True, True)),
1384+
(TimeRange.never(), TimeRange.from_str("[0:0_"),
1385+
(False, False, False, False, False, False, False, False, True, True)),
1386+
(TimeRange.never(), TimeRange.from_str("(0:0_"),
1387+
(False, False, False, False, False, False, False, False, True, True)),
1388+
(TimeRange.never(), TimeRange.from_str("[10:0_"),
1389+
(False, False, False, False, False, False, False, False, True, True)),
1390+
(TimeRange.never(), TimeRange.from_str("(10:0_"),
1391+
(False, False, False, False, False, False, False, False, True, True)),
1392+
(TimeRange.never(), TimeRange.from_str("_0:0]"),
1393+
(False, False, False, False, False, False, False, False, True, True)),
1394+
(TimeRange.never(), TimeRange.from_str("_0:0)"),
1395+
(False, False, False, False, False, False, False, False, True, True)),
1396+
(TimeRange.never(), TimeRange.from_str("_10:0]"),
1397+
(False, False, False, False, False, False, False, False, True, True)),
1398+
(TimeRange.never(), TimeRange.from_str("_10:0)"),
1399+
(False, False, False, False, False, False, False, False, True, True)),
1400+
(TimeRange.never(), TimeRange.from_str("[0:0_10:0)"),
1401+
(False, False, False, False, False, False, False, False, True, True)),
1402+
(TimeRange.never(), TimeRange.from_str("(0:0_10:0)"),
1403+
(False, False, False, False, False, False, False, False, True, True)),
1404+
(TimeRange.never(), TimeRange.from_str("[5:0_10:0)"),
1405+
(False, False, False, False, False, False, False, False, True, True)),
1406+
(TimeRange.never(), TimeRange.from_str("(5:0_10:0)"),
1407+
(False, False, False, False, False, False, False, False, True, True)),
13811408
]
13821409
functions = ("starts_inside_timerange",
13831410
"ends_inside_timerange",
@@ -1503,3 +1530,45 @@ def test_normalise(self):
15031530
self.assertEqual(result, expected,
15041531
msg=("{!r}.normalise({}, {}, rounding={}) == {!r}, expected {!r}"
15051532
.format(tr, rate.numerator, rate.denominator, rounding, result, expected)))
1533+
1534+
def test_union(self):
1535+
test_data = [
1536+
(TimeRange.from_str("()"), TimeRange.from_str("()"),
1537+
TimeRange.from_str("()")),
1538+
(TimeRange.from_str("[0:0_10:0)"), TimeRange.from_str("[10:0]"),
1539+
TimeRange.from_str("[0:0_10:0]")),
1540+
(TimeRange.from_str("_"), TimeRange.from_str("[0:0]"),
1541+
TimeRange.from_str("_")),
1542+
(TimeRange.from_str("_"), TimeRange.from_str("()"),
1543+
TimeRange.from_str("_")),
1544+
(TimeRange.from_str("()"), TimeRange.from_str("_"),
1545+
TimeRange.from_str("_")),
1546+
(TimeRange.from_str("_10:0)"), TimeRange.from_str("[0:0_"),
1547+
TimeRange.from_str("_")),
1548+
(TimeRange.from_str("[0:0_10:0)"), TimeRange.from_str("[5:0_"),
1549+
TimeRange.from_str("[0:0_")),
1550+
(TimeRange.from_str("[0:0_10:0)"), TimeRange.from_str("[5:0_15:0)"),
1551+
TimeRange.from_str("[0:0_15:0)")),
1552+
(TimeRange.from_str("[0:0_10:0)"), TimeRange.from_str("[10:0_15:0)"),
1553+
TimeRange.from_str("[0:0_15:0)")),
1554+
(TimeRange.from_str("()"), TimeRange.from_str("[5:0_"),
1555+
TimeRange.from_str("[5:0_")),
1556+
(TimeRange.from_str("()"), TimeRange.from_str("[5:0_15:0)"),
1557+
TimeRange.from_str("[5:0_15:0)")),
1558+
(TimeRange.from_str("()"), TimeRange.from_str("_15:0)"),
1559+
TimeRange.from_str("_15:0)")),
1560+
]
1561+
1562+
for (first, second, expected) in test_data:
1563+
with self.subTest(first=first, second=second, expected=expected):
1564+
self.assertEqual(first.union_with_timerange(second), expected)
1565+
1566+
test_data = [
1567+
(TimeRange.from_str("_0:0)"), TimeRange.from_str("(0:0_")),
1568+
(TimeRange.from_str("[0:0_5:0)"), TimeRange.from_str("[10:0_15:0)")),
1569+
]
1570+
1571+
for (first, second) in test_data:
1572+
with self.subTest(first=first, second=second):
1573+
with self.assertRaises(ValueError):
1574+
first.union_with_timerange(second)

0 commit comments

Comments
 (0)