Mercurial > p > roundup > code
diff roundup/backends/rdbms_common.py @ 3834:8068eb6c704e
Use """ instead of ''' for multi-line strings...
...(including comments). The christmas-tree (AKA syntax-highlighting)
mode of emacs gets confused by the use of '''.
| author | Erik Forsberg <forsberg@users.sourceforge.net> |
|---|---|
| date | Sun, 01 Apr 2007 18:48:06 +0000 |
| parents | 5ed4c9d30148 |
| children | cf8b716d9ac2 |
line wrap: on
line diff
--- a/roundup/backends/rdbms_common.py Mon Mar 26 06:19:55 2007 +0000 +++ b/roundup/backends/rdbms_common.py Sun Apr 01 18:48:06 2007 +0000 @@ -15,8 +15,8 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -#$Id: rdbms_common.py,v 1.183 2007-02-20 05:30:17 richard Exp $ -''' Relational database (SQL) backend common code. +#$Id: rdbms_common.py,v 1.184 2007-04-01 18:48:06 forsberg Exp $ +""" Relational database (SQL) backend common code. Basics: @@ -42,7 +42,7 @@ that maps to a table. If that information differs from the hyperdb schema, then we update it. We also store in the schema dict a version which allows us to upgrade the database schema when necessary. See upgrade_db(). -''' +""" __docformat__ = 'restructuredtext' # standard python modules @@ -84,8 +84,8 @@ return int(value) def connection_dict(config, dbnamestr=None): - ''' Used by Postgresql and MySQL to detemine the keyword args for - opening the database connection.''' + """ Used by Postgresql and MySQL to detemine the keyword args for + opening the database connection.""" d = { } if dbnamestr: d[dbnamestr] = config.RDBMS_NAME @@ -97,15 +97,15 @@ return d class Database(FileStorage, hyperdb.Database, roundupdb.Database): - ''' Wrapper around an SQL database that presents a hyperdb interface. + """ Wrapper around an SQL database that presents a hyperdb interface. - some functionality is specific to the actual SQL database, hence the sql_* methods that are NotImplemented - we keep a cache of the latest ROW_CACHE_SIZE row fetches. - ''' + """ def __init__(self, config, journaltag=None): - ''' Open the database and load the schema from it. - ''' + """ Open the database and load the schema from it. + """ self.config, self.journaltag = config, journaltag self.dir = config.DATABASE self.classes = {} @@ -139,15 +139,15 @@ return OneTimeKeys(self) def open_connection(self): - ''' Open a connection to the database, creating it if necessary. + """ Open a connection to the database, creating it if necessary. Must call self.load_dbschema() - ''' + """ raise NotImplemented def sql(self, sql, args=None): - ''' Execute the sql with the optional args. - ''' + """ Execute the sql with the optional args. + """ if __debug__: logging.getLogger('hyperdb').debug('SQL %r %r'%(sql, args)) if args: @@ -156,18 +156,18 @@ self.cursor.execute(sql) def sql_fetchone(self): - ''' Fetch a single row. If there's nothing to fetch, return None. - ''' + """ Fetch a single row. If there's nothing to fetch, return None. + """ return self.cursor.fetchone() def sql_fetchall(self): - ''' Fetch all rows. If there's nothing to fetch, return []. - ''' + """ Fetch all rows. If there's nothing to fetch, return []. + """ return self.cursor.fetchall() def sql_stringquote(self, value): - ''' Quote the string so it's safe to put in the 'sql quotes' - ''' + """ Quote the string so it's safe to put in the 'sql quotes' + """ return re.sub("'", "''", str(value)) def init_dbschema(self): @@ -177,8 +177,8 @@ } def load_dbschema(self): - ''' Load the schema definition that the database currently implements - ''' + """ Load the schema definition that the database currently implements + """ self.cursor.execute('select schema from schema') schema = self.cursor.fetchone() if schema: @@ -187,18 +187,18 @@ self.database_schema = {} def save_dbschema(self): - ''' Save the schema definition that the database currently implements - ''' + """ Save the schema definition that the database currently implements + """ s = repr(self.database_schema) self.sql('delete from schema') self.sql('insert into schema values (%s)'%self.arg, (s,)) def post_init(self): - ''' Called once the schema initialisation has finished. + """ Called once the schema initialisation has finished. We should now confirm that the schema defined by our "classes" attribute actually matches the schema in the database. - ''' + """ save = 0 # handle changes in the schema @@ -239,10 +239,10 @@ # of the backen database current_db_version = 4 def upgrade_db(self): - ''' Update the SQL database to reflect changes in the backend code. + """ Update the SQL database to reflect changes in the backend code. Return boolean whether we need to save the schema. - ''' + """ version = self.database_schema.get('version', 1) if version == self.current_db_version: # nothing to do @@ -281,11 +281,11 @@ self.sql('ALTER TABLE %ss ADD %s_value TEXT'%(name, name)) def fix_version_2_tables(self): - '''Default (used by sqlite): NOOP''' + """Default (used by sqlite): NOOP""" pass def _convert_journal_tables(self): - '''Get current journal table contents, drop the table and re-create''' + """Get current journal table contents, drop the table and re-create""" c = self.cursor cols = ','.join('nodeid date tag action params'.split()) for klass in self.classes.values(): @@ -307,8 +307,8 @@ self.cursor.execute(sql, row) def _convert_string_properties(self): - '''Get current Class tables that contain String properties, and - convert the VARCHAR columns to TEXT''' + """Get current Class tables that contain String properties, and + convert the VARCHAR columns to TEXT""" c = self.cursor for klass in self.classes.values(): # slurp and drop @@ -363,11 +363,11 @@ hyperdb.Number : 'REAL', } def determine_columns(self, properties): - ''' Figure the column names and multilink properties from the spec + """ Figure the column names and multilink properties from the spec "properties" is a list of (name, prop) where prop may be an instance of a hyperdb "type" _or_ a string repr of that type. - ''' + """ cols = [ ('_actor', self.hyperdb_to_sql_datatypes[hyperdb.Link]), ('_activity', self.hyperdb_to_sql_datatypes[hyperdb.Date]), @@ -397,11 +397,11 @@ return cols, mls def update_class(self, spec, old_spec, force=0): - ''' Determine the differences between the current spec and the + """ Determine the differences between the current spec and the database version of the spec, and update where necessary. If 'force' is true, update the database anyway. - ''' + """ new_has = spec.properties.has_key new_spec = spec.schema() new_spec[1].sort() @@ -500,8 +500,8 @@ return cols, mls def create_class_table(self, spec): - '''Create the class table for the given Class "spec". Creates the - indexes too.''' + """Create the class table for the given Class "spec". Creates the + indexes too.""" cols, mls = self.determine_all_columns(spec) # create the base table @@ -514,8 +514,8 @@ return cols, mls def create_class_table_indexes(self, spec): - ''' create the class table for the given spec - ''' + """ create the class table for the given spec + """ # create __retired__ index index_sql2 = 'create index _%s_retired_idx on _%s(__retired__)'%( spec.classname, spec.classname) @@ -545,8 +545,8 @@ self.sql(index_sql) def create_class_table_key_index(self, cn, key): - ''' create the class table for the given spec - ''' + """ create the class table for the given spec + """ sql = 'create index _%s_%s_idx on _%s(_%s)'%(cn, key, cn, key) self.sql(sql) @@ -559,15 +559,15 @@ self.sql(sql) def create_journal_table(self, spec): - ''' create the journal table for a class given the spec and + """ create the journal table for a class given the spec and already-determined cols - ''' + """ # journal table cols = ','.join(['%s varchar'%x for x in 'nodeid date tag action params'.split()]) - sql = '''create table %s__journal ( + sql = """create table %s__journal ( nodeid integer, date %s, tag varchar(255), - action varchar(255), params text)''' % (spec.classname, + action varchar(255), params text)""" % (spec.classname, self.hyperdb_to_sql_datatypes[hyperdb.Date]) self.sql(sql) self.create_journal_table_indexes(spec) @@ -586,9 +586,9 @@ self.sql(index_sql) def create_multilink_table(self, spec, ml): - ''' Create a multilink table for the "ml" property of the class + """ Create a multilink table for the "ml" property of the class given by the spec - ''' + """ # create the table sql = 'create table %s_%s (linkid INTEGER, nodeid INTEGER)'%( spec.classname, ml) @@ -619,8 +619,8 @@ self.sql(index_sql) def create_class(self, spec): - ''' Create a database table according to the given spec. - ''' + """ Create a database table according to the given spec. + """ cols, mls = self.create_class_table(spec) self.create_journal_table(spec) @@ -629,10 +629,10 @@ self.create_multilink_table(spec, ml) def drop_class(self, cn, spec): - ''' Drop the given table from the database. + """ Drop the given table from the database. Drop the journal and multilink tables too. - ''' + """ properties = spec[1] # figure the multilinks mls = [] @@ -664,15 +664,15 @@ # Classes # def __getattr__(self, classname): - ''' A convenient way of calling self.getclass(classname). - ''' + """ A convenient way of calling self.getclass(classname). + """ if self.classes.has_key(classname): return self.classes[classname] raise AttributeError, classname def addclass(self, cl): - ''' Add a Class to the hyperdatabase. - ''' + """ Add a Class to the hyperdatabase. + """ cn = cl.classname if self.classes.has_key(cn): raise ValueError, cn @@ -687,28 +687,28 @@ description="User is allowed to access "+cn) def getclasses(self): - ''' Return a list of the names of all existing classes. - ''' + """ Return a list of the names of all existing classes. + """ l = self.classes.keys() l.sort() return l def getclass(self, classname): - '''Get the Class object representing a particular class. + """Get the Class object representing a particular class. If 'classname' is not a valid class name, a KeyError is raised. - ''' + """ try: return self.classes[classname] except KeyError: raise KeyError, 'There is no class called "%s"'%classname def clear(self): - '''Delete all database contents. + """Delete all database contents. Note: I don't commit here, which is different behaviour to the "nuke from orbit" behaviour in the dbs. - ''' + """ logging.getLogger('hyperdb').info('clear') for cn in self.classes.keys(): sql = 'delete from _%s'%cn @@ -730,8 +730,8 @@ hyperdb.Multilink : lambda x: x, # used in journal marshalling } def addnode(self, classname, nodeid, node): - ''' Add the specified node to its class's db. - ''' + """ Add the specified node to its class's db. + """ if __debug__: logging.getLogger('hyperdb').debug('addnode %s%s %r'%(classname, nodeid, node)) @@ -805,8 +805,8 @@ self.sql(sql, (entry, nodeid)) def setnode(self, classname, nodeid, values, multilink_changes={}): - ''' Change the specified node. - ''' + """ Change the specified node. + """ if __debug__: logging.getLogger('hyperdb').debug('setnode %s%s %r' % (classname, nodeid, values)) @@ -919,8 +919,8 @@ hyperdb.Multilink : lambda x: x, # used in journal marshalling } def getnode(self, classname, nodeid): - ''' Get a node from the database. - ''' + """ Get a node from the database. + """ # see if we have this node cached key = (classname, nodeid) if self.cache.has_key(key): @@ -990,9 +990,9 @@ return node def destroynode(self, classname, nodeid): - '''Remove a node from the database. Called exclusively by the + """Remove a node from the database. Called exclusively by the destroy() method on Class. - ''' + """ logging.getLogger('hyperdb').info('destroynode %s%s'%(classname, nodeid)) # make sure the node exists @@ -1025,28 +1025,28 @@ self.sql(sql, (nodeid,)) def hasnode(self, classname, nodeid): - ''' Determine if the database has a given node. - ''' + """ Determine if the database has a given node. + """ sql = 'select count(*) from _%s where id=%s'%(classname, self.arg) self.sql(sql, (nodeid,)) return int(self.cursor.fetchone()[0]) def countnodes(self, classname): - ''' Count the number of nodes that exist for a particular Class. - ''' + """ Count the number of nodes that exist for a particular Class. + """ sql = 'select count(*) from _%s'%classname self.sql(sql) return self.cursor.fetchone()[0] def addjournal(self, classname, nodeid, action, params, creator=None, creation=None): - ''' Journal the Action + """ Journal the Action 'action' may be: 'create' or 'set' -- 'params' is a dictionary of property values 'link' or 'unlink' -- 'params' is (classname, nodeid, propname) 'retire' -- 'params' is None - ''' + """ # handle supply of the special journalling parameters (usually # supplied on importing an existing database) if creator: @@ -1078,7 +1078,7 @@ journaltag, action, params) def setjournal(self, classname, nodeid, journal): - '''Set the journal to the "journal" list.''' + """Set the journal to the "journal" list.""" # clear out any existing entries self.sql('delete from %s__journal where nodeid=%s'%(classname, self.arg), (nodeid,)) @@ -1102,8 +1102,8 @@ journaltag, action, params) def _journal_marshal(self, params, classname): - '''Convert the journal params values into safely repr'able and - eval'able values.''' + """Convert the journal params values into safely repr'able and + eval'able values.""" properties = self.getclass(classname).getprops() for param, value in params.items(): if not value: @@ -1120,8 +1120,8 @@ params[param] = cvt(value) def getjournal(self, classname, nodeid): - ''' get the journal for id - ''' + """ get the journal for id + """ # make sure the node exists if not self.hasnode(classname, nodeid): raise IndexError, '%s has no node %s'%(classname, nodeid) @@ -1158,8 +1158,8 @@ def save_journal(self, classname, cols, nodeid, journaldate, journaltag, action, params): - ''' Save the journal entry to the database - ''' + """ Save the journal entry to the database + """ entry = (nodeid, journaldate, journaltag, action, params) # do the insert @@ -1169,8 +1169,8 @@ self.sql(sql, entry) def load_journal(self, classname, cols, nodeid): - ''' Load the journal from the database - ''' + """ Load the journal from the database + """ # now get the journal entries sql = 'select %s from %s__journal where nodeid=%s order by date'%( cols, classname, self.arg) @@ -1178,8 +1178,8 @@ return self.cursor.fetchall() def pack(self, pack_before): - ''' Delete all journal entries except "create" before 'pack_before'. - ''' + """ Delete all journal entries except "create" before 'pack_before'. + """ date_stamp = self.hyperdb_to_sql_value[Date](pack_before) # do the delete @@ -1189,8 +1189,8 @@ self.sql(sql, (date_stamp,)) def sql_commit(self, fail_ok=False): - ''' Actually commit to the database. - ''' + """ Actually commit to the database. + """ logging.getLogger('hyperdb').info('commit') self.conn.commit() @@ -1199,7 +1199,7 @@ self.cursor = self.conn.cursor() def commit(self, fail_ok=False): - ''' Commit the current transactions. + """ Commit the current transactions. Save all data changed since the database was opened or since the last commit() or rollback(). @@ -1209,7 +1209,7 @@ database. We don't care if there's a concurrency issue there. The only backend this seems to affect is postgres. - ''' + """ # commit the database self.sql_commit(fail_ok) @@ -1227,11 +1227,11 @@ self.conn.rollback() def rollback(self): - ''' Reverse all actions from the current transaction. + """ Reverse all actions from the current transaction. Undo all the changes made since the database was opened or the last commit() or rollback() was performed. - ''' + """ logging.getLogger('hyperdb').info('rollback') self.sql_rollback() @@ -1251,8 +1251,8 @@ self.conn.close() def close(self): - ''' Close off the connection. - ''' + """ Close off the connection. + """ self.indexer.close() self.sql_close() @@ -1260,31 +1260,31 @@ # The base Class class # class Class(hyperdb.Class): - ''' The handle to a particular class of nodes in a hyperdatabase. + """ The handle to a particular class of nodes in a hyperdatabase. All methods except __repr__ and getnode must be implemented by a concrete backend Class. - ''' + """ def schema(self): - ''' A dumpable version of the schema that we can store in the + """ A dumpable version of the schema that we can store in the database - ''' + """ return (self.key, [(x, repr(y)) for x,y in self.properties.items()]) def enableJournalling(self): - '''Turn journalling on for this class - ''' + """Turn journalling on for this class + """ self.do_journal = 1 def disableJournalling(self): - '''Turn journalling off for this class - ''' + """Turn journalling off for this class + """ self.do_journal = 0 # Editing nodes: def create(self, **propvalues): - ''' Create a new node of this class and return its id. + """ Create a new node of this class and return its id. The keyword arguments in 'propvalues' map property names to values. @@ -1299,15 +1299,15 @@ If an id in a link or multilink property does not refer to a valid node, an IndexError is raised. - ''' + """ self.fireAuditors('create', None, propvalues) newid = self.create_inner(**propvalues) self.fireReactors('create', newid, None) return newid def create_inner(self, **propvalues): - ''' Called by create, in-between the audit and react calls. - ''' + """ Called by create, in-between the audit and react calls. + """ if propvalues.has_key('id'): raise KeyError, '"id" is reserved' @@ -1445,14 +1445,14 @@ return str(newid) def get(self, nodeid, propname, default=_marker, cache=1): - '''Get the value of a property on an existing node of this class. + """Get the value of a property on an existing node of this class. 'nodeid' must be the id of an existing node of this class or an IndexError is raised. 'propname' must be the name of a property of this class or a KeyError is raised. 'cache' exists for backwards compatibility, and is not used. - ''' + """ if propname == 'id': return nodeid @@ -1501,7 +1501,7 @@ return d[propname] def set(self, nodeid, **propvalues): - '''Modify a property on an existing node of this class. + """Modify a property on an existing node of this class. 'nodeid' must be the id of an existing node of this class or an IndexError is raised. @@ -1517,7 +1517,7 @@ If the value of a Link or Multilink property contains an invalid node id, a ValueError is raised. - ''' + """ self.fireAuditors('set', nodeid, propvalues) oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid)) propvalues = self.set_inner(nodeid, **propvalues) @@ -1525,8 +1525,8 @@ return propvalues def set_inner(self, nodeid, **propvalues): - ''' Called by set, in-between the audit and react calls. - ''' + """ Called by set, in-between the audit and react calls. + """ if not propvalues: return propvalues @@ -1734,14 +1734,14 @@ return propvalues def retire(self, nodeid): - '''Retire a node. + """Retire a node. The properties on the node remain available from the get() method, and the node's id is never reused. Retired nodes are not returned by the find(), list(), or lookup() methods, and other nodes may reuse the values of their key properties. - ''' + """ if self.db.journaltag is None: raise DatabaseError, 'Database open read-only' @@ -1758,10 +1758,10 @@ self.fireReactors('retire', nodeid, None) def restore(self, nodeid): - '''Restore a retired node. + """Restore a retired node. Make node available for all operations like it was before retirement. - ''' + """ if self.db.journaltag is None: raise DatabaseError, 'Database open read-only' @@ -1788,15 +1788,15 @@ self.fireReactors('restore', nodeid, None) def is_retired(self, nodeid): - '''Return true if the node is rerired - ''' + """Return true if the node is rerired + """ sql = 'select __retired__ from _%s where id=%s'%(self.classname, self.db.arg) self.db.sql(sql, (nodeid,)) return int(self.db.sql_fetchone()[0]) def destroy(self, nodeid): - '''Destroy a node. + """Destroy a node. WARNING: this method should never be used except in extremely rare situations where there could never be links to the node being @@ -1814,13 +1814,13 @@ The node is completely removed from the hyperdb, including all journal entries. It will no longer be available, and will generally break code if there are any references to the node. - ''' + """ if self.db.journaltag is None: raise DatabaseError, 'Database open read-only' self.db.destroynode(self.classname, nodeid) def history(self, nodeid): - '''Retrieve the journal of edits on a particular node. + """Retrieve the journal of edits on a particular node. 'nodeid' must be the id of an existing node of this class or an IndexError is raised. @@ -1831,41 +1831,41 @@ 'date' is a Timestamp object specifying the time of the change and 'tag' is the journaltag specified when the database was opened. - ''' + """ if not self.do_journal: raise ValueError, 'Journalling is disabled for this class' return self.db.getjournal(self.classname, nodeid) # Locating nodes: def hasnode(self, nodeid): - '''Determine if the given nodeid actually exists - ''' + """Determine if the given nodeid actually exists + """ return self.db.hasnode(self.classname, nodeid) def setkey(self, propname): - '''Select a String property of this class to be the key property. + """Select a String property of this class to be the key property. 'propname' must be the name of a String property of this class or None, or a TypeError is raised. The values of the key property on all existing nodes must be unique or a ValueError is raised. - ''' + """ prop = self.getprops()[propname] if not isinstance(prop, String): raise TypeError, 'key properties must be String' self.key = propname def getkey(self): - '''Return the name of the key property for this class or None.''' + """Return the name of the key property for this class or None.""" return self.key def lookup(self, keyvalue): - '''Locate a particular node by its key property and return its id. + """Locate a particular node by its key property and return its id. If this class has no key property, a TypeError is raised. If the 'keyvalue' matches one of the values for the key property among the nodes in this class, the matching node's id is returned; otherwise a KeyError is raised. - ''' + """ if not self.key: raise TypeError, 'No key property set for class %s'%self.classname @@ -1886,7 +1886,7 @@ return str(row[0]) def find(self, **propspec): - '''Get the ids of nodes in this class which link to the given nodes. + """Get the ids of nodes in this class which link to the given nodes. 'propspec' consists of keyword args propname=nodeid or propname={nodeid:1, } @@ -1899,7 +1899,7 @@ db.issue.find(messages='1') db.issue.find(messages={'1':1,'3':1}, files={'7':1}) - ''' + """ # shortcut if not propspec: return [] @@ -1939,8 +1939,8 @@ where.append('(' + s +')') if where: allvalues = (1, ) + allvalues - sql.append('''select id from _%s where __retired__ <> %s - and %s'''%(self.classname, a, ' and '.join(where))) + sql.append("""select id from _%s where __retired__ <> %s + and %s"""%(self.classname, a, ' and '.join(where))) # now multilinks for prop, values in propspec: @@ -1956,8 +1956,8 @@ allvalues += tuple(values.keys()) s = ','.join([a]*len(values)) tn = '%s_%s'%(self.classname, prop) - sql.append('''select id from _%s, %s where __retired__ <> %s - and id = %s.nodeid and %s.linkid in (%s)'''%(self.classname, + sql.append("""select id from _%s, %s where __retired__ <> %s + and id = %s.nodeid and %s.linkid in (%s)"""%(self.classname, tn, a, tn, tn, s)) if not sql: @@ -1969,13 +1969,13 @@ return l def stringFind(self, **requirements): - '''Locate a particular node by matching a set of its String + """Locate a particular node by matching a set of its String properties in a caseless search. If the property is not a String property, a TypeError is raised. The return is a list of the id of all nodes that match. - ''' + """ where = [] args = [] for propname in requirements.keys(): @@ -1996,16 +1996,16 @@ return l def list(self): - ''' Return a list of the ids of the active nodes in this class. - ''' + """ Return a list of the ids of the active nodes in this class. + """ return self.getnodeids(retired=0) def getnodeids(self, retired=None): - ''' Retrieve all the ids of the nodes for a particular Class. + """ Retrieve all the ids of the nodes for a particular Class. Set retired=None to get all nodes. Otherwise it'll get all the retired or non-retired nodes, depending on the flag. - ''' + """ # flip the sense of the 'retired' flag if we don't want all of them if retired is not None: if retired: @@ -2023,11 +2023,11 @@ return ids def _subselect(self, classname, multilink_table): - '''Create a subselect. This is factored out because some + """Create a subselect. This is factored out because some databases (hmm only one, so far) doesn't support subselects look for "I can't believe it's not a toy RDBMS" in the mysql backend. - ''' + """ return '_%s.id not in (select nodeid from %s)'%(classname, multilink_table) @@ -2040,7 +2040,7 @@ order_by_null_values = None def filter(self, search_matches, filterspec, sort=[], group=[]): - '''Return a list of the ids of the active nodes in this class that + """Return a list of the ids of the active nodes in this class that match the 'filter' spec, sorted by the group spec and then the sort spec @@ -2058,7 +2058,7 @@ 1. String properties must match all elements in the list, and 2. Other properties must match any of the elements in the list. - ''' + """ # we can't match anything if search_matches is empty if search_matches == {}: return [] @@ -2326,14 +2326,14 @@ return l def filter_sql(self, sql): - '''Return a list of the ids of the items in this class that match + """Return a list of the ids of the items in this class that match the SQL provided. The SQL is a complete "select" statement. The SQL select must include the item id as the first column. This function DOES NOT filter out retired items, add on a where clause "__retired__ <> 1" if you don't want retired nodes. - ''' + """ if __debug__: start_t = time.time() @@ -2345,20 +2345,20 @@ return l def count(self): - '''Get the number of nodes in this class. + """Get the number of nodes in this class. If the returned integer is 'numnodes', the ids of all the nodes in this class run from 1 to numnodes, and numnodes+1 will be the id of the next node to be created in this class. - ''' + """ return self.db.countnodes(self.classname) # Manipulating properties: def getprops(self, protected=1): - '''Return a dictionary mapping property names to property objects. + """Return a dictionary mapping property names to property objects. If the "protected" flag is true, we include protected properties - those which may not be modified. - ''' + """ d = self.properties.copy() if protected: d['id'] = String() @@ -2369,21 +2369,21 @@ return d def addprop(self, **properties): - '''Add properties to this class. + """Add properties to this class. The keyword arguments in 'properties' must map names to property objects, or a TypeError is raised. None of the keys in 'properties' may collide with the names of existing properties, or a ValueError is raised before any properties have been added. - ''' + """ for key in properties.keys(): if self.properties.has_key(key): raise ValueError, key self.properties.update(properties) def index(self, nodeid): - '''Add (or refresh) the node to search indexes - ''' + """Add (or refresh) the node to search indexes + """ # find all the String properties that have indexme for prop, propclass in self.getprops().items(): if isinstance(propclass, String) and propclass.indexme: @@ -2394,9 +2394,9 @@ # import / export support # def export_list(self, propnames, nodeid): - ''' Export a node - generate a list of CSV-able data in the order + """ Export a node - generate a list of CSV-able data in the order specified by propnames for the given node. - ''' + """ properties = self.getprops() l = [] for prop in propnames: @@ -2416,13 +2416,13 @@ return l def import_list(self, propnames, proplist): - ''' Import a node - all information including "id" is present and + """ Import a node - all information including "id" is present and should not be sanity checked. Triggers are not triggered. The journal should be initialised using the "creator" and "created" information. Return the nodeid of the node imported. - ''' + """ if self.db.journaltag is None: raise DatabaseError, 'Database open read-only' properties = self.getprops() @@ -2497,13 +2497,13 @@ return newid def export_journals(self): - '''Export a class's journal - generate a list of lists of + """Export a class's journal - generate a list of lists of CSV-able data: nodeid, date, user, action, params No heading here - the columns are fixed. - ''' + """ properties = self.getprops() r = [] for nodeid in self.getnodeids(): @@ -2533,9 +2533,9 @@ return r def import_journals(self, entries): - '''Import a class's journal. - - Uses setjournal() to set the journal for each item.''' + """Import a class's journal. + + Uses setjournal() to set the journal for each item.""" properties = self.getprops() d = {} for l in entries: @@ -2562,18 +2562,18 @@ self.db.setjournal(self.classname, nodeid, l) class FileClass(hyperdb.FileClass, Class): - '''This class defines a large chunk of data. To support this, it has a + """This class defines a large chunk of data. To support this, it has a mandatory String property "content" which is typically saved off externally to the hyperdb. The default MIME type of this data is defined by the "default_mime_type" class attribute, which may be overridden by each node if the class defines a "type" String property. - ''' + """ def __init__(self, db, classname, **properties): - '''The newly-created class automatically includes the "content" + """The newly-created class automatically includes the "content" and "type" properties. - ''' + """ if not properties.has_key('content'): properties['content'] = hyperdb.String(indexme='yes') if not properties.has_key('type'): @@ -2581,8 +2581,8 @@ Class.__init__(self, db, classname, **properties) def create(self, **propvalues): - ''' snaffle the file propvalue and store in a file - ''' + """ snaffle the file propvalue and store in a file + """ # we need to fire the auditors now, or the content property won't # be in propvalues for the auditors to play with self.fireAuditors('create', None, propvalues) @@ -2610,10 +2610,10 @@ return newid def get(self, nodeid, propname, default=_marker, cache=1): - ''' Trap the content propname and get it from the file + """ Trap the content propname and get it from the file 'cache' exists for backwards compatibility, and is not used. - ''' + """ poss_msg = 'Possibly a access right configuration problem.' if propname == 'content': try: @@ -2628,20 +2628,20 @@ return Class.get(self, nodeid, propname) def getprops(self, protected=1): - '''In addition to the actual properties on the node, these methods + """In addition to the actual properties on the node, these methods provide the "content" property. If the "protected" flag is true, we include protected properties - those which may not be modified. Note that the content prop is indexed separately, hence no indexme. - ''' + """ d = Class.getprops(self, protected=protected).copy() d['content'] = hyperdb.String() return d def set(self, itemid, **propvalues): - ''' Snarf the "content" propvalue and update it in a file - ''' + """ Snarf the "content" propvalue and update it in a file + """ self.fireAuditors('set', itemid, propvalues) oldvalues = copy.deepcopy(self.db.getnode(self.classname, itemid)) @@ -2669,10 +2669,10 @@ return propvalues def index(self, nodeid): - ''' Add (or refresh) the node to search indexes. + """ Add (or refresh) the node to search indexes. Use the content-type property for the content property. - ''' + """ # find all the String properties that have indexme for prop, propclass in self.getprops().items(): if prop == 'content' and propclass.indexme: @@ -2692,12 +2692,12 @@ class IssueClass(Class, roundupdb.IssueClass): # Overridden methods: def __init__(self, db, classname, **properties): - '''The newly-created class automatically includes the "messages", + """The newly-created class automatically includes the "messages", "files", "nosy", and "superseder" properties. If the 'properties' dictionary attempts to specify any of these properties or a "creation", "creator", "activity" or "actor" property, a ValueError is raised. - ''' + """ if not properties.has_key('title'): properties['title'] = hyperdb.String(indexme='yes') if not properties.has_key('messages'):
