# # $Id$ # # This file contains class definitions and interface to the database # itself while defining objects that the database will return. # import rcs, os # # The most generic exception that this library will raise. # class PVVDBError(Exception): pass # # PVVMember is the class that represents a record in the database. # Its interface is like a dict with a few additional methods to # talk to the database itself. # class PVVMember(dict): def __init__(self, data, dbref): # Populate the object with the fields and values provided in the # `data' object. `dbref' is a pointer to the PVVDB instance that # created the PVVMember instance. dict.__init__(self, data) self.dbref = dbref def save(self): # Synchronise the object to disk via the database interface. self.dbref.save(self) def load(self): # Update the object with the data on disk. o = self.dbref.load(data) self.update(o) def is_saved(self): # Checks whether the objects need to be saved or loaded. pass # # PVVDB is the class that interfaces to the directory structure and files. # It also verifies the data read from the files and written to it. # class PVVDB: def __init__(self, base): # Find the record definition file, read it, verify it and # set the legal set of fields. self.base = base # base database directory self.locks = [] # list of activated locks self.rcs = rcs.RCS() # an RCS interface function self.format = self.__format( os.path.join(self.base, ".format")) def __del__(self): # clean up locks and such before being deleted. for username in self.locks: self.rcs.unlock(username) def __filename(self, username): # transform the username into a tuple with the full name of # the databasefile and the latest revision number. return self.rcs.rcsname(os.path.join(self.base, username)) def __format(self, file_path): # parses and verifies the .format files for a database. fh = file(file_path) fmt = {} for line in fh: if line[0] == "#": continue name, value = line.strip().split(":", 1) value = value.split("#", 1)[0].strip() if not value in ['scalar', 'list']: raise PVVDBError, "%s not legal .format datatype." % value fmt[name] = value return fmt def lock(self, username, pedantic=False): # Lock the record. if username in self.locks and pedantic: raise PVVDBError, "%s already locked." % `username` self.rcs.lock(self.__filename(username)) self.locks.append(username) def unlock(self, username, pedantic=False): # Unlock the record. if not username in self.locks and pedantic: raise PVVDBError, "%s not locked." % `username` self.rcs.unlock(self.__filename(username)) self.locks.remove(username) def load(self, username, lock=False): # Read the record from the database. Will do an automatic lock # if `lock' is True. cwd = os.getcwd() os.chdir(self.base) rv = self.rcs.checkout(username + ",v") userfile = file(username) os.chdir(cwd) member = {} for line in userfile: key, value = line.strip().split(": ", 1) if not key in self.format: raise PVVDBError, "%s not a legal attribute." % `key` v = None # handle the list datatype if self.format[key] == 'list': v = [] # XXX: needs to respect escaping of field separators for e in value.split(", "): v.append(e) elif self.format[key] == 'scalar': v = value else: # we should not be here raise PVVDBError, "%s not legal datatype." % `self.format[key]` member[key] = v return PVVMember(member, self) def save(self, username, comment="", lock=False): # Write the record to the database. Will automatically unlock # the record if `lock' is True. A `comment' can be supplied that # explains the changes made. pass # # Regular interface to the database. # pathmap = {} def connect(path): # returns a PVVDB instance to that path. If asked for another # PVVDB instance with the same path as earlier, it will return # the same object. if not path in pathmap: pathmap[path] = PVVDB(path) return pathmap[path] def disconnect(dbo): # do not do anything, really. PVVDB objects will clean up # automatically when deleted. is here for future database # systems. pass