dhm-0.6/0000755000175000017500000000000010223252112013251 5ustar wichertwichert00000000000000dhm-0.6/src/0000755000175000017500000000000010223252112014040 5ustar wichertwichert00000000000000dhm-0.6/src/sql/0000755000175000017500000000000010223252112014637 5ustar wichertwichert00000000000000dhm-0.6/src/sql/__init__.py0000644000175000017500000000176610223251756016777 0ustar wichertwichert00000000000000# __init__.py # # Copyright 2005 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """DeepHackMode python SQL utilities """ __docformat__ = "epytext en" __author__ = "Wichert Akkerman " __copyright__ = "Copyright 2001,2002,2003,2004,2005 Wichert Akkerman" __all__ = [ "dict", "object", "persistence", "row", "wrap" ] dhm-0.6/src/sql/dict.py0000644000175000017500000001655410223251756016164 0ustar wichertwichert00000000000000# sqldict.py # # Copyright 2003 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """Basic SQL dictionary interface Often you want to treat a SQL table as a dictionary. This module implements the SQLTable class which makes it easy to do that. It expects to deal with tables with unqiue keys (combined keys are supported) which is used to index the dictionary. """ __docformat__ = "epytext en" import copy, UserDict class SQLLookup(UserDict.UserDict): """SQL lookup table. This class implements a trivial lookup table that uses a SQL table as data source. It loads the content of the table during construction, so only use this for situations where you do frequent lookups or the table size is small. It makes a few assumptions on the SQL table: - it contains only two columns: a key and a value column - duplicate key entries are not detected. It is highly recommended to use a unique constraint on the key column. @cvar table: SQL table which contains our data @type table: string """ def __init__(self, dbc, table, **kwargs): """Constructor. Initializing of SQLLookup is done using a Connection class and specifying the table containing the data. @param dbc: database connection @type dbc: sqlwrap.Connection class @param table: SQL table with data @type table: string """ UserDict.UserDict.__init__(self) self.table=table self.dbc=dbc self._Read() def _Read(self): res=self.dbc.query("SELECT * FROM %s;" % self.table); for data in res: (key,value)=data[0:2] self.data[key]=value class DictIterator: def __init__(self, db): (cmd,values)=db._genwhere() self.cursor=db.dbc.execute("SELECT DISTINCT %s FROM %s %s;" % (",".join(db.keycols), db.table, cmd), values, "format") def __del__(self): self.cursor.close() def __iter__(self): return self def next(self): res=self.cursor.fetchone() if res==None: raise StopIteration if len(res)==1: return res[0] else: return tuple(res) class SQLDict: """SQL dictionary This class provides a dictionary interface to a SQL table. This is an abstract class; to use it derive your own class and make a three simple changes: 1. set the 'table' class variable to the name of the SQL table containing the data 2. set the 'keycols' class variable to the list of the key columns 3. implement the __getitem__ method @cvar table: SQL table which contains our data @type table: string @cvar keycols: Columns which make up the unique key @type keycols: string """ table = None keycols = [] def __init__(self, dbc, table=None): """Constructor. @param dbc: database connection @type dbc: sqlwrap.Connection class @param table: SQL table with data @type table: string """ if table: self.table=table self.dbc=dbc def _GetKeys(self, args, kwargs={}): """Build a key dictionary Build a dictionary containing the keys based on both normal and keyword arguments. """ if isinstance(args[0], tuple): args=args[0] assert(len(args)<=len(self.keycols)) found={} for i in range(len(args)): found[self.keycols[i]]=args[i] for (key,value) in kwargs.items(): if not key in self.keycols: raise KeyError, "Illegal key" found[key]=value if len(found)!=len(self.keycols): raise KeyError, "Not all keys supplied" return found def _genwhere(self, keys=None): """Generate data for a WHERE clause to filter for a keyset. This function generates data which can be used to generate a WHERE clause to uniquely identify this object in a table. It returns a tuple containing a string with the SQL command and a tuple with the data values. This can be fed to the execute method for a database connection using the format paramstyle. @return: (command,values) tuple """ if not keys: return ("", (())) data=keys.items() keys=map(lambda x: x[0], data) values=tuple(map(lambda x: x[1], data)) return ("WHERE " + (" AND ".join(map(lambda x: x+"=%s", keys))), values) def __getitem__(self, key): raise NotImplementedError def __setitem__(self, key, item): raise NotImplementedError def __len__(self): (cmd,values)=self._genwhere() return int(self.dbc.query( "SELECT COUNT(*) FROM %s %s;" % (self.table, cmd), values, "format")[0][0]) def __iter__(self): return DictIterator(self) def __contains__(self, key): return self.has_key(key) def has_key(self, *arg, **kwargs): (cmd,values)=self._genwhere(self._GetKeys(arg, kwargs)) res=self.dbc.query( "SELECT COUNT(*) FROM %s %s;" % (self.table, cmd), values, "format") return res[0][0]>0 def keys(self): (cmd,values)=self._genwhere() keys=self.dbc.query("SELECT DISTINCT %s FROM %s %s;" % (",".join(self.keycols), self.table, cmd), values, "format") if len(self.keycols)>1: return keys else: return map(lambda x: x[0], keys) def values(self): keys=self.keys() return map(lambda x,s=self: s[x], keys) def clear(self): (cmd,values)=self._genwhere() self.dbc.execute("DELETE FROM %s %s;" % (self.table, cmd), values, "format") def __delitem__(self, *arg, **kwargs): (cmd,values)=self._genwhere(self._GetKeys(arg, kwargs)) self.dbc.execute("DELETE FROM %s %s;" % (self.table, cmd), values, "format") class SQLFilterDict(SQLDict): """SQL filtering dictionary This class works exactly like SQLDict but adds the ability to only work on a subset of rows in a SQL table This class provides a dictionary interface to a SQL table. This is an abstract class; to use it derive your own class and make a three simple changes: 1. set the 'table' class variable to the name of the SQL table containing the data 2. set the 'keycols' class variable to the list of the key columns 3. implement the __getitem__ method @cvar table: SQL table which contains our data @type table: string @cvar keycols: Columns which make up the unique key @type keycols: string """ def __init__(self, dbc, table=None, **kwargs): SQLDict.__init__(self, dbc=dbc, table=table) self.filter=copy.copy(kwargs) def _genwhere(self, keys={}): """Generate data for a WHERE clause to filter for a keyset. This function generates data which can be used to generate a WHERE clause to uniquely identify this object in a table. It returns a tuple containing a string with the SQL command and a tuple with the data values. This can be fed to the execute method for a database connection using the format paramstyle. @return: (command,values) tuple """ if not (keys or self.filter): return ("", (())) columns=copy.copy(self.filter.keys()) values=copy.copy(self.filter.values()) columns.extend(keys.keys()) values.extend(keys.values()) return (("WHERE " + " AND ".join(map(lambda x: x+"=%s", columns))), tuple(values)) dhm-0.6/src/sql/object.py0000644000175000017500000001276110223251756016503 0ustar wichertwichert00000000000000# SQLObject.py # # Copyright 2001, 2002,2003 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """SQL object helper This module implements SQLObject, an convenience class that allows one to use data stored in a SQL database as if it was a standard python mapping type. """ __docformat__ = "epytext en" import UserDict class SQLObjectException(Exception): """SQLObject exception class @ivar reason: reason for raising this exception @type reason: string """ def __init__(self, reason): """Constructor. @param reason: reason for raising this exception @type reason: string """ self.reason=reason def __str__(self): return self.reason class SQLObject(UserDict.UserDict): """SQL object A SQL object acts like a dictionary representing a row in a SQL table. It keeps track of changes made (and will not allow changes for columns that do not exist) and can efficiently update the data in the SQL database when requested. @cvar table: name of SQL table containing data for this object @type table: string @cvar keys: list of keys uniquely identifying an object @type keys: list of strings @cvar attrs: list of columns @type attrs: list of strings """ table = "" keys = [ ] attrs = [ ] def __init__(self, dbc=None, **kwargs): """Constructor Extra keyword parameters are used to set initial valus for columns/attributes. @param dbc: database connection @type dbc: sqlwrap.Connection class """ UserDict.UserDict.__init__(self) self.changes=[] cnonkey=0 ckey=0 for (key,value) in kwargs.items(): if key in self.keys: ckey+=1 else: cnonkey+=1 self[key]=value for a in self.attrs: if not self.has_key(a): self[a]=None if dbc: self.dbc=dbc if ckey==len(self.keys) and not cnonkey: self.retrieve(dbc) def __setitem__(self, key, item): if key in self.keys and self.data.has_key(key) and self.data[key]: raise SQLObjectException, "cannot change key" self.data[key]=item if not key in self.changes: self.changes.append(key) def clear(self): """Clear all data in this object. This method clear all data stored in a class instance. This can be used to recycle an instance so the cost of creating a new instance can be avoided. """ UserDict.UserDict.clear(self) self.changes=[] def _genwhere(self): """Generate data for a WHERE clause to identify this object. This method generates data which can be used to generate a WHERE clause to uniquely identify this object in a table. It returns a tuple containing a string with the SQL command and a tuple with the data values. This can be fed to the execute method for a database connection using the format paramstyle. @return: (command,values) tuple """ cmd=" AND ".join(map(lambda x: x+"=%s", self.keys)) values=map(lambda x,data=self.data: data[x], self.keys) return (cmd,tuple(values)) def retrieve(self, dbc=None): """Retrieve object from the database. It is possible to retrieve the object from another database by specifying a different database connection. @param dbc: database connection to save @type dbc: sqlwrap.Connection class """ if not dbc: dbc=self.dbc for k in self.keys: if not self.data.has_key(k): raise SQLObjectException, "Not all keys set" (cmd,values)=self._genwhere() cursor=dbc.execute("SELECT * FROM %s WHERE %s;" % (self.table, cmd), values, "format") res=cursor.fetchone() if not res: cursor.close() raise KeyError self.changes=[] for i in range(len(cursor.description)): self.data[cursor.description[i][0]]=res[i] cursor.close() def _sql_insert(self, dbc): attrs=[] vals=[] for a in self.attrs: if not (self.data.has_key(a) and self.data[a]!=None): continue attrs.append(a) vals.append(self.data[a]) dbc.execute("INSERT INTO %s (%s) VALUES (%s);" % (self.table, ", ".join(attrs), ",".join(["%s"]*len(vals))), vals, "format") def _sql_update(self, dbc): set=", ".join(map(lambda x: x+"=%s", self.changes)) data=tuple(map(lambda x,data=self.data: data[x], self.changes)) (cmd,values)=self._genwhere() dbc.execute("UPDATE %s SET %s WHERE %s;" % (self.table, set, cmd), data+values, "format") def update(self, dbc=None): """Commit changes to the database. If the key (or one of the keys) has been changed a new row will be inserted in the database. The old row will NOT be removed though, this has to be done manually. It is possible to store the object in another database by specifying a different database connection. @param dbc: database connection to save @type dbc: sqlwrap.Connection class """ if not dbc: dbc=self.dbc if not self.changes: return kchange=0 for k in self.keys: if k in self.changes: kchange+=1 if kchange or (dbc and dbc!=self.dbc): self._sql_insert(dbc) else: self._sql_update(dbc) self.changes=[] dhm-0.6/src/sql/persistence.py0000644000175000017500000000731010223251756017553 0ustar wichertwichert00000000000000# persistence.py # # Copyright 2004,2005 Wichert Akkerman class SessionObject(object): def __init__(self, dbc, table, session, object): self._dbc=dbc self._table=table self._session=session self._object=object data=dbc.query("SELECT key, value FROM %s WHERE session=%%s AND object=%%s" % self._table, (self._session, self._object), "format") for (key,value) in data: super(SessionObject, self).__setattr__(key, value) def __setattr__(self, name, value): if not name.startswith("_"): if hasattr(self, name): cmd="UPDATE %s SET value=%%s WHERE session=%%s AND object=%%s AND key=%%s" else: cmd="INSERT INTO %s (value,session,object,key) VALUES (%%s,%%s,%%s,%%s)" self._dbc.execute(cmd % self._table, (value, self._session, self._object, name), "format") super(SessionObject, self).__setattr__(name, value) def __delattr__(self, name): super(SessionObject, self).__delattr__(name) self._dbc.execute("DELETE FROM %s WHERE session=%%s AND object=%%s AND key=%%s" % self._table, (self._session, self._object, name), "format") class Session: def __init__(self, dbc, table, session): self.dbc=dbc self.table=table self.session=session def keys(self): return [x[0] for x in self.dbc.query("SELECT DISTINCT object FROM %s WHERE session=%%s" % self.table, (self.session,), "format")] def values(self): return [SessionObject(self.dbc, self.table, self.session, x) for x in self.keys()] def __delitem__(self, key): if not self.has_key(key): raise KeyError, "No sessionobject '%s' known" % key self.dbc.execute("DELETE FROM %s WHERE session=%%s AND object=%%s" % self.table, (self.session, key), "format") def __getitem__(self, key): if not self.has_key(key): raise KeyError, "No sessionobject '%s' known" % key return SessionObject(self.dbc, self.table, self.session, key) def __len__(self): return int(self.dbc.query("SELECT COUNT(DISTINCT object) FROM %s WHERE session=%%s" % self.table, (self.session,), "format")[0][0]) def __contains__(self, key): return self.has_key(key) def has_key(self, key): count=self.dbc.query("SELECT COUNT(DISTINCT object) FROM %s WHERE session=%%s AND object=%%s" % self.table, (self.session, key), "format")[0][0] return count!=0 def CreateVariable(self, key): return SessionObject(self.dbc, self.table, self.session, key) class SesDbIterator: def __init__(self, db): self.cursor=db.dbc.execute("SELECT DISTINCT session FROM %s" % db.table) def __del__(self): self.cursor.close() def __iter__(self): return self def next(self): res=self.cursor.fetchone() if res==None: raise StopIteration else: return res[0].rstrip(); class SessionDb: def __init__(self, dbc, table="sessiondata"): self.dbc=dbc self.table=table def keys(self): return [x[0] for x in self.dbc.query("SELECT DISTINCT session FROM %s" % self.table)] def values(self): return [Session(self.dbc, self.table, x) for x in self.keys()] def __len__(self): return int(self.dbc.query("SELECT COUNT(DISTINCT session) FROM %s" % self.table)[0][0]) def __iter__(self): return SesDbIterator(self) def __delitem__(self, key): if not self.has_key(key): raise KeyError, "No session '%s' known" % key self.dbc.execute("DELETE FROM %s WHERE session=%%s" % self.table, (key,), "format") def __getitem__(self, key): if not self.has_key(key): raise KeyError, "No session '%s' known" % key return Session(self.dbc, self.table, key) def __contains__(self, key): return self.has_key(key) def has_key(self, key): count=self.dbc.query("SELECT COUNT(DISTINCT session) FROM %s WHERE session=%%s" % self.table, (key,), "format")[0][0] return count!=0 dhm-0.6/src/sql/row.py0000644000175000017500000000731310223252043016027 0ustar wichertwichert00000000000000# sqlrow.py # # Copyright 2005 Wichert Akkerman class MetaParent(type): def __new__(cls, className, bases, d): instance=type.__new__(cls, className, bases, d) if not "dbc" in d: import sys for mod in [ instance.__module__, "__main__" ]: if hasattr(sys.modules[mod], "dbc"): instance.dbc=sys.modules[mod].dbc break for col in d.get("_columns", []): if hasattr(instance, col): continue setattr(instance, col, property( fset=lambda self,value,col=col: self._set(col, value), fget=lambda self,col=col: self._sqldata[col], doc="SQL column %s" % col)) return instance class SQLRow(object): """SQL row. A SQL row represents the data stored in a single row in a SQL table (or view). """ __metaclass__ = MetaParent def __init__(self, **kwargs): self._sqldata={} self._changed=[] self._stored=False for col in self._columns: self._sqldata[col]=None for (key,value) in kwargs.items(): setattr(self, key, value) # A simple comparison does not work since that also # compares the order if len(self._keys)!=len(self._changed): return for i in self._keys: if not i in self._changed: return self.retrieve() def _set(self, attr, value): if self._stored and attr in self._keys: raise NotImplementedError, "Changing keys from stored rows is not implemented" if self._sqldata[attr]==value: return if attr not in self._changed: self._changed.append(attr) self._sqldata[attr]=value def _genwhere(self): """Generate data for a WHERE clause to identify this object. This method generates data which can be used to generate a WHERE clause to uniquely identify this object in a table. It returns a tuple containing a string with the SQL command and a tuple with the data values. This can be fed to the execute method for a database connection using the format paramstyle. @return: (command,values) tuple """ cmd=" AND ".join([x+"=%s" for x in self._keys]) values=[self._sqldata[x] for x in self._keys] if None in values: raise KeyError, "Not all keys set" return (cmd,tuple(values)) def retrieve(self): (query,values)=self._genwhere() nonkeys=filter(lambda x,keys=self._keys: x not in keys, self._columns) c=self.dbc.execute("SELECT %s FROM %s WHERE %s;" % (",".join(nonkeys), self._table, query), values, "format") try: data=c.fetchall() if not data: raise KeyError, "No matching row found" elif len(data)>1: raise KeyError, "multiple rows match key" self._changed=[] self._added=[] for (key,value) in zip(nonkeys, data[0]): self._sqldata[key]=value self._stored=True finally: c.close() def _sql_insert(self): keys=filter(lambda x,data=self._sqldata: data[x]!=None, self._columns) values=[self._sqldata[x] for x in keys] self.dbc.execute("INSERT INTO %s (%s) VALUES (%s);" % (self._table, ",".join(keys), ",".join(["%s"]*len(keys))), tuple(values), "format") self._stored=True def _sql_update(self): keys=filter(lambda x,data=self._sqldata: data[x]!=None, self._changed) if not keys: return values=tuple([self._sqldata[x] for x in keys]) ucmd=[x+"=%s" for x in keys] (wquery,wvalues)=self._genwhere() self.dbc.execute("UPDATE %s SET %s WHERE %s;" % (self._table, ",".join([x+"=%s" for x in keys]), wquery), (values+wvalues), "format") def store(self): if not self._stored: self._sql_insert() else: self._sql_update() self._changed=[] # Compatibility layer for SQLObject-expecting code def __getitem__(self, attr): return self._sqldata[attr] def __setitem__(self, attr, value): self._set(key, value) def has_key(self, key): return self._sqldata.has_key(key) def commit(self): self.store() dhm-0.6/src/sql/utils.py0000644000175000017500000001605210223251756016372 0ustar wichertwichert00000000000000# dbutils.py # # Copyright 2001,2002,2003,2005 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """Utility functions to make using SQL databases easier. """ __docformat__ = "epytext en" import mx.DateTime import dhm.strtools _type_dtd = type(mx.DateTime.DateTimeDelta(0)) _type_dt = type(mx.DateTime.DateTime(0)) def encode(buf): """Encode a string. Escapes all characters in a string that have a special meaning in SQL so a string is safe to use in SQL expressions. @param buf: string to encode @type buf: string @return: encoded version of str @rtype: string """ global _type_dt, _type_dtd from pyPgSQL import libpq if buf==None: return "NULL" elif type(buf)==_type_dt: return buf.strftime("'%Y-%m-%d %H:%M:%S'") elif type(buf)==_type_dtd: return buf.strftime("'%d %H:%M:%S'") else: if not isinstance(buf, str): buf=str(buf) return libpq.PgQuoteBytea(buf) def decode(str): """Decode a string. Removes all escaping from a string. @param str: string to decode @type str: string @return: decoded version of str @rtype: string """ from pyPgSQL import libpq if str=="NULL": return None else: return libpq.PgUnQuoteBytea(str) def QmarkToPyformat(query, params): """Convert from qmark to pyformat parameter style. The python DB-API 2.0 specifies four different possible parameter styles that can be used by drivers. This function convers from the qmark style to pyformat style. @param query: SQL query to transform @type query: string @param params: arguments to query @type params: sequence of strings @return: converted query and parameters @rtype: tuple with the new command and a dictionary of arguments """ tokens=dhm.strtools.Tokenize(query, quotes="'") output=[] count=1 for token in tokens: if token.endswith("?"): output.append(token[:-1] + "%%(param%d)s" % count) count+=1 else: output.append(token) dict={} count=1 for param in params: dict["param%d" % count]=param count+=1 return (" ".join(output), dict) def FormatToPyformat(query, params): """Convert from format to pyformat parameter style. The python DB-API 2.0 specifies four different possible parameter styles that can be used by drivers. This function convers from the format style to pyformat style. @param query: SQL query to transform @type query: string @param params: arguments to query @type params: sequence of strings @return: converted query and parameters @rtype: tuple with the new command and a dictionary of arguments """ strborder=None newquery="" locquery=query locparams=params count=1 dict={} while locquery: (char,dchar,locquery)=(locquery[0], locquery[:2], locquery[1:]) if strborder: if char==strborder: strborder=None newquery+=char elif dchar=='\\'+strborder: newquery+=dchar locquery=locquery[1:] else: newquery+=char else: if char in [ '"', "'"]: strborder=char newquery+=char elif dchar=='%s': newquery+='%%(param%d)s' % count dict["param%d" % count]=locparams[0] locquery=locquery[1:] locparams=locparams[1:] count+=1 else: newquery+=char return (newquery, dict) def FormatToNamed(query, params): """Convert from format to named parameter style. The python DB-API 2.0 specifies four different possible parameter styles that can be used by drivers. This function convers from the format style to pyformat style. @param query: SQL query to transform @type query: string @param params: arguments to query @type params: sequence of strings @return: converted query and parameters @rtype: tuple with the new command and a dictionary of arguments """ strborder=None newquery="" locquery=query locparams=params count=1 dict={} while locquery: (char,dchar,locquery)=(locquery[0], locquery[:2], locquery[1:]) if strborder: if char==strborder: strborder=None newquery+=char elif dchar=='\\'+strborder: newquery+=dchar locquery=locquery[1:] else: newquery+=char else: if char in [ '"', "'"]: strborder=char newquery+=char elif dchar=='%s': newquery+=':param%d:' % count dict["param%d" % count]=locparams[0] locquery=locquery[1:] locparams=locparams[1:] count+=1 else: newquery+=char return (newquery, dict) def PyformatToFormat(query, params): """Convert from the pyformat to the format parameter style. The python DB-API 2.0 specifies four different possible parameter styles that can be used by drivers. This function convers from the format style to pyformat style. @param query: SQL query to transform @type query: string @param params: arguments to query @type params: dictionary of arguments @return: converted query and parameters @rtype: tuple with the new command and a string sequence of arguments """ import re matcher=re.compile("%\(([^)]+)\)s") keys=matcher.findall(query) return (matcher.sub("%s", query), map(lambda key,p=params: p[key], keys)) def __notimplemented(a, b): raise NotImplementedError, "Paramstyle conversion not implemented" __FormatConvertors = { "qmark" : { "qmark" : lambda a,b: (a,b), "numeric" : __notimplemented, "named" : __notimplemented, "format" : __notimplemented, "pyformat" : QmarkToPyformat, }, "numeric" : { "qmark" : __notimplemented, "numeric" : lambda a,b: (a,b), "named" : __notimplemented, "format" : __notimplemented, "pyformat" : __notimplemented }, "named" : { "qmark" : __notimplemented, "numeric" : __notimplemented, "named" : lambda a,b: (a,b), "format" : __notimplemented, "pyformat" : __notimplemented }, "format" : { "qmark" : __notimplemented, "numeric" : __notimplemented, "named" : FormatToNamed, "format" : lambda a,b: (a,b), "pyformat" : FormatToPyformat, }, "pyformat" : { "qmark" : __notimplemented, "numeric" : __notimplemented, "named" : __notimplemented, "format" : PyformatToFormat, "pyformat" : lambda a,b: (a,b), }, } def ParamStyleConvert(frm, to, query, params): """Convert parameter style from one style to another. @param frm: style to convert from @type frm: string @param to: style to convert to @type to: string @param query: SQL query to transform @type query: string @param params: arguments to query @type params: dictionary of arguments @return: converted query and parameters @rtype: tuple with the new command and a string sequence of arguments """ return __FormatConvertors[frm][to](query, params) dhm-0.6/src/sql/wrap.py0000644000175000017500000002123610223251756016203 0ustar wichertwichert00000000000000# sqlwrap.py # # Copyright 2003 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """DB-API wrappers The python DB-API 2.0 provides a standard API for accessing SQL databases. Unfortunately it has a few problems that make it hard to write portable SQL using code: 1. there is no portable way to access the exception classes; since they are put in the driver module you always have to know the module name throughout your code 2. it allows for multiple different parameter styles, and each driver module only implements one of them. This module implements some classes that make it easier to write portable SQL using code. It does this by adding some wrappers around DB-API which hide the incompatibilties: - it introduces database classes which always copy the exception class types into a connection object - it adds connection objects which can execute a SQL command and return a cursor with the results, with support for converting between different parameter styles """ __docformat__ = "epytext en" import dhm.sql.utils class Server: """SQL server. This is the general SQL server wrapper; it has connect and login information for a database and can setup database connections when asked to. This is an abstract base class; please use a derived class which overloads the _connect() method with real database connection code. @ivar user: username to use when connecting to the database @type user: string @ivar password: password to use when connecting to the database @type password: string @ivar host: hostname of database server @type host: string """ def __init__(self, user=None, password=None, host=None): self.user=user self.password=password self.host=host def _connect(self, db): """Connect to a database. This function connects to a database on a SQL server and returns the connection and the driver module used for the connection. @param db: database to connect to @type db: string @return: connection object and driver module @rtype: tuple with Connection instance and driver module """ raise NotImplementedError, "Abstract base class used" def __getitem__(self, key): return Connection(*self._connect(key)) class PsycoServer(Server): """Postgres SQL server using psycopg. """ def _connect(self, db): import psycopg dbc=psycopg.connect("host=%s dbname=%s user=%s password=%s" % (self.host, db, self.user, self.password)) return (dbc, psycopg) class PostgresServer(Server): """Postgres SQL server using psycopg. """ def _connect(self, db): import pyPgSQL.PgSQL dbc=pyPgSQL.PgSQL.connect(user=self.user, password=self.password, host=self.host, database=db, client_encoding="utf-8", unicode_results=True) return (dbc, pyPgSQL.PgSQL) class MysqlServer(Server): """MySQL SQL server. """ def _connect(self, db): import MySQLdb dbc=MySQLdb.connect(user=self.user, passwd=self.password, host=self.host, db=db) return (dbc, MySQLdb) class MssqlServer(Server): """MS-SQL server. """ def _connect(self, db): import MSSQL dbc=MSSQL.connect(self.host, self.user, self.password, db) return (dbc, MSSQL) class SybaseServer(Server): """Sybase server. """ def _connect(self, db): import Sybase dbc=Sybase.connect(self.host, self.user, self.password, db) return (dbc, Sybase) class Connection: """A database connection. This class can do automatic paramstyle conversion if desired; to do this simply change the paramstyle instance variable to the desires paramstyle. Please note that not all conversion types are implemented. @ivar paramstyle: DB-API 2.0 paramerstyle expected @type paramstyle: string @ivar driverparamstyle: DB-API 2.0 paramerstyle used by the db driver @type driverparamstyle: string @ivar dbc: low-level connection object used internally @type dbc: DB-API 2.0 connection object """ def __init__(self, dbc, module): """Constructor @param dbc: low-level connection object used internally @type dbc: DB-API 2.0 connection object @param module: SQL driver module @type module: python module """ self.dbc=dbc self.paramstyle=module.paramstyle self.driverparamstyle=module.paramstyle for error in [ "Warning", "Error", "InterfaceError", "DatabaseError", "DataError", "OperationalError", "IntegrityError", "InternalError", "ProgrammingError", "NotSupportedError" ]: setattr(self, error, getattr(module, error)) def cursor(self): """Return a DB-API cursor. """ return self.dbc.cursor() def execute(self, operation, params=(), paramstyle=None): """Execute a SQL command. @param operation: SQL command to execute @type operation: string @param params: parameters for SQL command @type params: depends on paramstyle used @param paramstyle: DB-API parameter style to used if not using the current connection paramstyle @type paramstyle: string @return: cursor with results @rtype: DB-API cursor instance """ if paramstyle==None: paramstyle=self.paramstyle cursor=self.dbc.cursor() if params: cursor.execute(*dhm.sql.utils.ParamStyleConvert( paramstyle, self.driverparamstyle, operation, params)) else: cursor.execute(operation) return cursor def query(self, operation, params=(), paramstyle=None): """Execute a SQL query and return the results. This is a convenience wrapper around the execute method which returns all results directly. @param operation: SQL command to execute @type operation: string @param params: parameters for SQL command @type params: depends on paramstyle used @param paramstyle: DB-API parameter style to used if not using the current connection paramstyle @type paramstyle: string @return: list of query results @rtype: list """ c=self.execute(operation, params, paramstyle) try: res=c.fetchall() finally: c.close() return res def close(self): """Close the connection to the database. """ self.dbc.close() def commit(self): """Commit an open transaction." """ self.dbc.commit() def rollback(self): """Roll back an open transaction." """ self.dbc.rollback() class Transaction: """Transaction object. This class makes using transactions a bit simpler: when you instantiate it it will open a transaction (which is a nop in python since the DB-API uses implicit transaction). When the class is garbage collected the transaction is rolled back, unless it is manually commited by calling commit(). Note that this relies on the reference counting garbage collection approach python uses, which will automatically delete the class instance when it goes out of scope. @ivar _connection: database connection @type _connection: Connection instance or DB-API 2.0 complient connection object @ivar _active: flag indicating if we are in an active transaction @type _active: boolean """ def __init__(self, connection): """Constructor. @param connection: database connection @type connection: Connection instance """ self._connection=connection self._active=1 def __del__(self): assert(not self._active) def commit(self): """Commit the transaction. """ self._connection.commit() self._active=0 def rollback(self): """Rollback the transaction. """ self._connection.rollback() self._active=0 def GetServer(driver, **kwargs): """Server factory function. This is a convenience function that returns a Server class instance for a specific server. The supported types are: - mysql: return a MysqlServer using the MySQLdb module - postgres: return a PostgresServer using the psycopg module @param driver: SQL driver to use @type driver: string @return: a server instance @type: Server class instance """ if driver=="mysql": return MysqlServer(**kwargs) elif driver=="postgres": return PostgresServer(**kwargs) elif driver=="psyco": return PsycoServer(**kwargs) elif driver=="mssql": return MssqlServer(**kwargs) elif driver=="sybase": return SybaseServer(**kwargs) raise KeyError, "Unknown server type" dhm-0.6/src/__init__.py0000644000175000017500000000225010223251756016165 0ustar wichertwichert00000000000000# __init__.py # # Copyright 2002, 2003, 2005 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """DeepHackMode python utilities This is part of the magic that is used at the DeepHackMode labs: basic python utilities that are used in various projects. """ __docformat__ = "epytext en" __author__ = "Wichert Akkerman " __copyright__ = "Copyright 2001,2002,2003,2004,2005 Wichert Akkerman" __all__ = [ "cgiutils", "command", "config", "event", "ldapObject", "nestdict", "strtools", "sql", ] dhm-0.6/src/cgiutils.py0000644000175000017500000001105110223251756016250 0ustar wichertwichert00000000000000# cgiutils.py # # Copyright 2001,2002 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """Utility functions to make writing CGI scripts in python easier. """ __docformat__ = "epytext en" # Import all system packages we need import os, sys, types, cgi, re, Cookie def setcookie(key, value): """Output HTTP header to store a cookie @param key: cookie name @type key: string @param value: data to store in the cookie @type value: string """ c=Cookie.SimpleCookie() c[key]=value print c def extractcookies(dict): """Extract HTTP cookies. Extract all http cookies that the browser passed to use and merge them in a dictionary. @param dict: dictionary to store cookies in @type dict: dictionary""" return dict.update(GetCookies()) def extractcgiparams(dict): """Extract form parameters Extract all CGI parameters that the browser passed to use and merge them in a dictionary. @param dict: dictionary to store CGI PUT/POST parameters in @type dict: dictionary. """ dict.update(GetParameters()) def GetCookies(): """Extract HTTP cookies. Extract all http cookies that the browser passed to use and return them as a dictionary. @return: cookies @rtype: dictionary """ dict={} if os.environ.has_key("HTTP_COOKIE"): c=Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE")) for key in c.keys(): dict[key]=c[key].value return dict def GetParameters(): """Extract CGI parameters. Extract all CGI parameters that the browser passed to use and return them as a dictionary. @return: CGI parameters @rtype: dictionary """ dict={} form=cgi.FieldStorage() for frm in form.keys(): if type(form[frm]) is types.ListType: dict[frm]=map(lambda x: x.value, form[frm]) else: dict[frm]=form[frm].value return dict def scriptname(script): """Return a link to another CGI script in the same directory. The SCRIPT_NAME environment variable is used to determine the location of the current CGI. @param script: name of CGI script to link to @type script: string @return: relative URL to CGI @rtype: string """ if os.environ.has_key("SCRIPT_NAME"): base=re.sub("[^/]*$", script, os.environ["SCRIPT_NAME"]) else: base=script return base def redirect(url): """Output redirector. Generate HTTP headers and a XHTML page to redirect a browser to another URL. @param url: URL to redirect to @type url: string """ print "Content-Type: text/html" print "Location: %s" % url print "Status: 302\n" print ''' Redirecting

