Spaces:
Running
Running
"""Generic interface to all dbm clones. | |
Use | |
import dbm | |
d = dbm.open(file, 'w', 0o666) | |
The returned object is a 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, and add interfaces to other dbm-like | |
implementations. | |
""" | |
__all__ = ['open', 'whichdb', 'error'] | |
import io | |
import os | |
import struct | |
import sys | |
class error(Exception): | |
pass | |
_names = ['dbm.gnu', 'dbm.ndbm', 'dbm.dumb'] | |
_defaultmod = None | |
_modules = {} | |
error = (error, OSError) | |
try: | |
from dbm import ndbm | |
except ImportError: | |
ndbm = None | |
def open(file, flag='r', mode=0o666): | |
"""Open or create database at path given by *file*. | |
Optional argument *flag* can be 'r' (default) 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. | |
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. | |
""" | |
global _defaultmod | |
if _defaultmod is None: | |
for name in _names: | |
try: | |
mod = __import__(name, fromlist=['open']) | |
except ImportError: | |
continue | |
if not _defaultmod: | |
_defaultmod = mod | |
_modules[name] = mod | |
if not _defaultmod: | |
raise ImportError("no dbm clone found; tried %s" % _names) | |
# guess the type of an existing database, if not creating a new one | |
result = whichdb(file) if 'n' not in flag else None | |
if result is None: | |
# db doesn't exist or 'n' flag was specified to create a new db | |
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[0]("db file doesn't exist; " | |
"use 'c' or 'n' flag to create a new db") | |
elif result == "": | |
# db type cannot be determined | |
raise error[0]("db type could not be determined") | |
elif result not in _modules: | |
raise error[0]("db type is {0}, but the module is not " | |
"available".format(result)) | |
else: | |
mod = _modules[result] | |
return mod.open(file, flag, mode) | |
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() | |
f = io.open(filename + ".dir", "rb") | |
f.close() | |
return "dbm.ndbm" | |
except OSError: | |
# 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 OSError: | |
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: | |
pass | |
# See if the file exists, return None if not | |
try: | |
f = io.open(filename, "rb") | |
except OSError: | |
return None | |
with f: | |
# Read the start of the file -- the magic number | |
s16 = f.read(16) | |
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 in (0x13579ace, 0x13579acd, 0x13579acf): | |
return "dbm.gnu" | |
# 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 "" | |
# Unknown | |
return "" | |
if __name__ == "__main__": | |
for filename in sys.argv[1:]: | |
print(whichdb(filename) or "UNKNOWN", filename) | |