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

Delta Between Two Patch Sets: Modules/_functoolsmodule.c

Issue 14373: C implementation of functools.lru_cache
Left Patch Set: Created 5 years, 12 months ago
Right Patch Set: Created 4 years, 4 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:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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 */
11 11
12 /* partial object **********************************************************/ 12 /* partial object **********************************************************/
13 13
14 typedef struct { 14 typedef struct {
15 PyObject_HEAD 15 PyObject_HEAD
16 PyObject *fn; 16 PyObject *fn;
17 PyObject *args; 17 PyObject *args;
18 PyObject *kw; 18 PyObject *kw;
19 PyObject *dict; 19 PyObject *dict;
20 PyObject *weakreflist; /* List of weak references */ 20 PyObject *weakreflist; /* List of weak references */
21 } partialobject; 21 } partialobject;
22 22
23 static PyTypeObject partial_type; 23 static PyTypeObject partial_type;
24 24
25 static PyObject * 25 static PyObject *
26 partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) 26 partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
27 { 27 {
28 PyObject *func; 28 PyObject *func, *pargs, *nargs, *pkw;
29 partialobject *pto; 29 partialobject *pto;
30 30
31 if (PyTuple_GET_SIZE(args) < 1) { 31 if (PyTuple_GET_SIZE(args) < 1) {
32 PyErr_SetString(PyExc_TypeError, 32 PyErr_SetString(PyExc_TypeError,
33 "type 'partial' takes at least one argument"); 33 "type 'partial' takes at least one argument");
34 return NULL; 34 return NULL;
35 } 35 }
36 36
37 pargs = pkw = Py_None;
37 func = PyTuple_GET_ITEM(args, 0); 38 func = PyTuple_GET_ITEM(args, 0);
39 if (Py_TYPE(func) == &partial_type && type == &partial_type) {
40 partialobject *part = (partialobject *)func;
41 if (part->dict == NULL) {
42 pargs = part->args;
43 pkw = part->kw;
44 func = part->fn;
45 }
46 }
38 if (!PyCallable_Check(func)) { 47 if (!PyCallable_Check(func)) {
39 PyErr_SetString(PyExc_TypeError, 48 PyErr_SetString(PyExc_TypeError,
40 "the first argument must be callable"); 49 "the first argument must be callable");
41 return NULL; 50 return NULL;
42 } 51 }
43 52
44 /* create partialobject structure */ 53 /* create partialobject structure */
45 pto = (partialobject *)type->tp_alloc(type, 0); 54 pto = (partialobject *)type->tp_alloc(type, 0);
46 if (pto == NULL) 55 if (pto == NULL)
47 return NULL; 56 return NULL;
48 57
49 pto->fn = func; 58 pto->fn = func;
50 Py_INCREF(func); 59 Py_INCREF(func);
51 pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); 60
52 if (pto->args == NULL) { 61 nargs = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX);
62 if (nargs == NULL) {
63 pto->args = NULL;
53 pto->kw = NULL; 64 pto->kw = NULL;
54 Py_DECREF(pto); 65 Py_DECREF(pto);
55 return NULL; 66 return NULL;
56 } 67 }
68 if (pargs == Py_None || PyTuple_GET_SIZE(pargs) == 0) {
69 pto->args = nargs;
70 Py_INCREF(nargs);
71 }
72 else if (PyTuple_GET_SIZE(nargs) == 0) {
73 pto->args = pargs;
74 Py_INCREF(pargs);
75 }
76 else {
77 pto->args = PySequence_Concat(pargs, nargs);
78 if (pto->args == NULL) {
79 pto->kw = NULL;
80 Py_DECREF(pto);
81 return NULL;
82 }
83 }
84 Py_DECREF(nargs);
85
57 if (kw != NULL) { 86 if (kw != NULL) {
58 pto->kw = PyDict_Copy(kw); 87 if (pkw == Py_None) {
88 pto->kw = PyDict_Copy(kw);
89 }
90 else {
91 pto->kw = PyDict_Copy(pkw);
92 if (pto->kw != NULL) {
93 if (PyDict_Merge(pto->kw, kw, 1) != 0) {
94 Py_DECREF(pto);
95 return NULL;
96 }
97 }
98 }
59 if (pto->kw == NULL) { 99 if (pto->kw == NULL) {
60 Py_DECREF(pto); 100 Py_DECREF(pto);
61 return NULL; 101 return NULL;
62 } 102 }
63 } else { 103 }
64 pto->kw = Py_None; 104 else {
65 Py_INCREF(Py_None); 105 if (pkw == Py_None) {
106 pto->kw = PyDict_New();
107 if (pto->kw == NULL) {
108 Py_DECREF(pto);
109 return NULL;
110 }
111 }
112 else {
113 pto->kw = pkw;
114 Py_INCREF(pkw);
115 }
66 } 116 }
67 117
68 pto->weakreflist = NULL; 118 pto->weakreflist = NULL;
69 pto->dict = NULL; 119 pto->dict = NULL;
70 120
71 return (PyObject *)pto; 121 return (PyObject *)pto;
72 } 122 }
73 123
74 static void 124 static void
75 partial_dealloc(partialobject *pto) 125 partial_dealloc(partialobject *pto)
(...skipping 648 matching lines...) Expand 10 before | Expand all | Expand 10 after
724 } 774 }
725 if (PyErr_Occurred()) { 775 if (PyErr_Occurred()) {
726 Py_DECREF(key); 776 Py_DECREF(key);
727 return NULL; 777 return NULL;
728 } 778 }
729 result = PyObject_Call(self->func, args, kwds); 779 result = PyObject_Call(self->func, args, kwds);
730 if (!result) { 780 if (!result) {
731 Py_DECREF(key); 781 Py_DECREF(key);
732 return NULL; 782 return NULL;
733 } 783 }
734 if (PyDict_SetItem(self->cache, key, result) < 0) { 784 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); 785 Py_DECREF(result);
736 Py_DECREF(key); 786 Py_DECREF(key);
737 return NULL; 787 return NULL;
738 } 788 }
739 Py_DECREF(key); 789 Py_DECREF(key);
740 self->misses++; 790 self->misses++;
741 return result; 791 return result;
742 } 792 }
743 793
744 static void 794 static void
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
804 /* Keep a reference to the old key and old result to 854 /* Keep a reference to the old key and old result to
805 prevent their ref counts from going to zero during the 855 prevent their ref counts from going to zero during the
806 update. That will prevent potentially arbitrary object 856 update. That will prevent potentially arbitrary object
807 clean-up code (i.e. __del__) from running while we're 857 clean-up code (i.e. __del__) from running while we're
808 still adjusting the links. */ 858 still adjusting the links. */
809 oldkey = link->key; 859 oldkey = link->key;
810 oldresult = link->result; 860 oldresult = link->result;
811 861
812 link->key = key; 862 link->key = key;
813 link->result = result; 863 link->result = result;
814 if (PyDict_SetItem(self->cache, key, (PyObject *)link) < 0) { 864 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); 865 Py_DECREF(link);
816 Py_DECREF(oldkey); 866 Py_DECREF(oldkey);
817 Py_DECREF(oldresult); 867 Py_DECREF(oldresult);
818 return NULL; 868 return NULL;
819 } 869 }
820 lru_cache_append_link(self, link); 870 lru_cache_append_link(self, link);
821 Py_INCREF(result); /* for return */ 871 Py_INCREF(result); /* for return */
822 Py_DECREF(oldkey); 872 Py_DECREF(oldkey);
823 Py_DECREF(oldresult); 873 Py_DECREF(oldresult);
824 } else { 874 } else {
(...skipping 17 matching lines...) Expand all
842 Py_INCREF(result); /* for return */ 892 Py_INCREF(result); /* for return */
843 self->full = (PyDict_Size(self->cache) >= self->maxsize); 893 self->full = (PyDict_Size(self->cache) >= self->maxsize);
844 } 894 }
845 self->misses++; 895 self->misses++;
846 return result; 896 return result;
847 } 897 }
848 898
849 static PyObject * 899 static PyObject *
850 lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw) 900 lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw)
851 { 901 {
852 PyObject *func, *maxsize_O, *cache_info_type; 902 PyObject *func, *maxsize_O, *cache_info_type, *cachedict;
853 int typed; 903 int typed;
854 lru_cache_object *obj; 904 lru_cache_object *obj;
855 Py_ssize_t maxsize; 905 Py_ssize_t maxsize;
856 PyObject *(*wrapper)(lru_cache_object *, PyObject *, PyObject *); 906 PyObject *(*wrapper)(lru_cache_object *, PyObject *, PyObject *);
857 static char *keywords[] = {"user_function", "maxsize", "typed", 907 static char *keywords[] = {"user_function", "maxsize", "typed",
858 "cache_info_type", NULL}; 908 "cache_info_type", NULL};
859 909
860 if (!PyArg_ParseTupleAndKeywords(args, kw, "OOpO:lru_cache", keywords, 910 if (!PyArg_ParseTupleAndKeywords(args, kw, "OOpO:lru_cache", keywords,
861 &func, &maxsize_O, &typed, 911 &func, &maxsize_O, &typed,
862 &cache_info_type)) { 912 &cache_info_type)) {
863 return NULL; 913 return NULL;
864 } 914 }
865 915
866 if (!PyCallable_Check(func)) { 916 if (!PyCallable_Check(func)) {
867 PyErr_SetString(PyExc_TypeError, 917 PyErr_SetString(PyExc_TypeError,
868 "the first argument must be callable"); 918 "the first argument must be callable");
869 return NULL; 919 return NULL;
870 } 920 }
871 921
872 /* select the caching function, and make/inc maxsize_O */ 922 /* select the caching function, and make/inc maxsize_O */
873 if (maxsize_O == Py_None) { 923 if (maxsize_O == Py_None) {
874 wrapper = infinite_lru_cache_wrapper; 924 wrapper = infinite_lru_cache_wrapper;
875 /* use this only to initialize lru_cache_object attribute maxsize */ 925 /* use this only to initialize lru_cache_object attribute maxsize */
876 maxsize = -1; 926 maxsize = -1;
877 } else if (PyNumber_Check(maxsize_O)) { 927 } else if (PyIndex_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); 928 maxsize = PyNumber_AsSsize_t(maxsize_O, PyExc_OverflowError);
879 if (maxsize == -1 && PyErr_Occurred()) 929 if (maxsize == -1 && PyErr_Occurred())
880 return NULL; 930 return NULL;
881 if (maxsize == 0) 931 if (maxsize == 0)
882 wrapper = uncached_lru_cache_wrapper; 932 wrapper = uncached_lru_cache_wrapper;
883 else 933 else
884 wrapper = bounded_lru_cache_wrapper; 934 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 { 935 } else {
886 PyErr_SetString(PyExc_TypeError, "maxsize should be integer or None"); 936 PyErr_SetString(PyExc_TypeError, "maxsize should be integer or None");
887 return NULL; 937 return NULL;
888 } 938 }
889 939
940 if (!(cachedict = PyDict_New()))
941 return NULL;
942
890 obj = (lru_cache_object *)type->tp_alloc(type, 0); 943 obj = (lru_cache_object *)type->tp_alloc(type, 0);
891 if (obj == NULL) 944 if (obj == NULL) {
892 return NULL; 945 Py_DECREF(cachedict);
893 946 return NULL;
894 if (!(obj->cache = PyDict_New())) { 947 }
895 Py_DECREF(obj); 948
896 return NULL; 949 obj->cache = cachedict;
897 }
898
899 obj->root.prev = &obj->root; 950 obj->root.prev = &obj->root;
900 obj->root.next = &obj->root; 951 obj->root.next = &obj->root;
901 obj->maxsize = maxsize; 952 obj->maxsize = maxsize;
902 Py_INCREF(maxsize_O); 953 Py_INCREF(maxsize_O);
903 obj->maxsize_O = maxsize_O; 954 obj->maxsize_O = maxsize_O;
904 Py_INCREF(func); 955 Py_INCREF(func);
905 obj->func = func; 956 obj->func = func;
906 obj->wrapper = wrapper; 957 obj->wrapper = wrapper;
907 obj->misses = obj->hits = 0; 958 obj->misses = obj->hits = 0;
908 obj->typed = typed; 959 obj->typed = typed;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
944 Py_XDECREF(obj->dict); 995 Py_XDECREF(obj->dict);
945 Py_XDECREF(obj->cache_info_type); 996 Py_XDECREF(obj->cache_info_type);
946 lru_cache_clear_list(list); 997 lru_cache_clear_list(list);
947 Py_TYPE(obj)->tp_free(obj); 998 Py_TYPE(obj)->tp_free(obj);
948 } 999 }
949 1000
950 static PyObject * 1001 static PyObject *
951 lru_cache_call(lru_cache_object *self, PyObject *args, PyObject *kwds) 1002 lru_cache_call(lru_cache_object *self, PyObject *args, PyObject *kwds)
952 { 1003 {
953 return self->wrapper(self, args, kwds); 1004 return self->wrapper(self, args, kwds);
1005 }
1006
1007 static PyObject *
1008 lru_cache_descr_get(PyObject *self, PyObject *obj, PyObject *type)
1009 {
1010 if (obj == Py_None || obj == NULL) {
1011 Py_INCREF(self);
1012 return self;
1013 }
1014 return PyMethod_New(self, obj);
954 } 1015 }
955 1016
956 static PyObject * 1017 static PyObject *
957 lru_cache_cache_info(lru_cache_object *self, PyObject *unused) 1018 lru_cache_cache_info(lru_cache_object *self, PyObject *unused)
958 { 1019 {
959 return PyObject_CallFunction(self->cache_info_type, "nnOn", 1020 return PyObject_CallFunction(self->cache_info_type, "nnOn",
960 self->hits, self->misses, self->maxsize_O, 1021 self->hits, self->misses, self->maxsize_O,
961 PyDict_Size(self->cache)); 1022 PyDict_Size(self->cache));
962 } 1023 }
963 1024
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
1058 (inquiry)lru_cache_tp_clear, /* tp_clear */ 1119 (inquiry)lru_cache_tp_clear, /* tp_clear */
1059 0, /* tp_richcompare */ 1120 0, /* tp_richcompare */
1060 0, /* tp_weaklistoffset */ 1121 0, /* tp_weaklistoffset */
1061 0, /* tp_iter */ 1122 0, /* tp_iter */
1062 0, /* tp_iternext */ 1123 0, /* tp_iternext */
1063 lru_cache_methods, /* tp_methods */ 1124 lru_cache_methods, /* tp_methods */
1064 0, /* tp_members */ 1125 0, /* tp_members */
1065 lru_cache_getsetlist, /* tp_getset */ 1126 lru_cache_getsetlist, /* tp_getset */
1066 0, /* tp_base */ 1127 0, /* tp_base */
1067 0, /* tp_dict */ 1128 0, /* tp_dict */
1068 0, /* tp_descr_get */ 1129 lru_cache_descr_get, /* tp_descr_get */
1069 0, /* tp_descr_set */ 1130 0, /* tp_descr_set */
1070 offsetof(lru_cache_object, dict), /* tp_dictoffset */ 1131 offsetof(lru_cache_object, dict), /* tp_dictoffset */
1071 0, /* tp_init */ 1132 0, /* tp_init */
1072 0, /* tp_alloc */ 1133 0, /* tp_alloc */
1073 lru_cache_new, /* tp_new */ 1134 lru_cache_new, /* tp_new */
1074 }; 1135 };
1075 1136
1076 /* module level code ********************************************************/ 1137 /* module level code ********************************************************/
1077 1138
1078 PyDoc_STRVAR(module_doc, 1139 PyDoc_STRVAR(module_doc,
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1130 Py_DECREF(m); 1191 Py_DECREF(m);
1131 return NULL; 1192 return NULL;
1132 } 1193 }
1133 name = strchr(typelist[i]->tp_name, '.'); 1194 name = strchr(typelist[i]->tp_name, '.');
1134 assert (name != NULL); 1195 assert (name != NULL);
1135 Py_INCREF(typelist[i]); 1196 Py_INCREF(typelist[i]);
1136 PyModule_AddObject(m, name+1, (PyObject *)typelist[i]); 1197 PyModule_AddObject(m, name+1, (PyObject *)typelist[i]);
1137 } 1198 }
1138 return m; 1199 return m;
1139 } 1200 }
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

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