Skip to content

Commit ee6a23b

Browse files
authored
DEP: remove allocation_tracking, deprecate PyDataMem_SetEventHook (numpy#20394)
* DEP: remove allocation_tracking * DOC: add release note * DEP: deprecate PyDataMem_SetEventHook * DOC: fix name of release note * fixes from review * DOC: document deprecation of PyDataMem_EventHookFunc
1 parent 258ce25 commit ee6a23b

File tree

10 files changed

+63
-730
lines changed

10 files changed

+63
-730
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Deprecate PyDataMem_SetEventHook
2+
--------------------------------
3+
4+
The ability to track allocations is now built-in to python via ``tracemalloc``.
5+
The hook function ``PyDataMem_SetEventHook`` has been deprecated and the
6+
demonstration of its use in tool/allocation_tracking has been removed.

doc/source/reference/c-api/data_memory.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ Historical overview
2020
Since version 1.7.0, NumPy has exposed a set of ``PyDataMem_*`` functions
2121
(:c:func:`PyDataMem_NEW`, :c:func:`PyDataMem_FREE`, :c:func:`PyDataMem_RENEW`)
2222
which are backed by `alloc`, `free`, `realloc` respectively. In that version
23-
NumPy also exposed the `PyDataMem_EventHook` function described below, which
24-
wrap the OS-level calls.
23+
NumPy also exposed the `PyDataMem_EventHook` function (now deprecated)
24+
described below, which wrap the OS-level calls.
2525

2626
Since those early days, Python also improved its memory management
2727
capabilities, and began providing
@@ -50,10 +50,10 @@ management routines can use :c:func:`PyDataMem_SetHandler`, which uses a
5050
:c:type:`PyDataMem_Handler` structure to hold pointers to functions used to
5151
manage the data memory. The calls are still wrapped by internal routines to
5252
call :c:func:`PyTraceMalloc_Track`, :c:func:`PyTraceMalloc_Untrack`, and will
53-
use the :c:func:`PyDataMem_EventHookFunc` mechanism. Since the functions may
54-
change during the lifetime of the process, each ``ndarray`` carries with it the
55-
functions used at the time of its instantiation, and these will be used to
56-
reallocate or free the data memory of the instance.
53+
use the deprecated :c:func:`PyDataMem_EventHookFunc` mechanism. Since the
54+
functions may change during the lifetime of the process, each ``ndarray``
55+
carries with it the functions used at the time of its instantiation, and these
56+
will be used to reallocate or free the data memory of the instance.
5757

5858
.. c:type:: PyDataMem_Handler
5959
@@ -119,7 +119,9 @@ For an example of setting up and using the PyDataMem_Handler, see the test in
119119
thread. The hook should be written to be reentrant, if it performs
120120
operations that might cause new allocation events (such as the
121121
creation/destruction numpy objects, or creating/destroying Python
122-
objects which might cause a gc)
122+
objects which might cause a gc).
123+
124+
Deprecated in v1.23
123125
124126
What happens when deallocating if there is no policy set
125127
--------------------------------------------------------

numpy/core/src/multiarray/alloc.c

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,24 @@ npy_free_cache_dim(void * p, npy_uintp sz)
186186
&PyArray_free);
187187
}
188188

189+
/* Similar to array_dealloc in arrayobject.c */
190+
static NPY_INLINE void
191+
WARN_NO_RETURN(PyObject* warning, const char * msg) {
192+
if (PyErr_WarnEx(warning, msg, 1) < 0) {
193+
PyObject * s;
194+
195+
s = PyUnicode_FromString("PyDataMem_UserFREE");
196+
if (s) {
197+
PyErr_WriteUnraisable(s);
198+
Py_DECREF(s);
199+
}
200+
else {
201+
PyErr_WriteUnraisable(Py_None);
202+
}
203+
}
204+
}
205+
206+
189207

