summaryrefslogtreecommitdiff
path: root/numpy/testing/tests/test_utils.py
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2018-04-12 22:07:58 -0700
committerEric Wieser <wieser.eric@gmail.com>2018-04-15 23:39:49 -0700
commit3ff0c5c82b8abc4c94b1801a13f488778631f38a (patch)
tree8068b5c4c11e212559427965505f6f6983ed98cf /numpy/testing/tests/test_utils.py
parentd21ec05eb006c072e4fd8c5fe1bd63619378aded (diff)
downloadnumpy-3ff0c5c82b8abc4c94b1801a13f488778631f38a.tar.gz
BUG: Ensure the garbage is clear first in assert_no_gc_cycles
It's not always possible to guarantee this, so also adds a test to verify that we don't hang
Diffstat (limited to 'numpy/testing/tests/test_utils.py')
-rw-r--r--numpy/testing/tests/test_utils.py83
1 files changed, 65 insertions, 18 deletions
diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py
index 52726db6e..0592e62f8 100644
--- a/numpy/testing/tests/test_utils.py
+++ b/numpy/testing/tests/test_utils.py
@@ -6,6 +6,7 @@ import os
import itertools
import textwrap
import pytest
+import weakref
import numpy as np
from numpy.testing import (
@@ -1363,27 +1364,73 @@ def test_clear_and_catch_warnings_inherit():
@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts")
-def test_assert_no_gc_cycles():
+class TestAssertNoGcCycles(object):
+ """ Test assert_no_gc_cycles """
+ def test_passes(self):
+ def no_cycle():
+ b = []
+ b.append([])
+ return b
- def no_cycle():
- b = []
- b.append([])
- return b
+ with assert_no_gc_cycles():
+ no_cycle()
- with assert_no_gc_cycles():
- no_cycle()
+ assert_no_gc_cycles(no_cycle)
- assert_no_gc_cycles(no_cycle)
- def make_cycle():
- a = []
- a.append(a)
- a.append(a)
- return a
+ def test_asserts(self):
+ def make_cycle():
+ a = []
+ a.append(a)
+ a.append(a)
+ return a
- with assert_raises(AssertionError):
- with assert_no_gc_cycles():
- make_cycle()
+ with assert_raises(AssertionError):
+ with assert_no_gc_cycles():
+ make_cycle()
+
+ with assert_raises(AssertionError):
+ assert_no_gc_cycles(make_cycle)
+
+
+ def test_fails(self):
+ """
+ Test that in cases where the garbage cannot be collected, we raise an
+ error, instead of hanging forever trying to clear it.
+ """
+
+ class ReferenceCycleInDel(object):
+ """
+ An object that not only contains a reference cycle, but creates new
+ cycles whenever it's garbage-collected and its __del__ runs
+ """
+ make_cycle = True
- with assert_raises(AssertionError):
- assert_no_gc_cycles(make_cycle)
+ def __init__(self):
+ self.cycle = self
+
+ def __del__(self):
+ # break the current cycle so that `self` can be freed
+ self.cycle = None
+
+ if ReferenceCycleInDel.make_cycle:
+ # but create a new one so that the garbage collector has more
+ # work to do.
+ ReferenceCycleInDel()
+
+ try:
+ w = weakref.ref(ReferenceCycleInDel())
+ try:
+ with assert_raises(RuntimeError):
+ # this will be unable to get a baseline empty garbage
+ assert_no_gc_cycles(lambda: None)
+ except AssertionError:
+ # the above test is only necessary if the GC actually tried to free
+ # our object anyway, which python 2.7 does not.
+ if w() is not None:
+ pytest.skip("GC does not call __del__ on cyclic objects")
+ raise
+
+ finally:
+ # make sure that we stop creating reference cycles
+ ReferenceCycleInDel.make_cycle = False