]> granicus.if.org Git - python/commitdiff
Apply (my) patch:
authorMichael W. Hudson <mwh@python.net>
Wed, 6 Mar 2002 17:07:49 +0000 (17:07 +0000)
committerMichael W. Hudson <mwh@python.net>
Wed, 6 Mar 2002 17:07:49 +0000 (17:07 +0000)
[ 526072 ] pickling os.stat results round II

structseq's constructors can now take "invisible" fields in a dict.
Gave the constructors better error messages.
their __reduce__ method puts these fields in a dict.

(this is all in aid of getting os.stat_result's to pickle portably)

Also fixes

[ 526039 ] devious code can crash structseqs

Thought needed about how much of this counts as a bugfix.  Certainly
#526039 needs to be fixed.

Objects/structseq.c

index e5f8e09cf45a0a1ff325763dac12b47d7af71c2b..9228e0fdf500ba9ccd847af8d2605257c59c22d6 100644 (file)
@@ -84,39 +84,79 @@ static PyObject *
 structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
        PyObject *arg = NULL;
+       PyObject *dict = NULL;
+       PyObject *ob;
        PyStructSequence *res = NULL;
-       int len, required_len, i;
-       static char *kwlist[] = {"sequence", 0};
-       static char msgbuf[128];
+       int len, min_len, max_len, i;
+       static char *kwlist[] = {"sequence", "dict", 0};
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:structseq", 
-                                        kwlist, &arg))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:structseq", 
+                                        kwlist, &arg, &dict))
                return NULL;
 
-       if (!PySequence_Check(arg)) {
-               PyErr_SetString(PyExc_TypeError,
-                               "constructor requires a sequence");
+       arg = PySequence_Fast(arg, "constructor requires a sequence");
+
+       if (!arg) {                             
                return NULL;
        }
 
-       len = PySequence_Length(arg);
-       required_len = REAL_SIZE_TP(type);
-       if (len != required_len) {
-               PyOS_snprintf(
-                       msgbuf, sizeof(msgbuf),
-                       "constructor takes exactly %d arguments (%d given)",
-                       required_len,
-                       len);
-               PyErr_SetString(PyExc_TypeError, msgbuf);
+       if (dict && !PyDict_Check(dict)) {
+               PyErr_Format(PyExc_TypeError, 
+                            "%.500s() takes a dict as second arg, if any",
+                            type->tp_name);
+               Py_DECREF(arg);
                return NULL;
        }
 
+       len = PySequence_Fast_GET_SIZE(arg);
+       min_len = VISIBLE_SIZE_TP(type);
+       max_len = REAL_SIZE_TP(type);
+
+       if (min_len != max_len) {
+               if (len < min_len) {
+                       PyErr_Format(PyExc_TypeError, 
+              "%.500s() takes an at least %d-sequence (%d-sequence given)",
+                                    type->tp_name, min_len, len);
+                       Py_DECREF(arg);
+                       return NULL;
+               }
+
+               if (len > max_len) {
+                       PyErr_Format(PyExc_TypeError, 
+              "%.500s() takes an at most %d-sequence (%d-sequence given)",
+                                    type->tp_name, max_len, len);
+                       Py_DECREF(arg);
+                       return NULL;
+               }
+       } 
+       else {
+               if (len != min_len) {
+                       PyErr_Format(PyExc_TypeError, 
+              "%.500s() takes a %d-sequence (%d-sequence given)",
+                                    type->tp_name, min_len, len);
+                       Py_DECREF(arg);
+                       return NULL;
+               }
+       }
+
        res = (PyStructSequence*) PyStructSequence_New(type);
        for (i = 0; i < len; ++i) {
-               /* INCREF???? XXXX */
-               res->ob_item[i] = PySequence_GetItem(arg, i);
+               PyObject *v = PySequence_Fast_GET_ITEM(arg, i);
+               Py_INCREF(v);
+               res->ob_item[i] = v;
+       }
+       for (; i < max_len; ++i) {
+               if (dict && (ob = PyDict_GetItemString(
+                       dict, type->tp_members[i].name))) {
+               }
+               else {
+                       ob = Py_None;
+               }
+               Py_INCREF(ob);
+               res->ob_item[i] = ob;
        }
        
+       Py_DECREF(arg);
        return (PyObject*) res;
 }
 
@@ -192,21 +232,34 @@ static PyObject *
 structseq_reduce(PyStructSequence* self)
 {
        PyObject* tup;
-       long n_fields;
+       PyObject* dict;
+       long n_fields, n_visible_fields;
        int i;
        
        n_fields = REAL_SIZE(self);
-       tup = PyTuple_New(n_fields);
+       n_visible_fields = VISIBLE_SIZE(self);
+       tup = PyTuple_New(n_visible_fields);
        if (!tup) {
                return NULL;
        }
 
-       for (i = 0; i < n_fields; i++) {
+       dict = PyDict_New();
+       if (!dict) {
+               Py_DECREF(tup);
+               return NULL;
+       }
+
+       for (i = 0; i < n_visible_fields; i++) {
                Py_INCREF(self->ob_item[i]);
                PyTuple_SET_ITEM(tup, i, self->ob_item[i]);
        }
        
-       return Py_BuildValue("(O(O))", self->ob_type, tup);
+       for (; i < n_fields; i++) {
+               PyDict_SetItemString(dict, self->ob_type->tp_members[i].name,
+                                    self->ob_item[i]);
+       }
+
+       return Py_BuildValue("(O(OO))", self->ob_type, tup, dict);
 }
 
 static PySequenceMethods structseq_as_sequence = {