comparing with http://hg.python.org/cpython searching for changes changeset: 68878:318f73c2e870 tag: tip parent: 68875:267578b2422d user: ysj.ray date: Wed Mar 23 21:26:10 2011 +0800 summary: Issue 9523: updated patch. diff -r 267578b2422d -r 318f73c2e870 Doc/library/dbm.rst --- a/Doc/library/dbm.rst Tue Mar 22 22:57:49 2011 -0700 +++ b/Doc/library/dbm.rst Wed Mar 23 21:26:10 2011 +0800 @@ -63,12 +63,17 @@ The object returned by :func:`.open` supports the same basic functionality as dictionaries; keys and their corresponding values can be stored, retrieved, and -deleted, and the :keyword:`in` operator and the :meth:`keys` method are -available, as well as :meth:`get` and :meth:`setdefault`. +deleted, and the :keyword:`in` operator and the :meth:`keys`, :meth:`values`, +:meth:`items` methods are available, as well as :meth:`get`, :meth:`setdefault`, +:meth:`pop`, :meth:`popitem` :meth:`clear` and :meth:`update`. .. versionchanged:: 3.2 :meth:`get` and :meth:`setdefault` are now available in all database modules. +.. versionchanged:: 3.3 + The :class:`~collections.MutableMapping` interface is now implemented in all + database modules. + Key and values are always stored as bytes. This means that when strings are used they are implicitly converted to the default encoding before being stored. @@ -126,9 +131,7 @@ The :mod:`dbm.gnu` module provides an interface to the GNU DBM library. ``dbm.gnu.gdbm`` objects behave like mappings (dictionaries), except that keys and values are always converted to bytes before storing. Printing a ``gdbm`` -object doesn't print the -keys and values, and the :meth:`items` and :meth:`values` methods are not -supported. +object doesn't print the keys and values. .. exception:: error @@ -228,7 +231,7 @@ The :mod:`dbm.ndbm` module provides an interface to the Unix "(n)dbm" library. Dbm objects behave like mappings (dictionaries), except that keys and values are always stored as bytes. Printing a ``dbm`` object doesn't print the keys and -values, and the :meth:`items` and :meth:`values` methods are not supported. +values. This module can be used with the "classic" ndbm interface or the GNU GDBM compatibility interface. On Unix, the :program:`configure` script will attempt diff -r 267578b2422d -r 318f73c2e870 Include/abstract.h --- a/Include/abstract.h Tue Mar 22 22:57:49 2011 -0700 +++ b/Include/abstract.h Wed Mar 23 21:26:10 2011 +0800 @@ -391,6 +391,14 @@ PyAPI_FUNC(Py_ssize_t) _PyObject_LengthHint(PyObject *o, Py_ssize_t); #endif +#ifndef Py_LIMITED_API + /* helper function for containers */ + + /* Return 1 if self is a subset of other, iterating over self; + 0 if not; -1 if an error occurred. */ + PyAPI_FUNC(int) _PyObject_AllContainedIn(PyObject *self, PyObject *other); +#endif + /* Guess the size of object o using len(o) or o.__length_hint__(). If neither of those return a non-negative value, then return the diff -r 267578b2422d -r 318f73c2e870 Lib/dbm/__init__.py --- a/Lib/dbm/__init__.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/dbm/__init__.py Wed Mar 23 21:26:10 2011 +0800 @@ -41,6 +41,8 @@ import os import struct import sys +import tempfile +from collections import MutableMapping, KeysView, ItemsView, ValuesView class error(Exception): @@ -178,6 +180,18 @@ return "" +def _register_with_abc(dbm_open_func): + with tempfile.NamedTemporaryFile() as tmp_file: + try: + _db = dbm_open_func(tmp_file.name, 'c') + MutableMapping.register(type(_db)) + KeysView.register(type(_db.keys())) + ItemsView.register(type(_db.items())) + ValuesView.register(type(_db.values())) + finally: + _db.close() + + if __name__ == "__main__": for filename in sys.argv[1:]: print(whichdb(filename) or "UNKNOWN", filename) diff -r 267578b2422d -r 318f73c2e870 Lib/dbm/dumb.py --- a/Lib/dbm/dumb.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/dbm/dumb.py Wed Mar 23 21:26:10 2011 +0800 @@ -208,12 +208,6 @@ # XXX why shouldn't __setitem__? self._commit() - def keys(self): - return list(self._index.keys()) - - def items(self): - return [(key, self[key]) for key in self._index.keys()] - def __contains__(self, key): if isinstance(key, str): key = key.encode('utf-8') diff -r 267578b2422d -r 318f73c2e870 Lib/dbm/gnu.py --- a/Lib/dbm/gnu.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/dbm/gnu.py Wed Mar 23 21:26:10 2011 +0800 @@ -1,3 +1,6 @@ """Provide the _gdbm module as a dbm submodule.""" from _gdbm import * +from . import _register_with_abc + +_register_with_abc(open) diff -r 267578b2422d -r 318f73c2e870 Lib/dbm/ndbm.py --- a/Lib/dbm/ndbm.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/dbm/ndbm.py Wed Mar 23 21:26:10 2011 +0800 @@ -1,3 +1,6 @@ """Provide the _dbm module as a dbm submodule.""" from _dbm import * +from . import _register_with_abc + +_register_with_abc(open) diff -r 267578b2422d -r 318f73c2e870 Lib/test/dbm_tests.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/dbm_tests.py Wed Mar 23 21:26:10 2011 +0800 @@ -0,0 +1,299 @@ +from test import support +import os +import unittest +from test.support import TESTFN, run_unittest, unlink +from collections import MutableMapping, KeysView, ItemsView, ValuesView + + +# We need three dbm objects at most at the same time to test. +filenames = filename, filename2, filenam3 = TESTFN, TESTFN + '2', TESTFN + '3' + + +class TestDbmBase(unittest.TestCase): + + _module = None + + def opener(self, fn=filename): + return self._module.open(fn, 'c') + + def setUp(self): + self.d, self.d2, self.d3 = None, None, None + + def _delete_db(self, file_name=filename): + for suffix in ['', '.pag', '.dir', '.dat', '.db']: + support.unlink(file_name + suffix) + + def tearDown(self): + for d in (self.d, self.d2, self.d3): + if d is not None: + self.d.close() + for fn in filenames: + self._delete_db(fn) + + def test_get(self): + self.d = self.opener() + self.d['a'] = 'b' + self.assertEqual(self.d.get('a'), b'b') + self.assertEqual(self.d.get(b'a'), b'b') + self.assertEqual(self.d.get('a', None), b'b') + self.assertIsNone(self.d.get('b')) + self.assertIsNone(self.d.get(b'b')) + self.assertEqual(self.d.get('b', 'c'), 'c') + + def test_contains(self): + self.d = self.opener() + self.d['a'] = 'b' + self.assertIn('a', self.d) + self.assertIn(b'a', self.d) + self.assertNotIn('b', self.d) + + def test_items(self): + self.d = self.opener() + self.assertEqual(self.d.items(), set()) + self.assertEqual(len(self.d.items()), 0) + self.assertNotIn(('a', 'b'), self.d.items()) + self.d['a'] = 'b' + self.d['c'] = 'd' + self.assertIn(('a', b'b'), self.d.items()) + self.assertIn((b'a', b'b'), self.d.items()) + self.assertIn((b'a', 'b'), self.d.items()) + self.assertIn(('a', 'b'), self.d.items()) + self.assertNotIn((b'a', 'c'), self.d.items()) + self.assertNotIn(('a', 'c'), self.d.items()) + self.assertNotIn(('a', None), self.d.items()) + self.assertEqual(self.d.items(), set(((b'a', b'b'), (b'c', b'd')))) + self.assertEqual(len(self.d.items()), 2) + + def test_items_set_operations(self): + self.d, self.d2, self.d3 = map(self.opener, filenames) + self.d.update({'a': '1', 'b': '2'}) + self.d2.update({'a': '2', 'b': '2'}) + self.d3.update({'d': '4', 'e': '5'}) + self.assertEqual( + self.d.items() & self.d.items(), {(b'a', b'1'), (b'b', b'2')}) + self.assertEqual(self.d.items() & self.d2.items(), {(b'b', b'2')}) + self.assertEqual(self.d.items() & self.d3.items(), set()) + self.assertEqual(self.d.items() & set(self.d.items()), + {(b'a', b'1'), (b'b', b'2')}) + self.assertEqual(self.d.items() & set(self.d2.items()), {(b'b', b'2')}) + self.assertEqual(self.d.items() & set(self.d3.items()), set()) + + self.assertEqual(self.d.items() | self.d.items(), + {(b'a', b'1'), (b'b', b'2')}) + self.assertEqual(self.d.items() | self.d2.items(), + {(b'a', b'1'), (b'a', b'2'), (b'b', b'2')}) + self.assertEqual(self.d.items() | self.d3.items(), + {(b'a', b'1'), (b'b', b'2'), (b'd', b'4'), (b'e', b'5')}) + self.assertEqual(self.d.items() | set(self.d.items()), + {(b'a', b'1'), (b'b', b'2')}) + self.assertEqual(self.d.items() | set(self.d2.items()), + {(b'a', b'1'), (b'a', b'2'), (b'b', b'2')}) + self.assertEqual(self.d.items() | set(self.d3.items()), + {(b'a', b'1'), (b'b', b'2'), (b'd', b'4'), (b'e', b'5')}) + + self.assertEqual(self.d.items() ^ self.d.items(), set()) + self.assertEqual(self.d.items() ^ self.d2.items(), + {(b'a', b'1'), (b'a', b'2')}) + self.assertEqual(self.d.items() ^ self.d3.items(), + {(b'a', b'1'), (b'b', b'2'), (b'd', b'4'), (b'e', b'5')}) + + self.assertFalse(self.d.items().isdisjoint(self.d.items())) + self.assertFalse(self.d.items().isdisjoint(self.d2.items())) + self.assertFalse(self.d.items().isdisjoint(list(self.d2.items()))) + self.assertFalse(self.d.items().isdisjoint(set(self.d2.items()))) + self.assertTrue(self.d.items().isdisjoint({'x', 'y', 'z'})) + self.assertTrue(self.d.items().isdisjoint(['x', 'y', 'z'])) + self.assertTrue(self.d.items().isdisjoint(set(['x', 'y', 'z']))) + self.assertTrue(self.d.items().isdisjoint(set(['x', 'y']))) + self.assertTrue(self.d.items().isdisjoint({})) + self.assertTrue(self.d.items().isdisjoint(self.d3.items())) + + self.d.clear() + self.assertTrue(self.d.items().isdisjoint(set())) + self.assertTrue(self.d.items().isdisjoint([])) + self.assertTrue(self.d.items().isdisjoint(self.d.items())) + self.assertTrue(self.d.items().isdisjoint([1])) + + def test_pop(self): + self.d = self.opener() + ar = self.d.pop('a') + self.assertIsNone(self.d.pop('a')) + self.d['a'] = 'b' + self.d['c'] = 'd' + self.assertIsNone(self.d.pop('e')) + self.assertEqual(self.d.keys(), set((b'a', b'c'))) + self.assertIsNone(self.d.pop('e', None)) + self.assertEqual(self.d.keys(), set((b'a', b'c'))) + self.assertEqual(self.d.pop('a'), b'b') + self.assertEqual(set(self.d.keys()), {b'c'}) + self.assertEqual(self.d.pop('c', None), b'd') + self.assertEqual(self.d.keys(), set()) + + def test_popitem(self): + self.d = self.opener() + self.assertRaises(KeyError, self.d.popitem) + self.d['a'] = 'b' + self.assertEqual(self.d.popitem(), (b'a', b'b')) + self.assertEqual(len(self.d), 0) + self.assertRaises(KeyError, self.d.popitem) + + def test_clear(self): + self.d = self.opener() + self.assertEqual(self.d.keys(), set()) + self.d['a'] = 'b' + self.d['c'] = 'c' + self.d.clear() + self.assertEqual(self.d.keys(), set()) + + def test_iteration(self): + self.d = self.opener() + self.assertEqual(self.d.keys(), set()) + self.assertRaises(StopIteration, next, iter(self.d)) + self.d['a'] = 'b' + self.d['c'] = 'd' + gi = iter(self.d) + self.assertEqual(set((next(gi), next(gi))), set((b'a', b'c'))) + self.assertRaises(StopIteration, next, gi) + + def test_keys(self): + self.d = self.opener() + self.assertEqual(self.d.keys(), set()) + self.assertEqual(len(self.d.keys()), 0) + self.d['a'] = 'b' + self.d[b'bytes'] = b'data' + self.d['12345678910'] = '019237410982340912840198242' + self.assertEqual(len(self.d.keys()), 3) + self.assertIn(b'a', self.d) + self.assertNotIn(b'b', self.d) + self.assertEqual(self.d[b'bytes'], b'data') + + def test_keys_set_operations(self): + self.d, self.d2, self.d3 = map(self.opener, filenames) + self.d.update({'a': '1', 'b': '2'}) + self.d2.update({'b': '3', 'c': '2'}) + self.d3.update({'d': '4', 'e': '5'}) + self.assertEqual(self.d.keys() & self.d.keys(), {b'a', b'b'}) + self.assertEqual(self.d.keys() & self.d2.keys(), {b'b'}) + self.assertEqual(self.d.keys() & self.d3.keys(), set()) + self.assertEqual(self.d.keys() & set(self.d.keys()), {b'a', b'b'}) + self.assertEqual(self.d.keys() & set(self.d2.keys()), {b'b'}) + self.assertEqual(self.d.keys() & set(self.d3.keys()), set()) + + self.assertEqual(self.d.keys() | self.d.keys(), {b'a', b'b'}) + self.assertEqual(self.d.keys() | self.d2.keys(), {b'a', b'b', b'c'}) + self.assertEqual(self.d.keys() | self.d3.keys(), {b'a', b'b', b'd', b'e'}) + self.assertEqual(self.d.keys() | set(self.d.keys()), {b'a', b'b'}) + self.assertEqual(self.d.keys() | set(self.d2.keys()), {b'a', b'b', b'c'}) + self.assertEqual(self.d.keys() | set(self.d3.keys()), + {b'a', b'b', b'd', b'e'}) + + self.assertEqual(self.d.keys() ^ self.d.keys(), set()) + self.assertEqual(self.d.keys() ^ self.d2.keys(), {b'a', b'c'}) + self.assertEqual(self.d.keys() ^ self.d3.keys(), {b'a', b'b', b'd', b'e'}) + self.assertEqual(self.d.keys() ^ set(self.d.keys()), set()) + self.assertEqual(self.d.keys() ^ set(self.d2.keys()), {b'a', b'c'}) + self.assertEqual(self.d.keys() ^ set(self.d3.keys()), + {b'a', b'b', b'd', b'e'}) + + self.assertFalse(self.d.keys().isdisjoint(self.d.keys())) + self.assertFalse(self.d.keys().isdisjoint(self.d2.keys())) + self.assertFalse(self.d.keys().isdisjoint(list(self.d2.keys()))) + self.assertFalse(self.d.keys().isdisjoint(set(self.d2.keys()))) + self.assertTrue(self.d.keys().isdisjoint({b'x', b'y', b'z'})) + self.assertTrue(self.d.keys().isdisjoint([b'x', b'y', b'z'])) + self.assertTrue(self.d.keys().isdisjoint(set([b'x', b'y', b'z']))) + self.assertTrue(self.d.keys().isdisjoint(set([b'x', b'y']))) + self.assertTrue(self.d.keys().isdisjoint([b'x', b'y'])) + self.assertTrue(self.d.keys().isdisjoint({})) + self.assertTrue(self.d.keys().isdisjoint(self.d3.keys())) + + self.d.clear() + self.assertTrue(self.d.keys().isdisjoint(set())) + self.assertTrue(self.d.keys().isdisjoint([])) + self.assertTrue(self.d.keys().isdisjoint(self.d.keys())) + self.assertTrue(self.d.keys().isdisjoint(['1'])) + + def test_keys_richcompare(self): + self.d, self.d2 = map(self.opener, filenames[:2]) + self.d['a'] = 'a' + self.d2['a'] = 'a' + keys1, keys2 = self.d.keys(), self.d2.keys() + self.assertEqual(keys1, keys2) + self.d['b'] = 'b' + self.assertNotEqual(keys1, keys2) + self.assertGreater(keys1, keys2) + self.assertGreaterEqual(keys1, keys2) + self.assertLess(keys2, keys1) + self.assertLessEqual(keys2, keys1) + + def test_values(self): + self.d = self.opener() + self.assertEqual(set(self.d.values()), set()) + self.assertEqual(len(self.d.values()), 0) + self.d['a'] = 'b' + self.d['c'] = 'd' + self.assertEqual(set((b'b', b'd')), set(self.d.values())) + self.assertEqual(len(self.d.values()), 2) + + def test_abc(self): + self.d = self.opener() + self.assertIsInstance(self.d, MutableMapping) + self.assertIsInstance(self.d.keys(), KeysView) + self.assertIsInstance(self.d.values(), ValuesView) + self.assertIsInstance(self.d.items(), ItemsView) + + def test_update1(self): + self.d = self.opener() + self.assertEqual(self.d.keys(), set()) + self.d['a'] = 'a' + self.d.update({'b':'b', 'c':'c'}) + self.assertRaises(TypeError, self.d.update, None) + self.assertRaises(TypeError, self.d.update, {1:1}) + self.assertRaises(TypeError, self.d.update, {'b':1}) + self.assertEqual(self.d.items(), set(((b'a', b'a'), (b'b', b'b'), + (b'c', b'c')))) + + def test_update2(self): + self.d = self.opener() + self.assertEqual(self.d.keys(), set()) + self.d['a'] = 'a' + class Other(): + def __init__(self, dic): + self._dict = dic + def keys(self): + return self._dict.keys() + def __getitem__(self, key): + return self._dict[key] + + self.d.update(Other({'b':'b', 'c':'c'})) + self.assertEqual(self.d.items(), set(((b'a', b'a'), (b'b', b'b'), + (b'c', b'c')))) + + def test_update3(self): + self.d = self.opener() + self.assertEqual(self.d.keys(), set()) + self.d['a'] = 'a' + self.d.update({b'b':b'b', b'c':b'c'}.items()) + self.assertEqual(self.d.items(), set(((b'a', b'a'), (b'b', b'b'), + (b'c', b'c')))) + + def test_error_conditions(self): + # Try to open a non-existent database. + self._delete_db() + self.assertRaises(self._module.error, self._module.open, filename, 'r') + # Try to access a closed database. + self.d = self.opener() + self.d.close() + with self.assertRaises(self._module.error): + self.d['a'] + # try pass an invalid open flag + with self.assertRaises(self._module.error): + self._module.open(filename, 'rx').close() + + def test_other(self): + self.d = self.opener() + self.assertEqual(self.d.keys(), set()) + self.d['a'] = 'a' + #self.d.update({b'1':1}.items()) + #self.assertEqual(self.d.items(), set(((b'a', b'a'), (b'b', b'b'), + # (b'c', b'c')))) diff -r 267578b2422d -r 318f73c2e870 Lib/test/test_dbm.py --- a/Lib/test/test_dbm.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/test/test_dbm.py Wed Mar 23 21:26:10 2011 +0800 @@ -64,7 +64,7 @@ def test_anydbm_creation(self): f = dbm.open(_fname, 'c') - self.assertEqual(list(f.keys()), []) + self.assertEqual(f.keys(), set()) for key in self._dict: f[key.encode("ascii")] = self._dict[key] self.read_helper(f) @@ -156,7 +156,7 @@ def test_keys(self): self.d = dbm.open(self.filename, 'c') - self.assertEqual(self.d.keys(), []) + self.assertEqual(self.d.keys(), set()) a = [(b'a', b'b'), (b'12345678910', b'019237410982340912840198242')] for k, v in a: self.d[k] = v diff -r 267578b2422d -r 318f73c2e870 Lib/test/test_dbm_dumb.py --- a/Lib/test/test_dbm_dumb.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/test/test_dbm_dumb.py Wed Mar 23 21:26:10 2011 +0800 @@ -31,10 +31,11 @@ def __init__(self, *args): unittest.TestCase.__init__(self, *args) + self.f = None def test_dumbdbm_creation(self): - f = dumbdbm.open(_fname, 'c') - self.assertEqual(list(f.keys()), []) + self.f = f = dumbdbm.open(_fname, 'c') + self.assertEqual(f.keys(), set()) for key in self._dict: f[key] = self._dict[key] self.read_helper(f) @@ -47,7 +48,7 @@ try: old_umask = os.umask(0o002) - f = dumbdbm.open(_fname, 'c', 0o637) + self.f = f = dumbdbm.open(_fname, 'c', 0o637) f.close() finally: os.umask(old_umask) @@ -65,7 +66,7 @@ self.assertEqual(stat.S_IMODE(st.st_mode), expected_mode) def test_close_twice(self): - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) f[b'a'] = b'b' self.assertEqual(f[b'a'], b'b') f.close() @@ -73,32 +74,44 @@ def test_dumbdbm_modification(self): self.init_db() - f = dumbdbm.open(_fname, 'w') + self.f = f = dumbdbm.open(_fname, 'w') self._dict[b'g'] = f[b'g'] = b"indented" self.read_helper(f) f.close() def test_dumbdbm_read(self): self.init_db() - f = dumbdbm.open(_fname, 'r') + self.f = f = dumbdbm.open(_fname, 'r') self.read_helper(f) f.close() def test_dumbdbm_keys(self): self.init_db() - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) keys = self.keys_helper(f) f.close() + def test_dumbdbm_values(self): + self.init_db() + self.f = f = dumbdbm.open(_fname) + values = self.values_helper(f) + f.close() + + def test_dumbdbm_items(self): + self.init_db() + self.f = f = dumbdbm.open(_fname) + items = self.items_helper(f) + f.close() + def test_write_contains(self): - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) f[b'1'] = b'hello' self.assertIn(b'1', f) f.close() def test_write_write_read(self): # test for bug #482460 - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) f[b'1'] = b'hello' f[b'1'] = b'hello2' f.close() @@ -108,12 +121,12 @@ def test_str_read(self): self.init_db() - f = dumbdbm.open(_fname, 'r') + self.f = f = dumbdbm.open(_fname, 'r') self.assertEqual(f['\u00fc'], self._dict['\u00fc'.encode('utf-8')]) def test_str_write_contains(self): self.init_db() - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) f['\u00fc'] = b'!' f['1'] = 'a' f.close() @@ -126,7 +139,7 @@ def test_line_endings(self): # test for bug #1172763: dumbdbm would die if the line endings # weren't what was expected. - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) f[b'1'] = b'hello' f[b'2'] = b'hello2' f.close() @@ -141,7 +154,7 @@ with io.open(_fname + '.dir', 'wb') as file: file.write(data) - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) self.assertEqual(f[b'1'], b'hello') self.assertEqual(f[b'2'], b'hello2') @@ -152,7 +165,7 @@ self.assertEqual(self._dict[key], f[key]) def init_db(self): - f = dumbdbm.open(_fname, 'w') + self.f = f = dumbdbm.open(_fname, 'w') for k in self._dict: f[k] = self._dict[k] f.close() @@ -161,15 +174,30 @@ keys = sorted(f.keys()) dkeys = sorted(self._dict.keys()) self.assertEqual(keys, dkeys) + for k in self._dict: + self.assertIn(k, f) + self.assertEqual(len(keys), len(dkeys)) return keys + def values_helper(self, f): + values = f.values() + dvalues = self._dict.values() + self.assertEqual(set(values), set(dvalues)) + return values + + def items_helper(self, f): + items = sorted(f.items()) + ditems = sorted(self._dict.items()) + self.assertEqual(items, ditems) + return items + # Perform randomized operations. This doesn't make assumptions about # what *might* fail. def test_random(self): import random d = {} # mirror the database for dummy in range(5): - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) for dummy in range(100): k = random.choice('abcdefghijklm') if random.random() < 0.2: @@ -183,7 +211,7 @@ self.assertEqual(f[k], v) f.close() - f = dumbdbm.open(_fname) + self.f = f = dumbdbm.open(_fname) expected = sorted((k.encode("latin-1"), v) for k, v in d.items()) got = sorted(f.items()) self.assertEqual(expected, got) @@ -191,6 +219,10 @@ def tearDown(self): _delete_files() + try: + self.f.close() + except: + pass def setUp(self): _delete_files() diff -r 267578b2422d -r 318f73c2e870 Lib/test/test_dbm_gnu.py --- a/Lib/test/test_dbm_gnu.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/test/test_dbm_gnu.py Wed Mar 23 21:26:10 2011 +0800 @@ -3,50 +3,30 @@ import unittest import os from test.support import verbose, TESTFN, run_unittest, unlink +from test import dbm_tests -filename = TESTFN +filename = dbm_tests.filename -class TestGdbm(unittest.TestCase): - def setUp(self): - self.g = None - def tearDown(self): - if self.g is not None: - self.g.close() - unlink(filename) +class TestGdbm(dbm_tests.TestDbmBase): + + _module = gdbm def test_key_methods(self): - self.g = gdbm.open(filename, 'c') - self.assertEqual(self.g.keys(), []) - self.g['a'] = 'b' - self.g['12345678910'] = '019237410982340912840198242' - self.g[b'bytes'] = b'data' - key_set = set(self.g.keys()) - self.assertEqual(key_set, set([b'a', b'bytes', b'12345678910'])) - self.assertIn(b'a', self.g) - self.assertEqual(self.g[b'bytes'], b'data') - key = self.g.firstkey() - while key: - self.assertIn(key, key_set) - key_set.remove(key) - key = self.g.nextkey(key) - self.assertRaises(KeyError, lambda: self.g['xxx']) - # get() and setdefault() work as in the dict interface - self.assertEqual(self.g.get(b'xxx', b'foo'), b'foo') - self.assertEqual(self.g.setdefault(b'xxx', b'foo'), b'foo') - self.assertEqual(self.g[b'xxx'], b'foo') - - def test_error_conditions(self): - # Try to open a non-existent database. - unlink(filename) - self.assertRaises(gdbm.error, gdbm.open, filename, 'r') - # Try to access a closed database. - self.g = gdbm.open(filename, 'c') - self.g.close() - self.assertRaises(gdbm.error, lambda: self.g['a']) - # try pass an invalid open flag - self.assertRaises(gdbm.error, lambda: gdbm.open(filename, 'rx').close()) + self.d = self.opener() + self.d['a'] = 'b' + self.d['12345678910'] = '019237410982340912840198242' + self.d[b'bytes'] = b'data' + dbm_iter = iter(self.d) + key = self.d.firstkey() + self.assertEqual(self.d.firstkey(), next(dbm_iter)) + while True: + try: + key = self.d.nextkey(key) + self.assertEqual(key, next(dbm_iter)) + except StopIteration: + break def test_flags(self): # Test the flag parameter open() by trying all supported flag modes. @@ -54,29 +34,29 @@ # Test standard flags (presumably "crwn"). modes = all - set('fsu') for mode in modes: - self.g = gdbm.open(filename, mode) - self.g.close() + self.d = self._module.open(filename, mode) + self.d.close() # Test additional flags (presumably "fsu"). flags = all - set('crwn') for mode in modes: for flag in flags: - self.g = gdbm.open(filename, mode + flag) - self.g.close() + self.d = self._module.open(filename, mode + flag) + self.d.close() def test_reorganize(self): - self.g = gdbm.open(filename, 'c') + self.d = self.opener(filename) size0 = os.path.getsize(filename) - self.g['x'] = 'x' * 10000 + self.d['x'] = 'x' * 10000 size1 = os.path.getsize(filename) self.assertTrue(size0 < size1) - del self.g['x'] + del self.d['x'] # 'size' is supposed to be the same even after deleting an entry. self.assertEqual(os.path.getsize(filename), size1) - self.g.reorganize() + self.d.reorganize() size2 = os.path.getsize(filename) self.assertTrue(size1 > size2 >= size0) diff -r 267578b2422d -r 318f73c2e870 Lib/test/test_dbm_ndbm.py --- a/Lib/test/test_dbm_ndbm.py Tue Mar 22 22:57:49 2011 -0700 +++ b/Lib/test/test_dbm_ndbm.py Wed Mar 23 21:26:10 2011 +0800 @@ -1,41 +1,27 @@ from test import support -support.import_module("dbm.ndbm") #skip if not supported +ndbm = support.import_module("dbm.ndbm") #skip if not supported import unittest -import os -import random -import dbm.ndbm -from dbm.ndbm import error +from test import dbm_tests -class DbmTestCase(unittest.TestCase): - def setUp(self): - self.filename = support.TESTFN - self.d = dbm.ndbm.open(self.filename, 'c') - self.d.close() +filename = dbm_tests.filename - def tearDown(self): - for suffix in ['', '.pag', '.dir', '.db']: - support.unlink(self.filename + suffix) - def test_keys(self): - self.d = dbm.ndbm.open(self.filename, 'c') - self.assertTrue(self.d.keys() == []) - self.d['a'] = 'b' - self.d[b'bytes'] = b'data' - self.d['12345678910'] = '019237410982340912840198242' - self.d.keys() - self.assertIn(b'a', self.d) - self.assertEqual(self.d[b'bytes'], b'data') - self.d.close() +class DbmTestCase(dbm_tests.TestDbmBase): + + _module = ndbm def test_modes(self): - for mode in ['r', 'rw', 'w', 'n']: + # Here the order is important, put the 'c' flags at the first make sure + # that 'r' won't raise file-not-exists error. + for mode in ['c', 'r', 'rw', 'w', 'n']: try: - self.d = dbm.ndbm.open(self.filename, mode) + self.d = self._module.open(filename, mode) self.d.close() except error: self.fail() + def test_main(): support.run_unittest(DbmTestCase) diff -r 267578b2422d -r 318f73c2e870 Modules/_dbmmodule.c --- a/Modules/_dbmmodule.c Tue Mar 22 22:57:49 2011 -0700 +++ b/Modules/_dbmmodule.c Wed Mar 23 21:26:10 2011 +0800 @@ -39,14 +39,25 @@ } dbmobject; static PyTypeObject Dbmtype; +PyTypeObject DbmKeys_Type; +PyTypeObject DbmValues_Type; +PyTypeObject DbmItems_Type; #define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype) #define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \ { PyErr_SetString(DbmError, "DBM object has already been closed"); \ return NULL; } +#define is_dbmkeysobject(op) PyObject_TypeCheck(op, &DbmKeys_Type) +#define is_dbmitemsobject(op) PyObject_TypeCheck(op, &DbmItems_Type) +#define is_dbmviewsetobject(op) \ + (is_dbmkeysobject(op) || is_dbmitemsobject(op)) static PyObject *DbmError; +static PyObject *dbmkeys_new(PyObject *dbm); +static PyObject *dbmvalues_new(PyObject *dbm); +static PyObject *dbmitems_new(PyObject *dbm); + static PyObject * newdbmobject(char *file, int flags, int mode) { @@ -313,11 +324,299 @@ return defvalue; } +static PyObject * +dbm_pop(register dbmobject *dp, PyObject *args) +{ + datum key, val; + PyObject *result, *defvalue = Py_None; + char *tmp_ptr; + Py_ssize_t tmp_size; + + check_dbmobject_open(dp); + + if (!PyArg_ParseTuple(args, "s#|O:get", &tmp_ptr, &tmp_size, &defvalue)) + return NULL; + key.dptr = tmp_ptr; + key.dsize = tmp_size; + val = dbm_fetch(dp->di_dbm, key); + if (val.dptr != NULL) { + result = PyBytes_FromStringAndSize(val.dptr, val.dsize); + if (result == NULL) + return NULL; + if (dbm_delete(dp->di_dbm, key) < 0) { + PyErr_SetString(DbmError, "Delete key error"); + Py_DECREF(result); + return NULL; + } + dp->di_size = -1; + return result; + } + else { + Py_INCREF(defvalue); + return defvalue; + } +} + +static PyObject * +dbm_popitem(register dbmobject *dp, PyObject *unused) +{ + datum k, v; + PyObject *result, *iter, *key, *val; + + check_dbmobject_open(dp); + + iter = PyObject_GetIter((PyObject *)dp); + if (iter == NULL) + return NULL; + key = PyIter_Next(iter); + Py_DECREF(iter); + if (key == NULL) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_KeyError, "DBM object has no more items"); + return NULL; + } + assert(PyBytes_Check(key)); + if (PyBytes_AsStringAndSize(key, &k.dptr, &k.dsize)) + goto fail; + v = dbm_fetch(dp->di_dbm, k); + if (v.dptr == NULL) + goto fail; + val = PyBytes_FromStringAndSize(v.dptr, v.dsize); + if (val == NULL) + goto fail; + result = PyTuple_New(2); + if (result == NULL) { + Py_DECREF(val); + goto fail; + } + if (PyTuple_SetItem(result, 0, key)) { + Py_DECREF(val); + Py_DECREF(result); + return NULL; + } + if (PyTuple_SetItem(result, 1, val)) { + Py_DECREF(result); + return NULL; + } + if (dbm_delete(dp->di_dbm, k) < 0) { + PyErr_SetString(DbmError, "Delete key error"); + Py_DECREF(result); + return NULL; + } + dp->di_size = -1; + return result; +fail: + Py_DECREF(key); + return NULL; +} + +static PyObject * +dbm_clear(dbmobject *dp, PyObject *unused) +{ + datum key; + + if (dp == NULL || !is_dbmobject(dp)) { + PyErr_BadInternalCall(); + return NULL; + } + check_dbmobject_open(dp); + + key = dbm_firstkey(dp->di_dbm); + while (key.dptr) { + if (dbm_delete(dp->di_dbm, key) < 0) { + PyErr_SetString(DbmError, "Delete key error"); + return NULL; + } + key = dbm_firstkey(dp->di_dbm); + } + Py_RETURN_NONE; +} + +static PyObject * +dbm_update(dbmobject *dp, PyObject *args, PyObject *kwds) +{ + datum krec, drec; + PyObject *arg = NULL; + PyObject *coll_mod, *mapping_abc; + PyObject *arg_iter, *arg_keys, *iter_item, *item_value; + PyObject *key, *value; + Py_ssize_t pos = 0; + + if (dp == NULL || !is_dbmobject(dp)) { + PyErr_BadInternalCall(); + return NULL; + } + check_dbmobject_open(dp); + + if ((coll_mod = PyImport_ImportModule("collections")) == NULL) + return NULL; + if ((mapping_abc = PyObject_GetAttrString(coll_mod, + "Mapping")) == NULL) { + Py_DECREF(coll_mod); + return NULL; + } + Py_DECREF(coll_mod); + + if (!PyArg_UnpackTuple(args, "update", 0, 1, &arg)) + Py_DECREF(mapping_abc); + else if (arg != NULL) { + if (PyObject_IsInstance(arg, mapping_abc)) { + Py_DECREF(mapping_abc); + if ((arg_iter = PyObject_GetIter(arg)) == NULL) + return NULL; + while ((iter_item = PyIter_Next(arg_iter)) != NULL) { + if (!PyArg_Parse(iter_item, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM keys can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(iter_item); + return NULL; + } + Py_DECREF(iter_item); + if ((item_value = PyObject_GetItem(arg, iter_item)) == NULL) { + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(item_value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM value can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(item_value); + return NULL; + } + Py_DECREF(item_value); + if (dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, "Cannot add item to DBM object"); + Py_DECREF(arg_iter); + return NULL; + } + } + Py_DECREF(arg_iter); + if (PyErr_Occurred()) + return NULL; + } + else if (PyObject_HasAttrString(arg, "keys")) { + Py_DECREF(mapping_abc); + if ((arg_keys = PyObject_CallMethod(arg, "keys", NULL)) == NULL) + return NULL; + if ((arg_iter = PyObject_GetIter(arg_keys)) == NULL) { + Py_DECREF(arg_keys); + return NULL; + } + Py_DECREF(arg_keys); + while ((iter_item = PyIter_Next(arg_iter)) != NULL) { + if (!PyArg_Parse(iter_item, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM keys can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(iter_item); + return NULL; + } + Py_DECREF(iter_item); + if ((item_value = PyObject_GetItem(arg, iter_item)) == NULL) { + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(item_value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM values can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(item_value); + return NULL; + } + Py_DECREF(item_value); + if (dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, "Cannot add item to DBM object"); + Py_DECREF(arg_iter); + return NULL; + } + } + Py_DECREF(arg_iter); + if (PyErr_Occurred()) + return NULL; + } + else { + Py_DECREF(mapping_abc); + if ((arg_iter = PyObject_GetIter(arg)) == NULL) + return NULL; + while ((iter_item = PyIter_Next(arg_iter)) != NULL) { + if (!PySequence_Check(iter_item) || + PySequence_Size(iter_item) != 2) { + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + return NULL; + } + if ((key = PySequence_GetItem(iter_item, 0)) == NULL) { + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM key can only be buffers"); + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + Py_DECREF(key); + return NULL; + } + Py_DECREF(key); + + if ((value = PySequence_GetItem(iter_item, 1)) == NULL) { + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM values can only be buffers"); + Py_DECREF(arg_iter); + return NULL; + } + Py_DECREF(value); + Py_DECREF(iter_item); + + if (dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, "Cannot add item to DBM object"); + Py_DECREF(arg_iter); + return NULL; + } + } + Py_DECREF(arg_iter); + if (PyErr_Occurred()) + return NULL; + } + } + if (kwds == NULL) + Py_RETURN_NONE; + while (PyDict_Next(kwds, &pos, &key, &value)) { + if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM keys can only be buffers"); + return NULL; + } + if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM values can only be buffers"); + return NULL; + } + if (dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, "Cannot add item to DBM object"); + return NULL; + } + } + Py_RETURN_NONE; +} + static PyMethodDef dbm_methods[] = { {"close", (PyCFunction)dbm__close, METH_NOARGS, "close()\nClose the database."}, - {"keys", (PyCFunction)dbm_keys, METH_NOARGS, - "keys() -> list\nReturn a list of all keys in the database."}, + {"keys", (PyCFunction)dbmkeys_new, METH_NOARGS, + "keys() -> keys view\n" + "Return a set-like object providing a view on the database's keys."}, {"get", (PyCFunction)dbm_get, METH_VARARGS, "get(key[, default]) -> value\n" "Return the value for key if present, otherwise default."}, @@ -325,9 +624,720 @@ "setdefault(key[, default]) -> value\n" "Return the value for key if present, otherwise default. If key\n" "is not in the database, it is inserted with default as the value."}, + {"values", (PyCFunction)dbmvalues_new, METH_NOARGS, + "values() -> values view.\n" + "Return an object providing a view on the database's values."}, + {"items", (PyCFunction)dbmitems_new, METH_NOARGS, + "items() -> items view\n" + "Return a set-like object providing a view on the database's items."}, + {"pop", (PyCFunction)dbm_pop, METH_VARARGS, + "pop(key[, default]) -> value\n" + "Return the value for key and delete the key-value pair if present,\n" + "otherwise return default if specified, raise KeyError if not " + "specified."}, + {"popitem", (PyCFunction)dbm_popitem, METH_NOARGS, + "popitem() -> (key, value)\n" + "Return the next (key, value) tuple if there is items in db, otherwise\n" + " raise KeyError."}, + {"clear", (PyCFunction)dbm_clear, METH_NOARGS, + "clear() -> None\n" + "Delete all the items in db."}, + {"update", (PyCFunction)dbm_update, METH_VARARGS | METH_KEYWORDS, + "update([other][, **keywords]) -> None\n" + "Add all the items in keywords to the databases.\n" + "If other is a mapping object, does: for k in other: self[k] = other[k]\n" + "Else if other has a \"keys()\" method, does: for k in other.keys(): " + "self[k] = other[k]\n" + "Else does: for k, v in other: self[k] = v"}, {NULL, NULL} /* sentinel */ }; +typedef struct { + PyObject_HEAD + dbmobject *dbm; /* Set to NULL when iterator is exhausted */ + + /* Hold all the dbm keys in a list and iter on the list, since + dbm_nextkey() can only returns the *INTERNAL* next key for each dbm + object, we cannot iter based on it. */ + PyObject *keys; + Py_ssize_t it_index; +} dbmiterobject; + +static void +dbmiter_dealloc(dbmiterobject *di) +{ + Py_XDECREF(di->dbm); + Py_XDECREF(di->keys); + PyObject_Del(di); +} + +static PyObject * +dbmiter_iternextvalue(dbmiterobject *di) +{ + PyListObject *seq; + PyObject *item; + datum key, value; + + assert(di != NULL && di->keys != NULL); + seq = (PyListObject *)di->keys; + assert(PyList_Check(seq)); + + if (di->it_index < PyList_GET_SIZE(seq)) { + item = PyList_GET_ITEM(seq, di->it_index); + ++di->it_index; + if (PyBytes_AsStringAndSize(item, &key.dptr, &key.dsize)) + return NULL; + value = dbm_fetch(di->dbm->di_dbm, key); + if (value.dptr == 0) { + PyErr_SetObject(PyExc_KeyError, item); + return NULL; + } + return PyBytes_FromStringAndSize(value.dptr, value.dsize); + } + + Py_DECREF(seq); + di->keys = NULL; + return NULL; +} + +PyTypeObject DbmIterValue_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "DbmIterValue_Type", /* tp_name */ + sizeof(dbmiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)dbmiter_iternextvalue, /* tp_iternext */ + 0, /* tp_methods */ + 0, +}; + +static PyObject * +dbmiter_iternextitem(dbmiterobject *di) +{ + PyListObject *seq; + PyObject *item, *item_value, *result; + datum key, value; + + assert(di != NULL && di->keys != NULL); + seq = (PyListObject *)di->keys; + assert(PyList_Check(seq)); + + if (di->it_index < PyList_GET_SIZE(seq)) { + item = PyList_GET_ITEM(seq, di->it_index); + if (PyBytes_AsStringAndSize(item, &key.dptr, &key.dsize)) + return NULL; + value = dbm_fetch(di->dbm->di_dbm, key); + if (value.dptr == 0) { + PyErr_SetObject(PyExc_KeyError, item); + return NULL; + } + item_value = PyBytes_FromStringAndSize(value.dptr, value.dsize); + if (item_value == NULL) + return NULL; + result = PyTuple_New(2); + if (result == NULL) { + Py_DECREF(item_value); + return NULL; + } + Py_INCREF(item); + if (PyTuple_SetItem(result, 0, item)) { + Py_DECREF(item_value); + Py_DECREF(result); + return NULL; + } + if (PyTuple_SetItem(result, 1, item_value)) { + Py_DECREF(result); + return NULL; + } + ++di->it_index; + return result; + } + + Py_DECREF(seq); + di->keys = NULL; + return NULL; +} + +PyTypeObject DbmIterItem_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "DbmIterValue_Type", /* tp_name */ + sizeof(dbmiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)dbmiter_iternextitem, /* tp_iternext */ + 0, /* tp_methods */ + 0, +}; + +static PyObject * +dbm_iter(dbmobject *dbm) +{ + PyObject *keys, *iter; + keys = dbm_keys(dbm, NULL); + if (keys == NULL) + return NULL; + iter = PyObject_GetIter(keys); + Py_DECREF(keys); + return iter; +} + +static PyObject * +dbmiter_new(dbmobject *dbm, PyTypeObject *iter_type) +{ + dbmiterobject *di = PyObject_New(dbmiterobject, iter_type); + if (di == NULL) + return NULL; + Py_INCREF(dbm); + di->dbm = dbm; + di->it_index = 0; + di->keys = dbm_keys(dbm, NULL); + return (PyObject *)di; +} + +typedef struct { + PyObject_HEAD + dbmobject *dv_dbm; +} dbmviewobject; + + +static void +dbmview_dealloc(dbmviewobject *dv) +{ + Py_XDECREF(dv->dv_dbm); + PyObject_Del(dv); +} + +static Py_ssize_t +dbmview_len(dbmviewobject *dv) +{ + return dbm_length(dv->dv_dbm); +} + +static PyObject * +dbmview_new(PyObject *dbm, PyTypeObject *type) +{ + dbmviewobject *dv; + + if (dbm == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + if (!is_dbmobject(dbm)) { + PyErr_Format(PyExc_TypeError, + "%s() requires a DBM argument, not '%s'", + type->tp_name, dbm->ob_type->tp_name); + return NULL; + } + dv = PyObject_New(dbmviewobject, type); + if (dv == NULL) + return NULL; + Py_INCREF(dbm); + dv->dv_dbm = (dbmobject *)dbm; + return (PyObject *)dv; +} + +static PyObject * +dbmview_richcompare(PyObject *self, PyObject *other, int op) +{ + Py_ssize_t len_self, len_other; + int ok; + PyObject *result; + + assert(self != NULL && other != NULL); + assert(is_dbmviewsetobject(self)); + + if (!PyAnySet_Check(other) && !is_dbmviewsetobject(other)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + len_self = PyObject_Size(self); + if (len_self < 0) + return NULL; + len_other = PyObject_Size(other); + if (len_other < 0) + return NULL; + + ok = 0; + switch(op) { + + case Py_NE: + case Py_EQ: + if (len_self == len_other) + ok = _PyObject_AllContainedIn(self, other); + if (op == Py_NE && ok >= 0) + ok = !ok; + break; + + case Py_LT: + if (len_self < len_other) + ok = _PyObject_AllContainedIn(self, other); + break; + + case Py_LE: + if (len_self <= len_other) + ok = _PyObject_AllContainedIn(self, other); + break; + + case Py_GT: + if (len_self > len_other) + ok = _PyObject_AllContainedIn(other, self); + break; + + case Py_GE: + if (len_self >= len_other) + ok = _PyObject_AllContainedIn(other, self); + break; + + } + if (ok < 0) + return NULL; + result = ok ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +static PyObject* +dbmviews_sub(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "difference_update", "O", other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyObject* +dbmviews_and(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "intersection_update", "O", other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyObject* +dbmviews_or(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "update", "O", other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyObject* +dbmviews_xor(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "symmetric_difference_update", "O", + other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyNumberMethods dbmviews_as_number = { + 0, /*nb_add*/ + (binaryfunc)dbmviews_sub, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_negative*/ + 0, /*nb_positive*/ + 0, /*nb_absolute*/ + 0, /*nb_bool*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + (binaryfunc)dbmviews_and, /*nb_and*/ + (binaryfunc)dbmviews_xor, /*nb_xor*/ + (binaryfunc)dbmviews_or, /*nb_or*/ +}; + +/*** dbm_keys ***/ + +static PyObject * +dbmkeys_iter(dbmviewobject *dv) +{ + if (dv->dv_dbm == NULL) { + Py_RETURN_NONE; + } + return dbm_iter(dv->dv_dbm); +} + +static int +dbmkeys_contains(dbmviewobject *dv, PyObject *obj) +{ + if (dv->dv_dbm == NULL) + return 0; + return dbm_contains((PyObject *)dv->dv_dbm, obj); +} + +static PySequenceMethods dbmkeys_as_sequence = { + (lenfunc)dbmview_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)dbmkeys_contains, /* sq_contains */ +}; + +static PyObject* +dbmviews_isdisjoint(PyObject *self, PyObject *other) +{ + PyObject *it; + PyObject *item = NULL; + + if (self == other) { + if (dbmview_len((dbmviewobject *)self) == 0) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + /* Iterate over the shorter object (only if other is a set, + * because PySequence_Contains may be expensive otherwise): */ + if (PyAnySet_Check(other) || is_dbmviewsetobject(other)) { + Py_ssize_t len_self = dbmview_len((dbmviewobject *)self); + Py_ssize_t len_other = PyObject_Size(other); + if (len_other == -1) + return NULL; + + if ((len_other > len_self)) { + PyObject *tmp = other; + other = self; + self = tmp; + } + } + + it = PyObject_GetIter(other); + if (it == NULL) + return NULL; + + while ((item = PyIter_Next(it)) != NULL) { + int contains = PySequence_Contains(self, item); + Py_DECREF(item); + if (contains == -1) { + Py_DECREF(it); + return NULL; + } + + if (contains) { + Py_DECREF(it); + Py_RETURN_FALSE; + } + } + Py_DECREF(it); + if (PyErr_Occurred()) + return NULL; /* PyIter_Next raised an exception. */ + Py_RETURN_TRUE; +} + +PyDoc_STRVAR(isdisjoint_doc, +"Return True if the view and the given iterable have a null intersection."); + +static PyMethodDef dbmviews_methods[] = { + {"isdisjoint", (PyCFunction)dbmviews_isdisjoint, METH_O, + isdisjoint_doc}, + {NULL, NULL} /* sentinel */ +}; + +PyTypeObject DbmKeys_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "dbm_keys", /* tp_name */ + sizeof(dbmviewobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmview_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + &dbmviews_as_number, /* tp_as_number */ + &dbmkeys_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + dbmview_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)dbmkeys_iter, /* tp_iter */ + 0, /* tp_iternext */ + dbmviews_methods, /* tp_methods */ + 0, +}; + +static PyObject * +dbmkeys_new(PyObject *dbm) +{ + return dbmview_new(dbm, &DbmKeys_Type); +} + +/*** dbm_values ***/ + +static PySequenceMethods dbmvalues_as_sequence = { + (lenfunc)dbmview_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ +}; + +static PyObject * +dbmvalues_iter(dbmviewobject *dv) +{ + if (dv->dv_dbm == NULL) { + Py_RETURN_NONE; + } + return dbmiter_new(dv->dv_dbm, &DbmIterValue_Type); +} + +PyTypeObject DbmValues_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "dbm_values", /* tp_name */ + sizeof(dbmviewobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmview_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &dbmvalues_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)dbmvalues_iter, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, +}; + +static PyObject * +dbmvalues_new(PyObject *dbm) +{ + return dbmview_new(dbm, &DbmValues_Type); +} + +/*** dbm_items ***/ + +static int +dbmitems_contains(dbmviewobject *dv, PyObject *obj) +{ + PyObject *key, *value, *key_bytes, *value_bytes, *vrec_bytes; + datum krec, vrec; + int result; + + if (dv->dv_dbm == NULL) + return 0; + if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) + return 0; + + key = PyTuple_GET_ITEM(obj, 0); + value = PyTuple_GET_ITEM(obj, 1); + if (PyUnicode_Check(key)) { + key_bytes = _PyUnicode_AsDefaultEncodedString(key); + if (key_bytes == NULL) + return -1; + } + else + key_bytes = key; + if (!PyBytes_Check(key_bytes)) { + PyErr_Format(PyExc_TypeError, + "DBM key must be string, not %100s", + key->ob_type->tp_name); + return -1; + } + krec.dptr = PyBytes_AS_STRING(key_bytes); + krec.dsize = PyBytes_GET_SIZE(key_bytes); + vrec = dbm_fetch(dv->dv_dbm->di_dbm, krec); + if (vrec.dptr != NULL) { + if ((vrec_bytes = PyBytes_FromStringAndSize(vrec.dptr, + vrec.dsize)) == NULL) + return -1; + if (PyUnicode_Check(value)) { + value_bytes = _PyUnicode_AsDefaultEncodedString(value); + if (value_bytes == NULL) { + Py_DECREF(vrec_bytes); + return -1; + } + } + else + value_bytes = value; + result = PyObject_RichCompareBool(value_bytes, vrec_bytes, Py_EQ); + Py_DECREF(vrec_bytes); + return result; + } + return 0; +} + +static PySequenceMethods dbmitems_as_sequence = { + (lenfunc)dbmview_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)dbmitems_contains, /* sq_contains */ +}; + +static PyObject * +dbmitems_iter(dbmviewobject *dv) +{ + if (dv->dv_dbm == NULL) { + Py_RETURN_NONE; + } + return dbmiter_new(dv->dv_dbm, &DbmIterItem_Type); +} + +PyTypeObject DbmItems_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "dbm_items", /* tp_name */ + sizeof(dbmviewobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmview_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + &dbmviews_as_number, /* tp_as_number */ + &dbmitems_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + dbmview_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)dbmitems_iter, /* tp_iter */ + 0, /* tp_iternext */ + dbmviews_methods, /* tp_methods */ + 0, +}; + +static PyObject * +dbmitems_new(PyObject *dbm) +{ + return dbmview_new(dbm, &DbmItems_Type); +} + static PyTypeObject Dbmtype = { PyVarObject_HEAD_INIT(NULL, 0) "_dbm.dbm", @@ -354,7 +1364,7 @@ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ + (getiterfunc)dbm_iter, /*tp_iter*/ 0, /*tp_iternext*/ dbm_methods, /*tp_methods*/ }; diff -r 267578b2422d -r 318f73c2e870 Modules/_gdbmmodule.c --- a/Modules/_gdbmmodule.c Tue Mar 22 22:57:49 2011 -0700 +++ b/Modules/_gdbmmodule.c Wed Mar 23 21:26:10 2011 +0800 @@ -35,11 +35,18 @@ } dbmobject; static PyTypeObject Dbmtype; +PyTypeObject DbmKeys_Type; +PyTypeObject DbmValues_Type; +PyTypeObject DbmItems_Type; #define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype) #define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \ { PyErr_SetString(DbmError, "GDBM object has already been closed"); \ return NULL; } +#define is_dbmkeysobject(op) PyObject_TypeCheck(op, &DbmKeys_Type) +#define is_dbmitemsobject(op) PyObject_TypeCheck(op, &DbmItems_Type) +#define is_dbmviewsetobject(op) \ + (is_dbmkeysobject(op) || is_dbmitemsobject(op)) @@ -241,50 +248,6 @@ return Py_None; } -/* XXX Should return a set or a set view */ -PyDoc_STRVAR(dbm_keys__doc__, -"keys() -> list_of_keys\n\ -Get a list of all keys in the database."); - -static PyObject * -dbm_keys(register dbmobject *dp, PyObject *unused) -{ - register PyObject *v, *item; - datum key, nextkey; - int err; - - if (dp == NULL || !is_dbmobject(dp)) { - PyErr_BadInternalCall(); - return NULL; - } - check_dbmobject_open(dp); - - v = PyList_New(0); - if (v == NULL) - return NULL; - - key = gdbm_firstkey(dp->di_dbm); - while (key.dptr) { - item = PyBytes_FromStringAndSize(key.dptr, key.dsize); - if (item == NULL) { - free(key.dptr); - Py_DECREF(v); - return NULL; - } - err = PyList_Append(v, item); - Py_DECREF(item); - if (err != 0) { - free(key.dptr); - Py_DECREF(v); - return NULL; - } - nextkey = gdbm_nextkey(dp->di_dbm, key); - free(key.dptr); - key = nextkey; - } - return v; -} - static int dbm_contains(PyObject *self, PyObject *arg) { @@ -296,14 +259,10 @@ "GDBM object has already been closed"); return -1; } - if (!PyBytes_Check(arg)) { - PyErr_Format(PyExc_TypeError, - "gdbm key must be bytes, not %.100s", - arg->ob_type->tp_name); + + if (!PyArg_Parse(arg, "s#", &key.dptr, &key.dsize)) return -1; - } - key.dptr = PyBytes_AS_STRING(arg); - key.dsize = PyBytes_GET_SIZE(arg); + return gdbm_exists(dp->di_dbm, key); } @@ -416,15 +375,1134 @@ return Py_None; } +PyDoc_STRVAR(dbm_pop__doc__, +"pop([key[, default]) -> value\n\ +Return the value for key and delete the key-value pair if present, \n\ +otherwise return default if specified, raise KeyError if not specified."); + +static PyObject * +dbm_pop(dbmobject *dp, PyObject *args) +{ + datum key, val; + PyObject *result, *defvalue = Py_None; + char *tmp_ptr; + Py_ssize_t tmp_size; + + check_dbmobject_open(dp); + + if (!PyArg_ParseTuple(args, "s#|O:get", &tmp_ptr, &tmp_size, &defvalue)) + return NULL; + + key.dptr = tmp_ptr; + key.dsize = tmp_size; + val = gdbm_fetch(dp->di_dbm, key); + if (val.dptr != NULL) { + result = PyBytes_FromStringAndSize(val.dptr, val.dsize); + free(val.dptr); + if (result == NULL) + return NULL; + if (gdbm_delete(dp->di_dbm, key) < 0) { + PyErr_SetString(DbmError, "Delete key error"); + Py_DECREF(result); + return NULL; + } + dp->di_size = -1; + return result; + } + else { + Py_INCREF(defvalue); + return defvalue; + } +} + +PyDoc_STRVAR(dbm_popitem__doc__, +"popitem() -> (key, value)\n\ +Return the next (key, value) tuple if there is items in db, \n\ +otherwise raise KeyError."); + +static PyObject * +dbm_popitem(dbmobject *dp, PyObject *unused) +{ + datum k, v; + PyObject *result, *iter, *key, *val; + + check_dbmobject_open(dp); + + iter = PyObject_GetIter((PyObject *)dp); + if (iter == NULL) + return NULL; + key = PyIter_Next(iter); + Py_DECREF(iter); + if (key == NULL) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_KeyError, "GDBM object has no more items"); + return NULL; + } + assert(PyBytes_Check(key)); + if (PyBytes_AsStringAndSize(key, &k.dptr, &k.dsize)) + goto fail; + v = gdbm_fetch(dp->di_dbm, k); + if (v.dptr == NULL) + goto fail; + val = PyBytes_FromStringAndSize(v.dptr, v.dsize); + free(v.dptr); + if (val == NULL) + goto fail; + result = PyTuple_New(2); + if (result == NULL) { + Py_DECREF(val); + goto fail; + } + if (PyTuple_SetItem(result, 0, key)) { + Py_DECREF(val); + Py_DECREF(result); + return NULL; + } + if (PyTuple_SetItem(result, 1, val)) { + Py_DECREF(result); + return NULL; + } + if (gdbm_delete(dp->di_dbm, k) < 0) { + PyErr_SetString(DbmError, "Delete key error"); + Py_DECREF(result); + return NULL; + } + dp->di_size = -1; + return result; +fail: + Py_DECREF(key); + return NULL; +} + +PyDoc_STRVAR(dbm_clear__doc__, +"clear() -> None\n\ +Delete all the items in db."); + +static PyObject * +dbm_clear(dbmobject *dp, PyObject *unused) +{ + datum key; + + if (dp == NULL || !is_dbmobject(dp)) { + PyErr_BadInternalCall(); + return NULL; + } + check_dbmobject_open(dp); + + key = gdbm_firstkey(dp->di_dbm); + while (key.dptr) { + if (gdbm_delete(dp->di_dbm, key) < 0) { + PyErr_SetString(DbmError, "Delete key error"); + free(key.dptr); + return NULL; + } + free(key.dptr); + key = gdbm_firstkey(dp->di_dbm); + } + Py_RETURN_NONE; +} + +static PyObject * +dbm_update(dbmobject *dp, PyObject *args, PyObject *kwds) +{ + datum krec, drec; + PyObject *arg = NULL; + PyObject *coll_mod, *mapping_abc; + PyObject *arg_iter, *arg_keys, *iter_item, *item_value; + PyObject *key, *value; + Py_ssize_t pos = 0; + + if (dp == NULL || !is_dbmobject(dp)) { + PyErr_BadInternalCall(); + return NULL; + } + check_dbmobject_open(dp); + + if ((coll_mod = PyImport_ImportModule("collections")) == NULL) + return NULL; + if ((mapping_abc = PyObject_GetAttrString(coll_mod, + "Mapping")) == NULL) { + Py_DECREF(coll_mod); + return NULL; + } + Py_DECREF(coll_mod); + + if (!PyArg_UnpackTuple(args, "update", 0, 1, &arg)) + Py_DECREF(mapping_abc); + else if (arg != NULL) { + if (PyObject_IsInstance(arg, mapping_abc)) { + Py_DECREF(mapping_abc); + if ((arg_iter = PyObject_GetIter(arg)) == NULL) + return NULL; + while ((iter_item = PyIter_Next(arg_iter)) != NULL) { + if (!PyArg_Parse(iter_item, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM keys can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(iter_item); + return NULL; + } + Py_DECREF(iter_item); + if ((item_value = PyObject_GetItem(arg, iter_item)) == NULL) { + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(item_value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM value can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(item_value); + return NULL; + } + Py_DECREF(item_value); + if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0 ) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + Py_DECREF(arg_iter); + return NULL; + } + } + Py_DECREF(arg_iter); + if (PyErr_Occurred()) + return NULL; + } + else if (PyObject_HasAttrString(arg, "keys")) { + Py_DECREF(mapping_abc); + if ((arg_keys = PyObject_CallMethod(arg, "keys", NULL)) == NULL) + return NULL; + if ((arg_iter = PyObject_GetIter(arg_keys)) == NULL) { + Py_DECREF(arg_keys); + return NULL; + } + Py_DECREF(arg_keys); + while ((iter_item = PyIter_Next(arg_iter)) != NULL) { + if (!PyArg_Parse(iter_item, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM keys can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(iter_item); + return NULL; + } + Py_DECREF(iter_item); + if ((item_value = PyObject_GetItem(arg, iter_item)) == NULL) { + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(item_value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM values can only be buffers"); + Py_DECREF(arg_iter); + Py_DECREF(item_value); + return NULL; + } + Py_DECREF(item_value); + if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0 ) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + Py_DECREF(arg_iter); + return NULL; + } + } + Py_DECREF(arg_iter); + if (PyErr_Occurred()) + return NULL; + } + else { + Py_DECREF(mapping_abc); + if ((arg_iter = PyObject_GetIter(arg)) == NULL) + return NULL; + while ((iter_item = PyIter_Next(arg_iter)) != NULL) { + if (!PySequence_Check(iter_item) || + PySequence_Size(iter_item) != 2) { + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + return NULL; + } + if ((key = PySequence_GetItem(iter_item, 0)) == NULL) { + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM key can only be buffers"); + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + Py_DECREF(key); + return NULL; + } + Py_DECREF(key); + + if ((value = PySequence_GetItem(iter_item, 1)) == NULL) { + Py_DECREF(iter_item); + Py_DECREF(arg_iter); + return NULL; + } + if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM value can only be buffers"); + Py_DECREF(arg_iter); + return NULL; + } + Py_DECREF(value); + Py_DECREF(iter_item); + + if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0 ) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + Py_DECREF(arg_iter); + return NULL; + } + } + Py_DECREF(arg_iter); + if (PyErr_Occurred()) + return NULL; + } + } + if (kwds == NULL) + Py_RETURN_NONE; + while (PyDict_Next(kwds, &pos, &key, &value)) { + if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM keys can only be buffers"); + return NULL; + } + if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "DBM values can only be buffers"); + return NULL; + } + if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0 ) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + return NULL; + } + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(dbm_update__doc__, +"update([other][, **keywords]) -> None\n\ +Add all the items in keywords to the databases.\n\ +If other is a mapping object, does: for k in other: self[k] = other[k]\n\ +Else if other has a \"keys()\" method, does: for k in other.keys(): \ +self[k] = other[k]\n\ +Else does: for k, v in other: self[k] = v"); + +typedef struct { + PyObject_HEAD + dbmobject *dbm; /* Set to NULL when iterator is exhausted */ + datum curr_key; +} dbmiterobject; + +static void +dbmiter_dealloc(dbmiterobject *di) +{ + Py_XDECREF(di->dbm); + if (di->curr_key.dptr) + free(di->curr_key.dptr); + PyObject_Del(di); +} + +static PyObject * +dbmiter_iternextkey(dbmiterobject *di) +{ + datum next; + PyObject *result; + + if (di->dbm != NULL) { + if (di->curr_key.dptr == NULL) + next = gdbm_firstkey(di->dbm->di_dbm); + else + next = gdbm_nextkey(di->dbm->di_dbm, di->curr_key); + if (next.dptr == NULL) { + PyErr_SetNone(PyExc_StopIteration); + Py_DECREF(di->dbm); + di->dbm = NULL; + return NULL; + } + result = PyBytes_FromStringAndSize(next.dptr, next.dsize); + + /* Only when get the next element successful, move forward. */ + if (di->curr_key.dptr) + free(di->curr_key.dptr); + di->curr_key = next; + return result; + } + else { + /* The iteration has already exhaust. */ + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +} + +PyTypeObject DbmIterKey_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "DbmIterKey_Type", /* tp_name */ + sizeof(dbmiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)dbmiter_iternextkey, /* tp_iternext */ + 0, /* tp_methods */ + 0, +}; + +static PyObject * +dbmiter_iternextvalue(dbmiterobject *di) +{ + PyObject *result_key, *result_value; + datum next, result_data; + if (di->dbm != NULL) { + if (di->curr_key.dptr == NULL) + next = gdbm_firstkey(di->dbm->di_dbm); + else + next = gdbm_nextkey(di->dbm->di_dbm, di->curr_key); + if (next.dptr == NULL) { + PyErr_SetNone(PyExc_StopIteration); + Py_DECREF(di->dbm); + di->dbm = NULL; + return NULL; + } + result_key = PyBytes_FromStringAndSize(next.dptr, next.dsize); + if (result_key == NULL) { + free(next.dptr); + return NULL; + } + result_data = gdbm_fetch(di->dbm->di_dbm, next); + if (result_data.dptr == 0) { + PyErr_SetObject(PyExc_KeyError, result_key); + Py_DECREF(result_key); + free(next.dptr); + return NULL; + } + Py_DECREF(result_key); + result_value = PyBytes_FromStringAndSize(result_data.dptr, + result_data.dsize); + free(result_data.dptr); + if (di->curr_key.dptr) + free(di->curr_key.dptr); + di->curr_key = next; + return result_value; + } + else { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +} + +PyTypeObject DbmIterValue_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "DbmIterValue_Type", /* tp_name */ + sizeof(dbmiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)dbmiter_iternextvalue, /* tp_iternext */ + 0, /* tp_methods */ + 0, +}; + +static PyObject * +dbmiter_iternextitem(dbmiterobject *di) +{ + PyObject *result_key, *result_value; + datum next, result_data; + PyObject *result; + if (di->dbm != NULL) { + if (di->curr_key.dptr == NULL) + next = gdbm_firstkey(di->dbm->di_dbm); + else + next = gdbm_nextkey(di->dbm->di_dbm, di->curr_key); + if (next.dptr == NULL) { + PyErr_SetNone(PyExc_StopIteration); + Py_DECREF(di->dbm); + di->dbm = NULL; + return NULL; + } + result_key = PyBytes_FromStringAndSize(next.dptr, next.dsize); + if (result_key == NULL) { + free(next.dptr); + return NULL; + } + result_data = gdbm_fetch(di->dbm->di_dbm, next); + if (result_data.dptr == 0) { + PyErr_SetObject(PyExc_KeyError, result_key); + Py_DECREF(result_key); + free(next.dptr); + return NULL; + } + result_value = PyBytes_FromStringAndSize(result_data.dptr, + result_data.dsize); + free(result_data.dptr); + if (result_value == NULL) { + Py_DECREF(result_key); + free(next.dptr); + return NULL; + } + result = PyTuple_New(2); + if (result == NULL) { + Py_DECREF(result_key); + Py_DECREF(result_value); + free(next.dptr); + return NULL; + } + if (PyTuple_SetItem(result, 0, result_key)) { + Py_DECREF(result); + Py_DECREF(result_value); + free(next.dptr); + return NULL; + } + if (PyTuple_SetItem(result, 1, result_value)) { + Py_DECREF(result); + free(next.dptr); + return NULL; + } + if (di->curr_key.dptr) + free(di->curr_key.dptr); + di->curr_key = next; + return result; + } + else { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +} + +PyTypeObject DbmIterItem_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "DbmIterItem_Type", /* tp_name */ + sizeof(dbmiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)dbmiter_iternextitem, /* tp_iternext */ + 0, /* tp_methods */ + 0, +}; + +static PyObject * +dbmiter_new(dbmobject *dbm, PyTypeObject *iter_type) +{ + dbmiterobject *di = PyObject_New(dbmiterobject, iter_type); + if (di == NULL) + return NULL; + Py_INCREF(dbm); + di->dbm = dbm; + di->curr_key.dptr = NULL; + return (PyObject *)di; +} + +static PyObject * +dbm_iter(dbmobject *dbm) +{ + return dbmiter_new(dbm, &DbmIterKey_Type); +} + +typedef struct { + PyObject_HEAD + dbmobject *dv_dbm; +} dbmviewobject; + +static void +dbmview_dealloc(dbmviewobject *dv) +{ + Py_XDECREF(dv->dv_dbm); + PyObject_Del(dv); +} + +static Py_ssize_t +dbmview_len(dbmviewobject *dv) +{ + return dbm_length(dv->dv_dbm); +} + +static PyObject * +dbmview_new(PyObject *dbm, PyTypeObject *type) +{ + dbmviewobject *dv; + + if (dbm == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + if (!is_dbmobject(dbm)) { + PyErr_Format(PyExc_TypeError, + "%s() requires a DBM argument, not '%s'", + type->tp_name, dbm->ob_type->tp_name); + return NULL; + } + dv = PyObject_New(dbmviewobject, type); + if (dv == NULL) + return NULL; + Py_INCREF(dbm); + dv->dv_dbm = (dbmobject *)dbm; + return (PyObject *)dv; +} + +static PyObject * +dbmview_richcompare(PyObject *self, PyObject *other, int op) +{ + Py_ssize_t len_self, len_other; + int ok; + PyObject *result; + + assert(self != NULL && other != NULL); + assert(is_dbmviewsetobject(self)); + + if (!PyAnySet_Check(other) && !is_dbmviewsetobject(other)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + len_self = PyObject_Size(self); + if (len_self < 0) + return NULL; + len_other = PyObject_Size(other); + if (len_other < 0) + return NULL; + + ok = 0; + switch(op) { + + case Py_NE: + case Py_EQ: + if (len_self == len_other) + ok = _PyObject_AllContainedIn(self, other); + if (op == Py_NE && ok >= 0) + ok = !ok; + break; + + case Py_LT: + if (len_self < len_other) + ok = _PyObject_AllContainedIn(self, other); + break; + + case Py_LE: + if (len_self <= len_other) + ok = _PyObject_AllContainedIn(self, other); + break; + + case Py_GT: + if (len_self > len_other) + ok = _PyObject_AllContainedIn(other, self); + break; + + case Py_GE: + if (len_self >= len_other) + ok = _PyObject_AllContainedIn(other, self); + break; + + } + if (ok < 0) + return NULL; + result = ok ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +static PyObject* +dbmviews_sub(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "difference_update", "O", other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyObject* +dbmviews_and(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "intersection_update", "O", other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyObject* +dbmviews_or(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "update", "O", other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyObject* +dbmviews_xor(PyObject* self, PyObject *other) +{ + PyObject *result = PySet_New(self); + PyObject *tmp; + + if (result == NULL) + return NULL; + + tmp = PyObject_CallMethod(result, "symmetric_difference_update", "O", + other); + if (tmp == NULL) { + Py_DECREF(result); + return NULL; + } + + Py_DECREF(tmp); + return result; +} + +static PyNumberMethods dbmviews_as_number = { + 0, /*nb_add*/ + (binaryfunc)dbmviews_sub, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_negative*/ + 0, /*nb_positive*/ + 0, /*nb_absolute*/ + 0, /*nb_bool*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + (binaryfunc)dbmviews_and, /*nb_and*/ + (binaryfunc)dbmviews_xor, /*nb_xor*/ + (binaryfunc)dbmviews_or, /*nb_or*/ +}; + +/*** dbm_keys ***/ + +static PyObject * +dbmkeys_iter(dbmviewobject *dv) +{ + if (dv->dv_dbm == NULL) { + Py_RETURN_NONE; + } + return dbm_iter(dv->dv_dbm); +} + +static int +dbmkeys_contains(dbmviewobject *dv, PyObject *obj) +{ + if (dv->dv_dbm == NULL) + return 0; + return dbm_contains((PyObject *)dv->dv_dbm, obj); +} + +static PySequenceMethods dbmkeys_as_sequence = { + (lenfunc)dbmview_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)dbmkeys_contains, /* sq_contains */ +}; + +static PyObject* +dbmviews_isdisjoint(PyObject *self, PyObject *other) +{ + PyObject *it; + PyObject *item = NULL; + + if (self == other) { + if (dbmview_len((dbmviewobject *)self) == 0) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + /* Iterate over the shorter object (only if other is a set, + * because PySequence_Contains may be expensive otherwise): */ + if (PyAnySet_Check(other) || is_dbmviewsetobject(other)) { + Py_ssize_t len_self = dbmview_len((dbmviewobject *)self); + Py_ssize_t len_other = PyObject_Size(other); + if (len_other == -1) + return NULL; + + if ((len_other > len_self)) { + PyObject *tmp = other; + other = self; + self = tmp; + } + } + + it = PyObject_GetIter(other); + if (it == NULL) + return NULL; + + while ((item = PyIter_Next(it)) != NULL) { + int contains = PySequence_Contains(self, item); + Py_DECREF(item); + if (contains == -1) { + Py_DECREF(it); + return NULL; + } + + if (contains) { + Py_DECREF(it); + Py_RETURN_FALSE; + } + } + Py_DECREF(it); + if (PyErr_Occurred()) + return NULL; /* PyIter_Next raised an exception. */ + Py_RETURN_TRUE; +} + +PyDoc_STRVAR(isdisjoint_doc, +"Return True if the view and the given iterable have a null intersection."); + +static PyMethodDef dbmviews_methods[] = { + {"isdisjoint", (PyCFunction)dbmviews_isdisjoint, METH_O, + isdisjoint_doc}, + {NULL, NULL} /* sentinel */ +}; + +PyTypeObject DbmKeys_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "dbm_keys", /* tp_name */ + sizeof(dbmviewobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmview_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + &dbmviews_as_number, /* tp_as_number */ + &dbmkeys_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + dbmview_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)dbmkeys_iter, /* tp_iter */ + 0, /* tp_iternext */ + dbmviews_methods, /* tp_methods */ + 0, +}; + +/* XXX Should return a set or a set view */ +PyDoc_STRVAR(dbm_keys__doc__, +"keys() -> keys view\n\ +Get a set-like object providing a view on the database's keys."); + +static PyObject * +dbmkeys_new(PyObject *dbm) +{ + return dbmview_new(dbm, &DbmKeys_Type); +} + + +/*** dbm_values ***/ + +static PyObject * +dbmvalues_iter(dbmviewobject *dv) +{ + if (dv->dv_dbm == NULL) { + Py_RETURN_NONE; + } + return dbmiter_new(dv->dv_dbm, &DbmIterValue_Type); +} + +static PySequenceMethods dbmvalues_as_sequence = { + (lenfunc)dbmview_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ +}; + +PyTypeObject DbmValues_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "dbm_values", /* tp_name */ + sizeof(dbmviewobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmview_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &dbmvalues_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)dbmvalues_iter, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, +}; + + +/*** dbm_items ***/ + +static int +dbmitems_contains(dbmviewobject *dv, PyObject *obj) +{ + PyObject *key, *value, *key_bytes, *value_bytes, *vrec_bytes; + datum krec, vrec; + int result; + + if (dv->dv_dbm == NULL) + return 0; + if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) + return 0; + + key = PyTuple_GET_ITEM(obj, 0); + value = PyTuple_GET_ITEM(obj, 1); + if (PyUnicode_Check(key)) { + key_bytes = _PyUnicode_AsDefaultEncodedString(key); + if (key_bytes == NULL) + return -1; + } + else + key_bytes = key; + if (!PyBytes_Check(key_bytes)) { + PyErr_Format(PyExc_TypeError, + "DBM key must be string, not %100s", + key->ob_type->tp_name); + return -1; + } + krec.dptr = PyBytes_AS_STRING(key_bytes); + krec.dsize = PyBytes_GET_SIZE(key_bytes); + vrec = gdbm_fetch(dv->dv_dbm->di_dbm, krec); + if (vrec.dptr != NULL) { + if ((vrec_bytes = PyBytes_FromStringAndSize(vrec.dptr, + vrec.dsize)) == NULL) { + free(vrec.dptr); + return -1; + } + free(vrec.dptr); + if (PyUnicode_Check(value)) { + value_bytes = _PyUnicode_AsDefaultEncodedString(value); + if (value_bytes == NULL) { + Py_DECREF(vrec_bytes); + return -1; + } + } + else + value_bytes = value; + result = PyObject_RichCompareBool(value_bytes, vrec_bytes, Py_EQ); + Py_DECREF(vrec_bytes); + return result; + } + return 0; +} + +static PyObject * +dbmitems_iter(dbmviewobject *dv) +{ + if (dv->dv_dbm == NULL) { + Py_RETURN_NONE; + } + return dbmiter_new(dv->dv_dbm, &DbmIterItem_Type); +} + +static PySequenceMethods dbmitems_as_sequence = { + (lenfunc)dbmview_len, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)dbmitems_contains, /* sq_contains */ +}; + +PyTypeObject DbmItems_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "dbm_items", /* tp_name */ + sizeof(dbmviewobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dbmview_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + &dbmviews_as_number, /* tp_as_number */ + &dbmitems_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + dbmview_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)dbmitems_iter, /* tp_iter */ + 0, /* tp_iternext */ + dbmviews_methods, /* tp_methods */ + 0, +}; + +PyDoc_STRVAR(dbm_items__doc__, +"items() -> items view\n\ +Get a set-like object providing a view on the database's items."); + +static PyObject * +dbmitems_new(PyObject *dbm) +{ + return dbmview_new(dbm, &DbmItems_Type); +} + +PyDoc_STRVAR(dbm_values__doc__, +"values() -> values view\n\ +Get an object providing a view on the database's values."); + +static PyObject * +dbmvalues_new(PyObject *dbm) +{ + return dbmview_new(dbm, &DbmValues_Type); +} + static PyMethodDef dbm_methods[] = { {"close", (PyCFunction)dbm_close, METH_NOARGS, dbm_close__doc__}, - {"keys", (PyCFunction)dbm_keys, METH_NOARGS, dbm_keys__doc__}, + {"keys", (PyCFunction)dbmkeys_new, METH_NOARGS, dbm_keys__doc__}, {"firstkey", (PyCFunction)dbm_firstkey,METH_NOARGS, dbm_firstkey__doc__}, {"nextkey", (PyCFunction)dbm_nextkey, METH_VARARGS, dbm_nextkey__doc__}, {"reorganize",(PyCFunction)dbm_reorganize,METH_NOARGS, dbm_reorganize__doc__}, {"sync", (PyCFunction)dbm_sync, METH_NOARGS, dbm_sync__doc__}, {"get", (PyCFunction)dbm_get, METH_VARARGS, dbm_get__doc__}, {"setdefault",(PyCFunction)dbm_setdefault,METH_VARARGS, dbm_setdefault__doc__}, + {"values", (PyCFunction)dbmvalues_new, METH_NOARGS, dbm_values__doc__}, + {"items", (PyCFunction)dbmitems_new, METH_NOARGS, dbm_items__doc__}, + {"pop", (PyCFunction)dbm_pop, METH_VARARGS, dbm_pop__doc__}, + {"popitem", (PyCFunction)dbm_popitem, METH_VARARGS, dbm_popitem__doc__}, + {"clear", (PyCFunction)dbm_clear, METH_VARARGS, dbm_clear__doc__}, + {"update", (PyCFunction)dbm_update, METH_VARARGS|METH_KEYWORDS, + dbm_update__doc__}, {NULL, NULL} /* sentinel */ }; @@ -454,7 +1532,7 @@ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ + (getiterfunc)dbm_iter, /*tp_iter*/ 0, /*tp_iternext*/ dbm_methods, /*tp_methods*/ }; diff -r 267578b2422d -r 318f73c2e870 Objects/abstract.c --- a/Objects/abstract.c Tue Mar 22 22:57:49 2011 -0700 +++ b/Objects/abstract.c Wed Mar 23 21:26:10 2011 +0800 @@ -109,6 +109,30 @@ return rv; } +int +_PyObject_AllContainedIn(PyObject *self, PyObject *other) +{ + PyObject *iter = PyObject_GetIter(self); + int ok = 1; + + if (iter == NULL) + return -1; + for (;;) { + PyObject *next = PyIter_Next(iter); + if (next == NULL) { + if (PyErr_Occurred()) + ok = -1; + break; + } + ok = PySequence_Contains(other, next); + Py_DECREF(next); + if (ok <= 0) + break; + } + Py_DECREF(iter); + return ok; +} + PyObject * PyObject_GetItem(PyObject *o, PyObject *key) { diff -r 267578b2422d -r 318f73c2e870 Objects/dictobject.c --- a/Objects/dictobject.c Tue Mar 22 22:57:49 2011 -0700 +++ b/Objects/dictobject.c Wed Mar 23 21:26:10 2011 +0800 @@ -2571,32 +2571,6 @@ - if public then they should probably be in builtins */ -/* Return 1 if self is a subset of other, iterating over self; - 0 if not; -1 if an error occurred. */ -static int -all_contained_in(PyObject *self, PyObject *other) -{ - PyObject *iter = PyObject_GetIter(self); - int ok = 1; - - if (iter == NULL) - return -1; - for (;;) { - PyObject *next = PyIter_Next(iter); - if (next == NULL) { - if (PyErr_Occurred()) - ok = -1; - break; - } - ok = PySequence_Contains(other, next); - Py_DECREF(next); - if (ok <= 0) - break; - } - Py_DECREF(iter); - return ok; -} - static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { @@ -2626,29 +2600,29 @@ case Py_NE: case Py_EQ: if (len_self == len_other) - ok = all_contained_in(self, other); + ok = _PyObject_AllContainedIn(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) - ok = all_contained_in(self, other); + ok = _PyObject_AllContainedIn(self, other); break; case Py_LE: if (len_self <= len_other) - ok = all_contained_in(self, other); + ok = _PyObject_AllContainedIn(self, other); break; case Py_GT: if (len_self > len_other) - ok = all_contained_in(other, self); + ok = _PyObject_AllContainedIn(other, self); break; case Py_GE: if (len_self >= len_other) - ok = all_contained_in(other, self); + ok = _PyObject_AllContainedIn(other, self); break; }