[Patches] [1.6] dictionary objects: new method 'supplement'

Peter Funk pf@artcom-gmbh.de
Mon, 3 Apr 2000 11:55:41 +0200 (MEST)


Dear Python patcher!

Please consider to apply the patch appended below and commit into the CVS tree.
It applies to: Python 1.6a1 as released on april 1st.
--=-- argument: --=--=--=--=--=--=--=--=--=--=-->8--=-
This patch adds a new method to dictionary and UserDict objects:
'.supplement()' is a "sibling" of '.update()', but it add only
those items that are not already there instead of replacing them.

This idea has been discussed on python-dev last month.
--=-- obligatory disclaimer: -=--=--=--=--=--=-->8--=-
I confirm that, to the best of my knowledge and belief, this
contribution is free of any claims of third parties under
copyright, patent or other rights or interests ("claims").  To
the extent that I have any such claims, I hereby grant to CNRI a
nonexclusive, irrevocable, royalty-free, worldwide license to
reproduce, distribute, perform and/or display publicly, prepare
derivative versions, and otherwise use this contribution as part
of the Python software and its related documentation, or any
derivative versions thereof, at no cost to CNRI or its licensed
users, and to authorize others to do so.

I acknowledge that CNRI may, at its sole discretion, decide
whether or not to incorporate this contribution in the Python
software and its related documentation.  I further grant CNRI
permission to use my name and other identifying information
provided to CNRI by me for use in connection with the Python
software and its related documentation.
--=-- dry signature: =--=--=--=--=--=--=--=--=-->8--=-
Regards, Peter
-- 
Peter Funk, Oldenburger Str.86, D-27777 Ganderkesee, Germany, Fax:+49 4222950260
office: +49 421 20419-0 (ArtCom GmbH, Grazer Str.8, D-28359 Bremen)
--=-- patch: --=--=--=--=--=--=--=--=--=--=--=-->8--=-
*** ../../cvs_01_04_00_orig/dist/src/Objects/dictobject.c	Fri Mar 31 11:45:02 2000
--- src/Objects/dictobject.c	Mon Apr  3 10:30:11 2000
***************
*** 734,739 ****
--- 734,781 ----
  }
  
  static PyObject *
+ dict_supplement(mp, args)
+       register dictobject *mp;
+       PyObject *args;
+ {
+ 	register int i;
+ 	dictobject *other;
+         dictentry *entry, *oldentry;
+ 	if (!PyArg_Parse(args, "O!", &PyDict_Type, &other))
+ 		return NULL;
+ 	if (other == mp)
+ 		goto done; /* a.supplement(a); nothing to do */
+ 	/* Do one big resize at the start, rather than incrementally
+ 	   resizing as we insert new items.  Expect that there will be
+ 	   no (or few) overlapping keys. */
+ 	if ((mp->ma_fill + other->ma_used)*3 >= mp->ma_size*2) {
+ 		if (dictresize(mp, (mp->ma_used + other->ma_used)*3/2) != 0)
+ 			return NULL;
+ 	}
+ 	for (i = 0; i < other->ma_size; i++) {
+ 		entry = &other->ma_table[i];
+ 		if (entry->me_value != NULL) {
+ 			oldentry = lookdict(mp, entry->me_key, entry->me_hash);
+ 			if (oldentry->me_value == NULL) {
+ 				/* TODO: optimize:
+ 				   'insertdict' does another call to 'lookdict'.
+ 				   But for sake of readability and symmetry with
+ 				   'dict_update' I didn't tried to avoid this.
+ 				   At least not now as we go into 1.6 alpha. */
+ 				Py_INCREF(entry->me_key);
+ 				Py_INCREF(entry->me_value);
+ 				insertdict(mp, entry->me_key, entry->me_hash,
+ 					   entry->me_value);
+ 			}
+ 		}
+ 	}
+   done:
+ 	Py_INCREF(Py_None);
+ 	return Py_None;
+ }
+ 
+ 
+ static PyObject *
  dict_copy(mp, args)
        register dictobject *mp;
        PyObject *args;
***************
*** 1045,1050 ****
--- 1087,1093 ----
  	{"clear",	(PyCFunction)dict_clear},
  	{"copy",	(PyCFunction)dict_copy},
  	{"get",         (PyCFunction)dict_get,          METH_VARARGS},
+ 	{"supplement",	(PyCFunction)dict_supplement},
  	{NULL,		NULL}		/* sentinel */
  };
  
*** ../../cvs_01_04_00_orig/dist/src/Lib/test/test_types.py	Wed Feb 23 23:23:17 2000
--- src/Lib/test/test_types.py	Mon Apr  3 10:41:53 2000
***************
*** 242,247 ****
--- 242,250 ----
  d.update({2:20})
  d.update({1:1, 2:2, 3:3})
  if d != {1:1, 2:2, 3:3}: raise TestFailed, 'dict update'
+ d.supplement({1:"not", 2:"neither", 4:4})
+ if d != {1:1, 2:2, 3:3, 4:4}: raise TestFailed, 'dict supplement'
+ del d[4]
  if d.copy() != {1:1, 2:2, 3:3}: raise TestFailed, 'dict copy'
  if {}.copy() != {}: raise TestFailed, 'empty dict copy'
  # dict.get()
*** ../../cvs_01_04_00_orig/dist/src/Lib/UserDict.py	Wed Feb  2 16:10:14 2000
--- src/Lib/UserDict.py	Mon Apr  3 10:45:17 2000
***************
*** 32,36 ****
--- 32,45 ----
          else:
              for k, v in dict.items():
                  self.data[k] = v
+     def supplement(self, dict):
+         if isinstance(dict, UserDict):
+             self.data.supplement(dict.data)
+         elif isinstance(dict, type(self.data)):
+             self.data.supplement(dict)
+         else:
+             for k, v in dict.items():
+ 		if not self.data.has_key(k):
+ 		    self.data[k] = v
      def get(self, key, failobj=None):
          return self.data.get(key, failobj)
*** ../../cvs_01_04_00_orig/dist/src/Lib/test/test_userdict.py	Fri Mar 26 16:32:02 1999
--- src/Lib/test/test_userdict.py	Mon Apr  3 10:50:29 2000
***************
*** 93,101 ****
--- 93,109 ----
  t.update(u2)
  assert t == u2
  
+ # Test supplement
+ 
+ t = UserDict(d1)
+ t.supplement(u2)
+ assert t == u2
+ 
  # Test get
  
  for i in u2.keys():
      assert u2.get(i) == u2[i]
      assert u1.get(i) == d1.get(i)
      assert u0.get(i) == d0.get(i)
+ 
+ # TODO: Add a test using dir({}) to test for unimplemented methods