190208
/* malloc/free/realloc hook */
191209
NPY_NO_EXPORT PyDataMem_EventHookFunc *_PyDataMem_eventhook = NULL;
@@ -210,6 +228,8 @@ NPY_NO_EXPORT void *_PyDataMem_eventhook_user_data = NULL;
210228
* operations that might cause new allocation events (such as the
211229
* creation/destruction numpy objects, or creating/destroying Python
212230
* objects which might cause a gc)
231+
*
232+
* Deprecated in 1.23
213233
*/
214234
NPY_NO_EXPORT PyDataMem_EventHookFunc *
215235
PyDataMem_SetEventHook(PyDataMem_EventHookFunc *newhook,
@@ -218,6 +238,10 @@ PyDataMem_SetEventHook(PyDataMem_EventHookFunc *newhook,
218238
PyDataMem_EventHookFunc *temp;
219239
NPY_ALLOW_C_API_DEF
220240
NPY_ALLOW_C_API
241+
/* 2021-11-18, 1.23 */
242+
WARN_NO_RETURN(PyExc_DeprecationWarning,
243+
"PyDataMem_SetEventHook is deprecated, use tracemalloc "
244+
"and the 'np.lib.tracemalloc_domain' domain");
221245
temp = _PyDataMem_eventhook;
222246
_PyDataMem_eventhook = newhook;
223247
if (old_data != NULL) {
@@ -435,33 +459,14 @@ PyDataMem_UserNEW_ZEROED(size_t nmemb, size_t size, PyObject *mem_handler)
435459
return result;
436460
}
437461

438-
/* Similar to array_dealloc in arrayobject.c */
439-
static NPY_INLINE void
440-
WARN_IN_FREE(PyObject* warning, const char * msg) {
441-
if (PyErr_WarnEx(warning, msg, 1) < 0) {
442-
PyObject * s;
443-
444-
s = PyUnicode_FromString("PyDataMem_UserFREE");
445-
if (s) {
446-
PyErr_WriteUnraisable(s);
447-
Py_DECREF(s);
448-
}
449-
else {
450-
PyErr_WriteUnraisable(Py_None);
451-
}
452-
}
453-
}
454-
455-
456462

457463
NPY_NO_EXPORT void
458464
PyDataMem_UserFREE(void *ptr, size_t size, PyObject *mem_handler)
459465
{
460466
PyDataMem_Handler *handler = (PyDataMem_Handler *) PyCapsule_GetPointer(mem_handler, "mem_handler");
461467
if (handler == NULL) {
462-
WARN_IN_FREE(PyExc_RuntimeWarning,
468+
WARN_NO_RETURN(PyExc_RuntimeWarning,
463469
"Could not get pointer to 'mem_handler' from PyCapsule");
464-
PyErr_Clear();
465470
return;
466471
}
467472
PyTraceMalloc_Untrack(NPY_TRACE_DOMAIN, (npy_uintp)ptr);

numpy/core/tests/test_deprecations.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
import numpy as np
1515
from numpy.testing import (
16-
assert_raises, assert_warns, assert_, assert_array_equal, SkipTest, KnownFailureException
16+
assert_raises, assert_warns, assert_, assert_array_equal, SkipTest,
17+
KnownFailureException, break_cycles,
1718
)
1819

1920
from numpy.core._multiarray_tests import fromstring_null_term_c_api
@@ -1250,3 +1251,22 @@ def test_both_passed(self, func):
12501251
warnings.simplefilter("always", DeprecationWarning)
12511252
with pytest.raises(TypeError):
12521253
func([0., 1.], 0., interpolation="nearest", method="nearest")
1254+
1255+
1256+
class TestMemEventHook(_DeprecationTestCase):
1257+
# Deprecated 2021-11-18, NumPy 1.23
1258+
def test_mem_seteventhook(self):
1259+
# The actual tests are within the C code in
1260+
# multiarray/_multiarray_tests.c.src
1261+
import numpy.core._multiarray_tests as ma_tests
1262+
with pytest.warns(DeprecationWarning,
1263+
match='PyDataMem_SetEventHook is deprecated'):
1264+
ma_tests.test_pydatamem_seteventhook_start()
1265+
# force an allocation and free of a numpy array
1266+
# needs to be larger then limit of small memory cacher in ctors.c
1267+
a = np.zeros(1000)
1268+
del a
1269+
break_cycles()
1270+
with pytest.warns(DeprecationWarning,
1271+
match='PyDataMem_SetEventHook is deprecated'):
1272+
ma_tests.test_pydatamem_seteventhook_end()

numpy/core/tests/test_multiarray.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8187,18 +8187,6 @@ def test_scalar_element_deletion():
81878187
assert_raises(ValueError, a[0].__delitem__, 'x')
81888188

81898189

8190-
class TestMemEventHook:
8191-
def test_mem_seteventhook(self):
8192-
# The actual tests are within the C code in
8193-
# multiarray/_multiarray_tests.c.src
8194-
_multiarray_tests.test_pydatamem_seteventhook_start()
8195-
# force an allocation and free of a numpy array
8196-
# needs to be larger then limit of small memory cacher in ctors.c
8197-
a = np.zeros(1000)
8198-
del a
8199-
break_cycles()
8200-
_multiarray_tests.test_pydatamem_seteventhook_end()
8201-
82028190
class TestMapIter:
82038191
def test_mapiter(self):
82048192
# The actual tests are within the C code in

tools/allocation_tracking/README.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
Example for using the `PyDataMem_SetEventHook` to track allocations inside numpy.
2-
3-
`alloc_hook.pyx` implements a hook in Cython that calls back into a python
4-
function. `track_allocations.py` uses it for a simple listing of allocations.
5-
It can be built with the `setup.py` file in this folder.
6-
71
Note that since Python 3.6 the builtin tracemalloc module can be used to
82
track allocations inside numpy.
93
Numpy places its CPU memory allocations into the `np.lib.tracemalloc_domain`
104
domain.
115
See https://docs.python.org/3/library/tracemalloc.html.
6+
7+
The tool that used to be here has been deprecated.

tools/allocation_tracking/alloc_hook.pyx

Lines changed: 0 additions & 42 deletions
This file was deleted.

tools/allocation_tracking/setup.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)