1 #include "_internal.h"
2 #include "Python.h"
3 
4 /* A small object that handles deallocation of some of a PyUFunc's fields */
5 typedef struct {
6     PyObject_HEAD
7     /* Borrowed reference */
8     PyUFuncObject *ufunc;
9     /* Owned reference to ancillary object */
10     PyObject *object;
11 } PyUFuncCleaner;
12 
13 PyTypeObject PyUFuncCleaner_Type;
14 
15 
16 static PyObject *
cleaner_new(PyUFuncObject * ufunc,PyObject * object)17 cleaner_new(PyUFuncObject *ufunc, PyObject *object)
18 {
19     PyUFuncCleaner *obj = PyObject_New(PyUFuncCleaner, &PyUFuncCleaner_Type);
20     if (obj != NULL) {
21         obj->ufunc = ufunc;
22         Py_XINCREF(object);
23         obj->object = object;
24     }
25     return (PyObject *) obj;
26 }
27 
28 /* Deallocate the PyArray_malloc calls */
29 static void
cleaner_dealloc(PyUFuncCleaner * self)30 cleaner_dealloc(PyUFuncCleaner *self)
31 {
32     PyUFuncObject *ufunc = self->ufunc;
33     Py_XDECREF(self->object);
34     if (ufunc->functions)
35         PyArray_free(ufunc->functions);
36     if (ufunc->types)
37         PyArray_free(ufunc->types);
38     if (ufunc->data)
39         PyArray_free(ufunc->data);
40     PyObject_Del(self);
41 }
42 
43 PyTypeObject PyUFuncCleaner_Type = {
44     PyVarObject_HEAD_INIT(NULL, 0)
45     "numba._UFuncCleaner",                      /* tp_name*/
46     sizeof(PyUFuncCleaner),                     /* tp_basicsize*/
47     0,                                          /* tp_itemsize */
48     /* methods */
49     (destructor) cleaner_dealloc,               /* tp_dealloc */
50     0,                                          /* tp_print */
51     0,                                          /* tp_getattr */
52     0,                                          /* tp_setattr */
53 #if defined(NPY_PY3K)
54     0,                                          /* tp_reserved */
55 #else
56     0,                                          /* tp_compare */
57 #endif
58     0,                                          /* tp_repr */
59     0,                                          /* tp_as_number */
60     0,                                          /* tp_as_sequence */
61     0,                                          /* tp_as_mapping */
62     0,                                          /* tp_hash */
63     0,                                          /* tp_call */
64     0,                                          /* tp_str */
65     0,                                          /* tp_getattro */
66     0,                                          /* tp_setattro */
67     0,                                          /* tp_as_buffer */
68     Py_TPFLAGS_DEFAULT,                         /* tp_flags */
69     0,                                          /* tp_doc */
70     0,                                          /* tp_traverse */
71     0,                                          /* tp_clear */
72     0,                                          /* tp_richcompare */
73     0,                                          /* tp_weaklistoffset */
74     0,                                          /* tp_iter */
75     0,                                          /* tp_iternext */
76     0,                                          /* tp_methods */
77     0,                                          /* tp_members */
78     0,                                          /* tp_getset */
79     0,                                          /* tp_base */
80     0,                                          /* tp_dict */
81     0,                                          /* tp_descr_get */
82     0,                                          /* tp_descr_set */
83     0,                                          /* tp_dictoffset */
84     0,                                          /* tp_init */
85     0,                                          /* tp_alloc */
86     0,                                          /* tp_new */
87     0,                                          /* tp_free */
88     0,                                          /* tp_is_gc */
89     0,                                          /* tp_bases */
90     0,                                          /* tp_mro */
91     0,                                          /* tp_cache */
92     0,                                          /* tp_subclasses */
93     0,                                          /* tp_weaklist */
94     0,                                          /* tp_del */
95     0,                                          /* tp_version_tag */
96 };
97 
98 /* ______________________________________________________________________
99  * DUFunc: A call-time (hence dynamic) specializable ufunc.
100  */
101 
102 typedef struct {
103     PyObject_HEAD
104     PyObject      * dispatcher;
105     PyUFuncObject * ufunc;
106     PyObject      * keepalive;
107     int             frozen;
108 } PyDUFuncObject;
109 
110 static void
dufunc_dealloc(PyDUFuncObject * self)111 dufunc_dealloc(PyDUFuncObject *self)
112 {
113     /* Note: There is no need to call PyArray_free() on
114        self->ufunc->ptr, since ufunc_dealloc() will do it for us. */
115     Py_XDECREF(self->ufunc);
116     Py_XDECREF(self->dispatcher);
117     Py_XDECREF(self->keepalive);
118     Py_TYPE(self)->tp_free((PyObject *)self);
119 }
120 
121 static PyObject *
dufunc_repr(PyDUFuncObject * dufunc)122 dufunc_repr(PyDUFuncObject *dufunc)
123 {
124     return PyString_FromFormat("<numba._DUFunc '%s'>", dufunc->ufunc->name);
125 }
126 
127 static PyObject *
dufunc_call(PyDUFuncObject * self,PyObject * args,PyObject * kws)128 dufunc_call(PyDUFuncObject *self, PyObject *args, PyObject *kws)
129 {
130     PyObject *result=NULL, *method=NULL;
131 
132     result = PyUFunc_Type.tp_call((PyObject *)self->ufunc, args, kws);
133     if ((!self->frozen) &&
134             (result == NULL) &&
135             (PyErr_Occurred()) &&
136             (PyErr_ExceptionMatches(PyExc_TypeError))) {
137 
138         /* Break back into Python when we fail at dispatch. */
139         PyErr_Clear();
140         method = PyObject_GetAttrString((PyObject*)self, "_compile_for_args");
141 
142         if (method) {
143             result = PyObject_Call(method, args, kws);
144             if (result) {
145                 Py_DECREF(result);
146                 result = PyUFunc_Type.tp_call((PyObject *)self->ufunc, args,
147                                               kws);
148             }
149         }
150         Py_XDECREF(method);
151     }
152     return result;
153 }
154 
155 static Py_ssize_t
_get_nin(PyObject * py_func_obj)156 _get_nin(PyObject * py_func_obj)
157 {
158     int result = -1;
159     PyObject *inspect=NULL, *getargspec=NULL, *argspec=NULL, *args=NULL;
160 
161     inspect = PyImport_ImportModule("inspect");
162     if (!inspect) goto _get_nin_cleanup;
163     getargspec = PyObject_GetAttrString(inspect, "getfullargspec");
164     if (!getargspec) goto _get_nin_cleanup;
165     argspec = PyObject_CallFunctionObjArgs(getargspec, py_func_obj, NULL);
166     if (!argspec) goto _get_nin_cleanup;
167     args = PyObject_GetAttrString(argspec, "args");
168     if (!args) goto _get_nin_cleanup;
169     result = PyList_Size(args);
170 
171   _get_nin_cleanup:
172     Py_XDECREF(args);
173     Py_XDECREF(argspec);
174     Py_XDECREF(getargspec);
175     Py_XDECREF(inspect);
176     return result;
177 }
178 
179 static int
dufunc_init(PyDUFuncObject * self,PyObject * args,PyObject * kws)180 dufunc_init(PyDUFuncObject *self, PyObject *args, PyObject *kws)
181 {
182     PyObject *dispatcher=NULL, *keepalive=NULL, *py_func_obj=NULL, *tmp;
183     PyUFuncObject *ufunc=NULL;
184     int identity=PyUFunc_None;
185     int nin=-1, nout=1;
186     const char *name=NULL, *doc=NULL;
187 
188     static char * kwlist[] = {"dispatcher", "identity", "_keepalive", "nin",
189                               "nout", NULL};
190 
191     if (!PyArg_ParseTupleAndKeywords(args, kws, "O|iO!nn", kwlist,
192                                      &dispatcher, &identity,
193                                      &PyList_Type, &keepalive, &nin, &nout)) {
194         return -1;
195     }
196 
197     py_func_obj = PyObject_GetAttrString(dispatcher, "py_func");
198     if (!py_func_obj) {
199         return -1;
200     }
201 
202     if (nin < 0) {
203         nin = (int)_get_nin(py_func_obj);
204         if ((nin < 0) || (PyErr_Occurred())) {
205             Py_XDECREF(py_func_obj);
206             return -1;
207         }
208     }
209 
210     /* Construct the UFunc. */
211     tmp = PyObject_GetAttrString(py_func_obj, "__name__");
212     if (tmp) {
213         name = PyString_AsString(tmp);
214     }
215     Py_XDECREF(tmp);
216     tmp = PyObject_GetAttrString(py_func_obj, "__doc__");
217     if (tmp && (tmp != Py_None)) {
218         doc = PyString_AsString(tmp);
219     }
220     Py_XDECREF(tmp);
221     tmp = NULL;
222     Py_XDECREF(py_func_obj);
223     py_func_obj = NULL;
224     if (!name) {
225         return -1;
226     }
227     ufunc = (PyUFuncObject *)PyUFunc_FromFuncAndData(NULL, NULL, NULL, 0,
228                                                      nin, nout, identity,
229                                                      name, doc, 0);
230     if (!ufunc) {
231         return -1;
232     }
233 
234     /* Construct a keepalive list if none was given. */
235     if (!keepalive) {
236         keepalive = PyList_New(0);
237         if (!keepalive) {
238             Py_XDECREF(ufunc);
239             return -1;
240         }
241     } else {
242         Py_INCREF(keepalive);
243     }
244 
245     tmp = self->dispatcher;
246     Py_INCREF(dispatcher);
247     self->dispatcher = dispatcher;
248     Py_XDECREF(tmp);
249 
250     tmp = (PyObject*)self->ufunc;
251     self->ufunc = ufunc;
252     Py_XDECREF(tmp);
253 
254     tmp = self->keepalive;
255     /* Already incref'ed, either by PyList_New(), or else clause, both above. */
256     self->keepalive = keepalive;
257     Py_XDECREF(tmp);
258 
259     self->frozen = 0;
260 
261     return 0;
262 }
263 
264 static PyMemberDef dufunc_members[] = {
265     {"_dispatcher", T_OBJECT_EX, offsetof(PyDUFuncObject, dispatcher), 0,
266          "Dispatcher object for the core Python function."},
267     {"ufunc", T_OBJECT_EX, offsetof(PyDUFuncObject, ufunc), 0,
268          "Numpy Ufunc for the dynamic ufunc."},
269     {"_keepalive", T_OBJECT_EX, offsetof(PyDUFuncObject, keepalive), 0,
270          "List of objects to keep alive during life of dufunc."},
271     {NULL}
272 };
273 
274 /* ____________________________________________________________
275  * Shims to expose ufunc methods.
276  */
277 
278 static struct _ufunc_dispatch {
279     PyCFunctionWithKeywords ufunc_reduce;
280     PyCFunctionWithKeywords ufunc_accumulate;
281     PyCFunctionWithKeywords ufunc_reduceat;
282     PyCFunctionWithKeywords ufunc_outer;
283 #if NPY_API_VERSION >= 0x00000008
284     PyCFunction ufunc_at;
285 #endif
286 } ufunc_dispatch;
287 
288 static int
init_ufunc_dispatch(void)289 init_ufunc_dispatch(void)
290 {
291     int result = 0;
292     PyMethodDef * crnt = PyUFunc_Type.tp_methods;
293     const char * crnt_name = NULL;
294     for (; crnt->ml_name != NULL; crnt++) {
295         crnt_name = crnt->ml_name;
296         switch(crnt_name[0]) {
297         case 'a':
298             if (strncmp(crnt_name, "accumulate", 11) == 0) {
299                 ufunc_dispatch.ufunc_accumulate =
300                     (PyCFunctionWithKeywords)crnt->ml_meth;
301 #if NPY_API_VERSION >= 0x00000008
302             } else if (strncmp(crnt_name, "at", 3) == 0) {
303                 ufunc_dispatch.ufunc_at = crnt->ml_meth;
304 #endif
305             } else {
306                 result = -1;
307             }
308             break;
309         case 'o':
310             if (strncmp(crnt_name, "outer", 6) == 0) {
311                 ufunc_dispatch.ufunc_outer =
312                     (PyCFunctionWithKeywords)crnt->ml_meth;
313             } else {
314                 result = -1;
315             }
316             break;
317         case 'r':
318             if (strncmp(crnt_name, "reduce", 7) == 0) {
319                 ufunc_dispatch.ufunc_reduce =
320                     (PyCFunctionWithKeywords)crnt->ml_meth;
321             } else if (strncmp(crnt_name, "reduceat", 9) == 0) {
322                 ufunc_dispatch.ufunc_reduceat =
323                     (PyCFunctionWithKeywords)crnt->ml_meth;
324             } else {
325                 result = -1;
326             }
327             break;
328         default:
329             result = -1; /* Unknown method */
330         }
331         if (result < 0) break;
332     }
333     if (result == 0) {
334         /* Sanity check. */
335         result = ((ufunc_dispatch.ufunc_reduce != NULL)
336                   && (ufunc_dispatch.ufunc_accumulate != NULL)
337                   && (ufunc_dispatch.ufunc_reduceat != NULL)
338                   && (ufunc_dispatch.ufunc_outer != NULL)
339 #if NPY_API_VERSION >= 0x00000008
340                   && (ufunc_dispatch.ufunc_at != NULL)
341 #endif
342                   );
343     }
344     return result;
345 }
346 
347 static PyObject *
dufunc_reduce(PyDUFuncObject * self,PyObject * args,PyObject * kws)348 dufunc_reduce(PyDUFuncObject * self, PyObject * args, PyObject *kws)
349 {
350     return ufunc_dispatch.ufunc_reduce((PyObject*)self->ufunc, args, kws);
351 }
352 
353 static PyObject *
dufunc_accumulate(PyDUFuncObject * self,PyObject * args,PyObject * kws)354 dufunc_accumulate(PyDUFuncObject * self, PyObject * args, PyObject *kws)
355 {
356     return ufunc_dispatch.ufunc_accumulate((PyObject*)self->ufunc, args, kws);
357 }
358 
359 static PyObject *
dufunc_reduceat(PyDUFuncObject * self,PyObject * args,PyObject * kws)360 dufunc_reduceat(PyDUFuncObject * self, PyObject * args, PyObject *kws)
361 {
362     return ufunc_dispatch.ufunc_reduceat((PyObject*)self->ufunc, args, kws);
363 }
364 
365 static PyObject *
dufunc_outer(PyDUFuncObject * self,PyObject * args,PyObject * kws)366 dufunc_outer(PyDUFuncObject * self, PyObject * args, PyObject *kws)
367 {
368     return ufunc_dispatch.ufunc_outer((PyObject*)self->ufunc, args, kws);
369 }
370 
371 #if NPY_API_VERSION >= 0x00000008
372 static PyObject *
dufunc_at(PyDUFuncObject * self,PyObject * args)373 dufunc_at(PyDUFuncObject * self, PyObject * args)
374 {
375     return ufunc_dispatch.ufunc_at((PyObject*)self->ufunc, args);
376 }
377 #endif
378 
379 static PyObject *
dufunc__compile_for_args(PyDUFuncObject * self,PyObject * args,PyObject * kws)380 dufunc__compile_for_args(PyDUFuncObject * self, PyObject * args,
381                         PyObject * kws)
382 {
383     PyErr_SetString(PyExc_NotImplementedError,
384                     "Abstract method _DUFunc._compile_for_args() called!");
385     return NULL;
386 }
387 
388 static int *
_build_arg_types_array(PyObject * type_list,Py_ssize_t nargs)389 _build_arg_types_array(PyObject * type_list, Py_ssize_t nargs)
390 {
391     int *arg_types_array=NULL;
392     Py_ssize_t idx, arg_types_size = PyList_Size(type_list);
393 
394     if (arg_types_size != nargs) {
395         PyErr_SetString(
396             PyExc_ValueError,
397             "argument type list size does not equal ufunc argument count");
398         return NULL;
399     }
400     arg_types_array = PyArray_malloc(sizeof(int) * nargs);
401     if (!arg_types_array) {
402         PyErr_NoMemory();
403         return NULL;
404     }
405     for (idx = 0; idx < nargs; idx++) {
406         arg_types_array[idx] = (int)PyLong_AsLong(PyList_GET_ITEM(type_list,
407                                                                   idx));
408     }
409     if (PyErr_Occurred()) {
410         PyArray_free(arg_types_array);
411         arg_types_array = NULL;
412     }
413     return arg_types_array;
414 }
415 
416 static PyObject *
dufunc__add_loop(PyDUFuncObject * self,PyObject * args)417 dufunc__add_loop(PyDUFuncObject * self, PyObject * args)
418 {
419     PyUFuncObject * ufunc=self->ufunc;
420     void *loop_ptr=NULL, *data_ptr=NULL;
421     int idx=-1, usertype=NPY_VOID;
422     int *arg_types_arr=NULL;
423     PyObject *arg_types=NULL, *loop_obj=NULL, *data_obj=NULL;
424     PyUFuncGenericFunction old_func=NULL;
425 
426     if (self->frozen) {
427         PyErr_SetString(PyExc_ValueError,
428                         "_DUFunc._add_loop() called for frozen dufunc");
429         return NULL;
430     }
431 
432     if (!PyArg_ParseTuple(args, "O!O!|O!",
433                           &PyLong_Type, &loop_obj, &PyList_Type, &arg_types,
434                           &PyLong_Type, &data_obj)) {
435         return NULL;
436     }
437 
438     loop_ptr = PyLong_AsVoidPtr(loop_obj);
439     if (PyErr_Occurred()) {
440         return NULL;
441     }
442     if (data_obj) {
443         data_ptr = PyLong_AsVoidPtr(data_obj);
444         if (PyErr_Occurred()) {
445             return NULL;
446         }
447     }
448 
449     arg_types_arr = _build_arg_types_array(arg_types, (Py_ssize_t)ufunc->nargs);
450     if (!arg_types_arr) goto _dufunc__add_loop_fail;
451 
452     /* Check to see if any of the input types are user defined dtypes.
453        If they are, we should use PyUFunc_RegisterLoopForType() since
454        dispatch on a user defined dtype uses a Python dictionary
455        keyed by usertype (and not the functions array).
456 
457        For more information, see how the usertype argument is used in
458        PyUFunc_RegisterLoopForType(), defined by Numpy at
459        .../numpy/core/src/umath/ufunc_object.c
460     */
461     for (idx = 0; idx < ufunc->nargs; idx++) {
462         if (arg_types_arr[idx] >= NPY_USERDEF) {
463             usertype = arg_types_arr[idx];
464         }
465     }
466 
467     if (usertype != NPY_VOID) {
468         if (PyUFunc_RegisterLoopForType(ufunc, usertype,
469                                         (PyUFuncGenericFunction)loop_ptr,
470                                         arg_types_arr, data_ptr) < 0) {
471             goto _dufunc__add_loop_fail;
472         }
473     } else if (PyUFunc_ReplaceLoopBySignature(ufunc,
474                                               (PyUFuncGenericFunction)loop_ptr,
475                                               arg_types_arr, &old_func) == 0) {
476         /* TODO: Consider freeing any memory held by the old loop (somehow) */
477         for (idx = 0; idx < ufunc->ntypes; idx++) {
478             if (ufunc->functions[idx] == (PyUFuncGenericFunction)loop_ptr) {
479                 ufunc->data[idx] = data_ptr;
480                 break;
481             }
482         }
483     } else {
484         /* The following is an attempt to loosely follow the allocation
485            code in Numpy.  See ufunc_frompyfunc() in
486            .../numpy/core/src/umath/umathmodule.c.
487 
488            The primary goal is to allocate a single chunk of memory to
489            hold the functions, data, and types loop arrays:
490 
491            ptr == |<- functions ->|<- data ->|<- types ->|
492 
493         */
494         int ntypes=ufunc->ntypes + 1;
495         PyUFuncGenericFunction *functions=NULL;
496         void **data=NULL;
497         char *types=NULL;
498         void *newptr=NULL, *oldptr=NULL;
499         size_t functions_size=sizeof(PyUFuncGenericFunction) * ntypes;
500         size_t data_size=sizeof(void *) * ntypes;
501         size_t type_ofs=sizeof(char) * ufunc->ntypes * ufunc->nargs;
502         size_t newsize=(functions_size + data_size +
503                         (sizeof(char) * ntypes * ufunc->nargs));
504 
505         oldptr = ufunc->ptr;
506         newptr = PyArray_malloc(newsize);
507         if (!newptr) {
508             PyErr_NoMemory();
509             goto _dufunc__add_loop_fail;
510         }
511         functions = (PyUFuncGenericFunction*)newptr;
512         memcpy(functions, ufunc->functions,
513                sizeof(PyUFuncGenericFunction) * ufunc->ntypes);
514         functions[ntypes - 1] = (PyUFuncGenericFunction)loop_ptr;
515         data = (void **)((char *)functions + functions_size);
516         memcpy(data, ufunc->data, sizeof(void *) * ufunc->ntypes);
517         data[ntypes - 1] = data_ptr;
518         types = (char *)data + data_size;
519         memcpy(types, ufunc->types, sizeof(char) * ufunc->ntypes *
520                ufunc->nargs);
521         for (idx = 0; idx < ufunc->nargs; idx++) {
522             types[idx + type_ofs] = (char)arg_types_arr[idx];
523         }
524 
525         ufunc->ntypes = ntypes;
526         ufunc->functions = functions;
527         ufunc->types = types;
528         ufunc->data = data;
529         ufunc->ptr = newptr;
530         PyArray_free(oldptr);
531     }
532 
533     PyArray_free(arg_types_arr);
534     Py_INCREF(Py_None);
535     return Py_None;
536 
537  _dufunc__add_loop_fail:
538     PyArray_free(arg_types_arr);
539     return NULL;
540 }
541 
542 static struct PyMethodDef dufunc_methods[] = {
543     {"reduce",
544         (PyCFunction)dufunc_reduce,
545         METH_VARARGS | METH_KEYWORDS, NULL },
546     {"accumulate",
547         (PyCFunction)dufunc_accumulate,
548         METH_VARARGS | METH_KEYWORDS, NULL },
549     {"reduceat",
550         (PyCFunction)dufunc_reduceat,
551         METH_VARARGS | METH_KEYWORDS, NULL },
552     {"outer",
553         (PyCFunction)dufunc_outer,
554         METH_VARARGS | METH_KEYWORDS, NULL},
555 #if NPY_API_VERSION >= 0x00000008
556     {"at",
557         (PyCFunction)dufunc_at,
558         METH_VARARGS, NULL},
559 #endif
560     {"_compile_for_args",
561         (PyCFunction)dufunc__compile_for_args,
562         METH_VARARGS | METH_KEYWORDS,
563         "Abstract method: subclasses should overload _compile_for_args() to compile the ufunc at the given arguments' types."},
564     {"_add_loop",
565         (PyCFunction)dufunc__add_loop,
566         METH_VARARGS,
567         NULL},
568     {NULL, NULL, 0, NULL}           /* sentinel */
569 };
570 
571 static PyObject *
dufunc_getfrozen(PyDUFuncObject * self,void * closure)572 dufunc_getfrozen(PyDUFuncObject * self, void * closure)
573 {
574     PyObject *result=(self->frozen) ? Py_True : Py_False;
575     Py_INCREF(result);
576     return result;
577 }
578 
579 static int
dufunc_setfrozen(PyDUFuncObject * self,PyObject * value,void * closure)580 dufunc_setfrozen(PyDUFuncObject * self, PyObject * value, void * closure)
581 {
582     int result=0;
583     if (PyObject_IsTrue(value)) {
584         self->frozen = 1;
585     } else {
586         PyErr_SetString(PyExc_ValueError,
587                         "cannot clear the _DUFunc.frozen flag");
588         result = -1;
589     }
590     return result;
591 }
592 
593 static PyGetSetDef dufunc_getsets[] = {
594     {"_frozen",
595      (getter)dufunc_getfrozen, (setter)dufunc_setfrozen,
596      "flag indicating call-time compilation has been disabled",
597      NULL},
598     {NULL}  /* Sentinel */
599 };
600 
601 PyTypeObject PyDUFunc_Type = {
602     PyVarObject_HEAD_INIT(NULL, 0)
603     "numba._DUFunc",                            /* tp_name*/
604     sizeof(PyDUFuncObject),                     /* tp_basicsize*/
605     0,                                          /* tp_itemsize */
606     /* methods */
607     (destructor) dufunc_dealloc,                /* tp_dealloc */
608     0,                                          /* tp_print */
609     0,                                          /* tp_getattr */
610     0,                                          /* tp_setattr */
611     0,                                          /* tp_compare/tp_reserved */
612     (reprfunc) dufunc_repr,                     /* tp_repr */
613     0,                                          /* tp_as_number */
614     0,                                          /* tp_as_sequence */
615     0,                                          /* tp_as_mapping */
616     0,                                          /* tp_hash */
617     (ternaryfunc) dufunc_call,                  /* tp_call */
618     (reprfunc) dufunc_repr,                     /* tp_str */
619     0,                                          /* tp_getattro */
620     0,                                          /* tp_setattro */
621     0,                                          /* tp_as_buffer */
622     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
623     0,                                          /* tp_doc */
624     0,                                          /* tp_traverse */
625     0,                                          /* tp_clear */
626     0,                                          /* tp_richcompare */
627     0,                                          /* tp_weaklistoffset */
628     0,                                          /* tp_iter */
629     0,                                          /* tp_iternext */
630     dufunc_methods,                             /* tp_methods */
631     dufunc_members,                             /* tp_members */
632     dufunc_getsets,                             /* tp_getset */
633     0,                                          /* tp_base */
634     0,                                          /* tp_dict */
635     0,                                          /* tp_descr_get */
636     0,                                          /* tp_descr_set */
637     0,                                          /* tp_dictoffset */
638     (initproc) dufunc_init,                     /* tp_init */
639     0,                                          /* tp_alloc */
640     0,                                          /* tp_new */
641     0,                                          /* tp_free */
642     0,                                          /* tp_is_gc */
643     0,                                          /* tp_bases */
644     0,                                          /* tp_mro */
645     0,                                          /* tp_cache */
646     0,                                          /* tp_subclasses */
647     0,                                          /* tp_weaklist */
648     0,                                          /* tp_del */
649     0,                                          /* tp_version_tag */
650 };
651 
652 /* ______________________________________________________________________
653  * Module initialization boilerplate follows.
654  */
655 
656 static PyMethodDef ext_methods[] = {
657     {"fromfunc", (PyCFunction) ufunc_fromfunc, METH_VARARGS, NULL},
658     { NULL }
659 };
660 
661 /* Don't remove this marker, it is used for inserting licensing code */
662 /*MARK1*/
663 
MOD_INIT(_internal)664 MOD_INIT(_internal)
665 {
666     PyObject *m;
667 
668     /* Don't remove this marker, it is used for inserting licensing code */
669     /*MARK2*/
670 
671     import_array();
672     import_umath();
673 
674     MOD_DEF(m, "_internal", "No docs",
675             ext_methods)
676 
677     if (m == NULL)
678         return MOD_ERROR_VAL;
679 
680     if (PyType_Ready(&PyUFuncCleaner_Type) < 0)
681         return MOD_ERROR_VAL;
682 
683     PyDUFunc_Type.tp_new = PyType_GenericNew;
684     if (init_ufunc_dispatch() <= 0)
685         return MOD_ERROR_VAL;
686     if (PyType_Ready(&PyDUFunc_Type) < 0)
687         return MOD_ERROR_VAL;
688     Py_INCREF(&PyDUFunc_Type);
689     if (PyModule_AddObject(m, "_DUFunc", (PyObject *)&PyDUFunc_Type) < 0)
690         return MOD_ERROR_VAL;
691 
692     if (PyModule_AddIntMacro(m, PyUFunc_One)
693         || PyModule_AddIntMacro(m, PyUFunc_Zero)
694         || PyModule_AddIntMacro(m, PyUFunc_None)
695 #if NPY_API_VERSION >= 0x00000007
696         || PyModule_AddIntMacro(m, PyUFunc_ReorderableNone)
697 #endif
698         )
699         return MOD_ERROR_VAL;
700 
701     return MOD_SUCCESS_VAL(m);
702 }
703 
704 
705 #include "_ufunc.c"
706