Repository: seung-lab/cloud-volume

Issue:
Writing to volume (by slicing notation) is not properly checking slice.start < slice.stop
cv[(slice(0,1),slice(0,1),slice(779,344))] = 0  appears to clear out 344:779 rather than the expected behavior of not writing anything because the effective range is empty.

Discussion:
william-silversmith: @shangmu what do you think of this fix? Originally I wanted people to specify bbox via any corners of the bounding box, but it seems like slice notation has additional semantics that make this approach non-sensical, so I added special casing for it.

Patch:
diff --git a/cloudvolume/frontends/precomputed.py b/cloudvolume/frontends/precomputed.py
index 807561d..6717c5e 100644
--- a/cloudvolume/frontends/precomputed.py
+++ b/cloudvolume/frontends/precomputed.py
@@ -522,6 +522,7 @@ class CloudVolumePrecomputed(object):
 
     slices = self.meta.bbox(self.mip).reify_slices(slices, bounded=self.bounded)
     steps = Vec(*[ slc.step for slc in slices ])
+    slices = [ slice(slc.start, slc.stop) for slc in slices ]
     channel_slice = slices.pop()
     requested_bbox = Bbox.from_slices(slices)
 
diff --git a/cloudvolume/lib.py b/cloudvolume/lib.py
index e3d00fb..2fae1f0 100644
--- a/cloudvolume/lib.py
+++ b/cloudvolume/lib.py
@@ -403,9 +403,13 @@ class Bbox(object):
         slices, bounded=bounded, autocrop=autocrop
       )
 
+    for slc in slices:
+      if slc.step not in (None, 1):
+        raise ValueError("Non-unitary steps are unsupported. Got: " + str(slc.step))
+
     return Bbox(
       [ slc.start for slc in slices ],
-      [ slc.stop for slc in slices ]
+      [ (slc.start if slc.stop < slc.start else slc.stop) for slc in slices ]
     )
 
   @classmethod


Test patch:
diff --git a/test/test_lib.py b/test/test_lib.py
index c175b5b..0813598 100644
--- a/test/test_lib.py
+++ b/test/test_lib.py
@@ -101,6 +101,21 @@ def test_bbox_division():
   bbox /= 3.0 
   assert np.all(x == point333)
 
+def test_bbox_slicing():
+  bbx_rect = Bbox.from_slices(np.s_[1:10,1:10,1:10])
+  bbx_plane = Bbox.from_slices(np.s_[1:10,10:1,1:10])
+
+  assert bbx_rect == Bbox((1,1,1), (10,10,10))
+  assert bbx_plane == Bbox((1,10,1), (10, 10, 10))
+
+  try:
+    bbx_plane = Bbox.from_slices(np.s_[1:10,10:1:-1,1:10])
+    assert False
+  except ValueError:
+    pass
+
+  bbx_plane = Bbox.from_slices(np.s_[1:10,10:1:1,1:10])
+  assert bbx_plane == Bbox((1,10,1), (10, 10, 10))
 
 
 def test_bbox_intersection():