Repository: chaimleib/intervaltree

Issue:
UnicodeDecodeError
hi,
E:\zephyr>pip install intervaltree
Collecting intervaltree
  Using cached https://files.pythonhosted.org/packages/6b/63/42329a3e503366be2be68384336db308a795516b362437368ddd82d3368f/intervaltree-3.0.1.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\gary\AppData\Local\Temp\pip-install-kcs7dz4o\intervaltree\setup.py", line 60, in <module>
        long_description = fh.read()
    UnicodeDecodeError: 'cp950' codec can't decode byte 0xe2 in position 5223: illegal multibyte sequence
    !!!>>> This is a RELEASE version <<<!!!

    Version: 3.0.1

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in C:\Users\gary\AppData\Local\Temp\pip-install-kcs7dz4o\intervaltree\



Comments:

Patch:
diff --git a/intervaltree/interval.py b/intervaltree/interval.py
index f38627b..865cca7 100644
--- a/intervaltree/interval.py
+++ b/intervaltree/interval.py
@@ -53,6 +53,29 @@ class Interval(namedtuple('IntervalBase', ['begin', 'end', 'data'])):
         except:
             return self.contains_point(begin)
 
+    def overlap_size(self, begin, end=None):
+        """
+        Return the overlap size between two intervals or a point
+        :param begin: beginning point of the range, or the point, or an Interval
+        :param end: end point of the range. Optional if not testing ranges.
+        :return: Return the overlap size, None if not overlap is found
+        :rtype: depends on the given input (e.g., int will be returned for int interval and timedelta for
+        datetime intervals)
+        """
+        overlaps = self.overlaps(begin, end)
+        if not overlaps:
+            return 0
+
+        if end is not None:
+            # case end is given
+            i0 = max(self.begin, begin)
+            i1 = min(self.end, end)
+            return i1 - i0
+        # assume the type is interval, in other cases, an exception will be thrown
+        i0 = max(self.begin, begin.begin)
+        i1 = min(self.end, begin.end)
+        return i1 - i0
+
     def contains_point(self, p):
         """
         Whether the Interval contains p.
diff --git a/intervaltree/node.py b/intervaltree/node.py
index 7b94406..ff04959 100644
--- a/intervaltree/node.py
+++ b/intervaltree/node.py
@@ -58,13 +58,22 @@ class Node(object):
 
     @classmethod
     def from_intervals(cls, intervals):
+        """
+        :rtype : Node
+        """
+        if not intervals:
+            return None
+        return Node.from_sorted_intervals(sorted(intervals))
+
+    @classmethod
+    def from_sorted_intervals(cls, intervals):
         """
         :rtype : Node
         """
         if not intervals:
             return None
         node = Node()
-        node = node.init_from_sorted(sorted(intervals))
+        node = node.init_from_sorted(intervals)
         return node
 
     def init_from_sorted(self, intervals):
@@ -82,8 +91,8 @@ class Node(object):
                 s_right.append(k)
             else:
                 self.s_center.add(k)
-        self.left_node = Node.from_intervals(s_left)
-        self.right_node = Node.from_intervals(s_right)
+        self.left_node = Node.from_sorted_intervals(s_left)
+        self.right_node = Node.from_sorted_intervals(s_right)
         return self.rotate()
 
     def center_hit(self, interval):
diff --git a/setup.py b/setup.py
index 1afc8e3..32bc151 100644
--- a/setup.py
+++ b/setup.py
@@ -33,7 +33,7 @@ from setuptools.command.test import test as TestCommand
 import subprocess
 
 ## CONFIG
-target_version = '3.0.2'
+target_version = '3.0.3'
 
 
 def version_info(target_version):


Test patch:
diff --git a/test/interval_methods/binary_test.py b/test/interval_methods/binary_test.py
index 4b8451b..68ef212 100644
--- a/test/interval_methods/binary_test.py
+++ b/test/interval_methods/binary_test.py
@@ -36,7 +36,20 @@ iv9 = Interval(15, 20)
 iv10 = Interval(-5, 0)
 
 
-def test_interval_overlaps_interval():
+def test_interval_overlaps_size_interval():
+    assert iv0.overlap_size(iv0) == 10
+    assert not iv0.overlap_size(iv1)
+    assert not iv0.overlap_size(iv2)
+    assert iv0.overlap_size(iv3) == 5
+    assert iv0.overlap_size(iv4) == 10
+    assert iv0.overlap_size(iv5) == 10
+    assert iv0.overlap_size(iv6) == 10
+    assert iv0.overlap_size(iv7) == 5
+    assert not iv0.overlap_size(iv8)
+    assert not iv0.overlap_size(iv9)
+
+
+def test_interval_overlap_interval():
     assert iv0.overlaps(iv0)
     assert not iv0.overlaps(iv1)
     assert not iv0.overlaps(iv2)
diff --git a/test/issues/issue67_test.py b/test/issues/issue67_test.py
index 5097bb6..d19363a 100644
--- a/test/issues/issue67_test.py
+++ b/test/issues/issue67_test.py
@@ -25,7 +25,7 @@ from __future__ import absolute_import
 from intervaltree import IntervalTree
 import pytest
 
-def test_interval_insersion_67():
+def test_interval_insertion_67():
     intervals = (
         (3657433088, 3665821696),
         (2415132672, 2415394816),
diff --git a/test/issues/issue72_test.py b/test/issues/issue72_test.py
new file mode 100644
index 0000000..3c96bb1
--- /dev/null
+++ b/test/issues/issue72_test.py
@@ -0,0 +1,42 @@
+"""
+intervaltree: A mutable, self-balancing interval tree for Python 2 and 3.
+Queries may be by point, by range overlap, or by range envelopment.
+
+Test module: IntervalTree, remove_overlap caused incorrect balancing
+where intervals overlapping an ancestor's x_center were buried too deep.
+Submitted as issue #72 (KeyError raised after calling remove_overlap)
+by alexandersoto
+
+Copyright 2013-2018 Chaim Leib Halbert
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+from __future__ import absolute_import
+from intervaltree import IntervalTree, Interval
+import pytest
+
+def test_interval_removal_72():
+    tree = IntervalTree([
+        Interval(0.0, 2.588, 841),
+        Interval(65.5, 85.8, 844),
+        Interval(93.6, 130.0, 837),
+        Interval(125.0, 196.5, 829),
+        Interval(391.8, 521.0, 825),
+        Interval(720.0, 726.0, 834),
+        Interval(800.0, 1033.0, 850),
+        Interval(800.0, 1033.0, 855),
+    ])
+    tree.verify()
+    tree.remove_overlap(0.0, 521.0)
+    tree.verify()
+