]> granicus.if.org Git - python/commitdiff
Make dictionary() a real constructor. Accepts at most one argument, "a
authorTim Peters <tim.peters@gmail.com>
Sun, 2 Sep 2001 08:22:48 +0000 (08:22 +0000)
committerTim Peters <tim.peters@gmail.com>
Sun, 2 Sep 2001 08:22:48 +0000 (08:22 +0000)
mapping object", in the same sense dict.update(x) requires of x (that x
has a keys() method and a getitem).
Questionable:  The other type constructors accept a keyword argument, so I
did that here too (e.g., dictionary(mapping={1:2}) works).  But type_call
doesn't pass the keyword args to the tp_new slot (it passes NULL), it only
passes them to the tp_init slot, so getting at them required adding a
tp_init slot to dicts.  Looks like that makes the normal case (i.e., no
args at all) a little slower (the time it takes to call dict.tp_init and
have it figure out there's nothing to do).

Lib/test/test_descr.py
Objects/dictobject.c

index f495a3728d9ae1b3b97f3c76e991d5662930a6ad..f6b9e1be1fd0bf10f322d0361c5f0b52b9e8eee7 100644 (file)
@@ -1,6 +1,6 @@
 # Test descriptor-related enhancements
 
-from test_support import verify, verbose
+from test_support import verify, verbose, TestFailed
 from copy import deepcopy
 
 def testunop(a, res, expr="len(a)", meth="__len__"):
@@ -123,6 +123,55 @@ def dicts():
     verify(eval(d.__repr__(), {}) == d)
     testset2op({1:2,3:4}, 2, 3, {1:2,2:3,3:4}, "a[b]=c", "__setitem__")
 
+def dict_constructor():
+    if verbose:
+        print "Testing dictionary constructor ..."
+    d = dictionary()
+    verify(d == {})
+    d = dictionary({})
+    verify(d == {})
+    d = dictionary(mapping={})
+    verify(d == {})
+    d = dictionary({1: 2, 'a': 'b'})
+    verify(d == {1: 2, 'a': 'b'})
+    for badarg in 0, 0L, 0j, "0", [0], (0,):
+        try:
+            dictionary(badarg)
+        except TypeError:
+            pass
+        else:
+            raise TestFailed("no TypeError from dictionary(%r)" % badarg)
+    try:
+        dictionary(senseless={})
+    except TypeError:
+        pass
+    else:
+        raise TestFailed("no TypeError from dictionary(senseless={}")
+
+    try:
+        dictionary({}, {})
+    except TypeError:
+        pass
+    else:
+        raise TestFailed("no TypeError from dictionary({}, {})")
+
+    class Mapping:
+        dict = {1:2, 3:4, 'a':1j}
+
+        def __getitem__(self, i):
+            return self.dict[i]
+
+    try:
+        dictionary(Mapping())
+    except TypeError:
+        pass
+    else:
+        raise TestFailed("no TypeError from dictionary(incomplete mapping)")
+
+    Mapping.keys = lambda self: self.dict.keys()
+    d = dictionary(mapping=Mapping())
+    verify(d == Mapping.dict)
+
 binops = {
     'add': '+',
     'sub': '-',
@@ -1299,6 +1348,7 @@ def inherits():
 def all():
     lists()
     dicts()
+    dict_constructor()
     ints()
     longs()
     floats()
index e8a564e9309f586da1de6d3a0525a5f0f86eae88..91d2c536ab6bf74d7a93fed386ad7b756346153f 100644 (file)
@@ -1692,12 +1692,39 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        return self;
 }
 
+static int
+dict_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+       PyObject *arg = NULL;
+       static char *kwlist[] = {"mapping", 0};
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:dictionary",
+                                        kwlist, &arg))
+               return -1;
+       if (arg != NULL) {
+               if (PyDict_Merge(self, arg, 1) < 0) {
+                       /* An error like "AttibuteError: keys" is too
+                          cryptic in this context. */
+                       if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+                               PyErr_SetString(PyExc_TypeError,
+                                       "argument must be of a mapping type");
+                       }
+                       return -1;
+               }
+       }
+       return 0;
+}
+
 static PyObject *
 dict_iter(dictobject *dict)
 {
        return dictiter_new(dict, select_key);
 }
 
+static char dictionary_doc[] =
+"dictionary() -> new empty dictionary\n"
+"dictionary(mapping) -> new dict initialized from mapping's key+value pairs";
+
 PyTypeObject PyDict_Type = {
        PyObject_HEAD_INIT(&PyType_Type)
        0,
@@ -1721,7 +1748,7 @@ PyTypeObject PyDict_Type = {
        0,                                      /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
                Py_TPFLAGS_BASETYPE,            /* tp_flags */
-       "dictionary type",                      /* tp_doc */
+       dictionary_doc,                         /* tp_doc */
        (traverseproc)dict_traverse,            /* tp_traverse */
        (inquiry)dict_tp_clear,                 /* tp_clear */
        dict_richcompare,                       /* tp_richcompare */
@@ -1736,7 +1763,7 @@ PyTypeObject PyDict_Type = {
        0,                                      /* tp_descr_get */
        0,                                      /* tp_descr_set */
        0,                                      /* tp_dictoffset */
-       0,                                      /* tp_init */
+       (initproc)dict_init,                    /* tp_init */
        PyType_GenericAlloc,                    /* tp_alloc */
        dict_new,                               /* tp_new */
 };