@@ -148,22 +148,25 @@ AK_ResolveDTypeIter(PyObject *dtypes)
148148 return resolved ;
149149}
150150
151- // Numpy implementation: https://github.com/numpy/numpy/blob/a14c41264855e44ebd6187d7541b5b8d59bb32cb/numpy/core/src/multiarray/methods.c#L1557
151+ // Perform a deepcopy on an array, using an optional memo dictionary, and specialized to depend on immutable arrays. Related Numpy implementation: https://github.com/numpy/numpy/blob/a14c41264855e44ebd6187d7541b5b8d59bb32cb/numpy/core/src/multiarray/methods.c#L1557
152152PyObject *
153153AK_ArrayDeepCopy (PyArrayObject * array , PyObject * memo )
154154{
155155 PyObject * id = PyLong_FromVoidPtr ((PyObject * )array );
156156 if (!id ) {
157157 return NULL ;
158158 }
159- PyObject * found = PyDict_GetItemWithError (memo , id );
160- if (found ) { // found will be NULL if not in dict
161- Py_INCREF (found ); // got a borrowed ref, increment first
162- Py_DECREF (id );
163- return found ;
164- }
165- else if (PyErr_Occurred ()) {
166- goto error ;
159+
160+ if (memo ) {
161+ PyObject * found = PyDict_GetItemWithError (memo , id );
162+ if (found ) { // found will be NULL if not in dict
163+ Py_INCREF (found ); // got a borrowed ref, increment first
164+ Py_DECREF (id );
165+ return found ;
166+ }
167+ else if (PyErr_Occurred ()) {
168+ goto error ;
169+ }
167170 }
168171
169172 // if dtype is object, call deepcopy with memo
@@ -187,14 +190,17 @@ AK_ArrayDeepCopy(PyArrayObject *array, PyObject *memo)
187190 }
188191 }
189192 else {
193+ // if not a n object dtype, we will force a copy (even if this is an immutable array) so as to not hold on to any references
190194 Py_INCREF (dtype ); // PyArray_FromArray steals a reference
191195 array_new = PyArray_FromArray (
192196 array ,
193197 dtype ,
194198 NPY_ARRAY_ENSURECOPY );
195- if (!array_new || PyDict_SetItem (memo , id , array_new )) {
196- Py_XDECREF (array_new );
197- goto error ;
199+ if (memo ) {
200+ if (!array_new || PyDict_SetItem (memo , id , array_new )) {
201+ Py_XDECREF (array_new );
202+ goto error ;
203+ }
198204 }
199205 }
200206 // set immutable
@@ -311,20 +317,25 @@ row_1d_filter(PyObject *Py_UNUSED(m), PyObject *a)
311317//------------------------------------------------------------------------------
312318// array utility
313319
314- // Specialized array deepcopy that stores immutable arrays in memo dict.
320+ static char * array_deepcopy_kwarg_names [] = {
321+ "array" ,
322+ "memo" ,
323+ NULL
324+ };
325+
326+ // Specialized array deepcopy that stores immutable arrays in an optional memo dict that can be provided with kwargs.
315327static PyObject *
316- array_deepcopy (PyObject * Py_UNUSED (m ), PyObject * args )
328+ array_deepcopy (PyObject * Py_UNUSED (m ), PyObject * args , PyObject * kwargs )
317329{
318- PyObject * array , * memo ;
319- if (!PyArg_UnpackTuple (args , "array_deepcopy" , 2 , 2 , & array , & memo )) {
330+ PyObject * array ;
331+ PyObject * memo = NULL ;
332+ if (!PyArg_ParseTupleAndKeywords (args , kwargs ,
333+ "O|O!:array_deepcopy" , array_deepcopy_kwarg_names ,
334+ & array ,
335+ & PyDict_Type , & memo )) {
320336 return NULL ;
321337 }
322338 AK_CHECK_NUMPY_ARRAY (array );
323- if (!PyDict_CheckExact (memo )) {
324- PyErr_Format (PyExc_TypeError , "expected a dict (got %s)" ,
325- Py_TYPE (memo )-> tp_name );
326- return NULL ;
327- }
328339 return AK_ArrayDeepCopy ((PyArrayObject * )array , memo );
329340}
330341
@@ -623,7 +634,10 @@ static PyMethodDef arraykit_methods[] = {
623634 {"column_2d_filter" , column_2d_filter , METH_O , NULL },
624635 {"column_1d_filter" , column_1d_filter , METH_O , NULL },
625636 {"row_1d_filter" , row_1d_filter , METH_O , NULL },
626- {"array_deepcopy" , array_deepcopy , METH_VARARGS , NULL },
637+ {"array_deepcopy" ,
638+ (PyCFunction )array_deepcopy ,
639+ METH_VARARGS | METH_KEYWORDS ,
640+ NULL },
627641 {"resolve_dtype" , resolve_dtype , METH_VARARGS , NULL },
628642 {"resolve_dtype_iter" , resolve_dtype_iter , METH_O , NULL },
629643 {NULL },
0 commit comments