From 0a7ac7d70d370544c6a9d118bbbd6886ad4f5ce5 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 26 May 2008 10:29:35 +0000 Subject: Create the dbm package from PEP 3108. #2881. --- Demo/classes/Dbm.py | 4 +- Doc/distutils/setupscript.rst | 2 +- Doc/library/anydbm.rst | 96 -------- Doc/library/bsddb.rst | 2 +- Doc/library/dbhash.rst | 114 --------- Doc/library/dbm.rst | 351 ++++++++++++++++++++++++++-- Doc/library/dumbdbm.rst | 78 ------- Doc/library/gdbm.rst | 122 ---------- Doc/library/persistence.rst | 6 - Doc/library/shelve.rst | 29 +-- Doc/library/whichdb.rst | 20 -- Doc/reference/datamodel.rst | 8 +- Lib/anydbm.py | 83 ------- Lib/dbhash.py | 16 -- Lib/dbm/__init__.py | 198 ++++++++++++++++ Lib/dbm/bsd.py | 10 + Lib/dbm/dumb.py | 257 ++++++++++++++++++++ Lib/dbm/gnu.py | 3 + Lib/dbm/ndbm.py | 3 + Lib/dumbdbm.py | 255 -------------------- Lib/io.py | 2 +- Lib/shelve.py | 8 +- Lib/test/test___all__.py | 2 +- Lib/test/test_anydbm.py | 90 ++++--- Lib/test/test_bsddb.py | 2 +- Lib/test/test_dbm.py | 40 ---- Lib/test/test_dbm_dumb.py | 185 +++++++++++++++ Lib/test/test_dbm_gnu.py | 81 +++++++ Lib/test/test_dbm_ndbm.py | 40 ++++ Lib/test/test_dumbdbm.py | 185 --------------- Lib/test/test_gdbm.py | 81 ------- Lib/test/test_whichdb.py | 58 ----- Lib/whichdb.py | 118 ---------- Misc/PURIFY.README | 4 +- Misc/cheatsheet | 21 +- Modules/Setup.dist | 8 +- Modules/_dbmmodule.c | 412 ++++++++++++++++++++++++++++++++ Modules/_gdbmmodule.c | 533 ++++++++++++++++++++++++++++++++++++++++++ Modules/_threadmodule.c | 4 +- Modules/dbmmodule.c | 412 -------------------------------- Modules/gdbmmodule.c | 533 ------------------------------------------ PC/os2emx/Makefile | 6 +- PC/os2vacpp/makefile | 4 +- PC/os2vacpp/makefile.omk | 8 +- Tools/scripts/db2pickle.py | 14 +- Tools/scripts/pickle2db.py | 18 +- setup.py | 16 +- 47 files changed, 2178 insertions(+), 2364 deletions(-) delete mode 100644 Doc/library/anydbm.rst delete mode 100644 Doc/library/dbhash.rst delete mode 100644 Doc/library/dumbdbm.rst delete mode 100644 Doc/library/gdbm.rst delete mode 100644 Doc/library/whichdb.rst delete mode 100644 Lib/anydbm.py delete mode 100644 Lib/dbhash.py create mode 100644 Lib/dbm/__init__.py create mode 100644 Lib/dbm/bsd.py create mode 100644 Lib/dbm/dumb.py create mode 100644 Lib/dbm/gnu.py create mode 100644 Lib/dbm/ndbm.py delete mode 100644 Lib/dumbdbm.py delete mode 100755 Lib/test/test_dbm.py create mode 100644 Lib/test/test_dbm_dumb.py create mode 100755 Lib/test/test_dbm_gnu.py create mode 100755 Lib/test/test_dbm_ndbm.py delete mode 100644 Lib/test/test_dumbdbm.py delete mode 100755 Lib/test/test_gdbm.py delete mode 100644 Lib/test/test_whichdb.py delete mode 100644 Lib/whichdb.py create mode 100644 Modules/_dbmmodule.c create mode 100644 Modules/_gdbmmodule.c delete mode 100644 Modules/dbmmodule.c delete mode 100644 Modules/gdbmmodule.c diff --git a/Demo/classes/Dbm.py b/Demo/classes/Dbm.py index 617fe99..f931e93 100755 --- a/Demo/classes/Dbm.py +++ b/Demo/classes/Dbm.py @@ -7,8 +7,8 @@ class Dbm: def __init__(self, filename, mode, perm): - import dbm - self.db = dbm.open(filename, mode, perm) + import dbm.ndbm + self.db = dbm.ndbm.open(filename, mode, perm) def __repr__(self): s = '' diff --git a/Doc/distutils/setupscript.rst b/Doc/distutils/setupscript.rst index 3cc1da9..616b99c 100644 --- a/Doc/distutils/setupscript.rst +++ b/Doc/distutils/setupscript.rst @@ -316,7 +316,7 @@ For example, if you need to link against libraries known to be in the standard library search path on target systems :: Extension(..., - libraries=['gdbm', 'readline']) + libraries=['_gdbm', 'readline']) If you need to link with libraries in a non-standard location, you'll have to include the location in ``library_dirs``:: diff --git a/Doc/library/anydbm.rst b/Doc/library/anydbm.rst deleted file mode 100644 index f35a416..0000000 --- a/Doc/library/anydbm.rst +++ /dev/null @@ -1,96 +0,0 @@ - -:mod:`anydbm` --- Generic access to DBM-style databases -======================================================= - -.. module:: anydbm - :synopsis: Generic interface to DBM-style database modules. - - -.. index:: - module: dbhash - module: bsddb - module: gdbm - module: dbm - module: dumbdbm - -:mod:`anydbm` is a generic interface to variants of the DBM database --- -:mod:`dbhash` (requires :mod:`bsddb`), :mod:`gdbm`, or :mod:`dbm`. If none of -these modules is installed, the slow-but-simple implementation in module -:mod:`dumbdbm` will be used. - - -.. function:: open(filename[, flag[, mode]]) - - Open the database file *filename* and return a corresponding object. - - If the database file already exists, the :mod:`whichdb` module is used to - determine its type and the appropriate module is used; if it does not exist, the - first module listed above that can be imported is used. - - The optional *flag* argument can be ``'r'`` to open an existing database for - reading only, ``'w'`` to open an existing database for reading and writing, - ``'c'`` to create the database if it doesn't exist, or ``'n'``, which will - always create a new empty database. If not specified, the default value is - ``'r'``. - - The optional *mode* argument is the Unix mode of the file, used only when the - database has to be created. It defaults to octal ``0666`` (and will be modified - by the prevailing umask). - - -.. exception:: error - - A tuple containing the exceptions that can be raised by each of the supported - modules, with a unique exception also named :exc:`anydbm.error` as the first - item --- the latter is used when :exc:`anydbm.error` is raised. - -The object returned by :func:`open` supports most of the same functionality as -dictionaries; keys and their corresponding values can be stored, retrieved, and -deleted, and the :meth:`has_key` and :meth:`keys` methods are available. Keys -and values must always be strings. - -The following example records some hostnames and a corresponding title, and -then prints out the contents of the database:: - - import anydbm - - # Open database, creating it if necessary. - db = anydbm.open('cache', 'c') - - # Record some values - db['www.python.org'] = 'Python Website' - db['www.cnn.com'] = 'Cable News Network' - - # Loop through contents. Other dictionary methods - # such as .keys(), .values() also work. - for k, v in db.iteritems(): - print(k, '\t', v) - - # Storing a non-string key or value will raise an exception (most - # likely a TypeError). - db['www.yahoo.com'] = 4 - - # Close when done. - db.close() - - -.. seealso:: - - Module :mod:`dbhash` - BSD ``db`` database interface. - - Module :mod:`dbm` - Standard Unix database interface. - - Module :mod:`dumbdbm` - Portable implementation of the ``dbm`` interface. - - Module :mod:`gdbm` - GNU database interface, based on the ``dbm`` interface. - - Module :mod:`shelve` - General object persistence built on top of the Python ``dbm`` interface. - - Module :mod:`whichdb` - Utility module used to determine the type of an existing database. - diff --git a/Doc/library/bsddb.rst b/Doc/library/bsddb.rst index 1b153c9..9fde725 100644 --- a/Doc/library/bsddb.rst +++ b/Doc/library/bsddb.rst @@ -92,7 +92,7 @@ arguments should be used in most instances. .. seealso:: - Module :mod:`dbhash` + Module :mod:`dbm.bsd` DBM-style interface to the :mod:`bsddb` diff --git a/Doc/library/dbhash.rst b/Doc/library/dbhash.rst deleted file mode 100644 index aadb14f..0000000 --- a/Doc/library/dbhash.rst +++ /dev/null @@ -1,114 +0,0 @@ - -:mod:`dbhash` --- DBM-style interface to the BSD database library -================================================================= - -.. module:: dbhash - :synopsis: DBM-style interface to the BSD database library. -.. sectionauthor:: Fred L. Drake, Jr. - - -.. index:: module: bsddb - -The :mod:`dbhash` module provides a function to open databases using the BSD -``db`` library. This module mirrors the interface of the other Python database -modules that provide access to DBM-style databases. The :mod:`bsddb` module is -required to use :mod:`dbhash`. - -This module provides an exception and a function: - - -.. exception:: error - - Exception raised on database errors other than :exc:`KeyError`. It is a synonym - for :exc:`bsddb.error`. - - -.. function:: open(path[, flag[, mode]]) - - Open a ``db`` database and return the database object. The *path* argument is - the name of the database file. - - The *flag* argument can be: - - +---------+-------------------------------------------+ - | Value | Meaning | - +=========+===========================================+ - | ``'r'`` | Open existing database for reading only | - | | (default) | - +---------+-------------------------------------------+ - | ``'w'`` | Open existing database for reading and | - | | writing | - +---------+-------------------------------------------+ - | ``'c'`` | Open database for reading and writing, | - | | creating it if it doesn't exist | - +---------+-------------------------------------------+ - | ``'n'`` | Always create a new, empty database, open | - | | for reading and writing | - +---------+-------------------------------------------+ - - For platforms on which the BSD ``db`` library supports locking, an ``'l'`` - can be appended to indicate that locking should be used. - - The optional *mode* parameter is used to indicate the Unix permission bits that - should be set if a new database must be created; this will be masked by the - current umask value for the process. - - -.. seealso:: - - Module :mod:`anydbm` - Generic interface to ``dbm``\ -style databases. - - Module :mod:`bsddb` - Lower-level interface to the BSD ``db`` library. - - Module :mod:`whichdb` - Utility module used to determine the type of an existing database. - - -.. _dbhash-objects: - -Database Objects ----------------- - -The database objects returned by :func:`open` provide the methods common to all -the DBM-style databases and mapping objects. The following methods are -available in addition to the standard methods. - - -.. method:: dbhash.first() - - It's possible to loop over every key/value pair in the database using this - method and the :meth:`next` method. The traversal is ordered by the databases - internal hash values, and won't be sorted by the key values. This method - returns the starting key. - - -.. method:: dbhash.last() - - Return the last key/value pair in a database traversal. This may be used to - begin a reverse-order traversal; see :meth:`previous`. - - -.. method:: dbhash.next() - - Returns the key next key/value pair in a database traversal. The following code - prints every key in the database ``db``, without having to create a list in - memory that contains them all:: - - print(db.first()) - for i in range(1, len(db)): - print(db.next()) - - -.. method:: dbhash.previous() - - Returns the previous key/value pair in a forward-traversal of the database. In - conjunction with :meth:`last`, this may be used to implement a reverse-order - traversal. - - -.. method:: dbhash.sync() - - This method forces any unwritten data to be written to the disk. - diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index 52923e8..2a314dc 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -1,14 +1,294 @@ - -:mod:`dbm` --- Simple "database" interface -========================================== +:mod:`dbm` --- Interfaces to Unix "databases" +============================================= .. module:: dbm + :synopsis: Interfaces to various Unix "database" formats. + +:mod:`dbm` is a generic interface to variants of the DBM database --- +:mod:`dbm.bsd` (requires :mod:`bsddb`), :mod:`dbm.gnu`, or :mod:`dbm.ndbm`. If +none of these modules is installed, the slow-but-simple implementation in module +:mod:`dbm.dumb` will be used. + + +.. exception:: error + + A tuple containing the exceptions that can be raised by each of the supported + modules, with a unique exception also named :exc:`dbm.error` as the first + item --- the latter is used when :exc:`dbm.error` is raised. + + +.. function:: whichdb(filename) + + This functionattempts to guess which of the several simple database modules + available --- :mod:`dbm.bsd`, :mod:`dbm.gnu`, :mod:`dbm.ndbm` or + :mod:`dbm.dumb` --- should be used to open a given file. + + Returns one of the following values: ``None`` if the file can't be opened + because it's unreadable or doesn't exist; the empty string (``''``) if the + file's format can't be guessed; or a string containing the required module + name, such as ``'dbm.ndbm'`` or ``'dbm.gnu'``. + + +.. function:: open(filename[, flag[, mode]]) + + Open the database file *filename* and return a corresponding object. + + If the database file already exists, the :func:`whichdb` function is used to + determine its type and the appropriate module is used; if it does not exist, + the first module listed above that can be imported is used. + + The optional *flag* argument can be ``'r'`` to open an existing database for + reading only, ``'w'`` to open an existing database for reading and writing, + ``'c'`` to create the database if it doesn't exist, or ``'n'``, which will + always create a new empty database. If not specified, the default value is + ``'r'``. + + The optional *mode* argument is the Unix mode of the file, used only when the + database has to be created. It defaults to octal ``0o666`` (and will be + modified by the prevailing umask). + + +The object returned by :func:`open` supports most of the same 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. Keys and values must always be strings. + +The following example records some hostnames and a corresponding title, and +then prints out the contents of the database:: + + import dbm + + # Open database, creating it if necessary. + db = dbm.open('cache', 'c') + + # Record some values + db['www.python.org'] = 'Python Website' + db['www.cnn.com'] = 'Cable News Network' + + # Loop through contents. Other dictionary methods + # such as .keys(), .values() also work. + for k, v in db.iteritems(): + print(k, '\t', v) + + # Storing a non-string key or value will raise an exception (most + # likely a TypeError). + db['www.yahoo.com'] = 4 + + # Close when done. + db.close() + + +.. seealso:: + + Module :mod:`shelve` + Persistence module which stores non-string data. + + +The individual submodules are described in the following sections. + + +:mod:`dbm.bsd` --- DBM-style interface to the BSD database library +------------------------------------------------------------------ + +.. module:: dbm.bsd + :synopsis: DBM-style interface to the BSD database library. +.. sectionauthor:: Fred L. Drake, Jr. + +.. index:: module: bsddb + +The :mod:`dbm.bsd` module provides a function to open databases using the BSD +``db`` library. This module mirrors the interface of the other Python database +modules that provide access to DBM-style databases. The :mod:`bsddb` module is +required to use :mod:`dbm.bsd`. + +.. exception:: error + + Exception raised on database errors other than :exc:`KeyError`. It is a synonym + for :exc:`bsddb.error`. + + +.. function:: open(path[, flag[, mode]]) + + Open a ``db`` database and return the database object. The *path* argument is + the name of the database file. + + The *flag* argument can be: + + +---------+-------------------------------------------+ + | Value | Meaning | + +=========+===========================================+ + | ``'r'`` | Open existing database for reading only | + | | (default) | + +---------+-------------------------------------------+ + | ``'w'`` | Open existing database for reading and | + | | writing | + +---------+-------------------------------------------+ + | ``'c'`` | Open database for reading and writing, | + | | creating it if it doesn't exist | + +---------+-------------------------------------------+ + | ``'n'`` | Always create a new, empty database, open | + | | for reading and writing | + +---------+-------------------------------------------+ + + For platforms on which the BSD ``db`` library supports locking, an ``'l'`` + can be appended to indicate that locking should be used. + + The optional *mode* parameter is used to indicate the Unix permission bits that + should be set if a new database must be created; this will be masked by the + current umask value for the process. + + The database objects returned by :func:`open` provide the methods common to all + the DBM-style databases and mapping objects. The following methods are + available in addition to the standard methods: + + .. method:: dbhash.first() + + It's possible to loop over every key/value pair in the database using this + method and the :meth:`next` method. The traversal is ordered by the databases + internal hash values, and won't be sorted by the key values. This method + returns the starting key. + + .. method:: dbhash.last() + + Return the last key/value pair in a database traversal. This may be used to + begin a reverse-order traversal; see :meth:`previous`. + + .. method:: dbhash.next() + + Returns the key next key/value pair in a database traversal. The following code + prints every key in the database ``db``, without having to create a list in + memory that contains them all:: + + print(db.first()) + for i in range(1, len(db)): + print(db.next()) + + .. method:: dbhash.previous() + + Returns the previous key/value pair in a forward-traversal of the database. In + conjunction with :meth:`last`, this may be used to implement a reverse-order + traversal. + + .. method:: dbhash.sync() + + This method forces any unwritten data to be written to the disk. + + +:mod:`dbm.gnu` --- GNU's reinterpretation of dbm +------------------------------------------------ + +.. module:: dbm.gnu + :platform: Unix + :synopsis: GNU's reinterpretation of dbm. + + +This module is quite similar to the :mod:`dbm` module, but uses the GNU library +``gdbm`` instead to provide some additional functionality. Please note that the +file formats created by ``gdbm`` and ``dbm`` are incompatible. + +The :mod:`dbm.gnu` module provides an interface to the GNU DBM library. +``gdbm`` objects behave like mappings (dictionaries), except that keys and +values are always strings. Printing a :mod:`dbm.gnu` object doesn't print the +keys and values, and the :meth:`items` and :meth:`values` methods are not +supported. + +.. exception:: error + + Raised on ``gdbm``\ -specific errors, such as I/O errors. :exc:`KeyError` is + raised for general mapping errors like specifying an incorrect key. + + +.. function:: open(filename, [flag, [mode]]) + + Open a ``gdbm`` database and return a :class:`gdbm` object. The *filename* + argument is the name of the database file. + + The optional *flag* argument can be: + + +---------+-------------------------------------------+ + | Value | Meaning | + +=========+===========================================+ + | ``'r'`` | Open existing database for reading only | + | | (default) | + +---------+-------------------------------------------+ + | ``'w'`` | Open existing database for reading and | + | | writing | + +---------+-------------------------------------------+ + | ``'c'`` | Open database for reading and writing, | + | | creating it if it doesn't exist | + +---------+-------------------------------------------+ + | ``'n'`` | Always create a new, empty database, open | + | | for reading and writing | + +---------+-------------------------------------------+ + + The following additional characters may be appended to the flag to control + how the database is opened: + + +---------+--------------------------------------------+ + | Value | Meaning | + +=========+============================================+ + | ``'f'`` | Open the database in fast mode. Writes | + | | to the database will not be synchronized. | + +---------+--------------------------------------------+ + | ``'s'`` | Synchronized mode. This will cause changes | + | | to the database to be immediately written | + | | to the file. | + +---------+--------------------------------------------+ + | ``'u'`` | Do not lock database. | + +---------+--------------------------------------------+ + + Not all flags are valid for all versions of ``gdbm``. The module constant + :const:`open_flags` is a string of supported flag characters. The exception + :exc:`error` is raised if an invalid flag is specified. + + The optional *mode* argument is the Unix mode of the file, used only when the + database has to be created. It defaults to octal ``0666``. + + In addition to the dictionary-like methods, ``gdbm`` objects have the + following methods: + + .. method:: gdbm.firstkey() + + It's possible to loop over every key in the database using this method and the + :meth:`nextkey` method. The traversal is ordered by ``gdbm``'s internal + hash values, and won't be sorted by the key values. This method returns + the starting key. + + .. method:: gdbm.nextkey(key) + + Returns the key that follows *key* in the traversal. The following code prints + every key in the database ``db``, without having to create a list in memory that + contains them all:: + + k = db.firstkey() + while k != None: + print(k) + k = db.nextkey(k) + + .. method:: gdbm.reorganize() + + If you have carried out a lot of deletions and would like to shrink the space + used by the ``gdbm`` file, this routine will reorganize the database. ``gdbm`` + will not shorten the length of a database file except by using this + reorganization; otherwise, deleted file space will be kept and reused as new + (key, value) pairs are added. + + .. method:: gdbm.sync() + + When the database has been opened in fast mode, this method forces any + unwritten data to be written to the disk. + + +:mod:`dbm.ndbm` --- Interface based on ndbm +------------------------------------------- + +.. module:: dbm.ndbm :platform: Unix :synopsis: The standard "database" interface, based on ndbm. -The :mod:`dbm` module provides an interface to the Unix "(n)dbm" library. Dbm -objects behave like mappings (dictionaries), except that keys and values are +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 strings. Printing a dbm object doesn't print the keys and values, and the :meth:`items` and :meth:`values` methods are not supported. @@ -17,13 +297,10 @@ compatibility interface, or the GNU GDBM compatibility interface. On Unix, the :program:`configure` script will attempt to locate the appropriate header file to simplify building this module. -The module defines the following: - - .. exception:: error - Raised on dbm-specific errors, such as I/O errors. :exc:`KeyError` is raised for - general mapping errors like specifying an incorrect key. + Raised on dbm-specific errors, such as I/O errors. :exc:`KeyError` is raised + for general mapping errors like specifying an incorrect key. .. data:: library @@ -61,14 +338,54 @@ The module defines the following: modified by the prevailing umask). -.. seealso:: - Module :mod:`anydbm` - Generic interface to ``dbm``\ -style databases. +:mod:`dbm.dumb` --- Portable DBM implementation +----------------------------------------------- + +.. module:: dbm.dumb + :synopsis: Portable implementation of the simple DBM interface. + +.. index:: single: databases + +.. note:: + + The :mod:`dbm.dumb` module is intended as a last resort fallback for the + :mod:`dbm` module when no more robust module is available. The :mod:`dbm.dumb` + module is not written for speed and is not nearly as heavily used as the other + database modules. + +The :mod:`dbm.dumb` module provides a persistent dictionary-like interface which +is written entirely in Python. Unlike other modules such as :mod:`gdbm` and +:mod:`bsddb`, no external library is required. As with other persistent +mappings, the keys and values must always be strings. + +The module defines the following: + + +.. exception:: error + + Raised on dbm.dumb-specific errors, such as I/O errors. :exc:`KeyError` is + raised for general mapping errors like specifying an incorrect key. + + +.. function:: open(filename[, flag[, mode]]) + + Open a dumbdbm database and return a dumbdbm object. The *filename* argument is + the basename of the database file (without any specific extensions). When a + dumbdbm database is created, files with :file:`.dat` and :file:`.dir` extensions + are created. + + The optional *flag* argument is currently ignored; the database is always opened + for update, and will be created if it does not exist. + + The optional *mode* argument is the Unix mode of the file, used only when the + database has to be created. It defaults to octal ``0o666`` (and will be modified + by the prevailing umask). - Module :mod:`gdbm` - Similar interface to the GNU GDBM library. + In addition to the methods provided by the :class:`collections.MutableMapping` class, + :class:`dumbdbm` objects provide the following method: - Module :mod:`whichdb` - Utility module used to determine the type of an existing database. + .. method:: dumbdbm.sync() + Synchronize the on-disk directory and data files. This method is called + by the :meth:`Shelve.sync` method. diff --git a/Doc/library/dumbdbm.rst b/Doc/library/dumbdbm.rst deleted file mode 100644 index 4e91ac9..0000000 --- a/Doc/library/dumbdbm.rst +++ /dev/null @@ -1,78 +0,0 @@ - -:mod:`dumbdbm` --- Portable DBM implementation -============================================== - -.. module:: dumbdbm - :synopsis: Portable implementation of the simple DBM interface. - - -.. index:: single: databases - -.. note:: - - The :mod:`dumbdbm` module is intended as a last resort fallback for the - :mod:`anydbm` module when no more robust module is available. The :mod:`dumbdbm` - module is not written for speed and is not nearly as heavily used as the other - database modules. - -The :mod:`dumbdbm` module provides a persistent dictionary-like interface which -is written entirely in Python. Unlike other modules such as :mod:`gdbm` and -:mod:`bsddb`, no external library is required. As with other persistent -mappings, the keys and values must always be strings. - -The module defines the following: - - -.. exception:: error - - Raised on dumbdbm-specific errors, such as I/O errors. :exc:`KeyError` is - raised for general mapping errors like specifying an incorrect key. - - -.. function:: open(filename[, flag[, mode]]) - - Open a dumbdbm database and return a dumbdbm object. The *filename* argument is - the basename of the database file (without any specific extensions). When a - dumbdbm database is created, files with :file:`.dat` and :file:`.dir` extensions - are created. - - The optional *flag* argument is currently ignored; the database is always opened - for update, and will be created if it does not exist. - - The optional *mode* argument is the Unix mode of the file, used only when the - database has to be created. It defaults to octal ``0666`` (and will be modified - by the prevailing umask). - - -.. seealso:: - - Module :mod:`anydbm` - Generic interface to ``dbm``\ -style databases. - - Module :mod:`dbm` - Similar interface to the DBM/NDBM library. - - Module :mod:`gdbm` - Similar interface to the GNU GDBM library. - - Module :mod:`shelve` - Persistence module which stores non-string data. - - Module :mod:`whichdb` - Utility module used to determine the type of an existing database. - - -.. _dumbdbm-objects: - -Dumbdbm Objects ---------------- - -In addition to the methods provided by the :class:`UserDict.DictMixin` class, -:class:`dumbdbm` objects provide the following methods. - - -.. method:: dumbdbm.sync() - - Synchronize the on-disk directory and data files. This method is called by the - :meth:`sync` method of :class:`Shelve` objects. - diff --git a/Doc/library/gdbm.rst b/Doc/library/gdbm.rst deleted file mode 100644 index f69e667..0000000 --- a/Doc/library/gdbm.rst +++ /dev/null @@ -1,122 +0,0 @@ - -:mod:`gdbm` --- GNU's reinterpretation of dbm -============================================= - -.. module:: gdbm - :platform: Unix - :synopsis: GNU's reinterpretation of dbm. - - -.. index:: module: dbm - -This module is quite similar to the :mod:`dbm` module, but uses ``gdbm`` instead -to provide some additional functionality. Please note that the file formats -created by ``gdbm`` and ``dbm`` are incompatible. - -The :mod:`gdbm` module provides an interface to the GNU DBM library. ``gdbm`` -objects behave like mappings (dictionaries), except that keys and values are -always strings. Printing a ``gdbm`` object doesn't print the keys and values, -and the :meth:`items` and :meth:`values` methods are not supported. - -The module defines the following constant and functions: - - -.. exception:: error - - Raised on ``gdbm``\ -specific errors, such as I/O errors. :exc:`KeyError` is - raised for general mapping errors like specifying an incorrect key. - - -.. function:: open(filename, [flag, [mode]]) - - Open a ``gdbm`` database and return a ``gdbm`` object. The *filename* argument - is the name of the database file. - - The optional *flag* argument can be: - - +---------+-------------------------------------------+ - | Value | Meaning | - +=========+===========================================+ - | ``'r'`` | Open existing database for reading only | - | | (default) | - +---------+-------------------------------------------+ - | ``'w'`` | Open existing database for reading and | - | | writing | - +---------+-------------------------------------------+ - | ``'c'`` | Open database for reading and writing, | - | | creating it if it doesn't exist | - +---------+-------------------------------------------+ - | ``'n'`` | Always create a new, empty database, open | - | | for reading and writing | - +---------+-------------------------------------------+ - - The following additional characters may be appended to the flag to control - how the database is opened: - - +---------+--------------------------------------------+ - | Value | Meaning | - +=========+============================================+ - | ``'f'`` | Open the database in fast mode. Writes | - | | to the database will not be synchronized. | - +---------+--------------------------------------------+ - | ``'s'`` | Synchronized mode. This will cause changes | - | | to the database to be immediately written | - | | to the file. | - +---------+--------------------------------------------+ - | ``'u'`` | Do not lock database. | - +---------+--------------------------------------------+ - - Not all flags are valid for all versions of ``gdbm``. The module constant - :const:`open_flags` is a string of supported flag characters. The exception - :exc:`error` is raised if an invalid flag is specified. - - The optional *mode* argument is the Unix mode of the file, used only when the - database has to be created. It defaults to octal ``0666``. - -In addition to the dictionary-like methods, ``gdbm`` objects have the following -methods: - - -.. function:: firstkey() - - It's possible to loop over every key in the database using this method and the - :meth:`nextkey` method. The traversal is ordered by ``gdbm``'s internal hash - values, and won't be sorted by the key values. This method returns the starting - key. - - -.. function:: nextkey(key) - - Returns the key that follows *key* in the traversal. The following code prints - every key in the database ``db``, without having to create a list in memory that - contains them all:: - - k = db.firstkey() - while k != None: - print(k) - k = db.nextkey(k) - - -.. function:: reorganize() - - If you have carried out a lot of deletions and would like to shrink the space - used by the ``gdbm`` file, this routine will reorganize the database. ``gdbm`` - will not shorten the length of a database file except by using this - reorganization; otherwise, deleted file space will be kept and reused as new - (key, value) pairs are added. - - -.. function:: sync() - - When the database has been opened in fast mode, this method forces any - unwritten data to be written to the disk. - - -.. seealso:: - - Module :mod:`anydbm` - Generic interface to ``dbm``\ -style databases. - - Module :mod:`whichdb` - Utility module used to determine the type of an existing database. - diff --git a/Doc/library/persistence.rst b/Doc/library/persistence.rst index 3708d17..c5c2aa4 100644 --- a/Doc/library/persistence.rst +++ b/Doc/library/persistence.rst @@ -22,11 +22,5 @@ The list of modules described in this chapter is: copyreg.rst shelve.rst marshal.rst - anydbm.rst - whichdb.rst dbm.rst - gdbm.rst - dbhash.rst - bsddb.rst - dumbdbm.rst sqlite3.rst diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index 262b4a4..ee839e8 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -21,7 +21,7 @@ lots of shared sub-objects. The keys are ordinary strings. the underlying database. As a side-effect, an extension may be added to the filename and more than one file may be created. By default, the underlying database file is opened for reading and writing. The optional *flag* parameter - has the same interpretation as the *flag* parameter of :func:`anydbm.open`. + has the same interpretation as the *flag* parameter of :func:`dbm.open`. By default, version 0 pickles are used to serialize values. The version of the pickle protocol can be specified with the *protocol* parameter. @@ -53,12 +53,12 @@ Restrictions ------------ .. index:: - module: dbm - module: gdbm + module: dbm.ndbm + module: dbm.gnu module: bsddb -* The choice of which database package will be used (such as :mod:`dbm`, - :mod:`gdbm` or :mod:`bsddb`) depends on which interface is available. Therefore +* The choice of which database package will be used (such as :mod:`dbm.ndbm`, + :mod:`dbm.gnu` or :mod:`bsddb`) depends on which interface is available. Therefore it is not safe to open the database directly using :mod:`dbm`. The database is also (unfortunately) subject to the limitations of :mod:`dbm`, if it is used --- this means that (the pickled representation of) the objects stored in the @@ -107,7 +107,7 @@ Restrictions .. class:: DbfilenameShelf(filename[, flag='c'[, protocol=None[, writeback=False]]]) A subclass of :class:`Shelf` which accepts a *filename* instead of a dict-like - object. The underlying file will be opened using :func:`anydbm.open`. By + object. The underlying file will be opened using :func:`dbm.open`. By default, the file will be created and opened for both read and write. The optional *flag* parameter has the same interpretation as for the :func:`open` function. The optional *protocol* and *writeback* parameters have the same @@ -152,25 +152,12 @@ object):: .. seealso:: - Module :mod:`anydbm` - Generic interface to ``dbm``\ -style databases. + Module :mod:`dbm` + Generic interface to ``dbm``-style databases. Module :mod:`bsddb` BSD ``db`` database interface. - Module :mod:`dbhash` - Thin layer around the :mod:`bsddb` which provides an :func:`open` function like - the other database modules. - - Module :mod:`dbm` - Standard Unix database interface. - - Module :mod:`dumbdbm` - Portable implementation of the ``dbm`` interface. - - Module :mod:`gdbm` - GNU database interface, based on the ``dbm`` interface. - Module :mod:`pickle` Object serialization used by :mod:`shelve`. diff --git a/Doc/library/whichdb.rst b/Doc/library/whichdb.rst deleted file mode 100644 index 5c69818..0000000 --- a/Doc/library/whichdb.rst +++ /dev/null @@ -1,20 +0,0 @@ - -:mod:`whichdb` --- Guess which DBM module created a database -============================================================ - -.. module:: whichdb - :synopsis: Guess which DBM-style module created a given database. - - -The single function in this module attempts to guess which of the several simple -database modules available--\ :mod:`dbm`, :mod:`gdbm`, or :mod:`dbhash`\ ---should be used to open a given file. - - -.. function:: whichdb(filename) - - Returns one of the following values: ``None`` if the file can't be opened - because it's unreadable or doesn't exist; the empty string (``''``) if the - file's format can't be guessed; or a string containing the required module name, - such as ``'dbm'`` or ``'gdbm'``. - diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 3912d1e..7460dce8 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -394,12 +394,12 @@ Mappings section :ref:`dict`). .. index:: - module: dbm - module: gdbm + module: dbm.ndbm + module: dbm.gnu module: bsddb - The extension modules :mod:`dbm`, :mod:`gdbm`, and :mod:`bsddb` provide - additional examples of mapping types. + The extension modules :mod:`dbm.ndbm`, :mod:`dbm.gnu`, and :mod:`bsddb` + provide additional examples of mapping types. Callable types .. index:: diff --git a/Lib/anydbm.py b/Lib/anydbm.py deleted file mode 100644 index 41335a3..0000000 --- a/Lib/anydbm.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Generic interface to all dbm clones. - -Instead of - - import dbm - d = dbm.open(file, 'w', 0o666) - -use - - import anydbm - d = anydbm.open(file, 'w') - -The returned object is a dbhash, gdbm, dbm or dumbdbm object, -dependent on the type of database being opened (determined by whichdb -module) in the case of an existing dbm. If the dbm does not exist and -the create or new flag ('c' or 'n') was specified, the dbm type will -be determined by the availability of the modules (tested in the above -order). - -It has the following interface (key and data are strings): - - d[key] = data # store data at key (may override data at - # existing key) - data = d[key] # retrieve data at key (raise KeyError if no - # such key) - del d[key] # delete data stored at key (raises KeyError - # if no such key) - flag = key in d # true if the key exists - list = d.keys() # return a list of all existing keys (slow!) - -Future versions may change the order in which implementations are -tested for existence, add interfaces to other dbm-like -implementations. - -The open function has an optional second argument. This can be 'r', -for read-only access, 'w', for read-write access of an existing -database, 'c' for read-write access to a new or existing database, and -'n' for read-write access to a new database. The default is 'r'. - -Note: 'r' and 'w' fail if the database doesn't exist; 'c' creates it -only if it doesn't exist; and 'n' always creates a new database. - -""" - -class error(Exception): - pass - -_names = ['dbhash', 'gdbm', 'dbm', 'dumbdbm'] -_errors = [error] -_defaultmod = None - -for _name in _names: - try: - _mod = __import__(_name) - except ImportError: - continue - if not _defaultmod: - _defaultmod = _mod - _errors.append(_mod.error) - -if not _defaultmod: - raise ImportError("no dbm clone found; tried %s" % _names) - -error = tuple(_errors) - -def open(file, flag = 'r', mode = 0o666): - # guess the type of an existing database - from whichdb import whichdb - result=whichdb(file) - if result is None: - # db doesn't exist - if 'c' in flag or 'n' in flag: - # file doesn't exist and the new - # flag was used so use default type - mod = _defaultmod - else: - raise error("need 'c' or 'n' flag to open new db") - elif result == "": - # db type cannot be determined - raise error("db type could not be determined") - else: - mod = __import__(result) - return mod.open(file, flag, mode) diff --git a/Lib/dbhash.py b/Lib/dbhash.py deleted file mode 100644 index 3c60812..0000000 --- a/Lib/dbhash.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Provide a (g)dbm-compatible interface to bsddb.hashopen.""" - -import sys -try: - import bsddb -except ImportError: - # prevent a second import of this module from spuriously succeeding - del sys.modules[__name__] - raise - -__all__ = ["error","open"] - -error = bsddb.error # Exported for anydbm - -def open(file, flag = 'r', mode=0o666): - return bsddb.hashopen(file, flag, mode) diff --git a/Lib/dbm/__init__.py b/Lib/dbm/__init__.py new file mode 100644 index 0000000..9fdd414 --- /dev/null +++ b/Lib/dbm/__init__.py @@ -0,0 +1,198 @@ +"""Generic interface to all dbm clones. + +Use + + import dbm + d = dbm.open(file, 'w', 0o666) + +The returned object is a dbm.bsd, dbm.gnu, dbm.ndbm or dbm.dumb +object, dependent on the type of database being opened (determined by +the whichdb function) in the case of an existing dbm. If the dbm does +not exist and the create or new flag ('c' or 'n') was specified, the +dbm type will be determined by the availability of the modules (tested +in the above order). + +It has the following interface (key and data are strings): + + d[key] = data # store data at key (may override data at + # existing key) + data = d[key] # retrieve data at key (raise KeyError if no + # such key) + del d[key] # delete data stored at key (raises KeyError + # if no such key) + flag = key in d # true if the key exists + list = d.keys() # return a list of all existing keys (slow!) + +Future versions may change the order in which implementations are +tested for existence, add interfaces to other dbm-like +implementations. + +The open function has an optional second argument. This can be 'r', +for read-only access, 'w', for read-write access of an existing +database, 'c' for read-write access to a new or existing database, and +'n' for read-write access to a new database. The default is 'r'. + +Note: 'r' and 'w' fail if the database doesn't exist; 'c' creates it +only if it doesn't exist; and 'n' always creates a new database. +""" + +__all__ = ['open', 'whichdb', 'error', 'errors'] + +import io +import os +import struct +import sys + + +class error(Exception): + pass + +_names = ['dbm.bsd', 'dbm.gnu', 'dbm.ndbm', 'dbm.dumb'] +_errors = [error] +_defaultmod = None +_modules = {} + +for _name in _names: + try: + _mod = __import__(_name, fromlist=['open']) + except ImportError: + continue + if not _defaultmod: + _defaultmod = _mod + _modules[_name] = _mod + _errors.append(_mod.error) + +if not _defaultmod: + raise ImportError("no dbm clone found; tried %s" % _names) + +error = tuple(_errors) + + +def open(file, flag = 'r', mode = 0o666): + # guess the type of an existing database + result = whichdb(file) + if result is None: + # db doesn't exist + if 'c' in flag or 'n' in flag: + # file doesn't exist and the new flag was used so use default type + mod = _defaultmod + else: + raise error("need 'c' or 'n' flag to open new db") + elif result == "": + # db type cannot be determined + raise error("db type could not be determined") + else: + mod = _modules[result] + return mod.open(file, flag, mode) + + +try: + from dbm import ndbm + _dbmerror = ndbm.error +except ImportError: + ndbm = None + # just some sort of valid exception which might be raised in the ndbm test + _dbmerror = IOError + +def whichdb(filename): + """Guess which db package to use to open a db file. + + Return values: + + - None if the database file can't be read; + - empty string if the file can be read but can't be recognized + - the name of the dbm submodule (e.g. "ndbm" or "gnu") if recognized. + + Importing the given module may still fail, and opening the + database using that module may still fail. + """ + + # Check for ndbm first -- this has a .pag and a .dir file + try: + f = io.open(filename + ".pag", "rb") + f.close() + # dbm linked with gdbm on OS/2 doesn't have .dir file + if not (ndbm.library == "GNU gdbm" and sys.platform == "os2emx"): + f = io.open(filename + ".dir", "rb") + f.close() + return "dbm.ndbm" + except IOError: + # some dbm emulations based on Berkeley DB generate a .db file + # some do not, but they should be caught by the bsd checks + try: + f = io.open(filename + ".db", "rb") + f.close() + # guarantee we can actually open the file using dbm + # kind of overkill, but since we are dealing with emulations + # it seems like a prudent step + if ndbm is not None: + d = ndbm.open(filename) + d.close() + return "dbm.ndbm" + except (IOError, _dbmerror): + pass + + # Check for dumbdbm next -- this has a .dir and a .dat file + try: + # First check for presence of files + os.stat(filename + ".dat") + size = os.stat(filename + ".dir").st_size + # dumbdbm files with no keys are empty + if size == 0: + return "dbm.dumb" + f = io.open(filename + ".dir", "rb") + try: + if f.read(1) in (b"'", b'"'): + return "dbm.dumb" + finally: + f.close() + except (OSError, IOError): + pass + + # See if the file exists, return None if not + try: + f = io.open(filename, "rb") + except IOError: + return None + + # Read the start of the file -- the magic number + s16 = f.read(16) + f.close() + s = s16[0:4] + + # Return "" if not at least 4 bytes + if len(s) != 4: + return "" + + # Convert to 4-byte int in native byte order -- return "" if impossible + try: + (magic,) = struct.unpack("=l", s) + except struct.error: + return "" + + # Check for GNU dbm + if magic == 0x13579ace: + return "dbm.gnu" + + ## Check for old Berkeley db hash file format v2 + #if magic in (0x00061561, 0x61150600): + # return "bsddb185" # not supported anymore + + # Later versions of Berkeley db hash file have a 12-byte pad in + # front of the file type + try: + (magic,) = struct.unpack("=l", s16[-4:]) + except struct.error: + return "" + + # Check for BSD hash + if magic in (0x00061561, 0x61150600): + return "dbm.bsd" + + # Unknown + return "" + + +if __name__ == "__main__": + for filename in sys.argv[1:]: + print(whichdb(filename) or "UNKNOWN", filename) diff --git a/Lib/dbm/bsd.py b/Lib/dbm/bsd.py new file mode 100644 index 0000000..8353f50 --- /dev/null +++ b/Lib/dbm/bsd.py @@ -0,0 +1,10 @@ +"""Provide a (g)dbm-compatible interface to bsddb.hashopen.""" + +import bsddb + +__all__ = ["error", "open"] + +error = bsddb.error + +def open(file, flag = 'r', mode=0o666): + return bsddb.hashopen(file, flag, mode) diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py new file mode 100644 index 0000000..76f4a63 --- /dev/null +++ b/Lib/dbm/dumb.py @@ -0,0 +1,257 @@ +"""A dumb and slow but simple dbm clone. + +For database spam, spam.dir contains the index (a text file), +spam.bak *may* contain a backup of the index (also a text file), +while spam.dat contains the data (a binary file). + +XXX TO DO: + +- seems to contain a bug when updating... + +- reclaim free space (currently, space once occupied by deleted or expanded +items is never reused) + +- support concurrent access (currently, if two processes take turns making +updates, they can mess up the index) + +- support efficient access to large databases (currently, the whole index +is read when the database is opened, and some updates rewrite the whole index) + +- support opening for read-only (flag = 'm') + +""" + +import io as _io +import os as _os +import collections + +__all__ = ["error", "open"] + +_BLOCKSIZE = 512 + +error = IOError + +class _Database(collections.MutableMapping): + + # The on-disk directory and data files can remain in mutually + # inconsistent states for an arbitrarily long time (see comments + # at the end of __setitem__). This is only repaired when _commit() + # gets called. One place _commit() gets called is from __del__(), + # and if that occurs at program shutdown time, module globals may + # already have gotten rebound to None. Since it's crucial that + # _commit() finish successfully, we can't ignore shutdown races + # here, and _commit() must not reference any globals. + _os = _os # for _commit() + _io = _io # for _commit() + + def __init__(self, filebasename, mode): + self._mode = mode + + # The directory file is a text file. Each line looks like + # "%r, (%d, %d)\n" % (key, pos, siz) + # where key is the string key, pos is the offset into the dat + # file of the associated value's first byte, and siz is the number + # of bytes in the associated value. + self._dirfile = filebasename + '.dir' + + # The data file is a binary file pointed into by the directory + # file, and holds the values associated with keys. Each value + # begins at a _BLOCKSIZE-aligned byte offset, and is a raw + # binary 8-bit string value. + self._datfile = filebasename + '.dat' + self._bakfile = filebasename + '.bak' + + # The index is an in-memory dict, mirroring the directory file. + self._index = None # maps keys to (pos, siz) pairs + + # Mod by Jack: create data file if needed + try: + f = _io.open(self._datfile, 'r') + except IOError: + f = _io.open(self._datfile, 'w') + self._chmod(self._datfile) + f.close() + self._update() + + # Read directory file into the in-memory index dict. + def _update(self): + self._index = {} + try: + f = _io.open(self._dirfile, 'r') + except IOError: + pass + else: + for line in f: + line = line.rstrip() + key, pos_and_siz_pair = eval(line) + self._index[key] = pos_and_siz_pair + f.close() + + # Write the index dict to the directory file. The original directory + # file (if any) is renamed with a .bak extension first. If a .bak + # file currently exists, it's deleted. + def _commit(self): + # CAUTION: It's vital that _commit() succeed, and _commit() can + # be called from __del__(). Therefore we must never reference a + # global in this routine. + if self._index is None: + return # nothing to do + + try: + self._os.unlink(self._bakfile) + except self._os.error: + pass + + try: + self._os.rename(self._dirfile, self._bakfile) + except self._os.error: + pass + + f = self._io.open(self._dirfile, 'w') + self._chmod(self._dirfile) + for key, pos_and_siz_pair in self._index.items(): + f.write("%r, %r\n" % (key, pos_and_siz_pair)) + f.close() + + sync = _commit + + def __getitem__(self, key): + key = key.decode("latin-1") + pos, siz = self._index[key] # may raise KeyError + f = _io.open(self._datfile, 'rb') + f.seek(pos) + dat = f.read(siz) + f.close() + return dat + + # Append val to the data file, starting at a _BLOCKSIZE-aligned + # offset. The data file is first padded with NUL bytes (if needed) + # to get to an aligned offset. Return pair + # (starting offset of val, len(val)) + def _addval(self, val): + f = _io.open(self._datfile, 'rb+') + f.seek(0, 2) + pos = int(f.tell()) + npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE + f.write(b'\0'*(npos-pos)) + pos = npos + f.write(val) + f.close() + return (pos, len(val)) + + # Write val to the data file, starting at offset pos. The caller + # is responsible for ensuring that there's enough room starting at + # pos to hold val, without overwriting some other value. Return + # pair (pos, len(val)). + def _setval(self, pos, val): + f = _io.open(self._datfile, 'rb+') + f.seek(pos) + f.write(val) + f.close() + return (pos, len(val)) + + # key is a new key whose associated value starts in the data file + # at offset pos and with length siz. Add an index record to + # the in-memory index dict, and append one to the directory file. + def _addkey(self, key, pos_and_siz_pair): + self._index[key] = pos_and_siz_pair + f = _io.open(self._dirfile, 'a') + self._chmod(self._dirfile) + f.write("%r, %r\n" % (key, pos_and_siz_pair)) + f.close() + + def __setitem__(self, key, val): + if not isinstance(key, bytes): + raise TypeError("keys must be bytes") + key = key.decode("latin-1") # hashable bytes + if not isinstance(val, (bytes, bytearray)): + raise TypeError("values must be byte strings") + if key not in self._index: + self._addkey(key, self._addval(val)) + else: + # See whether the new value is small enough to fit in the + # (padded) space currently occupied by the old value. + pos, siz = self._index[key] + oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE + newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE + if newblocks <= oldblocks: + self._index[key] = self._setval(pos, val) + else: + # The new value doesn't fit in the (padded) space used + # by the old value. The blocks used by the old value are + # forever lost. + self._index[key] = self._addval(val) + + # Note that _index may be out of synch with the directory + # file now: _setval() and _addval() don't update the directory + # file. This also means that the on-disk directory and data + # files are in a mutually inconsistent state, and they'll + # remain that way until _commit() is called. Note that this + # is a disaster (for the database) if the program crashes + # (so that _commit() never gets called). + + def __delitem__(self, key): + key = key.decode("latin-1") + # The blocks used by the associated value are lost. + del self._index[key] + # XXX It's unclear why we do a _commit() here (the code always + # XXX has, so I'm not changing it). _setitem__ doesn't try to + # XXX keep the directory file in synch. Why should we? Or + # XXX why shouldn't __setitem__? + self._commit() + + def keys(self): + return [key.encode("latin-1") for key in self._index.keys()] + + def items(self): + return [(key.encode("latin-1"), self[key.encode("latin-1")]) + for key in self._index.keys()] + + def __contains__(self, key): + key = key.decode("latin-1") + return key in self._index + + def iterkeys(self): + return iter(self._index.keys()) + __iter__ = iterkeys + + def __len__(self): + return len(self._index) + + def close(self): + self._commit() + self._index = self._datfile = self._dirfile = self._bakfile = None + + __del__ = close + + def _chmod (self, file): + if hasattr(self._os, 'chmod'): + self._os.chmod(file, self._mode) + + +def open(file, flag=None, mode=0o666): + """Open the database file, filename, and return corresponding object. + + The flag argument, used to control how the database is opened in the + other DBM implementations, is ignored in the dbm.dumb module; the + database is always opened for update, and will be created if it does + not exist. + + The optional mode argument is the UNIX mode of the file, used only when + the database has to be created. It defaults to octal code 0o666 (and + will be modified by the prevailing umask). + + """ + # flag argument is currently ignored + + # Modify mode depending on the umask + try: + um = _os.umask(0) + _os.umask(um) + except AttributeError: + pass + else: + # Turn off any bits that are set in the umask + mode = mode & (~um) + + return _Database(file, mode) diff --git a/Lib/dbm/gnu.py b/Lib/dbm/gnu.py new file mode 100644 index 0000000..b07a1de --- /dev/null +++ b/Lib/dbm/gnu.py @@ -0,0 +1,3 @@ +"""Provide the _gdbm module as a dbm submodule.""" + +from _gdbm import * diff --git a/Lib/dbm/ndbm.py b/Lib/dbm/ndbm.py new file mode 100644 index 0000000..23056a2 --- /dev/null +++ b/Lib/dbm/ndbm.py @@ -0,0 +1,3 @@ +"""Provide the _dbm module as a dbm submodule.""" + +from _dbm import * diff --git a/Lib/dumbdbm.py b/Lib/dumbdbm.py deleted file mode 100644 index 8d58f87..0000000 --- a/Lib/dumbdbm.py +++ /dev/null @@ -1,255 +0,0 @@ -"""A dumb and slow but simple dbm clone. - -For database spam, spam.dir contains the index (a text file), -spam.bak *may* contain a backup of the index (also a text file), -while spam.dat contains the data (a binary file). - -XXX TO DO: - -- seems to contain a bug when updating... - -- reclaim free space (currently, space once occupied by deleted or expanded -items is never reused) - -- support concurrent access (currently, if two processes take turns making -updates, they can mess up the index) - -- support efficient access to large databases (currently, the whole index -is read when the database is opened, and some updates rewrite the whole index) - -- support opening for read-only (flag = 'm') - -""" - -import io as _io -import os as _os -import collections - -_BLOCKSIZE = 512 - -error = IOError # For anydbm - -class _Database(collections.MutableMapping): - - # The on-disk directory and data files can remain in mutually - # inconsistent states for an arbitrarily long time (see comments - # at the end of __setitem__). This is only repaired when _commit() - # gets called. One place _commit() gets called is from __del__(), - # and if that occurs at program shutdown time, module globals may - # already have gotten rebound to None. Since it's crucial that - # _commit() finish successfully, we can't ignore shutdown races - # here, and _commit() must not reference any globals. - _os = _os # for _commit() - _io = _io # for _commit() - - def __init__(self, filebasename, mode): - self._mode = mode - - # The directory file is a text file. Each line looks like - # "%r, (%d, %d)\n" % (key, pos, siz) - # where key is the string key, pos is the offset into the dat - # file of the associated value's first byte, and siz is the number - # of bytes in the associated value. - self._dirfile = filebasename + '.dir' - - # The data file is a binary file pointed into by the directory - # file, and holds the values associated with keys. Each value - # begins at a _BLOCKSIZE-aligned byte offset, and is a raw - # binary 8-bit string value. - self._datfile = filebasename + '.dat' - self._bakfile = filebasename + '.bak' - - # The index is an in-memory dict, mirroring the directory file. - self._index = None # maps keys to (pos, siz) pairs - - # Mod by Jack: create data file if needed - try: - f = _io.open(self._datfile, 'r') - except IOError: - f = _io.open(self._datfile, 'w') - self._chmod(self._datfile) - f.close() - self._update() - - # Read directory file into the in-memory index dict. - def _update(self): - self._index = {} - try: - f = _io.open(self._dirfile, 'r') - except IOError: - pass - else: - for line in f: - line = line.rstrip() - key, pos_and_siz_pair = eval(line) - self._index[key] = pos_and_siz_pair - f.close() - - # Write the index dict to the directory file. The original directory - # file (if any) is renamed with a .bak extension first. If a .bak - # file currently exists, it's deleted. - def _commit(self): - # CAUTION: It's vital that _commit() succeed, and _commit() can - # be called from __del__(). Therefore we must never reference a - # global in this routine. - if self._index is None: - return # nothing to do - - try: - self._os.unlink(self._bakfile) - except self._os.error: - pass - - try: - self._os.rename(self._dirfile, self._bakfile) - except self._os.error: - pass - - f = self._io.open(self._dirfile, 'w') - self._chmod(self._dirfile) - for key, pos_and_siz_pair in self._index.items(): - f.write("%r, %r\n" % (key, pos_and_siz_pair)) - f.close() - - sync = _commit - - def __getitem__(self, key): - key = key.decode("latin-1") - pos, siz = self._index[key] # may raise KeyError - f = _io.open(self._datfile, 'rb') - f.seek(pos) - dat = f.read(siz) - f.close() - return dat - - # Append val to the data file, starting at a _BLOCKSIZE-aligned - # offset. The data file is first padded with NUL bytes (if needed) - # to get to an aligned offset. Return pair - # (starting offset of val, len(val)) - def _addval(self, val): - f = _io.open(self._datfile, 'rb+') - f.seek(0, 2) - pos = int(f.tell()) - npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE - f.write(b'\0'*(npos-pos)) - pos = npos - f.write(val) - f.close() - return (pos, len(val)) - - # Write val to the data file, starting at offset pos. The caller - # is responsible for ensuring that there's enough room starting at - # pos to hold val, without overwriting some other value. Return - # pair (pos, len(val)). - def _setval(self, pos, val): - f = _io.open(self._datfile, 'rb+') - f.seek(pos) - f.write(val) - f.close() - return (pos, len(val)) - - # key is a new key whose associated value starts in the data file - # at offset pos and with length siz. Add an index record to - # the in-memory index dict, and append one to the directory file. - def _addkey(self, key, pos_and_siz_pair): - self._index[key] = pos_and_siz_pair - f = _io.open(self._dirfile, 'a') - self._chmod(self._dirfile) - f.write("%r, %r\n" % (key, pos_and_siz_pair)) - f.close() - - def __setitem__(self, key, val): - if not isinstance(key, bytes): - raise TypeError("keys must be bytes") - key = key.decode("latin-1") # hashable bytes - if not isinstance(val, (bytes, bytearray)): - raise TypeError("values must be byte strings") - if key not in self._index: - self._addkey(key, self._addval(val)) - else: - # See whether the new value is small enough to fit in the - # (padded) space currently occupied by the old value. - pos, siz = self._index[key] - oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE - newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE - if newblocks <= oldblocks: - self._index[key] = self._setval(pos, val) - else: - # The new value doesn't fit in the (padded) space used - # by the old value. The blocks used by the old value are - # forever lost. - self._index[key] = self._addval(val) - - # Note that _index may be out of synch with the directory - # file now: _setval() and _addval() don't update the directory - # file. This also means that the on-disk directory and data - # files are in a mutually inconsistent state, and they'll - # remain that way until _commit() is called. Note that this - # is a disaster (for the database) if the program crashes - # (so that _commit() never gets called). - - def __delitem__(self, key): - key = key.decode("latin-1") - # The blocks used by the associated value are lost. - del self._index[key] - # XXX It's unclear why we do a _commit() here (the code always - # XXX has, so I'm not changing it). _setitem__ doesn't try to - # XXX keep the directory file in synch. Why should we? Or - # XXX why shouldn't __setitem__? - self._commit() - - def keys(self): - return [key.encode("latin-1") for key in self._index.keys()] - - def items(self): - return [(key.encode("latin-1"), self[key.encode("latin-1")]) - for key in self._index.keys()] - - def __contains__(self, key): - key = key.decode("latin-1") - return key in self._index - - def iterkeys(self): - return iter(self._index.keys()) - __iter__ = iterkeys - - def __len__(self): - return len(self._index) - - def close(self): - self._commit() - self._index = self._datfile = self._dirfile = self._bakfile = None - - __del__ = close - - def _chmod (self, file): - if hasattr(self._os, 'chmod'): - self._os.chmod(file, self._mode) - - -def open(file, flag=None, mode=0o666): - """Open the database file, filename, and return corresponding object. - - The flag argument, used to control how the database is opened in the - other DBM implementations, is ignored in the dumbdbm module; the - database is always opened for update, and will be created if it does - not exist. - - The optional mode argument is the UNIX mode of the file, used only when - the database has to be created. It defaults to octal code 0o666 (and - will be modified by the prevailing umask). - - """ - # flag argument is currently ignored - - # Modify mode depending on the umask - try: - um = _os.umask(0) - _os.umask(um) - except AttributeError: - pass - else: - # Turn off any bits that are set in the umask - mode = mode & (~um) - - return _Database(file, mode) diff --git a/Lib/io.py b/Lib/io.py index 207e428..0c993b1 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -270,7 +270,7 @@ class OpenWrapper: """Wrapper for builtins.open Trick so that open won't become a bound method when stored - as a class variable (as dumbdbm does). + as a class variable (as dbm.dumb does). See initstdio() in Python/pythonrun.c. """ diff --git a/Lib/shelve.py b/Lib/shelve.py index e6d6d40..d651b9e 100644 --- a/Lib/shelve.py +++ b/Lib/shelve.py @@ -190,15 +190,15 @@ class BsdDbShelf(Shelf): class DbfilenameShelf(Shelf): - """Shelf implementation using the "anydbm" generic dbm interface. + """Shelf implementation using the "dbm" generic dbm interface. This is initialized with the filename for the dbm database. See the module's __doc__ string for an overview of the interface. """ def __init__(self, filename, flag='c', protocol=None, writeback=False): - import anydbm - Shelf.__init__(self, anydbm.open(filename, flag), protocol, writeback) + import dbm + Shelf.__init__(self, dbm.open(filename, flag), protocol, writeback) def open(filename, flag='c', protocol=None, writeback=False): @@ -208,7 +208,7 @@ def open(filename, flag='c', protocol=None, writeback=False): database. As a side-effect, an extension may be added to the filename and more than one file may be created. The optional flag parameter has the same interpretation as the flag parameter of - anydbm.open(). The optional protocol parameter specifies the + dbm.open(). The optional protocol parameter specifies the version of the pickle protocol (0, 1, or 2). See the module's __doc__ string for an overview of the interface. diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index 615f6c5..ed8d8d9 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -57,7 +57,7 @@ class AllTest(unittest.TestCase): self.check_all("copy") self.check_all("copyreg") self.check_all("csv") - self.check_all("dbhash") + self.check_all("dbm.bsd") self.check_all("decimal") self.check_all("difflib") self.check_all("dircache") diff --git a/Lib/test/test_anydbm.py b/Lib/test/test_anydbm.py index ace9dd2..aab1388 100644 --- a/Lib/test/test_anydbm.py +++ b/Lib/test/test_anydbm.py @@ -1,50 +1,34 @@ #! /usr/bin/env python -"""Test script for the anydbm module - based on testdumbdbm.py -""" +"""Test script for the dbm.open function based on testdumbdbm.py""" import os import unittest -import anydbm +import dbm import glob -from test import support - -_fname = support.TESTFN - -_all_modules = [] - -for _name in anydbm._names: - try: - _module = __import__(_name) - except ImportError: - continue - _all_modules.append(_module) +import test.support +_fname = test.support.TESTFN # -# Iterates over every database module supported by anydbm -# currently available, setting anydbm to use each in turn, -# and yielding that module +# Iterates over every database module supported by dbm currently available, +# setting dbm to use each in turn, and yielding that module # def dbm_iterator(): - old_default = anydbm._defaultmod - for module in _all_modules: - anydbm._defaultmod = module + old_default = dbm._defaultmod + for module in dbm._modules.values(): + dbm._defaultmod = module yield module - anydbm._defaultmod = old_default + dbm._defaultmod = old_default # -# Clean up all scratch databases we might have created -# during testing +# Clean up all scratch databases we might have created during testing # def delete_files(): # we don't know the precise name the underlying database uses # so we use glob to locate all names for f in glob.glob(_fname + "*"): - try: - os.unlink(f) - except OSError: - pass + test.support.unlink(f) + class AnyDBMTestCase(unittest.TestCase): _dict = {'0': b'', @@ -60,7 +44,7 @@ class AnyDBMTestCase(unittest.TestCase): unittest.TestCase.__init__(self, *args) def test_anydbm_creation(self): - f = anydbm.open(_fname, 'c') + f = dbm.open(_fname, 'c') self.assertEqual(list(f.keys()), []) for key in self._dict: f[key.encode("ascii")] = self._dict[key] @@ -69,26 +53,26 @@ class AnyDBMTestCase(unittest.TestCase): def test_anydbm_modification(self): self.init_db() - f = anydbm.open(_fname, 'c') + f = dbm.open(_fname, 'c') self._dict['g'] = f[b'g'] = b"indented" self.read_helper(f) f.close() def test_anydbm_read(self): self.init_db() - f = anydbm.open(_fname, 'r') + f = dbm.open(_fname, 'r') self.read_helper(f) f.close() def test_anydbm_keys(self): self.init_db() - f = anydbm.open(_fname, 'r') + f = dbm.open(_fname, 'r') keys = self.keys_helper(f) f.close() def test_anydbm_access(self): self.init_db() - f = anydbm.open(_fname, 'r') + f = dbm.open(_fname, 'r') key = "a".encode("ascii") assert(key in f) assert(f[key] == b"Python:") @@ -100,7 +84,7 @@ class AnyDBMTestCase(unittest.TestCase): self.assertEqual(self._dict[key], f[key.encode("ascii")]) def init_db(self): - f = anydbm.open(_fname, 'n') + f = dbm.open(_fname, 'n') for k in self._dict: f[k.encode("ascii")] = self._dict[k] f.close() @@ -118,10 +102,44 @@ class AnyDBMTestCase(unittest.TestCase): delete_files() +class WhichDBTestCase(unittest.TestCase): + # Actual test methods are added to namespace after class definition. + def __init__(self, *args): + unittest.TestCase.__init__(self, *args) + + def test_whichdb(self): + for module in dbm_iterator(): + # Check whether whichdb correctly guesses module name + # for databases opened with "module" module. + # Try with empty files first + name = module.__name__ + if name == 'dbm.dumb': + continue # whichdb can't support dbm.dumb + test.support.unlink(_fname) + f = module.open(_fname, 'c') + f.close() + self.assertEqual(name, dbm.whichdb(_fname)) + # Now add a key + f = module.open(_fname, 'w') + f[b"1"] = b"1" + # and test that we can find it + self.assertTrue(b"1" in f) + # and read it + self.assertTrue(f[b"1"] == b"1") + f.close() + self.assertEqual(name, dbm.whichdb(_fname)) + + def tearDown(self): + delete_files() + + def setUp(self): + delete_files() + + def test_main(): try: for module in dbm_iterator(): - support.run_unittest(AnyDBMTestCase) + test.support.run_unittest(AnyDBMTestCase, WhichDBTestCase) finally: delete_files() diff --git a/Lib/test/test_bsddb.py b/Lib/test/test_bsddb.py index 3eb291f..a722d8c 100755 --- a/Lib/test/test_bsddb.py +++ b/Lib/test/test_bsddb.py @@ -5,7 +5,7 @@ import os, sys import copy import bsddb -import dbhash # Just so we know it's imported +import dbm.bsd # Just so we know it's imported import unittest from test import support diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py deleted file mode 100755 index 2c6ce99..0000000 --- a/Lib/test/test_dbm.py +++ /dev/null @@ -1,40 +0,0 @@ -from test import support -import unittest -import os -import random -import dbm -from dbm import error - -class DbmTestCase(unittest.TestCase): - - def setUp(self): - self.filename = support.TESTFN - self.d = dbm.open(self.filename, 'c') - self.d.close() - - def tearDown(self): - for suffix in ['', '.pag', '.dir', '.db']: - support.unlink(self.filename + suffix) - - def test_keys(self): - self.d = dbm.open(self.filename, 'c') - self.assert_(self.d.keys() == []) - self.d['a'] = 'b' - self.d['12345678910'] = '019237410982340912840198242' - self.d.keys() - self.assert_(b'a' in self.d) - self.d.close() - - def test_modes(self): - for mode in ['r', 'rw', 'w', 'n']: - try: - self.d = dbm.open(self.filename, mode) - self.d.close() - except dbm.error: - self.fail() - -def test_main(): - support.run_unittest(DbmTestCase) - -if __name__ == '__main__': - test_main() diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py new file mode 100644 index 0000000..9bdc240 --- /dev/null +++ b/Lib/test/test_dbm_dumb.py @@ -0,0 +1,185 @@ +#! /usr/bin/env python +"""Test script for the dumbdbm module + Original by Roger E. Masse +""" + +import io +import os +import unittest +import dbm.dumb as dumbdbm +from test import support + +_fname = support.TESTFN + +def _delete_files(): + for ext in [".dir", ".dat", ".bak"]: + try: + os.unlink(_fname + ext) + except OSError: + pass + +class DumbDBMTestCase(unittest.TestCase): + _dict = {'0': b'', + 'a': b'Python:', + 'b': b'Programming', + 'c': b'the', + 'd': b'way', + 'f': b'Guido', + 'g': b'intended', + } + + def __init__(self, *args): + unittest.TestCase.__init__(self, *args) + + def test_dumbdbm_creation(self): + f = dumbdbm.open(_fname, 'c') + self.assertEqual(list(f.keys()), []) + for key in self._dict: + f[key.encode("ascii")] = self._dict[key] + self.read_helper(f) + f.close() + + def test_dumbdbm_creation_mode(self): + # On platforms without chmod, don't do anything. + if not (hasattr(os, 'chmod') and hasattr(os, 'umask')): + return + + try: + old_umask = os.umask(0o002) + f = dumbdbm.open(_fname, 'c', 0o637) + f.close() + finally: + os.umask(old_umask) + + expected_mode = 0o635 + if os.name != 'posix': + # Windows only supports setting the read-only attribute. + # This shouldn't fail, but doesn't work like Unix either. + expected_mode = 0o666 + + import stat + st = os.stat(_fname + '.dat') + self.assertEqual(stat.S_IMODE(st.st_mode), expected_mode) + st = os.stat(_fname + '.dir') + self.assertEqual(stat.S_IMODE(st.st_mode), expected_mode) + + def test_close_twice(self): + f = dumbdbm.open(_fname) + f[b'a'] = b'b' + self.assertEqual(f[b'a'], b'b') + f.close() + f.close() + + def test_dumbdbm_modification(self): + self.init_db() + f = dumbdbm.open(_fname, 'w') + self._dict['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.read_helper(f) + f.close() + + def test_dumbdbm_keys(self): + self.init_db() + f = dumbdbm.open(_fname) + keys = self.keys_helper(f) + f.close() + + def test_write_contains(self): + f = dumbdbm.open(_fname) + f[b'1'] = b'hello' + self.assertTrue(b'1' in f) + f.close() + + def test_write_write_read(self): + # test for bug #482460 + f = dumbdbm.open(_fname) + f[b'1'] = b'hello' + f[b'1'] = b'hello2' + f.close() + f = dumbdbm.open(_fname) + self.assertEqual(f[b'1'], b'hello2') + f.close() + + 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) + f[b'1'] = b'hello' + f[b'2'] = b'hello2' + f.close() + + # Mangle the file by changing the line separator to Windows or Unix + data = io.open(_fname + '.dir', 'rb').read() + if os.linesep == '\n': + data = data.replace(b'\n', b'\r\n') + else: + data = data.replace(b'\r\n', b'\n') + io.open(_fname + '.dir', 'wb').write(data) + + f = dumbdbm.open(_fname) + self.assertEqual(f[b'1'], b'hello') + self.assertEqual(f[b'2'], b'hello2') + + + def read_helper(self, f): + keys = self.keys_helper(f) + for key in self._dict: + self.assertEqual(self._dict[key], f[key.encode("ascii")]) + + def init_db(self): + f = dumbdbm.open(_fname, 'w') + for k in self._dict: + f[k.encode("ascii")] = self._dict[k] + f.close() + + def keys_helper(self, f): + keys = sorted(k.decode("ascii") for k in f.keys()) + dkeys = sorted(self._dict.keys()) + self.assertEqual(keys, dkeys) + return keys + + # 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) + for dummy in range(100): + k = random.choice('abcdefghijklm') + if random.random() < 0.2: + if k in d: + del d[k] + del f[k.encode("ascii")] + else: + v = random.choice((b'a', b'b', b'c')) * random.randrange(10000) + d[k] = v + f[k.encode("ascii")] = v + self.assertEqual(f[k.encode("ascii")], v) + f.close() + + 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) + f.close() + + def tearDown(self): + _delete_files() + + def setUp(self): + _delete_files() + +def test_main(): + try: + support.run_unittest(DumbDBMTestCase) + finally: + _delete_files() + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py new file mode 100755 index 0000000..eddb970 --- /dev/null +++ b/Lib/test/test_dbm_gnu.py @@ -0,0 +1,81 @@ +import dbm.gnu as gdbm +import unittest +import os +from test.support import verbose, TESTFN, run_unittest, unlink + + +filename = TESTFN + +class TestGdbm(unittest.TestCase): + def setUp(self): + self.g = None + + def tearDown(self): + if self.g is not None: + self.g.close() + unlink(filename) + + def test_key_methods(self): + self.g = gdbm.open(filename, 'c') + self.assertEqual(self.g.keys(), []) + self.g['a'] = 'b' + self.g['12345678910'] = '019237410982340912840198242' + key_set = set(self.g.keys()) + self.assertEqual(key_set, set([b'a', b'12345678910'])) + self.assert_(b'a' in self.g) + key = self.g.firstkey() + while key: + self.assert_(key in key_set) + key_set.remove(key) + key = self.g.nextkey(key) + self.assertRaises(KeyError, lambda: self.g['xxx']) + + 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()) + + def test_flags(self): + # Test the flag parameter open() by trying all supported flag modes. + all = set(gdbm.open_flags) + # Test standard flags (presumably "crwn"). + modes = all - set('fsu') + for mode in modes: + self.g = gdbm.open(filename, mode) + self.g.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() + + def test_reorganize(self): + self.g = gdbm.open(filename, 'c') + size0 = os.path.getsize(filename) + + self.g['x'] = 'x' * 10000 + size1 = os.path.getsize(filename) + self.assert_(size0 < size1) + + del self.g['x'] + # 'size' is supposed to be the same even after deleting an entry. + self.assertEqual(os.path.getsize(filename), size1) + + self.g.reorganize() + size2 = os.path.getsize(filename) + self.assert_(size1 > size2 >= size0) + + +def test_main(): + run_unittest(TestGdbm) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py new file mode 100755 index 0000000..74d3238 --- /dev/null +++ b/Lib/test/test_dbm_ndbm.py @@ -0,0 +1,40 @@ +from test import support +import unittest +import os +import random +import dbm.ndbm +from dbm.ndbm import error + +class DbmTestCase(unittest.TestCase): + + def setUp(self): + self.filename = support.TESTFN + self.d = dbm.ndbm.open(self.filename, 'c') + self.d.close() + + 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.assert_(self.d.keys() == []) + self.d['a'] = 'b' + self.d['12345678910'] = '019237410982340912840198242' + self.d.keys() + self.assert_(b'a' in self.d) + self.d.close() + + def test_modes(self): + for mode in ['r', 'rw', 'w', 'n']: + try: + self.d = dbm.ndbm.open(self.filename, mode) + self.d.close() + except error: + self.fail() + +def test_main(): + support.run_unittest(DbmTestCase) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_dumbdbm.py b/Lib/test/test_dumbdbm.py deleted file mode 100644 index 24c178f..0000000 --- a/Lib/test/test_dumbdbm.py +++ /dev/null @@ -1,185 +0,0 @@ -#! /usr/bin/env python -"""Test script for the dumbdbm module - Original by Roger E. Masse -""" - -import io -import os -import unittest -import dumbdbm -from test import support - -_fname = support.TESTFN - -def _delete_files(): - for ext in [".dir", ".dat", ".bak"]: - try: - os.unlink(_fname + ext) - except OSError: - pass - -class DumbDBMTestCase(unittest.TestCase): - _dict = {'0': b'', - 'a': b'Python:', - 'b': b'Programming', - 'c': b'the', - 'd': b'way', - 'f': b'Guido', - 'g': b'intended', - } - - def __init__(self, *args): - unittest.TestCase.__init__(self, *args) - - def test_dumbdbm_creation(self): - f = dumbdbm.open(_fname, 'c') - self.assertEqual(list(f.keys()), []) - for key in self._dict: - f[key.encode("ascii")] = self._dict[key] - self.read_helper(f) - f.close() - - def test_dumbdbm_creation_mode(self): - # On platforms without chmod, don't do anything. - if not (hasattr(os, 'chmod') and hasattr(os, 'umask')): - return - - try: - old_umask = os.umask(0o002) - f = dumbdbm.open(_fname, 'c', 0o637) - f.close() - finally: - os.umask(old_umask) - - expected_mode = 0o635 - if os.name != 'posix': - # Windows only supports setting the read-only attribute. - # This shouldn't fail, but doesn't work like Unix either. - expected_mode = 0o666 - - import stat - st = os.stat(_fname + '.dat') - self.assertEqual(stat.S_IMODE(st.st_mode), expected_mode) - st = os.stat(_fname + '.dir') - self.assertEqual(stat.S_IMODE(st.st_mode), expected_mode) - - def test_close_twice(self): - f = dumbdbm.open(_fname) - f[b'a'] = b'b' - self.assertEqual(f[b'a'], b'b') - f.close() - f.close() - - def test_dumbdbm_modification(self): - self.init_db() - f = dumbdbm.open(_fname, 'w') - self._dict['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.read_helper(f) - f.close() - - def test_dumbdbm_keys(self): - self.init_db() - f = dumbdbm.open(_fname) - keys = self.keys_helper(f) - f.close() - - def test_write_contains(self): - f = dumbdbm.open(_fname) - f[b'1'] = b'hello' - self.assertTrue(b'1' in f) - f.close() - - def test_write_write_read(self): - # test for bug #482460 - f = dumbdbm.open(_fname) - f[b'1'] = b'hello' - f[b'1'] = b'hello2' - f.close() - f = dumbdbm.open(_fname) - self.assertEqual(f[b'1'], b'hello2') - f.close() - - 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) - f[b'1'] = b'hello' - f[b'2'] = b'hello2' - f.close() - - # Mangle the file by changing the line separator to Windows or Unix - data = io.open(_fname + '.dir', 'rb').read() - if os.linesep == '\n': - data = data.replace(b'\n', b'\r\n') - else: - data = data.replace(b'\r\n', b'\n') - io.open(_fname + '.dir', 'wb').write(data) - - f = dumbdbm.open(_fname) - self.assertEqual(f[b'1'], b'hello') - self.assertEqual(f[b'2'], b'hello2') - - - def read_helper(self, f): - keys = self.keys_helper(f) - for key in self._dict: - self.assertEqual(self._dict[key], f[key.encode("ascii")]) - - def init_db(self): - f = dumbdbm.open(_fname, 'w') - for k in self._dict: - f[k.encode("ascii")] = self._dict[k] - f.close() - - def keys_helper(self, f): - keys = sorted(k.decode("ascii") for k in f.keys()) - dkeys = sorted(self._dict.keys()) - self.assertEqual(keys, dkeys) - return keys - - # 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) - for dummy in range(100): - k = random.choice('abcdefghijklm') - if random.random() < 0.2: - if k in d: - del d[k] - del f[k.encode("ascii")] - else: - v = random.choice((b'a', b'b', b'c')) * random.randrange(10000) - d[k] = v - f[k.encode("ascii")] = v - self.assertEqual(f[k.encode("ascii")], v) - f.close() - - 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) - f.close() - - def tearDown(self): - _delete_files() - - def setUp(self): - _delete_files() - -def test_main(): - try: - support.run_unittest(DumbDBMTestCase) - finally: - _delete_files() - -if __name__ == "__main__": - test_main() diff --git a/Lib/test/test_gdbm.py b/Lib/test/test_gdbm.py deleted file mode 100755 index 42cb136..0000000 --- a/Lib/test/test_gdbm.py +++ /dev/null @@ -1,81 +0,0 @@ -import gdbm -import unittest -import os -from test.support import verbose, TESTFN, run_unittest, unlink - - -filename = TESTFN - -class TestGdbm(unittest.TestCase): - def setUp(self): - self.g = None - - def tearDown(self): - if self.g is not None: - self.g.close() - unlink(filename) - - def test_key_methods(self): - self.g = gdbm.open(filename, 'c') - self.assertEqual(self.g.keys(), []) - self.g['a'] = 'b' - self.g['12345678910'] = '019237410982340912840198242' - key_set = set(self.g.keys()) - self.assertEqual(key_set, set([b'a', b'12345678910'])) - self.assert_(b'a' in self.g) - key = self.g.firstkey() - while key: - self.assert_(key in key_set) - key_set.remove(key) - key = self.g.nextkey(key) - self.assertRaises(KeyError, lambda: self.g['xxx']) - - 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()) - - def test_flags(self): - # Test the flag parameter open() by trying all supported flag modes. - all = set(gdbm.open_flags) - # Test standard flags (presumably "crwn"). - modes = all - set('fsu') - for mode in modes: - self.g = gdbm.open(filename, mode) - self.g.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() - - def test_reorganize(self): - self.g = gdbm.open(filename, 'c') - size0 = os.path.getsize(filename) - - self.g['x'] = 'x' * 10000 - size1 = os.path.getsize(filename) - self.assert_(size0 < size1) - - del self.g['x'] - # 'size' is supposed to be the same even after deleting an entry. - self.assertEqual(os.path.getsize(filename), size1) - - self.g.reorganize() - size2 = os.path.getsize(filename) - self.assert_(size1 > size2 >= size0) - - -def test_main(): - run_unittest(TestGdbm) - -if __name__ == '__main__': - test_main() diff --git a/Lib/test/test_whichdb.py b/Lib/test/test_whichdb.py deleted file mode 100644 index d908ac5..0000000 --- a/Lib/test/test_whichdb.py +++ /dev/null @@ -1,58 +0,0 @@ -#! /usr/bin/env python -"""Test script for the whichdb module - based on test_anydbm.py -""" - -import os -import test.support -import unittest -import whichdb -import anydbm -import glob -from test.test_anydbm import delete_files, dbm_iterator - -_fname = test.support.TESTFN - -class WhichDBTestCase(unittest.TestCase): - # Actual test methods are added to namespace - # after class definition. - def __init__(self, *args): - unittest.TestCase.__init__(self, *args) - - def test_whichdb(self): - for module in dbm_iterator(): - # Check whether whichdb correctly guesses module name - # for databases opened with "module" module. - # Try with empty files first - name = module.__name__ - if name == 'dumbdbm': - continue # whichdb can't support dumbdbm - test.support.unlink(_fname) - f = module.open(_fname, 'c') - f.close() - self.assertEqual(name, whichdb.whichdb(_fname)) - # Now add a key - f = module.open(_fname, 'w') - f[b"1"] = b"1" - # and test that we can find it - self.assertTrue(b"1" in f) - # and read it - self.assertTrue(f[b"1"] == b"1") - f.close() - self.assertEqual(name, whichdb.whichdb(_fname)) - - def tearDown(self): - delete_files() - - def setUp(self): - delete_files() - - -def test_main(): - try: - test.support.run_unittest(WhichDBTestCase) - finally: - delete_files() - -if __name__ == "__main__": - test_main() diff --git a/Lib/whichdb.py b/Lib/whichdb.py deleted file mode 100644 index ca9c736..0000000 --- a/Lib/whichdb.py +++ /dev/null @@ -1,118 +0,0 @@ -# !/usr/bin/env python -"""Guess which db package to use to open a db file.""" - -import io -import os -import struct -import sys - -try: - import dbm - _dbmerror = dbm.error -except ImportError: - dbm = None - # just some sort of valid exception which might be raised in the - # dbm test - _dbmerror = IOError - -def whichdb(filename): - """Guess which db package to use to open a db file. - - Return values: - - - None if the database file can't be read; - - empty string if the file can be read but can't be recognized - - the module name (e.g. "dbm" or "gdbm") if recognized. - - Importing the given module may still fail, and opening the - database using that module may still fail. - """ - - # Check for dbm first -- this has a .pag and a .dir file - try: - f = io.open(filename + ".pag", "rb") - f.close() - # dbm linked with gdbm on OS/2 doesn't have .dir file - if not (dbm.library == "GNU gdbm" and sys.platform == "os2emx"): - f = io.open(filename + ".dir", "rb") - f.close() - return "dbm" - except IOError: - # some dbm emulations based on Berkeley DB generate a .db file - # some do not, but they should be caught by the dbhash checks - try: - f = io.open(filename + ".db", "rb") - f.close() - # guarantee we can actually open the file using dbm - # kind of overkill, but since we are dealing with emulations - # it seems like a prudent step - if dbm is not None: - d = dbm.open(filename) - d.close() - return "dbm" - except (IOError, _dbmerror): - pass - - # Check for dumbdbm next -- this has a .dir and a .dat file - try: - # First check for presence of files - os.stat(filename + ".dat") - size = os.stat(filename + ".dir").st_size - # dumbdbm files with no keys are empty - if size == 0: - return "dumbdbm" - f = io.open(filename + ".dir", "rb") - try: - if f.read(1) in (b"'", b'"'): - return "dumbdbm" - finally: - f.close() - except (OSError, IOError): - pass - - # See if the file exists, return None if not - try: - f = io.open(filename, "rb") - except IOError: - return None - - # Read the start of the file -- the magic number - s16 = f.read(16) - f.close() - s = s16[0:4] - - # Return "" if not at least 4 bytes - if len(s) != 4: - return "" - - # Convert to 4-byte int in native byte order -- return "" if impossible - try: - (magic,) = struct.unpack("=l", s) - except struct.error: - return "" - - # Check for GNU dbm - if magic == 0x13579ace: - return "gdbm" - - # Check for old Berkeley db hash file format v2 - if magic in (0x00061561, 0x61150600): - return "bsddb185" - - # Later versions of Berkeley db hash file have a 12-byte pad in - # front of the file type - try: - (magic,) = struct.unpack("=l", s16[-4:]) - except struct.error: - return "" - - # Check for BSD hash - if magic in (0x00061561, 0x61150600): - return "dbhash" - - # Unknown - return "" - -if __name__ == "__main__": - for filename in sys.argv[1:]: - print(whichdb(filename) or "UNKNOWN", filename) diff --git a/Misc/PURIFY.README b/Misc/PURIFY.README index 1e5d2ac..c641510 100644 --- a/Misc/PURIFY.README +++ b/Misc/PURIFY.README @@ -57,8 +57,8 @@ following in your .purify file: suppress umr ...; "socketmodule.c" suppress umr ...; time_strftime - suppress umr ...; "dbmmodule.c" - suppress umr ...; "gdbmmodule.c" + suppress umr ...; "_dbmmodule.c" + suppress umr ...; "_gdbmmodule.c" suppress umr ...; "grpmodule.c" suppress umr ...; "nismodule.c" suppress umr ...; "pwdmodule.c" diff --git a/Misc/cheatsheet b/Misc/cheatsheet index c383e4a..c959de5 100644 --- a/Misc/cheatsheet +++ b/Misc/cheatsheet @@ -1795,8 +1795,8 @@ List of modules and packages in base distribution Standard library modules Operation Result aifc Stuff to parse AIFF-C and AIFF files. -anydbm Generic interface to all dbm clones. (dbhash, gdbm, - dbm,dumbdbm) +dbm Generic interface to all dbm clones. (dbm.bsd, dbm.gnu, + dbm.ndbm, dbm.dumb) asynchat Support for 'chat' style protocols asyncore Asynchronous File I/O (in select style) atexit Register functions to be called at exit of Python interpreter. @@ -1822,21 +1822,16 @@ ConfigParser Configuration file parser (much like windows .ini files) copy Generic shallow and deep copying operations. copy_reg Helper to provide extensibility for pickle/cPickle. csv Read and write files with comma separated values. -dbhash (g)dbm-compatible interface to bsdhash.hashopen. dircache Sorted list of files in a dir, using a cache. -[DEL:dircmp:DEL] [DEL:Defines a class to build directory diff tools on.:DEL] difflib Tool for creating delta between sequences. dis Bytecode disassembler. distutils Package installation system. doctest Tool for running and verifying tests inside doc strings. dospath Common operations on DOS pathnames. -dumbdbm A dumb and slow but simple dbm clone. -[DEL:dump:DEL] [DEL:Print python code that reconstructs a variable.:DEL] email Comprehensive support for internet email. filecmp File comparison. fileinput Helper class to quickly write a loop over all standard input files. -[DEL:find:DEL] [DEL:Find files directory hierarchy matching a pattern.:DEL] fnmatch Filename matching with shell patterns. formatter A test formatter. fpformat General floating point formatting functions. @@ -1847,7 +1842,6 @@ getopt Standard command line processing. See also ftp:// www.pauahtun.org/pub/getargspy.zip getpass Utilities to get a password and/or the current user name. glob filename globbing. -[DEL:grep:DEL] [DEL:'grep' utilities.:DEL] gzip Read & write gzipped files. heapq Priority queue implemented using lists organized as heaps. HMAC Keyed-Hashing for Message Authentication -- RFC 2104. @@ -1882,8 +1876,6 @@ ntpath Common operations on DOS pathnames. nturl2path Mac specific module for conversion between pathnames and URLs. optparse A comprehensive tool for processing command line options. os Either mac, dos or posix depending system. -[DEL:packmail: [DEL:Create a self-unpacking shell archive.:DEL] -DEL] pdb A Python debugger. pickle Pickling (save and restore) of Python objects (a faster Cimplementation exists in built-in module: cPickle). @@ -1929,7 +1921,7 @@ StringIO File-like objects that read/write a string buffer (a fasterC sunau Stuff to parse Sun and NeXT audio files. sunaudio Interpret sun audio headers. symbol Non-terminal symbols of Python grammar (from "graminit.h"). -tabnanny,/font> Check Python source for ambiguous indentation. +tabnanny Check Python source for ambiguous indentation. tarfile Facility for reading and writing to the *nix tarfile format. telnetlib TELNET client class. Based on RFC 854. tempfile Temporary file name allocation. @@ -1950,15 +1942,11 @@ user Hook to allow user-specified customization code to run. UserDict A wrapper to allow subclassing of built-in dict class. UserList A wrapper to allow subclassing of built-in list class. UserString A wrapper to allow subclassing of built-in string class. -[DEL:util:DEL] [DEL:some useful functions that don't fit elsewhere !!:DEL] uu UUencode/UUdecode. unittest Utilities for implementing unit testing. wave Stuff to parse WAVE files. weakref Tools for creating and managing weakly referenced objects. webbrowser Platform independent URL launcher. -[DEL:whatsound: [DEL:Several routines that help recognizing sound files.:DEL] -DEL] -whichdb Guess which db package to use to open a db file. xdrlib Implements (a subset of) Sun XDR (eXternal Data Representation) xmllib A parser for XML, using the derived class as static DTD. @@ -1966,7 +1954,6 @@ xml.dom Classes for processing XML using the Document Object Model. xml.sax Classes for processing XML using the SAX API. xmlrpclib Support for remote procedure calls using XML. zipfile Read & write PK zipped files. -[DEL:zmod:DEL] [DEL:Demonstration of abstruse mathematical concepts.:DEL] @@ -1993,7 +1980,7 @@ zipfile Read & write PK zipped files. * Unix/Posix * - dbm Interface to Unix ndbm database library + dbm Interface to Unix dbm databases grp Interface to Unix group database posix OS functionality standardized by C and POSIX standards posixpath POSIX pathname functions diff --git a/Modules/Setup.dist b/Modules/Setup.dist index 6c201c9..88c85b5 100644 --- a/Modules/Setup.dist +++ b/Modules/Setup.dist @@ -294,8 +294,8 @@ _symtable symtablemodule.c # Modules that provide persistent dictionary-like semantics. You will # probably want to arrange for at least one of them to be available on # your machine, though none are defined by default because of library -# dependencies. The Python module anydbm.py provides an -# implementation independent wrapper for these; dumbdbm.py provides +# dependencies. The Python module dbm/__init__.py provides an +# implementation independent wrapper for these; dbm/dumb.py provides # similar functionality (but slower of course) implemented in Python. # The standard Unix dbm module has been moved to Setup.config so that @@ -305,13 +305,13 @@ _symtable symtablemodule.c # # First, look at Setup.config; configure may have set this for you. -#dbm dbmmodule.c # dbm(3) may require -lndbm or similar +#_dbm _dbmmodule.c # dbm(3) may require -lndbm or similar # Anthony Baxter's gdbm module. GNU dbm(3) will require -lgdbm: # # First, look at Setup.config; configure may have set this for you. -#gdbm gdbmmodule.c -I/usr/local/include -L/usr/local/lib -lgdbm +#_gdbm _gdbmmodule.c -I/usr/local/include -L/usr/local/lib -lgdbm # Sleepycat Berkeley DB interface. diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c new file mode 100644 index 0000000..1a49e24 --- /dev/null +++ b/Modules/_dbmmodule.c @@ -0,0 +1,412 @@ + +/* DBM module using dictionary interface */ + + +#include "Python.h" + +#include +#include +#include + +/* Some Linux systems install gdbm/ndbm.h, but not ndbm.h. This supports + * whichever configure was able to locate. + */ +#if defined(HAVE_NDBM_H) +#include +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +static char *which_dbm = "ndbm"; +#else +static char *which_dbm = "GNU gdbm"; /* EMX port of GDBM */ +#endif +#elif defined(HAVE_GDBM_NDBM_H) +#include +static char *which_dbm = "GNU gdbm"; +#elif defined(HAVE_BERKDB_H) +#include +static char *which_dbm = "Berkeley DB"; +#else +#error "No ndbm.h available!" +#endif + +typedef struct { + PyObject_HEAD + int di_size; /* -1 means recompute */ + DBM *di_dbm; +} dbmobject; + +static PyTypeObject Dbmtype; + +#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; } + +static PyObject *DbmError; + +static PyObject * +newdbmobject(char *file, int flags, int mode) +{ + dbmobject *dp; + + dp = PyObject_New(dbmobject, &Dbmtype); + if (dp == NULL) + return NULL; + dp->di_size = -1; + if ( (dp->di_dbm = dbm_open(file, flags, mode)) == 0 ) { + PyErr_SetFromErrno(DbmError); + Py_DECREF(dp); + return NULL; + } + return (PyObject *)dp; +} + +/* Methods */ + +static void +dbm_dealloc(register dbmobject *dp) +{ + if ( dp->di_dbm ) + dbm_close(dp->di_dbm); + PyObject_Del(dp); +} + +static Py_ssize_t +dbm_length(dbmobject *dp) +{ + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, "DBM object has already been closed"); + return -1; + } + if ( dp->di_size < 0 ) { + datum key; + int size; + + size = 0; + for ( key=dbm_firstkey(dp->di_dbm); key.dptr; + key = dbm_nextkey(dp->di_dbm)) + size++; + dp->di_size = size; + } + return dp->di_size; +} + +static PyObject * +dbm_subscript(dbmobject *dp, register PyObject *key) +{ + datum drec, krec; + Py_ssize_t tmp_size; + + if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size) ) + return NULL; + + krec.dsize = tmp_size; + check_dbmobject_open(dp); + drec = dbm_fetch(dp->di_dbm, krec); + if ( drec.dptr == 0 ) { + PyErr_SetObject(PyExc_KeyError, key); + return NULL; + } + if ( dbm_error(dp->di_dbm) ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, ""); + return NULL; + } + return PyBytes_FromStringAndSize(drec.dptr, drec.dsize); +} + +static int +dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) +{ + datum krec, drec; + Py_ssize_t tmp_size; + + if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { + PyErr_SetString(PyExc_TypeError, + "dbm mappings have string keys only"); + return -1; + } + krec.dsize = tmp_size; + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, "DBM object has already been closed"); + return -1; + } + dp->di_size = -1; + if (w == NULL) { + if ( dbm_delete(dp->di_dbm, krec) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetObject(PyExc_KeyError, v); + return -1; + } + } else { + if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { + PyErr_SetString(PyExc_TypeError, + "dbm mappings have byte string elements only"); + return -1; + } + drec.dsize = tmp_size; + if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, + "cannot add item to database"); + return -1; + } + } + if ( dbm_error(dp->di_dbm) ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, ""); + return -1; + } + return 0; +} + +static PyMappingMethods dbm_as_mapping = { + (lenfunc)dbm_length, /*mp_length*/ + (binaryfunc)dbm_subscript, /*mp_subscript*/ + (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/ +}; + +static PyObject * +dbm__close(register dbmobject *dp, PyObject *unused) +{ + if (dp->di_dbm) + dbm_close(dp->di_dbm); + dp->di_dbm = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +dbm_keys(register dbmobject *dp, PyObject *unused) +{ + register PyObject *v, *item; + datum key; + int err; + + check_dbmobject_open(dp); + v = PyList_New(0); + if (v == NULL) + return NULL; + for (key = dbm_firstkey(dp->di_dbm); key.dptr; + key = dbm_nextkey(dp->di_dbm)) { + item = PyBytes_FromStringAndSize(key.dptr, key.dsize); + if (item == NULL) { + Py_DECREF(v); + return NULL; + } + err = PyList_Append(v, item); + Py_DECREF(item); + if (err != 0) { + Py_DECREF(v); + return NULL; + } + } + return v; +} + +static int +dbm_contains(PyObject *self, PyObject *arg) +{ + dbmobject *dp = (dbmobject *)self; + datum key, val; + + if ((dp)->di_dbm == NULL) { + PyErr_SetString(DbmError, + "DBM object has already been closed"); + return -1; + } + if (PyUnicode_Check(arg)) { + arg = _PyUnicode_AsDefaultEncodedString(arg, NULL); + if (arg == NULL) + return -1; + } + if (!PyString_Check(arg)) { + PyErr_Format(PyExc_TypeError, + "dbm key must be string, not %.100s", + arg->ob_type->tp_name); + return -1; + } + key.dptr = PyString_AS_STRING(arg); + key.dsize = PyString_GET_SIZE(arg); + val = dbm_fetch(dp->di_dbm, key); + return val.dptr != NULL; +} + +static PySequenceMethods dbm_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + dbm_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * +dbm_get(register dbmobject *dp, PyObject *args) +{ + datum key, val; + PyObject *defvalue = Py_None; + char *tmp_ptr; + Py_ssize_t tmp_size; + + if (!PyArg_ParseTuple(args, "s#|O:get", + &tmp_ptr, &tmp_size, &defvalue)) + return NULL; + key.dptr = tmp_ptr; + key.dsize = tmp_size; + check_dbmobject_open(dp); + val = dbm_fetch(dp->di_dbm, key); + if (val.dptr != NULL) + return PyBytes_FromStringAndSize(val.dptr, val.dsize); + else { + Py_INCREF(defvalue); + return defvalue; + } +} + +static PyObject * +dbm_setdefault(register dbmobject *dp, PyObject *args) +{ + datum key, val; + PyObject *defvalue = NULL; + char *tmp_ptr; + Py_ssize_t tmp_size; + + if (!PyArg_ParseTuple(args, "s#|O:setdefault", + &tmp_ptr, &tmp_size, &defvalue)) + return NULL; + key.dptr = tmp_ptr; + key.dsize = tmp_size; + check_dbmobject_open(dp); + val = dbm_fetch(dp->di_dbm, key); + if (val.dptr != NULL) + return PyBytes_FromStringAndSize(val.dptr, val.dsize); + if (defvalue == NULL) { + defvalue = PyBytes_FromStringAndSize(NULL, 0); + if (defvalue == NULL) + return NULL; + val.dptr = NULL; + val.dsize = 0; + } + else { + if ( !PyArg_Parse(defvalue, "s#", &val.dptr, &tmp_size) ) { + PyErr_SetString(PyExc_TypeError, + "dbm mappings have byte string elements only"); + return NULL; + } + val.dsize = tmp_size; + Py_INCREF(defvalue); + } + if (dbm_store(dp->di_dbm, key, val, DBM_INSERT) < 0) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, "cannot add item to database"); + Py_DECREF(defvalue); + return NULL; + } + return defvalue; +} + +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."}, + {"get", (PyCFunction)dbm_get, METH_VARARGS, + "get(key[, default]) -> value\n" + "Return the value for key if present, otherwise default."}, + {"setdefault", (PyCFunction)dbm_setdefault, METH_VARARGS, + "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."}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +dbm_getattr(dbmobject *dp, char *name) +{ + return Py_FindMethod(dbm_methods, (PyObject *)dp, name); +} + +static PyTypeObject Dbmtype = { + PyVarObject_HEAD_INIT(NULL, 0) + "_dbm.dbm", + sizeof(dbmobject), + 0, + (destructor)dbm_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)dbm_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + &dbm_as_sequence, /*tp_as_sequence*/ + &dbm_as_mapping, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_xxx4*/ +}; + +/* ----------------------------------------------------------------- */ + +static PyObject * +dbmopen(PyObject *self, PyObject *args) +{ + char *name; + char *flags = "r"; + int iflags; + int mode = 0666; + + if ( !PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode) ) + return NULL; + if ( strcmp(flags, "r") == 0 ) + iflags = O_RDONLY; + else if ( strcmp(flags, "w") == 0 ) + iflags = O_RDWR; + else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */ + iflags = O_RDWR|O_CREAT; + else if ( strcmp(flags, "c") == 0 ) + iflags = O_RDWR|O_CREAT; + else if ( strcmp(flags, "n") == 0 ) + iflags = O_RDWR|O_CREAT|O_TRUNC; + else { + PyErr_SetString(DbmError, + "arg 2 to open should be 'r', 'w', 'c', or 'n'"); + return NULL; + } + return newdbmobject(name, iflags, mode); +} + +static PyMethodDef dbmmodule_methods[] = { + { "open", (PyCFunction)dbmopen, METH_VARARGS, + "open(path[, flag[, mode]]) -> mapping\n" + "Return a database object."}, + { 0, 0 }, +}; + +PyMODINIT_FUNC +init_dbm(void) { + PyObject *m, *d, *s; + + if (PyType_Ready(&Dbmtype) < 0) + return; + m = Py_InitModule("_dbm", dbmmodule_methods); + if (m == NULL) + return; + d = PyModule_GetDict(m); + if (DbmError == NULL) + DbmError = PyErr_NewException("_dbm.error", NULL, NULL); + s = PyUnicode_FromString(which_dbm); + if (s != NULL) { + PyDict_SetItemString(d, "library", s); + Py_DECREF(s); + } + if (DbmError != NULL) + PyDict_SetItemString(d, "error", DbmError); +} diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c new file mode 100644 index 0000000..4404d2f --- /dev/null +++ b/Modules/_gdbmmodule.c @@ -0,0 +1,533 @@ + +/* DBM module using dictionary interface */ +/* Author: Anthony Baxter, after dbmmodule.c */ +/* Doc strings: Mitch Chapman */ + + +#include "Python.h" + +#include +#include +#include +#include "gdbm.h" + +#if defined(WIN32) && !defined(__CYGWIN__) +#include "gdbmerrno.h" +extern const char * gdbm_strerror(gdbm_error); +#endif + +PyDoc_STRVAR(gdbmmodule__doc__, +"This module provides an interface to the GNU DBM (GDBM) library.\n\ +\n\ +This module is quite similar to the dbm module, but uses GDBM instead to\n\ +provide some additional functionality. Please note that the file formats\n\ +created by GDBM and dbm are incompatible. \n\ +\n\ +GDBM objects behave like mappings (dictionaries), except that keys and\n\ +values are always strings. Printing a GDBM object doesn't print the\n\ +keys and values, and the items() and values() methods are not\n\ +supported."); + +typedef struct { + PyObject_HEAD + int di_size; /* -1 means recompute */ + GDBM_FILE di_dbm; +} dbmobject; + +static PyTypeObject Dbmtype; + +#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; } + + + +static PyObject *DbmError; + +PyDoc_STRVAR(gdbm_object__doc__, +"This object represents a GDBM database.\n\ +GDBM objects behave like mappings (dictionaries), except that keys and\n\ +values are always strings. Printing a GDBM object doesn't print the\n\ +keys and values, and the items() and values() methods are not\n\ +supported.\n\ +\n\ +GDBM objects also support additional operations such as firstkey,\n\ +nextkey, reorganize, and sync."); + +static PyObject * +newdbmobject(char *file, int flags, int mode) +{ + dbmobject *dp; + + dp = PyObject_New(dbmobject, &Dbmtype); + if (dp == NULL) + return NULL; + dp->di_size = -1; + errno = 0; + if ((dp->di_dbm = gdbm_open(file, 0, flags, mode, NULL)) == 0) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + Py_DECREF(dp); + return NULL; + } + return (PyObject *)dp; +} + +/* Methods */ + +static void +dbm_dealloc(register dbmobject *dp) +{ + if (dp->di_dbm) + gdbm_close(dp->di_dbm); + PyObject_Del(dp); +} + +static Py_ssize_t +dbm_length(dbmobject *dp) +{ + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, "GDBM object has already been closed"); + return -1; + } + if (dp->di_size < 0) { + datum key,okey; + int size; + okey.dsize=0; + okey.dptr=NULL; + + size = 0; + for (key=gdbm_firstkey(dp->di_dbm); key.dptr; + key = gdbm_nextkey(dp->di_dbm,okey)) { + size++; + if(okey.dsize) free(okey.dptr); + okey=key; + } + dp->di_size = size; + } + return dp->di_size; +} + +static PyObject * +dbm_subscript(dbmobject *dp, register PyObject *key) +{ + PyObject *v; + datum drec, krec; + + if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize) ) + return NULL; + + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, + "GDBM object has already been closed"); + return NULL; + } + drec = gdbm_fetch(dp->di_dbm, krec); + if (drec.dptr == 0) { + PyErr_SetObject(PyExc_KeyError, key); + return NULL; + } + v = PyString_FromStringAndSize(drec.dptr, drec.dsize); + free(drec.dptr); + return v; +} + +static int +dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) +{ + datum krec, drec; + + if (!PyArg_Parse(v, "s#", &krec.dptr, &krec.dsize) ) { + PyErr_SetString(PyExc_TypeError, + "gdbm mappings have string indices only"); + return -1; + } + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, + "GDBM object has already been closed"); + return -1; + } + dp->di_size = -1; + if (w == NULL) { + if (gdbm_delete(dp->di_dbm, krec) < 0) { + PyErr_SetObject(PyExc_KeyError, v); + return -1; + } + } + else { + if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "gdbm mappings have byte string elements only"); + return -1; + } + errno = 0; + 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 -1; + } + } + return 0; +} + +static PyMappingMethods dbm_as_mapping = { + (lenfunc)dbm_length, /*mp_length*/ + (binaryfunc)dbm_subscript, /*mp_subscript*/ + (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/ +}; + +PyDoc_STRVAR(dbm_close__doc__, +"close() -> None\n\ +Closes the database."); + +static PyObject * +dbm_close(register dbmobject *dp, PyObject *unused) +{ + if (dp->di_dbm) + gdbm_close(dp->di_dbm); + dp->di_dbm = NULL; + Py_INCREF(Py_None); + 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 = PyString_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) +{ + dbmobject *dp = (dbmobject *)self; + datum key; + + if ((dp)->di_dbm == NULL) { + PyErr_SetString(DbmError, + "GDBM object has already been closed"); + return -1; + } + if (!PyString_Check(arg)) { + PyErr_Format(PyExc_TypeError, + "gdbm key must be bytes, not %.100s", + arg->ob_type->tp_name); + return -1; + } + key.dptr = PyString_AS_STRING(arg); + key.dsize = PyString_GET_SIZE(arg); + return gdbm_exists(dp->di_dbm, key); +} + +static PySequenceMethods dbm_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + dbm_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +PyDoc_STRVAR(dbm_firstkey__doc__, +"firstkey() -> key\n\ +It's possible to loop over every key in the database using this method\n\ +and the nextkey() method. The traversal is ordered by GDBM's internal\n\ +hash values, and won't be sorted by the key values. This method\n\ +returns the starting key."); + +static PyObject * +dbm_firstkey(register dbmobject *dp, PyObject *unused) +{ + register PyObject *v; + datum key; + + check_dbmobject_open(dp); + key = gdbm_firstkey(dp->di_dbm); + if (key.dptr) { + v = PyString_FromStringAndSize(key.dptr, key.dsize); + free(key.dptr); + return v; + } + else { + Py_INCREF(Py_None); + return Py_None; + } +} + +PyDoc_STRVAR(dbm_nextkey__doc__, +"nextkey(key) -> next_key\n\ +Returns the key that follows key in the traversal.\n\ +The following code prints every key in the database db, without having\n\ +to create a list in memory that contains them all:\n\ +\n\ + k = db.firstkey()\n\ + while k != None:\n\ + print k\n\ + k = db.nextkey(k)"); + +static PyObject * +dbm_nextkey(register dbmobject *dp, PyObject *args) +{ + register PyObject *v; + datum key, nextkey; + + if (!PyArg_ParseTuple(args, "s#:nextkey", &key.dptr, &key.dsize)) + return NULL; + check_dbmobject_open(dp); + nextkey = gdbm_nextkey(dp->di_dbm, key); + if (nextkey.dptr) { + v = PyString_FromStringAndSize(nextkey.dptr, nextkey.dsize); + free(nextkey.dptr); + return v; + } + else { + Py_INCREF(Py_None); + return Py_None; + } +} + +PyDoc_STRVAR(dbm_reorganize__doc__, +"reorganize() -> None\n\ +If you have carried out a lot of deletions and would like to shrink\n\ +the space used by the GDBM file, this routine will reorganize the\n\ +database. GDBM will not shorten the length of a database file except\n\ +by using this reorganization; otherwise, deleted file space will be\n\ +kept and reused as new (key,value) pairs are added."); + +static PyObject * +dbm_reorganize(register dbmobject *dp, PyObject *unused) +{ + check_dbmobject_open(dp); + errno = 0; + if (gdbm_reorganize(dp->di_dbm) < 0) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(dbm_sync__doc__, +"sync() -> None\n\ +When the database has been opened in fast mode, this method forces\n\ +any unwritten data to be written to the disk."); + +static PyObject * +dbm_sync(register dbmobject *dp, PyObject *unused) +{ + check_dbmobject_open(dp); + gdbm_sync(dp->di_dbm); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef dbm_methods[] = { + {"close", (PyCFunction)dbm_close, METH_NOARGS, dbm_close__doc__}, + {"keys", (PyCFunction)dbm_keys, 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__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +dbm_getattr(dbmobject *dp, char *name) +{ + return Py_FindMethod(dbm_methods, (PyObject *)dp, name); +} + +static PyTypeObject Dbmtype = { + PyVarObject_HEAD_INIT(0, 0) + "_gdbm.gdbm", + sizeof(dbmobject), + 0, + (destructor)dbm_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)dbm_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + &dbm_as_sequence, /*tp_as_sequence*/ + &dbm_as_mapping, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_xxx4*/ + gdbm_object__doc__, /*tp_doc*/ +}; + +/* ----------------------------------------------------------------- */ + +PyDoc_STRVAR(dbmopen__doc__, +"open(filename, [flags, [mode]]) -> dbm_object\n\ +Open a dbm database and return a dbm object. The filename argument is\n\ +the name of the database file.\n\ +\n\ +The optional flags argument can be 'r' (to open an existing database\n\ +for reading only -- default), 'w' (to open an existing database for\n\ +reading and writing), 'c' (which creates the database if it doesn't\n\ +exist), or 'n' (which always creates a new empty database).\n\ +\n\ +Some versions of gdbm support additional flags which must be\n\ +appended to one of the flags described above. The module constant\n\ +'open_flags' is a string of valid additional flags. The 'f' flag\n\ +opens the database in fast mode; altered data will not automatically\n\ +be written to the disk after every change. This results in faster\n\ +writes to the database, but may result in an inconsistent database\n\ +if the program crashes while the database is still open. Use the\n\ +sync() method to force any unwritten data to be written to the disk.\n\ +The 's' flag causes all database operations to be synchronized to\n\ +disk. The 'u' flag disables locking of the database file.\n\ +\n\ +The optional mode argument is the Unix mode of the file, used only\n\ +when the database has to be created. It defaults to octal 0666. "); + +static PyObject * +dbmopen(PyObject *self, PyObject *args) +{ + char *name; + char *flags = "r"; + int iflags; + int mode = 0666; + + if (!PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode)) + return NULL; + switch (flags[0]) { + case 'r': + iflags = GDBM_READER; + break; + case 'w': + iflags = GDBM_WRITER; + break; + case 'c': + iflags = GDBM_WRCREAT; + break; + case 'n': + iflags = GDBM_NEWDB; + break; + default: + PyErr_SetString(DbmError, + "First flag must be one of 'r', 'w', 'c' or 'n'"); + return NULL; + } + for (flags++; *flags != '\0'; flags++) { + char buf[40]; + switch (*flags) { +#ifdef GDBM_FAST + case 'f': + iflags |= GDBM_FAST; + break; +#endif +#ifdef GDBM_SYNC + case 's': + iflags |= GDBM_SYNC; + break; +#endif +#ifdef GDBM_NOLOCK + case 'u': + iflags |= GDBM_NOLOCK; + break; +#endif + default: + PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.", + *flags); + PyErr_SetString(DbmError, buf); + return NULL; + } + } + + return newdbmobject(name, iflags, mode); +} + +static char dbmmodule_open_flags[] = "rwcn" +#ifdef GDBM_FAST + "f" +#endif +#ifdef GDBM_SYNC + "s" +#endif +#ifdef GDBM_NOLOCK + "u" +#endif + ; + +static PyMethodDef dbmmodule_methods[] = { + { "open", (PyCFunction)dbmopen, METH_VARARGS, dbmopen__doc__}, + { 0, 0 }, +}; + +PyMODINIT_FUNC +init_gdbm(void) { + PyObject *m, *d, *s; + + if (PyType_Ready(&Dbmtype) < 0) + return; + m = Py_InitModule4("_gdbm", dbmmodule_methods, + gdbmmodule__doc__, (PyObject *)NULL, + PYTHON_API_VERSION); + if (m == NULL) + return; + d = PyModule_GetDict(m); + DbmError = PyErr_NewException("_gdbm.error", NULL, NULL); + if (DbmError != NULL) { + PyDict_SetItemString(d, "error", DbmError); + s = PyUnicode_FromString(dbmmodule_open_flags); + PyDict_SetItemString(d, "open_flags", s); + Py_DECREF(s); + } +} diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 971ff56..5b8fc98 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -127,7 +127,7 @@ lock_getattr(lockobject *self, char *name) static PyTypeObject Locktype = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "thread.lock", /*tp_name*/ + "_thread.lock", /*tp_name*/ sizeof(lockobject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ @@ -336,7 +336,7 @@ static PyObject *local_getattro(localobject *, PyObject *); static PyTypeObject localtype = { PyVarObject_HEAD_INIT(NULL, 0) - /* tp_name */ "thread._local", + /* tp_name */ "_thread._local", /* tp_basicsize */ sizeof(localobject), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)local_dealloc, diff --git a/Modules/dbmmodule.c b/Modules/dbmmodule.c deleted file mode 100644 index 875f0e7..0000000 --- a/Modules/dbmmodule.c +++ /dev/null @@ -1,412 +0,0 @@ - -/* DBM module using dictionary interface */ - - -#include "Python.h" - -#include -#include -#include - -/* Some Linux systems install gdbm/ndbm.h, but not ndbm.h. This supports - * whichever configure was able to locate. - */ -#if defined(HAVE_NDBM_H) -#include -#if defined(PYOS_OS2) && !defined(PYCC_GCC) -static char *which_dbm = "ndbm"; -#else -static char *which_dbm = "GNU gdbm"; /* EMX port of GDBM */ -#endif -#elif defined(HAVE_GDBM_NDBM_H) -#include -static char *which_dbm = "GNU gdbm"; -#elif defined(HAVE_BERKDB_H) -#include -static char *which_dbm = "Berkeley DB"; -#else -#error "No ndbm.h available!" -#endif - -typedef struct { - PyObject_HEAD - int di_size; /* -1 means recompute */ - DBM *di_dbm; -} dbmobject; - -static PyTypeObject Dbmtype; - -#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; } - -static PyObject *DbmError; - -static PyObject * -newdbmobject(char *file, int flags, int mode) -{ - dbmobject *dp; - - dp = PyObject_New(dbmobject, &Dbmtype); - if (dp == NULL) - return NULL; - dp->di_size = -1; - if ( (dp->di_dbm = dbm_open(file, flags, mode)) == 0 ) { - PyErr_SetFromErrno(DbmError); - Py_DECREF(dp); - return NULL; - } - return (PyObject *)dp; -} - -/* Methods */ - -static void -dbm_dealloc(register dbmobject *dp) -{ - if ( dp->di_dbm ) - dbm_close(dp->di_dbm); - PyObject_Del(dp); -} - -static Py_ssize_t -dbm_length(dbmobject *dp) -{ - if (dp->di_dbm == NULL) { - PyErr_SetString(DbmError, "DBM object has already been closed"); - return -1; - } - if ( dp->di_size < 0 ) { - datum key; - int size; - - size = 0; - for ( key=dbm_firstkey(dp->di_dbm); key.dptr; - key = dbm_nextkey(dp->di_dbm)) - size++; - dp->di_size = size; - } - return dp->di_size; -} - -static PyObject * -dbm_subscript(dbmobject *dp, register PyObject *key) -{ - datum drec, krec; - Py_ssize_t tmp_size; - - if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size) ) - return NULL; - - krec.dsize = tmp_size; - check_dbmobject_open(dp); - drec = dbm_fetch(dp->di_dbm, krec); - if ( drec.dptr == 0 ) { - PyErr_SetObject(PyExc_KeyError, key); - return NULL; - } - if ( dbm_error(dp->di_dbm) ) { - dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, ""); - return NULL; - } - return PyBytes_FromStringAndSize(drec.dptr, drec.dsize); -} - -static int -dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) -{ - datum krec, drec; - Py_ssize_t tmp_size; - - if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { - PyErr_SetString(PyExc_TypeError, - "dbm mappings have string keys only"); - return -1; - } - krec.dsize = tmp_size; - if (dp->di_dbm == NULL) { - PyErr_SetString(DbmError, "DBM object has already been closed"); - return -1; - } - dp->di_size = -1; - if (w == NULL) { - if ( dbm_delete(dp->di_dbm, krec) < 0 ) { - dbm_clearerr(dp->di_dbm); - PyErr_SetObject(PyExc_KeyError, v); - return -1; - } - } else { - if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { - PyErr_SetString(PyExc_TypeError, - "dbm mappings have byte string elements only"); - return -1; - } - drec.dsize = tmp_size; - if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { - dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, - "cannot add item to database"); - return -1; - } - } - if ( dbm_error(dp->di_dbm) ) { - dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, ""); - return -1; - } - return 0; -} - -static PyMappingMethods dbm_as_mapping = { - (lenfunc)dbm_length, /*mp_length*/ - (binaryfunc)dbm_subscript, /*mp_subscript*/ - (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/ -}; - -static PyObject * -dbm__close(register dbmobject *dp, PyObject *unused) -{ - if (dp->di_dbm) - dbm_close(dp->di_dbm); - dp->di_dbm = NULL; - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject * -dbm_keys(register dbmobject *dp, PyObject *unused) -{ - register PyObject *v, *item; - datum key; - int err; - - check_dbmobject_open(dp); - v = PyList_New(0); - if (v == NULL) - return NULL; - for (key = dbm_firstkey(dp->di_dbm); key.dptr; - key = dbm_nextkey(dp->di_dbm)) { - item = PyBytes_FromStringAndSize(key.dptr, key.dsize); - if (item == NULL) { - Py_DECREF(v); - return NULL; - } - err = PyList_Append(v, item); - Py_DECREF(item); - if (err != 0) { - Py_DECREF(v); - return NULL; - } - } - return v; -} - -static int -dbm_contains(PyObject *self, PyObject *arg) -{ - dbmobject *dp = (dbmobject *)self; - datum key, val; - - if ((dp)->di_dbm == NULL) { - PyErr_SetString(DbmError, - "DBM object has already been closed"); - return -1; - } - if (PyUnicode_Check(arg)) { - arg = _PyUnicode_AsDefaultEncodedString(arg, NULL); - if (arg == NULL) - return -1; - } - if (!PyString_Check(arg)) { - PyErr_Format(PyExc_TypeError, - "dbm key must be string, not %.100s", - arg->ob_type->tp_name); - return -1; - } - key.dptr = PyString_AS_STRING(arg); - key.dsize = PyString_GET_SIZE(arg); - val = dbm_fetch(dp->di_dbm, key); - return val.dptr != NULL; -} - -static PySequenceMethods dbm_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - dbm_contains, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ -}; - -static PyObject * -dbm_get(register dbmobject *dp, PyObject *args) -{ - datum key, val; - PyObject *defvalue = Py_None; - char *tmp_ptr; - Py_ssize_t tmp_size; - - if (!PyArg_ParseTuple(args, "s#|O:get", - &tmp_ptr, &tmp_size, &defvalue)) - return NULL; - key.dptr = tmp_ptr; - key.dsize = tmp_size; - check_dbmobject_open(dp); - val = dbm_fetch(dp->di_dbm, key); - if (val.dptr != NULL) - return PyBytes_FromStringAndSize(val.dptr, val.dsize); - else { - Py_INCREF(defvalue); - return defvalue; - } -} - -static PyObject * -dbm_setdefault(register dbmobject *dp, PyObject *args) -{ - datum key, val; - PyObject *defvalue = NULL; - char *tmp_ptr; - Py_ssize_t tmp_size; - - if (!PyArg_ParseTuple(args, "s#|O:setdefault", - &tmp_ptr, &tmp_size, &defvalue)) - return NULL; - key.dptr = tmp_ptr; - key.dsize = tmp_size; - check_dbmobject_open(dp); - val = dbm_fetch(dp->di_dbm, key); - if (val.dptr != NULL) - return PyBytes_FromStringAndSize(val.dptr, val.dsize); - if (defvalue == NULL) { - defvalue = PyBytes_FromStringAndSize(NULL, 0); - if (defvalue == NULL) - return NULL; - val.dptr = NULL; - val.dsize = 0; - } - else { - if ( !PyArg_Parse(defvalue, "s#", &val.dptr, &tmp_size) ) { - PyErr_SetString(PyExc_TypeError, - "dbm mappings have byte string elements only"); - return NULL; - } - val.dsize = tmp_size; - Py_INCREF(defvalue); - } - if (dbm_store(dp->di_dbm, key, val, DBM_INSERT) < 0) { - dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, "cannot add item to database"); - Py_DECREF(defvalue); - return NULL; - } - return defvalue; -} - -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."}, - {"get", (PyCFunction)dbm_get, METH_VARARGS, - "get(key[, default]) -> value\n" - "Return the value for key if present, otherwise default."}, - {"setdefault", (PyCFunction)dbm_setdefault, METH_VARARGS, - "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."}, - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -dbm_getattr(dbmobject *dp, char *name) -{ - return Py_FindMethod(dbm_methods, (PyObject *)dp, name); -} - -static PyTypeObject Dbmtype = { - PyVarObject_HEAD_INIT(NULL, 0) - "dbm.dbm", - sizeof(dbmobject), - 0, - (destructor)dbm_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)dbm_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - &dbm_as_sequence, /*tp_as_sequence*/ - &dbm_as_mapping, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_xxx4*/ -}; - -/* ----------------------------------------------------------------- */ - -static PyObject * -dbmopen(PyObject *self, PyObject *args) -{ - char *name; - char *flags = "r"; - int iflags; - int mode = 0666; - - if ( !PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode) ) - return NULL; - if ( strcmp(flags, "r") == 0 ) - iflags = O_RDONLY; - else if ( strcmp(flags, "w") == 0 ) - iflags = O_RDWR; - else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */ - iflags = O_RDWR|O_CREAT; - else if ( strcmp(flags, "c") == 0 ) - iflags = O_RDWR|O_CREAT; - else if ( strcmp(flags, "n") == 0 ) - iflags = O_RDWR|O_CREAT|O_TRUNC; - else { - PyErr_SetString(DbmError, - "arg 2 to open should be 'r', 'w', 'c', or 'n'"); - return NULL; - } - return newdbmobject(name, iflags, mode); -} - -static PyMethodDef dbmmodule_methods[] = { - { "open", (PyCFunction)dbmopen, METH_VARARGS, - "open(path[, flag[, mode]]) -> mapping\n" - "Return a database object."}, - { 0, 0 }, -}; - -PyMODINIT_FUNC -initdbm(void) { - PyObject *m, *d, *s; - - if (PyType_Ready(&Dbmtype) < 0) - return; - m = Py_InitModule("dbm", dbmmodule_methods); - if (m == NULL) - return; - d = PyModule_GetDict(m); - if (DbmError == NULL) - DbmError = PyErr_NewException("dbm.error", NULL, NULL); - s = PyUnicode_FromString(which_dbm); - if (s != NULL) { - PyDict_SetItemString(d, "library", s); - Py_DECREF(s); - } - if (DbmError != NULL) - PyDict_SetItemString(d, "error", DbmError); -} diff --git a/Modules/gdbmmodule.c b/Modules/gdbmmodule.c deleted file mode 100644 index a8abbd3..0000000 --- a/Modules/gdbmmodule.c +++ /dev/null @@ -1,533 +0,0 @@ - -/* DBM module using dictionary interface */ -/* Author: Anthony Baxter, after dbmmodule.c */ -/* Doc strings: Mitch Chapman */ - - -#include "Python.h" - -#include -#include -#include -#include "gdbm.h" - -#if defined(WIN32) && !defined(__CYGWIN__) -#include "gdbmerrno.h" -extern const char * gdbm_strerror(gdbm_error); -#endif - -PyDoc_STRVAR(gdbmmodule__doc__, -"This module provides an interface to the GNU DBM (GDBM) library.\n\ -\n\ -This module is quite similar to the dbm module, but uses GDBM instead to\n\ -provide some additional functionality. Please note that the file formats\n\ -created by GDBM and dbm are incompatible. \n\ -\n\ -GDBM objects behave like mappings (dictionaries), except that keys and\n\ -values are always strings. Printing a GDBM object doesn't print the\n\ -keys and values, and the items() and values() methods are not\n\ -supported."); - -typedef struct { - PyObject_HEAD - int di_size; /* -1 means recompute */ - GDBM_FILE di_dbm; -} dbmobject; - -static PyTypeObject Dbmtype; - -#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; } - - - -static PyObject *DbmError; - -PyDoc_STRVAR(gdbm_object__doc__, -"This object represents a GDBM database.\n\ -GDBM objects behave like mappings (dictionaries), except that keys and\n\ -values are always strings. Printing a GDBM object doesn't print the\n\ -keys and values, and the items() and values() methods are not\n\ -supported.\n\ -\n\ -GDBM objects also support additional operations such as firstkey,\n\ -nextkey, reorganize, and sync."); - -static PyObject * -newdbmobject(char *file, int flags, int mode) -{ - dbmobject *dp; - - dp = PyObject_New(dbmobject, &Dbmtype); - if (dp == NULL) - return NULL; - dp->di_size = -1; - errno = 0; - if ((dp->di_dbm = gdbm_open(file, 0, flags, mode, NULL)) == 0) { - if (errno != 0) - PyErr_SetFromErrno(DbmError); - else - PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); - Py_DECREF(dp); - return NULL; - } - return (PyObject *)dp; -} - -/* Methods */ - -static void -dbm_dealloc(register dbmobject *dp) -{ - if (dp->di_dbm) - gdbm_close(dp->di_dbm); - PyObject_Del(dp); -} - -static Py_ssize_t -dbm_length(dbmobject *dp) -{ - if (dp->di_dbm == NULL) { - PyErr_SetString(DbmError, "GDBM object has already been closed"); - return -1; - } - if (dp->di_size < 0) { - datum key,okey; - int size; - okey.dsize=0; - okey.dptr=NULL; - - size = 0; - for (key=gdbm_firstkey(dp->di_dbm); key.dptr; - key = gdbm_nextkey(dp->di_dbm,okey)) { - size++; - if(okey.dsize) free(okey.dptr); - okey=key; - } - dp->di_size = size; - } - return dp->di_size; -} - -static PyObject * -dbm_subscript(dbmobject *dp, register PyObject *key) -{ - PyObject *v; - datum drec, krec; - - if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize) ) - return NULL; - - if (dp->di_dbm == NULL) { - PyErr_SetString(DbmError, - "GDBM object has already been closed"); - return NULL; - } - drec = gdbm_fetch(dp->di_dbm, krec); - if (drec.dptr == 0) { - PyErr_SetObject(PyExc_KeyError, key); - return NULL; - } - v = PyString_FromStringAndSize(drec.dptr, drec.dsize); - free(drec.dptr); - return v; -} - -static int -dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) -{ - datum krec, drec; - - if (!PyArg_Parse(v, "s#", &krec.dptr, &krec.dsize) ) { - PyErr_SetString(PyExc_TypeError, - "gdbm mappings have string indices only"); - return -1; - } - if (dp->di_dbm == NULL) { - PyErr_SetString(DbmError, - "GDBM object has already been closed"); - return -1; - } - dp->di_size = -1; - if (w == NULL) { - if (gdbm_delete(dp->di_dbm, krec) < 0) { - PyErr_SetObject(PyExc_KeyError, v); - return -1; - } - } - else { - if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) { - PyErr_SetString(PyExc_TypeError, - "gdbm mappings have byte string elements only"); - return -1; - } - errno = 0; - 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 -1; - } - } - return 0; -} - -static PyMappingMethods dbm_as_mapping = { - (lenfunc)dbm_length, /*mp_length*/ - (binaryfunc)dbm_subscript, /*mp_subscript*/ - (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/ -}; - -PyDoc_STRVAR(dbm_close__doc__, -"close() -> None\n\ -Closes the database."); - -static PyObject * -dbm_close(register dbmobject *dp, PyObject *unused) -{ - if (dp->di_dbm) - gdbm_close(dp->di_dbm); - dp->di_dbm = NULL; - Py_INCREF(Py_None); - 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 = PyString_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) -{ - dbmobject *dp = (dbmobject *)self; - datum key; - - if ((dp)->di_dbm == NULL) { - PyErr_SetString(DbmError, - "GDBM object has already been closed"); - return -1; - } - if (!PyString_Check(arg)) { - PyErr_Format(PyExc_TypeError, - "gdbm key must be bytes, not %.100s", - arg->ob_type->tp_name); - return -1; - } - key.dptr = PyString_AS_STRING(arg); - key.dsize = PyString_GET_SIZE(arg); - return gdbm_exists(dp->di_dbm, key); -} - -static PySequenceMethods dbm_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - dbm_contains, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ -}; - -PyDoc_STRVAR(dbm_firstkey__doc__, -"firstkey() -> key\n\ -It's possible to loop over every key in the database using this method\n\ -and the nextkey() method. The traversal is ordered by GDBM's internal\n\ -hash values, and won't be sorted by the key values. This method\n\ -returns the starting key."); - -static PyObject * -dbm_firstkey(register dbmobject *dp, PyObject *unused) -{ - register PyObject *v; - datum key; - - check_dbmobject_open(dp); - key = gdbm_firstkey(dp->di_dbm); - if (key.dptr) { - v = PyString_FromStringAndSize(key.dptr, key.dsize); - free(key.dptr); - return v; - } - else { - Py_INCREF(Py_None); - return Py_None; - } -} - -PyDoc_STRVAR(dbm_nextkey__doc__, -"nextkey(key) -> next_key\n\ -Returns the key that follows key in the traversal.\n\ -The following code prints every key in the database db, without having\n\ -to create a list in memory that contains them all:\n\ -\n\ - k = db.firstkey()\n\ - while k != None:\n\ - print k\n\ - k = db.nextkey(k)"); - -static PyObject * -dbm_nextkey(register dbmobject *dp, PyObject *args) -{ - register PyObject *v; - datum key, nextkey; - - if (!PyArg_ParseTuple(args, "s#:nextkey", &key.dptr, &key.dsize)) - return NULL; - check_dbmobject_open(dp); - nextkey = gdbm_nextkey(dp->di_dbm, key); - if (nextkey.dptr) { - v = PyString_FromStringAndSize(nextkey.dptr, nextkey.dsize); - free(nextkey.dptr); - return v; - } - else { - Py_INCREF(Py_None); - return Py_None; - } -} - -PyDoc_STRVAR(dbm_reorganize__doc__, -"reorganize() -> None\n\ -If you have carried out a lot of deletions and would like to shrink\n\ -the space used by the GDBM file, this routine will reorganize the\n\ -database. GDBM will not shorten the length of a database file except\n\ -by using this reorganization; otherwise, deleted file space will be\n\ -kept and reused as new (key,value) pairs are added."); - -static PyObject * -dbm_reorganize(register dbmobject *dp, PyObject *unused) -{ - check_dbmobject_open(dp); - errno = 0; - if (gdbm_reorganize(dp->di_dbm) < 0) { - if (errno != 0) - PyErr_SetFromErrno(DbmError); - else - PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - -PyDoc_STRVAR(dbm_sync__doc__, -"sync() -> None\n\ -When the database has been opened in fast mode, this method forces\n\ -any unwritten data to be written to the disk."); - -static PyObject * -dbm_sync(register dbmobject *dp, PyObject *unused) -{ - check_dbmobject_open(dp); - gdbm_sync(dp->di_dbm); - Py_INCREF(Py_None); - return Py_None; -} - -static PyMethodDef dbm_methods[] = { - {"close", (PyCFunction)dbm_close, METH_NOARGS, dbm_close__doc__}, - {"keys", (PyCFunction)dbm_keys, 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__}, - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -dbm_getattr(dbmobject *dp, char *name) -{ - return Py_FindMethod(dbm_methods, (PyObject *)dp, name); -} - -static PyTypeObject Dbmtype = { - PyVarObject_HEAD_INIT(0, 0) - "gdbm.gdbm", - sizeof(dbmobject), - 0, - (destructor)dbm_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)dbm_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - &dbm_as_sequence, /*tp_as_sequence*/ - &dbm_as_mapping, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_xxx4*/ - gdbm_object__doc__, /*tp_doc*/ -}; - -/* ----------------------------------------------------------------- */ - -PyDoc_STRVAR(dbmopen__doc__, -"open(filename, [flags, [mode]]) -> dbm_object\n\ -Open a dbm database and return a dbm object. The filename argument is\n\ -the name of the database file.\n\ -\n\ -The optional flags argument can be 'r' (to open an existing database\n\ -for reading only -- default), 'w' (to open an existing database for\n\ -reading and writing), 'c' (which creates the database if it doesn't\n\ -exist), or 'n' (which always creates a new empty database).\n\ -\n\ -Some versions of gdbm support additional flags which must be\n\ -appended to one of the flags described above. The module constant\n\ -'open_flags' is a string of valid additional flags. The 'f' flag\n\ -opens the database in fast mode; altered data will not automatically\n\ -be written to the disk after every change. This results in faster\n\ -writes to the database, but may result in an inconsistent database\n\ -if the program crashes while the database is still open. Use the\n\ -sync() method to force any unwritten data to be written to the disk.\n\ -The 's' flag causes all database operations to be synchronized to\n\ -disk. The 'u' flag disables locking of the database file.\n\ -\n\ -The optional mode argument is the Unix mode of the file, used only\n\ -when the database has to be created. It defaults to octal 0666. "); - -static PyObject * -dbmopen(PyObject *self, PyObject *args) -{ - char *name; - char *flags = "r"; - int iflags; - int mode = 0666; - - if (!PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode)) - return NULL; - switch (flags[0]) { - case 'r': - iflags = GDBM_READER; - break; - case 'w': - iflags = GDBM_WRITER; - break; - case 'c': - iflags = GDBM_WRCREAT; - break; - case 'n': - iflags = GDBM_NEWDB; - break; - default: - PyErr_SetString(DbmError, - "First flag must be one of 'r', 'w', 'c' or 'n'"); - return NULL; - } - for (flags++; *flags != '\0'; flags++) { - char buf[40]; - switch (*flags) { -#ifdef GDBM_FAST - case 'f': - iflags |= GDBM_FAST; - break; -#endif -#ifdef GDBM_SYNC - case 's': - iflags |= GDBM_SYNC; - break; -#endif -#ifdef GDBM_NOLOCK - case 'u': - iflags |= GDBM_NOLOCK; - break; -#endif - default: - PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.", - *flags); - PyErr_SetString(DbmError, buf); - return NULL; - } - } - - return newdbmobject(name, iflags, mode); -} - -static char dbmmodule_open_flags[] = "rwcn" -#ifdef GDBM_FAST - "f" -#endif -#ifdef GDBM_SYNC - "s" -#endif -#ifdef GDBM_NOLOCK - "u" -#endif - ; - -static PyMethodDef dbmmodule_methods[] = { - { "open", (PyCFunction)dbmopen, METH_VARARGS, dbmopen__doc__}, - { 0, 0 }, -}; - -PyMODINIT_FUNC -initgdbm(void) { - PyObject *m, *d, *s; - - if (PyType_Ready(&Dbmtype) < 0) - return; - m = Py_InitModule4("gdbm", dbmmodule_methods, - gdbmmodule__doc__, (PyObject *)NULL, - PYTHON_API_VERSION); - if (m == NULL) - return; - d = PyModule_GetDict(m); - DbmError = PyErr_NewException("gdbm.error", NULL, NULL); - if (DbmError != NULL) { - PyDict_SetItemString(d, "error", DbmError); - s = PyUnicode_FromString(dbmmodule_open_flags); - PyDict_SetItemString(d, "open_flags", s); - Py_DECREF(s); - } -} diff --git a/PC/os2emx/Makefile b/PC/os2emx/Makefile index 5f4cab8..a2947a7 100644 --- a/PC/os2emx/Makefile +++ b/PC/os2emx/Makefile @@ -464,7 +464,7 @@ ifeq ($(HAVE_NCURSES),yes) HARDEXTMODULES+= _curses_ endif ifeq ($(HAVE_GDBM),yes) - HARDEXTMODULES+= gdbm dbm + HARDEXTMODULES+= _gdbm _dbm endif ifeq ($(HAVE_BZ2),yes) HARDEXTMODULES+= bz2 @@ -626,10 +626,10 @@ _curses_panel$(MODULE.EXT): $(OUT)_curses_panel$O $(OUT)_curses_panel_m.def $(PY _curses_$(MODULE.EXT): _curses_panel$(MODULE.EXT) cp $^ $@ -dbm$(MODULE.EXT): $(OUT)dbmmodule$O $(OUT)dbm_m.def $(PYTHON.IMPLIB) +_dbm$(MODULE.EXT): $(OUT)_dbmmodule$O $(OUT)dbm_m.def $(PYTHON.IMPLIB) $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lgdbm -gdbm$(MODULE.EXT): $(OUT)gdbmmodule$O $(OUT)gdbm_m.def $(PYTHON.IMPLIB) +_gdbm$(MODULE.EXT): $(OUT)_gdbmmodule$O $(OUT)gdbm_m.def $(PYTHON.IMPLIB) $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lgdbm diff --git a/PC/os2vacpp/makefile b/PC/os2vacpp/makefile index 50119a0..55bb783 100644 --- a/PC/os2vacpp/makefile +++ b/PC/os2vacpp/makefile @@ -494,7 +494,7 @@ cursesmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ $(PY_INCLUDE)\tupleobject.h -dbmmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ +_dbmmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ @@ -576,7 +576,7 @@ fpetestmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ $(PY_INCLUDE)\tupleobject.h -gdbmmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ +_gdbmmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ diff --git a/PC/os2vacpp/makefile.omk b/PC/os2vacpp/makefile.omk index a925efb..7083c78 100644 --- a/PC/os2vacpp/makefile.omk +++ b/PC/os2vacpp/makefile.omk @@ -171,8 +171,8 @@ MODULES = \ # audioop.c -- Various Compute Operations on Audio Samples # Database: - # dbmmodule.c -- Wrapper of DBM Database API (Generic Flavor) - # gdbmmodule.c -- Wrapper of DBM Database API (GNU Flavor) + # _dbmmodule.c -- Wrapper of DBM Database API (Generic Flavor) + # _gdbmmodule.c -- Wrapper of DBM Database API (GNU Flavor) # Cryptography: # cryptmodule.c -- Simple Wrapper for crypt() Function @@ -410,7 +410,7 @@ cursesmodule.obj: abstract.h ceval.h classobject.h cobject.h \ pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ stringobject.h sysmodule.h traceback.h tupleobject.h -dbmmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ +_dbmmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ import.h intobject.h intrcheck.h listobject.h longobject.h \ methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ @@ -458,7 +458,7 @@ fpetestmodule.obj: abstract.h ceval.h classobject.h cobject.h \ pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ stringobject.h sysmodule.h traceback.h tupleobject.h -gdbmmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ +_gdbmmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ import.h intobject.h intrcheck.h listobject.h longobject.h \ methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ diff --git a/Tools/scripts/db2pickle.py b/Tools/scripts/db2pickle.py index 795011b..0c9b6bf 100644 --- a/Tools/scripts/db2pickle.py +++ b/Tools/scripts/db2pickle.py @@ -6,7 +6,7 @@ Synopsis: %(prog)s [-h|-g|-b|-r|-a] dbfile [ picklefile ] Convert the database file given on the command line to a pickle representation. The optional flags indicate the type of the database: - -a - open using anydbm + -a - open using dbm (any supported format) -b - open as bsddb btree file -d - open as dbm file -g - open as gdbm file @@ -25,15 +25,15 @@ try: except ImportError: bsddb = None try: - import dbm + import dbm.ndbm as dbm except ImportError: dbm = None try: - import gdbm + import dbm.gnu as gdbm except ImportError: gdbm = None try: - import anydbm + import dbm as anydbm except ImportError: anydbm = None import sys @@ -94,19 +94,19 @@ def main(args): try: dbopen = anydbm.open except AttributeError: - sys.stderr.write("anydbm module unavailable.\n") + sys.stderr.write("dbm module unavailable.\n") return 1 elif opt in ("-g", "--gdbm"): try: dbopen = gdbm.open except AttributeError: - sys.stderr.write("gdbm module unavailable.\n") + sys.stderr.write("dbm.gnu module unavailable.\n") return 1 elif opt in ("-d", "--dbm"): try: dbopen = dbm.open except AttributeError: - sys.stderr.write("dbm module unavailable.\n") + sys.stderr.write("dbm.ndbm module unavailable.\n") return 1 if dbopen is None: if bsddb is None: diff --git a/Tools/scripts/pickle2db.py b/Tools/scripts/pickle2db.py index c66f5e9..357e628 100644 --- a/Tools/scripts/pickle2db.py +++ b/Tools/scripts/pickle2db.py @@ -7,10 +7,10 @@ Read the given picklefile as a series of key/value pairs and write to a new database. If the database already exists, any contents are deleted. The optional flags indicate the type of the output database: - -a - open using anydbm + -a - open using dbm (open any supported format) -b - open as bsddb btree file - -d - open as dbm file - -g - open as gdbm file + -d - open as dbm.ndbm file + -g - open as dbm.gnu file -h - open as bsddb hash file -r - open as bsddb recno file @@ -30,15 +30,15 @@ try: except ImportError: bsddb = None try: - import dbm + import dbm.ndbm as dbm except ImportError: dbm = None try: - import gdbm + import dbm.gnu as gdbm except ImportError: gdbm = None try: - import anydbm + import dbm as anydbm except ImportError: anydbm = None import sys @@ -99,19 +99,19 @@ def main(args): try: dbopen = anydbm.open except AttributeError: - sys.stderr.write("anydbm module unavailable.\n") + sys.stderr.write("dbm module unavailable.\n") return 1 elif opt in ("-g", "--gdbm"): try: dbopen = gdbm.open except AttributeError: - sys.stderr.write("gdbm module unavailable.\n") + sys.stderr.write("dbm.gnu module unavailable.\n") return 1 elif opt in ("-d", "--dbm"): try: dbopen = dbm.open except AttributeError: - sys.stderr.write("dbm module unavailable.\n") + sys.stderr.write("dbm.ndbm module unavailable.\n") return 1 if dbopen is None: if bsddb is None: diff --git a/setup.py b/setup.py index 34e6c39..639b42f 100644 --- a/setup.py +++ b/setup.py @@ -637,8 +637,8 @@ class PyBuildExt(build_ext): # Modules that provide persistent dictionary-like semantics. You will # probably want to arrange for at least one of them to be available on # your machine, though none are defined by default because of library - # dependencies. The Python module anydbm.py provides an - # implementation independent wrapper for these; dumbdbm.py provides + # dependencies. The Python module dbm/__init__.py provides an + # implementation independent wrapper for these; dbm/dumb.py provides # similar functionality (but slower of course) implemented in Python. # Sleepycat^WOracle Berkeley DB interface. @@ -902,16 +902,16 @@ class PyBuildExt(build_ext): ndbm_libs = ['ndbm'] else: ndbm_libs = [] - exts.append( Extension('dbm', ['dbmmodule.c'], + exts.append( Extension('_dbm', ['_dbmmodule.c'], define_macros=[('HAVE_NDBM_H',None)], libraries = ndbm_libs ) ) elif (self.compiler.find_library_file(lib_dirs, 'gdbm') and find_file("gdbm/ndbm.h", inc_dirs, []) is not None): - exts.append( Extension('dbm', ['dbmmodule.c'], + exts.append( Extension('_dbm', ['_dbmmodule.c'], define_macros=[('HAVE_GDBM_NDBM_H',None)], libraries = ['gdbm'] ) ) elif db_incs is not None: - exts.append( Extension('dbm', ['dbmmodule.c'], + exts.append( Extension('_dbm', ['_dbmmodule.c'], library_dirs=dblib_dir, runtime_library_dirs=dblib_dir, include_dirs=db_incs, @@ -919,14 +919,14 @@ class PyBuildExt(build_ext): ('DB_DBM_HSEARCH',None)], libraries=dblibs)) else: - missing.append('dbm') + missing.append('_dbm') # Anthony Baxter's gdbm module. GNU dbm(3) will require -lgdbm: if (self.compiler.find_library_file(lib_dirs, 'gdbm')): - exts.append( Extension('gdbm', ['gdbmmodule.c'], + exts.append( Extension('_gdbm', ['_gdbmmodule.c'], libraries = ['gdbm'] ) ) else: - missing.append('gdbm') + missing.append('_gdbm') # Unix-only modules if platform not in ['mac', 'win32']: -- cgit v0.12