@@ -616,6 +616,9 @@ typedef struct PicklerObject {
616616 PyObject * pers_func_self ; /* borrowed reference to self if pers_func
617617 is an unbound method, NULL otherwise */
618618 PyObject * dispatch_table ; /* private dispatch_table, can be NULL */
619+ PyObject * reducer_override ; /* hook for invoking user-defined callbacks
620+ instead of save_global when pickling
621+ functions and classes*/
619622
620623 PyObject * write ; /* write() method of the output stream. */
621624 PyObject * output_buffer ; /* Write into a local bytearray buffer before
@@ -1110,6 +1113,7 @@ _Pickler_New(void)
11101113 self -> fast_memo = NULL ;
11111114 self -> max_output_len = WRITE_BUF_SIZE ;
11121115 self -> output_len = 0 ;
1116+ self -> reducer_override = NULL ;
11131117
11141118 self -> memo = PyMemoTable_New ();
11151119 self -> output_buffer = PyBytes_FromStringAndSize (NULL ,
@@ -2220,7 +2224,7 @@ save_bytes(PicklerObject *self, PyObject *obj)
22202224 Python 2 *and* the appropriate 'bytes' object when unpickled
22212225 using Python 3. Again this is a hack and we don't need to do this
22222226 with newer protocols. */
2223- PyObject * reduce_value = NULL ;
2227+ PyObject * reduce_value ;
22242228 int status ;
22252229
22262230 if (PyBytes_GET_SIZE (obj ) == 0 ) {
@@ -4058,7 +4062,25 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
40584062 status = save_tuple (self , obj );
40594063 goto done ;
40604064 }
4061- else if (type == & PyType_Type ) {
4065+
4066+ /* Now, check reducer_override. If it returns NotImplemented,
4067+ * fallback to save_type or save_global, and then perhaps to the
4068+ * regular reduction mechanism.
4069+ */
4070+ if (self -> reducer_override != NULL ) {
4071+ reduce_value = PyObject_CallFunctionObjArgs (self -> reducer_override ,
4072+ obj , NULL );
4073+ if (reduce_value == NULL ) {
4074+ goto error ;
4075+ }
4076+ if (reduce_value != Py_NotImplemented ) {
4077+ goto reduce ;
4078+ }
4079+ Py_DECREF (reduce_value );
4080+ reduce_value = NULL ;
4081+ }
4082+
4083+ if (type == & PyType_Type ) {
40624084 status = save_type (self , obj );
40634085 goto done ;
40644086 }
@@ -4149,6 +4171,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
41494171 if (reduce_value == NULL )
41504172 goto error ;
41514173
4174+ reduce :
41524175 if (PyUnicode_Check (reduce_value )) {
41534176 status = save_global (self , obj , reduce_value );
41544177 goto done ;
@@ -4180,6 +4203,20 @@ static int
41804203dump (PicklerObject * self , PyObject * obj )
41814204{
41824205 const char stop_op = STOP ;
4206+ PyObject * tmp ;
4207+ _Py_IDENTIFIER (reducer_override );
4208+
4209+ if (_PyObject_LookupAttrId ((PyObject * )self , & PyId_reducer_override ,
4210+ & tmp ) < 0 ) {
4211+ return -1 ;
4212+ }
4213+ /* Cache the reducer_override method, if it exists. */
4214+ if (tmp != NULL ) {
4215+ Py_XSETREF (self -> reducer_override , tmp );
4216+ }
4217+ else {
4218+ Py_CLEAR (self -> reducer_override );
4219+ }
41834220
41844221 if (self -> proto >= 2 ) {
41854222 char header [2 ];
@@ -4304,6 +4341,7 @@ Pickler_dealloc(PicklerObject *self)
43044341 Py_XDECREF (self -> pers_func );
43054342 Py_XDECREF (self -> dispatch_table );
43064343 Py_XDECREF (self -> fast_memo );
4344+ Py_XDECREF (self -> reducer_override );
43074345
43084346 PyMemoTable_Del (self -> memo );
43094347
@@ -4317,6 +4355,7 @@ Pickler_traverse(PicklerObject *self, visitproc visit, void *arg)
43174355 Py_VISIT (self -> pers_func );
43184356 Py_VISIT (self -> dispatch_table );
43194357 Py_VISIT (self -> fast_memo );
4358+ Py_VISIT (self -> reducer_override );
43204359 return 0 ;
43214360}
43224361
@@ -4328,6 +4367,7 @@ Pickler_clear(PicklerObject *self)
43284367 Py_CLEAR (self -> pers_func );
43294368 Py_CLEAR (self -> dispatch_table );
43304369 Py_CLEAR (self -> fast_memo );
4370+ Py_CLEAR (self -> reducer_override );
43314371
43324372 if (self -> memo != NULL ) {
43334373 PyMemoTable * memo = self -> memo ;
0 commit comments