def release(self): """Releases any past acquired locks (partial or otherwise).""" height = len(self._lock_stacks) if not height: # Raise the same error type as the threading.Lock raises so that # it matches the behavior of the built-in class (it's odd though # that the threading.RLock raises a runtime error on this same # method instead...) raise threading.ThreadError('Release attempted on unlocked lock') # Cleans off one level of the stack (this is done so that if there # are multiple __enter__() and __exit__() pairs active that this will # only remove one level (the last one), and not all levels... for left in misc.countdown_iter(self._lock_stacks[-1]): lock_idx = left - 1 lock = self._locks[lock_idx] try: lock.release() except (threading.ThreadError, RuntimeError) as e: # Ensure that we adjust the lock stack under failure so that # if release is attempted again that we do not try to release # the locks we already released... self._lock_stacks[-1] = left raise threading.ThreadError( "Unable to release lock %s/%s due to '%s'" % (left, len(self._locks), e)) # At the end only clear it off, so that under partial failure we don't # lose any locks... self._lock_stacks.pop()
def test_expected_count(self): upper = 100 it = misc.countdown_iter(upper) items = [] for i in it: self.assertEqual(upper, i) upper -= 1 items.append(i) self.assertEqual(0, upper) self.assertEqual(100, len(items))
def test_expected_count_custom_decr(self): upper = 100 it = misc.countdown_iter(upper, decr=2) items = [] for i in it: self.assertEqual(upper, i) upper -= 2 items.append(i) self.assertEqual(0, upper) self.assertEqual(50, len(items))
def test_invalid_decr(self): it = misc.countdown_iter(10, -1) self.assertRaises(ValueError, six.next, it)
def test_no_count(self): it = misc.countdown_iter(0) self.assertEqual(0, len(list(it))) it = misc.countdown_iter(-1) self.assertEqual(0, len(list(it)))