8000 Merge pull request #10621 from ahaldane/fix_arrayprint_recursive_closure · numpy/numpy@71555c4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 71555c4

Browse files
authored
Merge pull request #10621 from ahaldane/fix_arrayprint_recursive_closure
BUG: deallocate recursive closure in arrayprint.py
2 parents bd7919a + 50fde71 commit 71555c4

File tree

4 files changed

+37
-9
lines changed

4 files changed

+37
-9
lines changed

numpy/core/arrayprint.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,8 @@ def recurser(index, hanging_indent, curr_width):
772772
s += hanging_indent + summary_insert + line_sep
773773

774774
for i in range(trailing_items, 1, -1):
775-
nested = recurser(index + (-i,), next_hanging_indent, next_width)
775+
nested = recurser(index + (-i,), next_hanging_indent,
776+
next_width)
776777
s += hanging_indent + nested + line_sep
777778

778779
nested = recurser(index + (-1,), next_hanging_indent, next_width)
@@ -782,12 +783,16 @@ def recurser(index, hanging_indent, curr_width):
782783
s = '[' + s[len(hanging_indent):] + ']'
783784
return s
784785

785-
# invoke the recursive part with an initial index and prefix
786-
return recurser(
787-
index=(),
788-
hanging_indent=next_line_prefix,
789-
curr_width=line_width)
790-
786+
try:
787+
# invoke the recursive part with an initial index and prefix
788+
return recurser(index=(),
789+
hanging_indent=next_line_prefix,
790+
curr_width=line_width)
791+
finally:
792+
# recursive closures have a cyclic reference to themselves, which
793+
# requires gc to collect (gh-10620). To avoid this problem, for
794+
# performance and PyPy friendliness, we break the cycle:
795+
recurser = None
791796

792797
def _none_or_positive_arg(x, name):
793798
if x is None:

numpy/core/shape_base.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,13 @@ def block_recursion(arrays, depth=0):
446446
# type(arrays) is not list
447447
return atleast_nd(arrays, result_ndim)
448448

449-
return block_recursion(arrays)
449+
try:
450+
return block_recursion(arrays)
451+
finally:
452+
# recursive closures have a cyclic reference to themselves, which
453+
# requires gc to collect (gh-10620). To avoid this problem, for
454+
# performance and PyPy friendliness, we break the cycle:
455+
block_recursion = None
450456

451457

452458
def block(arrays):

numpy/core/tests/test_arrayprint.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import division, absolute_import, print_function
33

4-
import sys
4+
import sys, gc
55

66
import numpy as np
77
from numpy.testing import (
@@ -355,6 +355,18 @@ def test_wide_element(self):
355355
"[ 'xxxxx']"
356356
)
357357

358+
def test_refcount(self):
359+
# make sure we do not hold references to the array due to a recursive
360+
# closure (gh-10620)
361+
gc.disable()
362+
a = np.arange(2)
363+
r1 = sys.getrefcount(a)
364+
np.array2string(a)
365+
np.array2string(a)
366+
r2 = sys.getrefcount(a)
367+
gc.collect()
368+
gc.enable()
369+
assert_(r1 == r2)
358370

359371
class TestPrintOptions(object):
360372
"""Test getting and setting global print options."""

numpy/lib/npyio.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,11 @@ def tobytes_first(x, conv):
11091109
finally:
11101110
if fown:
11111111
fh.close()
1112+
# recursive closures have a cyclic reference to themselves, which
1113+
# requires gc to collect (gh-10620). To avoid this problem, for
1114+
# performance and PyPy friendliness, we break the cycle:
1115+
flatten_dtype_internal = None
1116+
pack_items = None
11121117

11131118
if X is None:
11141119
X = np.array([], dtype)

0 commit comments

Comments
 (0)
0