Redirecting. If this does not work automatically please go to %s manually.

''' % (url, url) def getsessionkey(Parameters): """Extract a session key. This function should not exist. """ if Parameters.has_key("SESSION"): return Parameters["SESSION"] redirect("/nocookies.xhtml") sys.exit(0) class Request: """CGI request object. This class extracts all the available information about a CGI requests and stores that in its instance. """ def __init__(self): self.params=GetParameters() self.cookies=GetCookies() for key in [ "SERVER_PROTOCOL", "SERVER_PORT", "REQUEST_URI", "REQUEST_METHOD", "PATH_INFO", "PATH_TRANSLATED", "SCRIPT_NAME", "QUERY_STRING", "REMOTE_HOST", "REMOTE_ADDR", "AUTH_TYPE", "REMOTE_USER", "REMOTE_IDENT", "CONTENT_TYPE", "CONTENT_LENGTH" ] : if os.environ.has_key(key): setattr(self, key.lower(), os.environ[key]) self.headers={} for key in filter(lambda x: x.startswith("HTTP_"), os.environ.keys()): self.headers[key[5:].lower().replace("_", "-")]=os.environ[key] dhm-0.6/src/command.py0000644000175000017500000001435610223251756016056 0ustar wichertwichert00000000000000# command.py # # Copyright 2003 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """Command execution tools The standard python library only implements lowlevel tools to run external commands. This module builds on those primitives and adds more highlevel tools to run and interface with external commands. """ __docformat__ = "epytext en" import errno, fcntl, os, sys import strtools def _SetFdFlag(fd, flag): """Set a filedescriptor flag. @param fd: filedescriptor @type fd: integer @param flag: flag to set @type flag: integer """ flags=fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags|flag) class Command: """Command executor This class represents an external command that can be executed. This is as a simple base class: it does nothing more than execute a command. If you want to feed the command input or examine its output please use a derived class such as OutputCommand instead. There are a few methods that can be overriden in derived classes to customize behaviour: - _ProcessStdout: called while a command is run to process any data it outputs to stdout. - _ProcessStderr: called while a command is run to process any data it outputs to stderr. @ivar command: argv list for command to run @type command: list of strings """ def __init__(self, *args, **kwargs): """Constructor There are two ways to initialize a Command class: by passing the argv list directly, or by supplying command as a string using the command keyword argument. In that case the command will be tokenized automatically using strtools.Tokenize(). Since Command will execute the command directly it is not vulnerable to shell escape tricks. However it will also not do glob expansion; to do this you will have to run the command via a shell (/bin/sh -c "command") or expand the glob manually. """ if args and kwargs.has_key("command"): raise AttributeError, \ "cannot supply both an argument list and the command keyword argument" if args: self.command=args elif kwargs.has_key("command"): self.command=strtools.Tokenize(kwargs["command"]) def _TryRead(self, fd): """Try to read data from a file descriptor. @param fd: file descriptor to read from @type fd: integer @return: data read or None if no data is available) @rtype: string """ try: return os.read(fd, 4096) except OSError, e: if e.errno==errno.EAGAIN: return None raise e def _ProcessStdout(self, data): """Process data received via stdout. The function can be used to process data received via stdout. In this base class it does nothing. @param data: data to process @type data: string """ pass def _ProcessStderr(self, data): """Process data received via stderr. The function can be used to process data received via stderr. In this base class it does nothing. @param data: data to process @type data: string """ pass def _RunChild(self, stdin, stdout, stderr): """The child part of running a command. This function takes care of setting up the environment in which to run the command and executed the command itself. @param stdin: pipe fds to use for stdin @type stdin: (read-fd, write-fd) tuple @param stdout: pipe fds to use for stdout @type stdout: (read-fd, write-fd) tuple @param stderr: pipe fds to use for stderr @type stderr: (read-fd, write-fd) tuple """ os.close(stdin[1]) os.dup2(stdin[0], 0) os.close(stdout[0]) os.dup2(stdout[1], 1) os.close(stderr[0]) os.dup2(stderr[1], 2) os.execvp(self.command[0], self.command) sys.exit(0) def _RunParent(self, pid, stdin, stdout, stderr): """The parent part of running a command. Waits for the command being run by the child process to finish while processing any data received from it. @param stdin: pipe fds to use for stdin @type stdin: (read-fd, write-fd) tuple @param stdout: pipe fds to use for stdout @type stdout: (read-fd, write-fd) tuple @param stderr: pipe fds to use for stderr @type stderr: (read-fd, write-fd) tuple @return: exitcode returned by command @rtype: integer """ os.close(stdin[0]) _SetFdFlag(stdin[1], os.O_NONBLOCK) os.close(stdout[1]) _SetFdFlag(stdout[0], os.O_NONBLOCK) os.close(stderr[1]) _SetFdFlag(stderr[0], os.O_NONBLOCK) exit=0 while exit<2: data=self._TryRead(stdout[0]) if data!=None: self._ProcessStdout(data) data=self._TryRead(stderr[0]) if data!=None: self._ProcessStderr(data) if exit>0: exit=2 else: (pid, status)=os.waitpid(pid, os.WNOHANG) if pid and os.WIFEXITED(status): exit=1 os.close(stdin[1]) os.close(stdout[0]) os.close(stderr[0]) return os.WEXITSTATUS(status) def Run(self): """Run the command @return: exitcode returned by command @rtype: integer """ stdin=os.pipe() stdout=os.pipe() stderr=os.pipe() pid=os.fork() if pid==0: self._RunChild(stdin, stdout, stderr) else: return self._RunParent(pid, stdin, stdout, stderr) class OutputCommand(Command): """Command executor with output processing This class works exactly like Command, but data output to stdout and stderr is captured for later usage. >>> import dhm.command cmd=dhm.command.OutputCommand(command="echo Hello, world") >>> cmd.Run() 0 >>> cmd.stdout 'Hello, world' @ivar stdout: Data received via stdout @type stdout: string @ivar stderr: Data received via stderr @type stderr: string """ def _ProcessStdout(self, data): self.stdout+=data def _ProcessStderr(self, data): self.stderr+=data def Run(self): self.stdout="" self.stderr="" return Command.Run(self) dhm-0.6/src/config.py0000644000175000017500000000657710223252043015701 0ustar wichertwichert00000000000000# config.py # # Copyright 2002 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """Routines to read ISC config-alike configuration files. There are a few changes from the real ISC Style: - comments start with a # and last until the end of the line - IP addresses are not supported, you will have to use strings for those An example of a file in this format:: # Example configuration datasource1 { server "server1.your.domain"; username "client"; password "secret"; }; datasource2 { server "server2.your.domain"; username "client"; password "secret"; }; tables { "users"; "groups"; } """ __docformat__ = "epytext en" import shlex, UserDict, weakref class ParseError(Exception): """Parse error @ivar file: file being parsed @type file: string @ivar line: linenumber @type line: integer @ivar reason: problem description @type reason: string """ def __init__(self, file, line, reason): self.file=file self.line=line self.reason=reason def __str__(self): return "%s[%d]: %s" % (self.file, self.line, self.reason) def Parse(file): """Read a file in a ISC-like config style. @param file: filename to read @type file: string @return: the parser configuration @rtype: dictionary of dictionaries and strings """ tokenizer=shlex.shlex(open(file), file) tokenizer.wordchars+="/._-" return _Parse(tokenizer) def _Decode(token): if token[0]=='"': return token[1:-1] else: return int(token) def _Parse(input): (type_list, type_dict)=(1, 2) stack=[] top={} type=type_dict try: command=input.get_token() while command: needsep=1 if command=="}": (stack, top)=(stack[:-1], stack[-1]) type=type_dict elif type==type_list: top.append(_Decode(command)) else: value=input.get_token() if value=="{": one=input.get_token(); two=input.get_token(); if two==";": type=type_list; top[command]=[] else: type=type_dict; top[command]={} input.push_token(two) input.push_token(one) stack.append(top) top=top[command] needsep=0 elif value==";": raise ParseError, (input.infile, input.lineno, "Unexpected seperator found") else: top[command]=_Decode(value) if needsep: seperator=input.get_token() if seperator!=";": raise ParseError, (input.infile, input.lineno, "Required seperator missing") command=input.get_token() except ValueError: raise ParseError, (input.infile, input.lineno, "Illegal value") if stack: raise ParseError, (input.infile, input.lineno, "Unexpected end of file") return top if __name__=="__main__": import sys print _Parse(shlex.shlex(sys.stdin, "stdin")) dhm-0.6/src/ldapObject.py0000644000175000017500000002364610223251756016511 0ustar wichertwichert00000000000000# ldapObject.py # # Copyright 2000,2001,2003 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """LDAP object helper This python module implements the ldapObject class, which represents an entry in a LDAP directory. You can use an ldapObject as a normal map, when you are ready to commit your changes to the LDAP directory the object will perform the necessary actions itself. Creating a new entry in a directory =================================== Adding a new entry to a directory is simple: first you create a ldapObject with the right dn, then you fill it with the necessary data and finally you commit it to the directory using the modify() function. A simple example:: import ldap, ldapObject # Open a connection to the LDAP directory db=ldap.open("ldap", 389) db.simple_bind("user", "password") # Create a new object obj=ldapObject.ldapObject("sn=John Doe,ou=People,dc=domain") obj["objectClass"].append("inetOrgPerson") obj["sn"]="John Doe" obj["givenName"]="John" # Commit changes to the directory try: obj.modify(db) except ldap.LDAPError, e: print "Error adding entry %s: %s" % (obj.dn, str(e)) Turning LDAP search results into ldapObjects ============================================ Since ldapObject aims to be a minimal class it does not have specific functionality to search in a LDAP directory. However you can easily convert the results from ldap.search_s into a sequence of ldapObjects using map(). An example:: import ldap, ldapObject # Open a connection to the LDAP directory db=ldap.open("ldap", 389) db.simple_bind("user", "password") # Perform a search for all objects with a uid attribute results=db.search_s("ou=People,dc=domain", ldap.SCOPE_SUBTREE, "uid=*") results=map(ldapObject.ldapObject, results) Deriving from ldapObject ======================== You might want to dervice a new class from ldapObject if you want to add new functions or change the sorting order. Simple derived class -------------------- This example shows a new class that represents a companies office:: class Office(ldapObject.ldapObject): "A simple class to represent an office" ObjectClasses = [ "locality", "organizationalUnit" ] def __init__(self, init=""): "Initialize the Office object" ldapObject.ldapObject.__init__(self, init) # Add the essential objectClasses for oc in self.ObjectClases: if not oc in self.attr["ObjectClass"]: self.attr["ObjectClass"].append(oc) # Snapshot the current state of the object self._snapshot() def name(self): '''Return a string with a name for this office. The name is assembled from the location, state and country.''' name=[] if self.attr.has_key("l"): name.append(self.attr["l"][0]) if self.attr.has_key("st"): name.append(self.attr["st"][0]) if self.attr.has_key("c"): name.append(data.Countries[self.attr["c"][0]]) if len(name): name=", ".join(name) else: name=self.attr["ou"] return name Changing the sort order ----------------------- The sorting order for classes can be changed by modifying the SortOrder variable. SortOder is a list for tuples containing the attribute to sort on and the direction (one of ldapObject.SORT_ASCENDING or ldapObject.SORT_DESCENDING). An example:: class Office(ldapObject.ldapObject): "A simple class to represent an office" SortOrder = [ ("c", ldapObject.SORT_ASCENDING), # Sort by country first ("st", ldapObject.SORT_ASCENDING), # Then by state ("l", ldapObject.SORT_ASCENDING), # And finally sort by city ] @var SORT_ASCENDING: use ascending sort order, used for ldapObject.SortOrder. @var SORT_DESCENDING: use ascending sort order, used for ldapObject.SortOrder. """ __docformat__ = "epytext en" # Import system packages we need import copy, types, sys import ldap, ldap.modlist, ldif # Sort orders SORT_ASCENDING = 0 SORT_DESCENDING = 1 class ldapObjectException(Exception): """ldapObject exception class @ivar reason: reason for raising this exception @type reason: string """ def __init__(self, reason): """Constructor. @param reason: reason for raising this exception @type reason: string """ self.reason=reason def __str__(self): return self.reason class ldapObject: """ @cvar ObjectClasses: list of object classes this class is a member of @type ObjectClasses: sequence of strings @cvar SortOrder: Sorting order used when sorting ldapObjects. @type SortOrder: sequence of (attribute, SORT_ASCENDING|SORT_DESCENDING) pairs """ SortOrder = [ ] ObjectClasses = [ "top" ] def __init__(self,init=""): """ldapObject constructor. There are three methods to initialize a ldapObject: 1. Using a DN 2. Using a tuple as returned by a LDAP search 3. Using another ldapObject """ if type(init)==types.StringType: # String initializer, assume it is a DN self.dn=init self.attr={ } elif type(init)==types.TupleType and \ ((type(init[0])==types.StringType) and (type(init[1])==types.DictionaryType)): # This looks like the result of a LDAP search self.dn=init[0] self.attr=init[1] elif type(init)==types.InstanceType and \ (init.__dict__.has_key("orgAttr") and init.__dict__.has_key("attr") and init.__dict__.has_key("dn")): # This looks more like another ldapObject self.dn=other.dn self.attr=copy.deepcopy(other.attr) else: # Can't determine what type init is.. lets abort raise ldapObjectException, "Trying to initialize object with unknown initializer" # Having a objectClass is mandatory if not self.attr.has_key("objectClass"): self.attr["objectClass"]=["top"] for oc in self.ObjectClasses: if oc not in self.attr["objectClass"]: self.attr["objectClass"].append(oc) self._snapshot() def delete(self,db): """Delete this object from the database. @param db: database to store data in @type db: LDAP connection """ db.delete_s(self.dn) def load(self,db): """Load this object from the database. @param db: database to store data in @type db: LDAP connection """ (self.dn,self.attr)=db.search_s(self.dn, ldap.SCOPE_BASE, self._classfilter())[0] self._snapshot() def _snapshot(self): """Snapshot current state. Assume we have an untouched object: initialize orgAttr and reset changes. For internal use only!""" self.orgAttr=copy.deepcopy(self.attr) self.changes=[] def update(self,db): """Commit changes to directory. Build a list of changes made to our attributes and try to commit them to the database. @param db: database to store data in @type db: LDAP connection """ try: todo=ldap.modlist.modifyModlist(self.orgAttr, self.attr) db.modify_s(self.dn, todo) except ldap.NO_SUCH_OBJECT: todo=ldap.modlist.addModlist(self.attr) db.add_s(self.dn, todo) # We have to reload to make sure self.attr and self.orgAttr # are up to date. self.load(db) def ldif(self, fd=sys.stdout): """Output the object in LDIF format. @param fd: File to output LDIF data to @type fd: file object @return: LDIF data @rtype: string """ writer=ldif.LDIFWriter(fd) writer.unparse(self.dn, self.attr) def _classfilter(self): """Return a LDAP search filter for our ObjectClasses""" return "".join(map(lambda x: "(objectClass=%s)" % x, self.ObjectClasses)) def __str__(self): return self.ldif() def _getkey(self,key): """Return the sorting-key used for sorting. Used by __cmp__ to simplify our soring code. If an attribute is not found an empty list is returned instead""" if self.attr.has_key(key): return self.attr[key] else: return [] def __cmp__(self, other): """Compare ourself to another ldapObject class using the sorting options defined in SortOrder. Take into account that we are actually comparing lists of items.""" for key in self.SortOrder: (a,b)=(self._getkey(key[0]), other._getkey(key[0])) if key[1]==SORT_ASCENDING: aWins=1 else: aWins=-1 for i in range(min(len(a), len(b))): (x,y)=(a[i].lower(),b[i].lower()) if (x>y): return aWins elif (xlen(b): return -aWins return 0 # Set of members to emulate a mapping def clear(self): self.attr.clear() self.orgAttr.clear() def has_key(self,key): return self.attr.has_key(key) def items(self): return self.attr.items() def keys(self): return self.attr.keys() def __len__(self): return len(self.attr) def __delitem__(self, key): del self.attr[key] def __getitem__(self, key): return self.attr[key] def __setitem__(self, key, value): self.attr[key]=value dhm-0.6/src/nestdict.py0000644000175000017500000000455310223251756016253 0ustar wichertwichert00000000000000# nestdict.py # # Copyright 2002 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """ Nested dictionaries This module implements the NestedDict class, which is a simple tool to make it simpler to deal with nested dictionaries. Here is a simple example showing how to use it:: dict=NestedDict() dict["sql/username"]="guest" dict["sql/password"]="secret" print "Username: " + dict["sql"]["username"] print "Password: " + dict["sql/password"] """ import types, UserDict class NestedDict(UserDict.UserDict): """Nested dictionary class @ivar seperator: seperator string @type seperator: string """ def __init__(self, dict=None): self.seperator="/" UserDict.UserDict.__init__(self, dict) def __getitem__(self, key): keys=key.split(self.seperator) top=self.data while len(keys)>1: top=top[keys[0]] keys=keys[1:] if type(top[keys[0]])==types.DictType: return NestedDict(top[keys[0]]) else: return top[keys[0]] def __setitem__(self, key, item): keys=key.split(self.seperator) top=self.data while len(keys)>1: if not top.has_key(keys[0]): top[keys[0]]={} top=top[keys[0]] keys=keys[1:] top[keys[0]]=item def __delitem__(self, key): top=self.data path=key.split(self.seperator) (path, leaf)=(path[:-1], path[-1]) try: for subkey in path: top=top[subkey] except KeyError: raise KeyError, key del top[leaf] def has_key(self, key): top=self.data try: for subkey in key.split(self.seperator): top=top[subkey] except KeyError: return 0 return 1 if __name__=="__main__": d=NestedDict() d["sql/hostname"]="sql.your.domain" d["sql/table"]="users" d["radius/hostname"]="radius.your.domain" d["pants"]="off" print d dhm-0.6/src/sectionedfile.py0000644000175000017500000000521110223251756017243 0ustar wichertwichert00000000000000# sectionedfile.py # # Copyright 2002 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """Multipart file reader A SectionedFile is a file that contains multiple sections, which are seperator by a specific seperator. It works much like python's multifile, except this eats consecutive seperators as well. """ __docformat__ = "epytext en" class SectionedFile: """Sectioned file reader. """ def __init__(self, fp): """Simple constructor to initialize our data. @param fp: file to be read @type fp: class instance supporting a readline() method """ self.fp=fp self.blocked=0 self.eof=0 self.divider="" self.mustunget=0 self.unget="" def readline(self): """Read the next line from our input. If a divider is hit an empty line is returned and further reading is blocked until unblock() is called. Dividers found at the beginning are skipped. @return: line of text @rtype: string """ if self.blocked: return "" eating=0 while 1: line="" if self.mustunget: line=self.unget self.mustunget=0 else: line=self.fp.readline() if not line: self.eof=1 self.blocked=1 return "" if line.strip()==self.divider: eating=1 continue; else: if eating: self.mustunget=1 self.unget=line self.blocked=1 return "" else: return line self.newblock=0 return line def readlines(self): """Return multiple lines. Read as much lines from our input as possible until we hit a divider. @return: lines read @rtype: list of strings """ lines=[] while 1: line=self.readline() if not line: break lines.append(line) return lines def read(self): """Read all lines up to a divider and return them. @return: data read @rtype: string """ return "".join(self.readlines()) def unblock(self): """Unblock ourselves so we can proceed to the next section. @return: wether unblocking succeeded @rtype: boolean """ if self.eof: return 0 self.blocked=0 self.newblock=1 return 1 dhm-0.6/src/simplerfc822.py0000644000175000017500000000465110223251756016655 0ustar wichertwichert00000000000000# simplerfc822.py # # Copyright 2002 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """Trivial RFC822-like parser. Much like the rfc822 module from the standard python library, but much simpler and faster. This module only supports simple "field: value" style fields with the normal rfc822 line continuation. Repeated fields are also not supported. Assumes it reads from a TextFile so it can produce more informative error message. """ __docformat__ = "epytext en" import UserDict class FileException(Exception): """Parse error. @ivar file: name of file being parsed @type file: string @ivar line: linenumber on which error occured @type line: integer """ def __init__(self, file, line, reason): """Constructur @param file: name of file being parsed @type file: string @param line: linenumber on which error occured @type line: integer @param reason: description of the error @type reason: string """ Exception.__init__(reason) self.file=file self.line=line def __str__(self): return "%s(%d): %s" % (self.file, self.line, self.value) class Message(UserDict.UserDict): """Simple-RFC822 message object. This class works just like the standard python rfc822 module. """ def __init__(self, fp=None): UserDict.UserDict.__init__(self) if fp: self.Parse(fp) def Parse(self, fp): """Parse a textfile @param fp: file to parse @type fp: dpkg.textfile.TextFile instance """ self.data.clear() lasthdr=None while 1: line=fp.readline() if not line or line in [ "\r\n", "\n" ]: break line=line.rstrip() if line[0] in " \t": self.data[lasthdr]+="\n"+line[1:] else: i=line.find(":") if i==-1: raise FileException(fp.filename, fp.lineno, "Syntax error") lasthdr=line[:i] self.data[lasthdr]=line[(i+1):].lstrip() dhm-0.6/src/strtools.py0000644000175000017500000001033610223251756016323 0ustar wichertwichert00000000000000# strtools.py # # Copyright 2003 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Calculate shared library dependencies """String handling tools Utility functions for some standard string processing tasks """ __docformat__ = "epytext en" import codecs, re class TokenizeError(Exception): """Tokenizer error class""" pass def Tokenize(str, whitespace=" \t\r\n", quotes="\"", escapes="\\"): """String tokenizer This function tokenizes a string while taking quotation and escaping into account. >>> import dhm.strtools >>> dhm.strtools.Tokenize("this is a test") ['this', 'is', 'a', 'test'] >>> dhm.strtools.Tokenize("this \"is a\" test") ['this', 'is a', 'test'] >>> dhm.strtools.Tokenize("this \\\"is\\\" a test") ['this', '"is"', 'a', 'test'] >>> dhm.strtools.Tokenize("this \"is a test") Traceback (most recent call last): File "", line 1, in ? File "/usr/local/lib/python2.2/site-packages/dhm/strtools.py", line 80, in Tokenize raise TokenizeError, "Unexpected end of string in quoted text" dhm.strtools.TokenizeError: Unexecpted end of string in quoted text @param str: string to tokenize @type str: string @param whitespace: whitespace characters seperating tokens @type whitespace: string @param quotes: legal quoting characters @type quotes: string @param escapes: characters which can escape quoting characters @type escapes: string @return: list of tokens @rtype: sequence of strings """ (buffer, tokens, curtoken, quote)=(str, [], None, None) try: while buffer: if buffer[0]==quote: quote=None elif (quote==None) and (buffer[0] in quotes): quote=buffer[0] elif buffer[0] in whitespace: if quote!=None: curtoken+=buffer[0] else: tokens.append(curtoken) curtoken=None while buffer[1] in whitespace: buffer=buffer[1:] elif buffer[0] in escapes: if curtoken==None: curtoken=buffer[1] else: curtoken+=buffer[1] buffer=buffer[1:] else: if curtoken==None: curtoken=buffer[0] else: curtoken+=buffer[0] buffer=buffer[1:] except IndexError: raise TokenizeError, "Unexpected end of string" if quote: raise TokenizeError, "Unexpected end of string in quoted text" if curtoken!=None: tokens.append(curtoken) return tokens def RegexFilter(regexp, *args): """Extract regexp matches from a string. Its can be useful to extract certain parts of a string based on a regular expression. This function automates that task. >>> import strtools >>> strtools.RegexFilter("([^=]*)=(.*)", "username=wichert", "# a comment", "password=secret") [('username', 'wichert'), ('password', 'secret')] @param regexp: regular expression to look for @type regexp: string @param *args: strings to filter @type *args: string argument list @return: selected data @rtype: list of list of matched strings """ lst=[] matcher=re.compile(regexp) for str in args: mo=matcher.search(str) if mo: lst.append(mo.groups()) return lst def CodecFile(fo, encoding="utf=8"): """Return a new file object for a special codec. This function wraps a file object in a StreamReaderWriter of a specific encoding. This is especially useful if you want to read data in a different encoding than the default ASCII. @param fo: file to wrap @type fo: file object instange @param encoding: name of the encoding to use @type encoding: string @return: file object with proper encoding @rype: file instance """ (e,d,sr,sw)=codecs.lookup(encoding) srw=codecs.StreamReaderWriter(fo, sr, sw, "strict") srw.encoding=encoding return srw dhm-0.6/src/textfile.py0000644000175000017500000000407010223251756016254 0ustar wichertwichert00000000000000# textfile.py # # Copyright 2002 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """Simple file abstraction. The TextFile class which this class implements is a small subset of the standard python file object which only supports reading complete lines of text. It keeps track of the filename and linenumber currently being read, which can be useful to produce more informative error messages. """ __docformat__ = "epytext en" class TextFile: """Text file This class works mostly similar to a standard file object, featuring the methods as open(), close() and readline(). @ivar filename: name of file being read @type filename: string @ivar lineno: linenumber last read @type lineno: integer @ivar file: file being read @type file: file object """ def __init__(self, file=None): """Constructor. @param file: name of file to read @type file: string """ self.filename=None self.lineno=0 self.file=None if file: self.open(file) def open(self, file): """Open a file for reading. @param file: name of file to read @type file: string """ self.filename=file self.lineno=0 self.file=open(file, "rt") def close(self): """Close the file and reset context. """ self.filename=None self.lineno=0 if self.file: self.file.close() self.file=None def readline(self): """Read a line. @return: line of text @rtype: string """ line=self.file.readline() self.lineno+=1 return line dhm-0.6/NEWS0000644000175000017500000000401010223251756013760 0ustar wichertwichert000000000000000.6 * Add MANIFEST.in and use that to install some documentation files * Add clear method to SQLObject * Fix typo in cgiutils.Request which caused REMOTE_USER to be missed * Add SQLFilterDict class * Change order of parameters for sqldict classes to be more logical (use named parameters if you want to be backwards compatible) * Close race where command.Command would miss data written just before a child process exited * Allow /._ in a token in the config parser * Add CodecFile function to strtools module to get a file instance with a specific encoding * Support Sybase using Dave Cole's Sybase module * Remove sqlsession module in favour of new sqlpersistence * Add new sql.row module, will replace SQLObject * Move all SQL-related modules to dhm.sql module * Remove session module, it never was stable enough and I use dhm.sql.persistence now * Make sqldict instances iterable * Add event module, which implements a C# event type in python 0.5 * Switch all SQL using code to using sqlwrap parameter encoding * Add a factory function to sqlwrap to get the correct Server class instance * Change transaction class: require explicit rollback instead of waiting for garbage collection since we do not know when that will happen. assert on this in the Transaction destructor. * Update ldapObject to use current python-ldap LDIF interface * Remove cgiutils.OutputPage, might return using a SimpleTAL interface * Switch to using psycopg in favour of pgdb * Support MS-SQL using Dave Cole's MSSQL module 0.4 * Fix encoding of DateTime types * Add sectionedfile, simplerfc822, sqldict, sqlsession, sqlwrap and textfile modules * Add trivial DB-API 2 paramstyle conversion routines to dbutils 0.3 * Use string functions instead of the string module * New strtools function: RegexFilter * procutils has been removed; its functionality can be gotten by combining the command and strtools modules * dbutils.encode can now deal with dates and times using mx.DatTime 0.2 * first public release dhm-0.6/setup.py0000755000175000017500000000054310223251756015005 0ustar wichertwichert00000000000000#!/usr/bin/python from distutils.core import setup setup( name = "dhm", version = "0.6", author = "Wichert Akkerman", author_email = "wichert@deephackmode.org", license = "GPL version 2", description = "DeepHackMode python tools", packages = [ "dhm", "dhm.sql" ], package_dir = { "dhm" : "src" }, keywords = [ "SQL", "LDAP", "utility" ], ) dhm-0.6/PKG-INFO0000644000175000017500000000037410223252112014352 0ustar wichertwichert00000000000000Metadata-Version: 1.0 Name: dhm Version: 0.6 Summary: DeepHackMode python tools Home-page: UNKNOWN Author: Wichert Akkerman Author-email: wichert@deephackmode.org License: GPL version 2 Description: UNKNOWN Keywords: SQL,LDAP,utility Platform: UNKNOWN