@@ -330,6 +330,165 @@ static PyTypeObject partial_type = {
330330};
331331
332332
333+ /* cmp_to_key ***************************************************************/
334+
335+ typedef struct {
336+ PyObject_HEAD ;
337+ PyObject * cmp ;
338+ PyObject * object ;
339+ } keyobject ;
340+
341+ static void
342+ keyobject_dealloc (keyobject * ko )
343+ {
344+ Py_DECREF (ko -> cmp );
345+ Py_XDECREF (ko -> object );
346+ PyObject_FREE (ko );
347+ }
348+
349+ static int
350+ keyobject_traverse (keyobject * ko , visitproc visit , void * arg )
351+ {
352+ Py_VISIT (ko -> cmp );
353+ if (ko -> object )
354+ Py_VISIT (ko -> object );
355+ return 0 ;
356+ }
357+
358+ static PyMemberDef keyobject_members [] = {
359+ {"obj" , T_OBJECT ,
360+ offsetof(keyobject , object ), 0 ,
361+ PyDoc_STR ("Value wrapped by a key function." )},
362+ {NULL }
363+ };
364+
365+ static PyObject *
366+ keyobject_call (keyobject * ko , PyObject * args , PyObject * kw );
367+
368+ static PyObject *
369+ keyobject_richcompare (PyObject * ko , PyObject * other , int op );
370+
371+ static PyTypeObject keyobject_type = {
372+ PyVarObject_HEAD_INIT (& PyType_Type , 0 )
373+ "functools.KeyWrapper" , /* tp_name */
374+ sizeof (keyobject ), /* tp_basicsize */
375+ 0 , /* tp_itemsize */
376+ /* methods */
377+ (destructor )keyobject_dealloc , /* tp_dealloc */
378+ 0 , /* tp_print */
379+ 0 , /* tp_getattr */
380+ 0 , /* tp_setattr */
381+ 0 , /* tp_reserved */
382+ 0 , /* tp_repr */
383+ 0 , /* tp_as_number */
384+ 0 , /* tp_as_sequence */
385+ 0 , /* tp_as_mapping */
386+ 0 , /* tp_hash */
387+ (ternaryfunc )keyobject_call , /* tp_call */
388+ 0 , /* tp_str */
389+ PyObject_GenericGetAttr , /* tp_getattro */
390+ 0 , /* tp_setattro */
391+ 0 , /* tp_as_buffer */
392+ Py_TPFLAGS_DEFAULT , /* tp_flags */
393+ 0 , /* tp_doc */
394+ (traverseproc )keyobject_traverse , /* tp_traverse */
395+ 0 , /* tp_clear */
396+ keyobject_richcompare , /* tp_richcompare */
397+ 0 , /* tp_weaklistoffset */
398+ 0 , /* tp_iter */
399+ 0 , /* tp_iternext */
400+ 0 , /* tp_methods */
401+ keyobject_members , /* tp_members */
402+ 0 , /* tp_getset */
403+ };
404+
405+ static PyObject *
406+ keyobject_call (keyobject * ko , PyObject * args , PyObject * kwds )
407+ {
408+ PyObject * object ;
409+ keyobject * result ;
410+ static char * kwargs [] = {"obj" , NULL };
411+
412+ if (!PyArg_ParseTupleAndKeywords (args , kwds , "O:K" , kwargs , & object ))
413+ return NULL ;
414+ result = PyObject_New (keyobject , & keyobject_type );
415+ if (!result )
416+ return NULL ;
417+ Py_INCREF (ko -> cmp );
418+ result -> cmp = ko -> cmp ;
419+ Py_INCREF (object );
420+ result -> object = object ;
421+ return (PyObject * )result ;
422+ }
423+
424+ static PyObject *
425+ keyobject_richcompare (PyObject * ko , PyObject * other , int op )
426+ {
427+ PyObject * res ;
428+ PyObject * args ;
429+ PyObject * x ;
430+ PyObject * y ;
431+ PyObject * compare ;
432+ PyObject * answer ;
433+ static PyObject * zero ;
434+
435+ if (zero == NULL ) {
436+ zero = PyLong_FromLong (0 );
437+ if (!zero )
438+ return NULL ;
439+ }
440+
441+ if (Py_TYPE (other ) != & keyobject_type ){
442+ PyErr_Format (PyExc_TypeError , "other argument must be K instance" );
443+ return NULL ;
444+ }
445+ compare = ((keyobject * ) ko )-> cmp ;
446+ assert (compare != NULL );
447+ x = ((keyobject * ) ko )-> object ;
448+ y = ((keyobject * ) other )-> object ;
449+ if (!x || !y ){
450+ PyErr_Format (PyExc_AttributeError , "object" );
451+ return NULL ;
452+ }
453+
454+ /* Call the user's comparison function and translate the 3-way
455+ * result into true or false (or error).
456+ */
457+ args = PyTuple_New (2 );
458+ if (args == NULL )
459+ return NULL ;
460+ Py_INCREF (x );
461+ Py_INCREF (y );
462+ PyTuple_SET_ITEM (args , 0 , x );
463+ PyTuple_SET_ITEM (args , 1 , y );
464+ res = PyObject_Call (compare , args , NULL );
465+ Py_DECREF (args );
466+ if (res == NULL )
467+ return NULL ;
468+ answer = PyObject_RichCompare (res , zero , op );
469+ Py_DECREF (res );
470+ return answer ;
471+ }
472+
473+ static PyObject *
474+ functools_cmp_to_key (PyObject * self , PyObject * args , PyObject * kwds ){
475+ PyObject * cmp ;
476+ static char * kwargs [] = {"mycmp" , NULL };
477+
478+ if (!PyArg_ParseTupleAndKeywords (args , kwds , "O:cmp_to_key" , kwargs , & cmp ))
479+ return NULL ;
480+ keyobject * object = PyObject_New (keyobject , & keyobject_type );
481+ if (!object )
482+ return NULL ;
483+ Py_INCREF (cmp );
484+ object -> cmp = cmp ;
485+ object -> object = NULL ;
486+ return (PyObject * )object ;
487+ }
488+
489+ PyDoc_STRVAR (functools_cmp_to_key_doc ,
490+ "Convert a cmp= function into a key= function." );
491+
333492/* reduce (used to be a builtin) ********************************************/
334493
335494static PyObject *
@@ -413,6 +572,8 @@ PyDoc_STRVAR(module_doc,
413572
414573static PyMethodDef module_methods [] = {
415574 {"reduce" , functools_reduce , METH_VARARGS , functools_reduce_doc },
575+ {"cmp_to_key" , functools_cmp_to_key , METH_VARARGS | METH_KEYWORDS ,
576+ functools_cmp_to_key_doc },
416577 {NULL , NULL } /* sentinel */
417578};
418579
0 commit comments