@@ -4687,6 +4687,7 @@ PyUFunc_FromFuncAndDataAndSignatureAndIdentity(PyUFuncGenericFunction *func, voi
4687
4687
ufunc -> core_signature = NULL ;
4688
4688
ufunc -> core_enabled = 0 ;
4689
4689
ufunc -> obj = NULL ;
4690
+ ufunc -> dict = NULL ;
4690
4691
ufunc -> core_num_dims = NULL ;
4691
4692
ufunc -> core_num_dim_ix = 0 ;
4692
4693
ufunc -> core_offsets = NULL ;
@@ -4771,6 +4772,11 @@ PyUFunc_FromFuncAndDataAndSignatureAndIdentity(PyUFuncGenericFunction *func, voi
4771
4772
return NULL ;
4772
4773
}
4773
4774
}
4775
+ ufunc -> dict = PyDict_New ();
4776
+ if (ufunc -> dict == NULL ) {
4777
+ Py_DECREF (ufunc );
4778
+ return NULL ;
4779
+ }
4774
4780
/*
4775
4781
* TODO: I tried adding a default promoter here (either all object for
4776
4782
* some special cases, or all homogeneous). Those are reasonable
@@ -5177,6 +5183,7 @@ ufunc_dealloc(PyUFuncObject *ufunc)
5177
5183
Py_DECREF (ufunc -> identity_value );
5178
5184
}
5179
5185
Py_XDECREF (ufunc -> obj );
5186
+ Py_XDECREF (ufunc -> dict );
5180
5187
Py_XDECREF (ufunc -> _loops );
5181
5188
if (ufunc -> _dispatch_cache != NULL ) {
5182
5189
PyArrayIdentityHash_Dealloc (ufunc -> _dispatch_cache );
@@ -5197,6 +5204,7 @@ ufunc_traverse(PyUFuncObject *self, visitproc visit, void *arg)
5197
5204
if (self -> identity == PyUFunc_IdentityValue ) {
5198
5205
Py_VISIT (self -> identity_value );
5199
5206
}
5207
+ Py_VISIT (self -> dict );
5200
5208
return 0 ;
5201
5209
}
5202
5210
@@ -6411,6 +6419,15 @@ ufunc_get_doc(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored))
6411
6419
{
6412
6420
PyObject * doc ;
6413
6421
6422
+ // If there is a __doc__ in the instance __dict__, use it.
6423
+ int result = PyDict_GetItemRef (ufunc -> dict , npy_interned_str .__doc__ , & doc );
6424
+ if (result == -1 ) {
6425
+ return NULL ;
6426
+ }
6427
+ else if (result == 1 ) {
6428
+ return doc ;
6429
+ }
6430
+
6414
6431
if (npy_cache_import_runtime (
6415
6432
"numpy._core._internal" , "_ufunc_doc_signature_formatter" ,
6416
6433
& npy_runtime_imports ._ufunc_doc_signature_formatter ) == -1 ) {
@@ -6434,6 +6451,15 @@ ufunc_get_doc(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored))
6434
6451
return doc ;
6435
6452
}
6436
6453
6454
+ static int
6455
+ ufunc_set_doc (PyUFuncObject * ufunc , PyObject * doc , void * NPY_UNUSED (ignored ))
6456
+ {
6457
+ if (doc == NULL ) {
6458
+ return PyDict_DelItem (ufunc -> dict , npy_interned_str .__doc__ );
6459
+ } else {
6460
+ return PyDict_SetItem (ufunc -> dict , npy_interned_str .__doc__ , doc );
6461
+ }
6462
+ }
6437
6463
6438
6464
static PyObject *
6439
6465
ufunc_get_nin (PyUFuncObject * ufunc , void * NPY_UNUSED (ignored ))
@@ -6519,8 +6545,8 @@ ufunc_get_signature(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored))
6519
6545
6520
6546
static PyGetSetDef ufunc_getset [] = {
6521
6547
{"__doc__" ,
6522
- (getter )ufunc_get_doc ,
6523
- NULL , NULL , NULL },
6548
+ (getter )ufunc_get_doc , ( setter ) ufunc_set_doc ,
6549
+ NULL , NULL },
6524
6550
{"nin" ,
6525
6551
(getter )ufunc_get_nin ,
6526
6552
NULL , NULL , NULL },
@@ -6549,6 +6575,17 @@ static PyGetSetDef ufunc_getset[] = {
6549
6575
};
6550
6576
6551
6577
6578
+ /******************************************************************************
6579
+ *** UFUNC MEMBERS ***
6580
+ *****************************************************************************/
6581
+
6582
+ static PyMemberDef ufunc_members [] = {
6583
+ {"__dict__" , T_OBJECT , offsetof(PyUFuncObject , dict ),
6584
+ READONLY },
6585
+ {NULL },
6586
+ };
6587
+
6588
+
6552
6589
/******************************************************************************
6553
6590
*** UFUNC TYPE OBJECT ***
6554
6591
*****************************************************************************/
@@ -6568,6 +6605,12 @@ NPY_NO_EXPORT PyTypeObject PyUFunc_Type = {
6568
6605
.tp_traverse = (traverseproc )ufunc_traverse ,
6569
6606
.tp_methods = ufunc_methods ,
6570
6607
.tp_getset = ufunc_getset ,
6608
+ .tp_getattro = PyObject_GenericGetAttr ,
6609
+ .tp_setattro = PyObject_GenericSetAttr ,
6610
+ // TODO when Python 3.12 is the minimum supported version,
6611
+ // use Py_TPFLAGS_MANAGED_DICT
6612
+ .tp_members = ufunc_members ,
6613
+ .tp_dictoffset = offsetof(PyUFuncObject , dict ),
6571
6614
};
6572
6615
6573
6616
/* End of code for ufunc objects */
0 commit comments