Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(10427)

Side by Side Diff: Modules/_functoolsmodule.c

Issue 14373: C implementation of functools.lru_cache
Patch Set: Created 5 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Lib/test/test_functools.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 #include "Python.h" 2 #include "Python.h"
3 #include "structmember.h" 3 #include "structmember.h"
4 4
5 /* _functools module written and maintained 5 /* _functools module written and maintained
6 by Hye-Shik Chang <perky@FreeBSD.org> 6 by Hye-Shik Chang <perky@FreeBSD.org>
7 with adaptations by Raymond Hettinger <python@rcn.com> 7 with adaptations by Raymond Hettinger <python@rcn.com>
8 Copyright (c) 2004, 2005, 2006 Python Software Foundation. 8 Copyright (c) 2004, 2005, 2006 Python Software Foundation.
9 All rights reserved. 9 All rights reserved.
10 */ 10 */
(...skipping 522 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 PyDoc_STRVAR(functools_reduce_doc, 533 PyDoc_STRVAR(functools_reduce_doc,
534 "reduce(function, sequence[, initial]) -> value\n\ 534 "reduce(function, sequence[, initial]) -> value\n\
535 \n\ 535 \n\
536 Apply a function of two arguments cumulatively to the items of a sequence,\n\ 536 Apply a function of two arguments cumulatively to the items of a sequence,\n\
537 from left to right, so as to reduce the sequence to a single value.\n\ 537 from left to right, so as to reduce the sequence to a single value.\n\
538 For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\ 538 For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
539 ((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\ 539 ((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\
540 of the sequence in the calculation, and serves as a default when the\n\ 540 of the sequence in the calculation, and serves as a default when the\n\
541 sequence is empty."); 541 sequence is empty.");
542 542
543 /* lru_cache object **********************************************************/
544
545 /* this object is used delimit args and keywords in the cache keys */
546 static PyObject *kwd_mark = NULL;
547
548 struct lru_list_elem;
549 struct lru_cache_object;
550
551 typedef struct lru_list_elem {
552 PyObject_HEAD
553 struct lru_list_elem *prev, *next; /* borrowed links */
554 PyObject *key, *result;
555 } lru_list_elem;
556
557 static void
558 lru_list_elem_dealloc(lru_list_elem *link)
559 {
560 _PyObject_GC_UNTRACK(link);
561 Py_XDECREF(link->key);
562 Py_XDECREF(link->result);
563 PyObject_GC_Del(link);
564 }
565
566 static int
567 lru_list_elem_traverse(lru_list_elem *link, visitproc visit, void *arg)
568 {
569 Py_VISIT(link->key);
570 Py_VISIT(link->result);
571 return 0;
572 }
573
574 static int
575 lru_list_elem_clear(lru_list_elem *link)
576 {
577 Py_CLEAR(link->key);
578 Py_CLEAR(link->result);
579 return 0;
580 }
581
582 static PyTypeObject lru_list_elem_type = {
583 PyVarObject_HEAD_INIT(&PyType_Type, 0)
584 "functools._lru_list_elem", /* tp_name */
585 sizeof(lru_list_elem), /* tp_basicsize */
586 0, /* tp_itemsize */
587 /* methods */
588 (destructor)lru_list_elem_dealloc, /* tp_dealloc */
589 0, /* tp_print */
590 0, /* tp_getattr */
591 0, /* tp_setattr */
592 0, /* tp_reserved */
593 0, /* tp_repr */
594 0, /* tp_as_number */
595 0, /* tp_as_sequence */
596 0, /* tp_as_mapping */
597 0, /* tp_hash */
598 0, /* tp_call */
599 0, /* tp_str */
600 0, /* tp_getattro */
601 0, /* tp_setattro */
602 0, /* tp_as_buffer */
603 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
604 0, /* tp_doc */
605 (traverseproc)lru_list_elem_traverse, /* tp_traverse */
606 (inquiry)lru_list_elem_clear, /* tp_clear */
607 };
608
609
610 typedef PyObject *(*lru_cache_ternaryfunc)(struct lru_cache_object *, PyObject * , PyObject *);
611
612 typedef struct lru_cache_object {
613 lru_list_elem root; /* includes PyObject_HEAD */
614 Py_ssize_t maxsize;
615 PyObject *maxsize_O;
616 PyObject *func;
617 lru_cache_ternaryfunc wrapper;
618 PyObject *cache;
619 PyObject *cache_info_type;
620 Py_ssize_t misses, hits;
621 int typed;
622 PyObject *dict;
623 int full;
624 } lru_cache_object;
625
626 static PyTypeObject lru_cache_type;
627
628 static PyObject *
629 lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
630 {
631 PyObject *key, *sorted_items;
632 Py_ssize_t key_size, pos, key_pos;
633
634 /* short path, key will match args anyway, which is a tuple */
635 if (!typed && !kwds) {
636 Py_INCREF(args);
637 return args;
638 }
639
640 if (kwds && PyDict_Size(kwds) > 0) {
641 sorted_items = PyDict_Items(kwds);
642 if (!sorted_items)
643 return NULL;
644 if (PyList_Sort(sorted_items) < 0) {
645 Py_DECREF(sorted_items);
646 return NULL;
647 }
648 } else
649 sorted_items = NULL;
650
651 key_size = PyTuple_GET_SIZE(args);
652 if (sorted_items)
653 key_size += PyList_GET_SIZE(sorted_items);
654 if (typed)
655 key_size *= 2;
656 if (sorted_items)
657 key_size++;
658
659 key = PyTuple_New(key_size);
660 if (key == NULL)
661 goto done;
662
663 key_pos = 0;
664 for (pos = 0; pos < PyTuple_GET_SIZE(args); ++pos) {
665 PyObject *item = PyTuple_GET_ITEM(args, pos);
666 Py_INCREF(item);
667 PyTuple_SET_ITEM(key, key_pos++, item);
668 }
669 if (sorted_items) {
670 Py_INCREF(kwd_mark);
671 PyTuple_SET_ITEM(key, key_pos++, kwd_mark);
672 for (pos = 0; pos < PyList_GET_SIZE(sorted_items); ++pos) {
673 PyObject *item = PyList_GET_ITEM(sorted_items, pos);
674 Py_INCREF(item);
675 PyTuple_SET_ITEM(key, key_pos++, item);
676 }
677 }
678 if (typed) {
679 for (pos = 0; pos < PyTuple_GET_SIZE(args); ++pos) {
680 PyObject *item = (PyObject *)Py_TYPE(PyTuple_GET_ITEM(args, pos));
681 Py_INCREF(item);
682 PyTuple_SET_ITEM(key, key_pos++, item);
683 }
684 if (sorted_items) {
685 for (pos = 0; pos < PyList_GET_SIZE(sorted_items); ++pos) {
686 PyObject *tp_items = PyList_GET_ITEM(sorted_items, pos);
687 PyObject *item = (PyObject *)Py_TYPE(PyTuple_GET_ITEM(tp_items, 1));
688 Py_INCREF(item);
689 PyTuple_SET_ITEM(key, key_pos++, item);
690 }
691 }
692 }
693 assert(key_pos == key_size);
694
695 done:
696 if (sorted_items)
697 Py_DECREF(sorted_items);
698 return key;
699 }
700
701 static PyObject *
702 uncached_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwd s)
703 {
704 PyObject *result = PyObject_Call(self->func, args, kwds);
705 if (!result)
706 return NULL;
707 self->misses++;
708 return result;
709 }
710
711 static PyObject *
712 infinite_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwd s)
713 {
714 PyObject *result;
715 PyObject *key = lru_cache_make_key(args, kwds, self->typed);
716 if (!key)
717 return NULL;
718 result = PyDict_GetItemWithError(self->cache, key);
719 if (result) {
720 Py_INCREF(result);
721 self->hits++;
722 Py_DECREF(key);
723 return result;
724 }
725 if (PyErr_Occurred()) {
726 Py_DECREF(key);
727 return NULL;
728 }
729 result = PyObject_Call(self->func, args, kwds);
730 if (!result) {
731 Py_DECREF(key);
732 return NULL;
733 }
734 if (PyDict_SetItem(self->cache, key, result) < 0) {
Josh.R 2014/04/10 07:37:29 Would it make sense to use PyDict_SetDefault inste
storchaka 2014/08/10 20:46:46 Thank you for suggestion. Currently I'm restoring
storchaka 2015/05/23 21:02:54 Left as is, because it matches current Python impl
735 Py_DECREF(result);
736 Py_DECREF(key);
737 return NULL;
738 }
739 Py_DECREF(key);
740 self->misses++;
741 return result;
742 }
743
744 static void
745 lru_cache_extricate_link(lru_list_elem *link)
746 {
747 link->prev->next = link->next;
748 link->next->prev = link->prev;
749 }
750
751 static void
752 lru_cache_append_link(lru_cache_object *self, lru_list_elem *link)
753 {
754 lru_list_elem *root = &self->root;
755 lru_list_elem *last = root->prev;
756 last->next = root->prev = link;
757 link->prev = last;
758 link->next = root;
759 }
760
761 static PyObject *
762 bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds )
763 {
764 lru_list_elem *link;
765 PyObject *key, *result;
766
767 key = lru_cache_make_key(args, kwds, self->typed);
768 if (!key)
769 return NULL;
770 link = (lru_list_elem *)PyDict_GetItemWithError(self->cache, key);
771 if (link) {
772 lru_cache_extricate_link(link);
773 lru_cache_append_link(self, link);
774 self->hits++;
775 result = link->result;
776 Py_INCREF(result);
777 Py_DECREF(key);
778 return result;
779 }
780 if (PyErr_Occurred()) {
781 Py_DECREF(key);
782 return NULL;
783 }
784 result = PyObject_Call(self->func, args, kwds);
785 if (!result) {
786 Py_DECREF(key);
787 return NULL;
788 }
789 if (self->full && self->root.next != &self->root) {
790 /* Use the oldest item to store the new key and result. */
791 PyObject *oldkey, *oldresult;
792 /* Extricate the oldest item. */
793 link = self->root.next;
794 lru_cache_extricate_link(link);
795 /* Remove it from the cache.
796 The cache dict holds one reference to the link,
797 and the linked list holds yet one reference to it. */
798 if (PyDict_DelItem(self->cache, link->key) < 0) {
799 lru_cache_append_link(self, link);
800 Py_DECREF(key);
801 Py_DECREF(result);
802 return NULL;
803 }
804 /* Keep a reference to the old key and old result to
805 prevent their ref counts from going to zero during the
806 update. That will prevent potentially arbitrary object
807 clean-up code (i.e. __del__) from running while we're
808 still adjusting the links. */
809 oldkey = link->key;
810 oldresult = link->result;
811
812 link->key = key;
813 link->result = result;
814 if (PyDict_SetItem(self->cache, key, (PyObject *)link) < 0) {
Josh.R 2014/04/10 07:37:29 Same comment about avoiding duplicate SetItems app
815 Py_DECREF(link);
816 Py_DECREF(oldkey);
817 Py_DECREF(oldresult);
818 return NULL;
819 }
820 lru_cache_append_link(self, link);
821 Py_INCREF(result); /* for return */
822 Py_DECREF(oldkey);
823 Py_DECREF(oldresult);
824 } else {
825 /* Put result in a new link at the front of the queue. */
826 link = (lru_list_elem *)PyObject_GC_New(lru_list_elem,
827 &lru_list_elem_type);
828 if (link == NULL) {
829 Py_DECREF(key);
830 Py_DECREF(result);
831 return NULL;
832 }
833
834 link->key = key;
835 link->result = result;
836 _PyObject_GC_TRACK(link);
837 if (PyDict_SetItem(self->cache, key, (PyObject *)link) < 0) {
838 Py_DECREF(link);
839 return NULL;
840 }
841 lru_cache_append_link(self, link);
842 Py_INCREF(result); /* for return */
843 self->full = (PyDict_Size(self->cache) >= self->maxsize);
844 }
845 self->misses++;
846 return result;
847 }
848
849 static PyObject *
850 lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw)
851 {
852 PyObject *func, *maxsize_O, *cache_info_type;
853 int typed;
854 lru_cache_object *obj;
855 Py_ssize_t maxsize;
856 PyObject *(*wrapper)(lru_cache_object *, PyObject *, PyObject *);
857 static char *keywords[] = {"user_function", "maxsize", "typed",
858 "cache_info_type", NULL};
859
860 if (!PyArg_ParseTupleAndKeywords(args, kw, "OOpO:lru_cache", keywords,
861 &func, &maxsize_O, &typed,
862 &cache_info_type)) {
863 return NULL;
864 }
865
866 if (!PyCallable_Check(func)) {
867 PyErr_SetString(PyExc_TypeError,
868 "the first argument must be callable");
869 return NULL;
870 }
871
872 /* select the caching function, and make/inc maxsize_O */
873 if (maxsize_O == Py_None) {
874 wrapper = infinite_lru_cache_wrapper;
875 /* use this only to initialize lru_cache_object attribute maxsize */
876 maxsize = -1;
877 } else if (PyNumber_Check(maxsize_O)) {
Josh.R 2014/04/10 07:37:29 I believe you want PyIndex_Check here (checking fo
storchaka 2014/08/10 20:46:46 Done.
878 maxsize = PyNumber_AsSsize_t(maxsize_O, PyExc_OverflowError);
879 if (maxsize == -1 && PyErr_Occurred())
880 return NULL;
881 if (maxsize == 0)
882 wrapper = uncached_lru_cache_wrapper;
883 else
884 wrapper = bounded_lru_cache_wrapper;
Josh.R 2014/04/10 07:37:29 If someone actually passes a negative number, it l
storchaka 2014/08/10 20:46:46 Yes, but this matches Python implementation.
885 } else {
886 PyErr_SetString(PyExc_TypeError, "maxsize should be integer or None");
887 return NULL;
888 }
889
890 obj = (lru_cache_object *)type->tp_alloc(type, 0);
891 if (obj == NULL)
892 return NULL;
893
894 if (!(obj->cache = PyDict_New())) {
895 Py_DECREF(obj);
896 return NULL;
897 }
898
899 obj->root.prev = &obj->root;
900 obj->root.next = &obj->root;
901 obj->maxsize = maxsize;
902 Py_INCREF(maxsize_O);
903 obj->maxsize_O = maxsize_O;
904 Py_INCREF(func);
905 obj->func = func;
906 obj->wrapper = wrapper;
907 obj->misses = obj->hits = 0;
908 obj->typed = typed;
909 Py_INCREF(cache_info_type);
910 obj->cache_info_type = cache_info_type;
911
912 return (PyObject *)obj;
913 }
914
915 static lru_list_elem *
916 lru_cache_unlink_list(lru_cache_object *self)
917 {
918 lru_list_elem *root = &self->root;
919 lru_list_elem *link = root->next;
920 if (link == root)
921 return NULL;
922 root->prev->next = NULL;
923 root->next = root->prev = root;
924 return link;
925 }
926
927 static void
928 lru_cache_clear_list(lru_list_elem *link)
929 {
930 while (link != NULL) {
931 lru_list_elem *next = link->next;
932 Py_DECREF(link);
933 link = next;
934 }
935 }
936
937 static void
938 lru_cache_dealloc(lru_cache_object *obj)
939 {
940 lru_list_elem *list = lru_cache_unlink_list(obj);
941 Py_XDECREF(obj->maxsize_O);
942 Py_XDECREF(obj->func);
943 Py_XDECREF(obj->cache);
944 Py_XDECREF(obj->dict);
945 Py_XDECREF(obj->cache_info_type);
946 lru_cache_clear_list(list);
947 Py_TYPE(obj)->tp_free(obj);
948 }
949
950 static PyObject *
951 lru_cache_call(lru_cache_object *self, PyObject *args, PyObject *kwds)
952 {
953 return self->wrapper(self, args, kwds);
954 }
955
956 static PyObject *
957 lru_cache_cache_info(lru_cache_object *self, PyObject *unused)
958 {
959 return PyObject_CallFunction(self->cache_info_type, "nnOn",
960 self->hits, self->misses, self->maxsize_O,
961 PyDict_Size(self->cache));
962 }
963
964 static PyObject *
965 lru_cache_cache_clear(lru_cache_object *self, PyObject *unused)
966 {
967 lru_list_elem *list = lru_cache_unlink_list(self);
968 self->hits = self->misses = 0;
969 self->full = 0;
970 PyDict_Clear(self->cache);
971 lru_cache_clear_list(list);
972 Py_RETURN_NONE;
973 }
974
975 static int
976 lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg)
977 {
978 lru_list_elem *link = self->root.next;
979 while (link != &self->root) {
980 lru_list_elem *next = link->next;
981 Py_VISIT(link);
982 link = next;
983 }
984 Py_VISIT(self->maxsize_O);
985 Py_VISIT(self->func);
986 Py_VISIT(self->cache);
987 Py_VISIT(self->cache_info_type);
988 Py_VISIT(self->dict);
989 return 0;
990 }
991
992 static int
993 lru_cache_tp_clear(lru_cache_object *self)
994 {
995 lru_list_elem *list = lru_cache_unlink_list(self);
996 Py_CLEAR(self->maxsize_O);
997 Py_CLEAR(self->func);
998 Py_CLEAR(self->cache);
999 Py_CLEAR(self->cache_info_type);
1000 Py_CLEAR(self->dict);
1001 lru_cache_clear_list(list);
1002 return 0;
1003 }
1004
1005
1006 PyDoc_STRVAR(lru_cache_doc,
1007 "Create a cached callable that wraps another function.\n\
1008 \n\
1009 user_function: the function being cached\n\
1010 \n\
1011 maxsize: 0 for no caching\n\
1012 None for unlimited cache size\n\
1013 n for a bounded cache\n\
1014 \n\
1015 typed: False cache f(3) and f(3.0) as identical calls\n\
1016 True cache f(3) and f(3.0) as distinct calls\n\
1017 \n\
1018 cache_info_type: namedtuple class with the fields:\n\
1019 hits misses currsize maxsize\n"
1020 );
1021
1022 static PyMethodDef lru_cache_methods[] = {
1023 {"cache_info", (PyCFunction)lru_cache_cache_info, METH_NOARGS},
1024 {"cache_clear", (PyCFunction)lru_cache_cache_clear, METH_NOARGS},
1025 {NULL}
1026 };
1027
1028 static PyGetSetDef lru_cache_getsetlist[] = {
1029 {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
1030 {NULL}
1031 };
1032
1033 static PyTypeObject lru_cache_type = {
1034 PyVarObject_HEAD_INIT(NULL, 0)
1035 "functools._lru_cache_wrapper", /* tp_name */
1036 sizeof(lru_cache_object), /* tp_basicsize */
1037 0, /* tp_itemsize */
1038 /* methods */
1039 (destructor)lru_cache_dealloc, /* tp_dealloc */
1040 0, /* tp_print */
1041 0, /* tp_getattr */
1042 0, /* tp_setattr */
1043 0, /* tp_reserved */
1044 0, /* tp_repr */
1045 0, /* tp_as_number */
1046 0, /* tp_as_sequence */
1047 0, /* tp_as_mapping */
1048 0, /* tp_hash */
1049 (ternaryfunc)lru_cache_call, /* tp_call */
1050 0, /* tp_str */
1051 0, /* tp_getattro */
1052 0, /* tp_setattro */
1053 0, /* tp_as_buffer */
1054 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC,
1055 /* tp_flags */
1056 lru_cache_doc, /* tp_doc */
1057 (traverseproc)lru_cache_tp_traverse,/* tp_traverse */
1058 (inquiry)lru_cache_tp_clear, /* tp_clear */
1059 0, /* tp_richcompare */
1060 0, /* tp_weaklistoffset */
1061 0, /* tp_iter */
1062 0, /* tp_iternext */
1063 lru_cache_methods, /* tp_methods */
1064 0, /* tp_members */
1065 lru_cache_getsetlist, /* tp_getset */
1066 0, /* tp_base */
1067 0, /* tp_dict */
1068 0, /* tp_descr_get */
1069 0, /* tp_descr_set */
1070 offsetof(lru_cache_object, dict), /* tp_dictoffset */
1071 0, /* tp_init */
1072 0, /* tp_alloc */
1073 lru_cache_new, /* tp_new */
1074 };
1075
543 /* module level code ********************************************************/ 1076 /* module level code ********************************************************/
544 1077
545 PyDoc_STRVAR(module_doc, 1078 PyDoc_STRVAR(module_doc,
546 "Tools that operate on functions."); 1079 "Tools that operate on functions.");
547 1080
548 static PyMethodDef module_methods[] = { 1081 static PyMethodDef module_methods[] = {
549 {"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc }, 1082 {"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc },
550 {"cmp_to_key", (PyCFunction)functools_cmp_to_key, 1083 {"cmp_to_key", (PyCFunction)functools_cmp_to_key,
551 METH_VARARGS | METH_KEYWORDS, functools_cmp_to_key_doc}, 1084 METH_VARARGS | METH_KEYWORDS, functools_cmp_to_key_doc},
552 {NULL, NULL} /* sentinel */ 1085 {NULL, NULL} /* sentinel */
553 }; 1086 };
554 1087
1088 static void
1089 module_free(void *m)
1090 {
1091 Py_CLEAR(kwd_mark);
1092 }
555 1093
556 static struct PyModuleDef _functoolsmodule = { 1094 static struct PyModuleDef _functoolsmodule = {
557 PyModuleDef_HEAD_INIT, 1095 PyModuleDef_HEAD_INIT,
558 "_functools", 1096 "_functools",
559 module_doc, 1097 module_doc,
560 -1, 1098 -1,
561 module_methods, 1099 module_methods,
562 NULL, 1100 NULL,
563 NULL, 1101 NULL,
564 NULL, 1102 NULL,
565 NULL 1103 module_free,
566 }; 1104 };
567 1105
568 PyMODINIT_FUNC 1106 PyMODINIT_FUNC
569 PyInit__functools(void) 1107 PyInit__functools(void)
570 { 1108 {
571 int i; 1109 int i;
572 PyObject *m; 1110 PyObject *m;
573 char *name; 1111 char *name;
574 PyTypeObject *typelist[] = { 1112 PyTypeObject *typelist[] = {
575 &partial_type, 1113 &partial_type,
1114 &lru_cache_type,
576 NULL 1115 NULL
577 }; 1116 };
578 1117
579 m = PyModule_Create(&_functoolsmodule); 1118 m = PyModule_Create(&_functoolsmodule);
580 if (m == NULL) 1119 if (m == NULL)
581 return NULL; 1120 return NULL;
1121
1122 kwd_mark = PyObject_CallObject((PyObject *)&PyBaseObject_Type, NULL);
1123 if (!kwd_mark) {
1124 Py_DECREF(m);
1125 return NULL;
1126 }
582 1127
583 for (i=0 ; typelist[i] != NULL ; i++) { 1128 for (i=0 ; typelist[i] != NULL ; i++) {
584 if (PyType_Ready(typelist[i]) < 0) { 1129 if (PyType_Ready(typelist[i]) < 0) {
585 Py_DECREF(m); 1130 Py_DECREF(m);
586 return NULL; 1131 return NULL;
587 } 1132 }
588 name = strchr(typelist[i]->tp_name, '.'); 1133 name = strchr(typelist[i]->tp_name, '.');
589 assert (name != NULL); 1134 assert (name != NULL);
590 Py_INCREF(typelist[i]); 1135 Py_INCREF(typelist[i]);
591 PyModule_AddObject(m, name+1, (PyObject *)typelist[i]); 1136 PyModule_AddObject(m, name+1, (PyObject *)typelist[i]);
592 } 1137 }
593 return m; 1138 return m;
594 } 1139 }
OLDNEW
« no previous file with comments | « Lib/test/test_functools.py ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+