summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 4137fc9)
raw | patch | inline | side by side (parent: 4137fc9)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Fri, 9 Nov 2001 10:11:08 +0000 (10:11 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Fri, 9 Nov 2001 10:11:08 +0000 (10:11 +0000) |
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@388 57a73879-2fb5-44c3-a270-3262357dd7e2
CHANGES.txt | patch | blob | history | |
roundup-admin | patch | blob | history | |
roundup/hyperdb.py | patch | blob | history |
diff --git a/CHANGES.txt b/CHANGES.txt
index c79c3af2fbca70968b11267ccec238a7f0bef2a3..5a02713384834f3d91a7b976c011bb6634961709 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
. "roundup.cgi" is now installed to "<python-prefix>/share/roundup/cgi-bin"
. roundup-admin now accepts abbreviated commands (eg. l = li = lis = list)
. roundup-mailgw now supports unix mailbox and POP as sources of mail.
+ . roundup-admin now handles all hyperdb exceptions
Fixed:
. Fixed a bug in HTMLTemplate changes.
. bug #477892 ] Password edit doesn't fix login cookie
. newuser_action now presents error messages rather than tracebacks.
. bug #479511 ] mailgw to pop
+ . bad error report in hyperdb
2001-10-23 - 0.3.0 pre 3
Feature:
diff --git a/roundup-admin b/roundup-admin
index ef2ea2adaf46e8a54d4946e39f64ca947b9056ce..f36e35da8615b8b8858bd46d60cafcde3a167b68 100755 (executable)
--- a/roundup-admin
+++ b/roundup-admin
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: roundup-admin,v 1.41 2001-11-09 01:25:40 richard Exp $
+# $Id: roundup-admin,v 1.42 2001-11-09 10:11:08 richard Exp $
import sys
if int(sys.version[0]) < 2:
raise KeyError, key
return l
+class UsageError(ValueError):
+ pass
+
class AdminTool:
def __init__(self):
# try help_ methods
if self.help.has_key(topic):
self.help[topic]()
- return
+ return 0
# try command docstrings
try:
l = self.commands.get(topic)
except KeyError:
print 'Sorry, no help for "%s"'%topic
- return
+ return 1
# display the help for each match, removing the docsring indent
for name, help in l:
print line[indent:]
else:
print line
+ return 0
def help_initopts(self):
import roundup.templates
designators = string.split(args[1], ',')
l = []
for designator in designators:
+ # decode the node designator
try:
classname, nodeid = roundupdb.splitDesignator(designator)
except roundupdb.DesignatorError, message:
- print 'Error: %s'%message
- return 1
- if self.comma_sep:
- l.append(self.db.getclass(classname).get(nodeid, propname))
- else:
- print self.db.getclass(classname).get(nodeid, propname)
+ raise UsageError, message
+
+ # get the class
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'invalid class "%s"'%classname
+ try:
+ if self.comma_sep:
+ l.append(cl.get(nodeid, propname))
+ else:
+ print cl.get(nodeid, propname)
+ except IndexError:
+ raise UsageError, 'no such %s node "%s"'%(classname, nodeid)
+ except KeyError:
+ raise UsageError, 'no such %s property "%s"'%(classname,
+ propname)
if self.comma_sep:
print ','.join(l)
return 0
designators = string.split(args[0], ',')
props = {}
for prop in args[1:]:
- key, value = prop.split('=')
+ if prop.find('=') == -1:
+ raise UsageError, 'argument "%s" not propname=value'%prop
+ try:
+ key, value = prop.split('=')
+ except (TypeError, ValueError):
+ raise UsageError, 'argument "%s" not propname=value'%prop
props[key] = value
for designator in designators:
+ # decode the node designator
try:
classname, nodeid = roundupdb.splitDesignator(designator)
except roundupdb.DesignatorError, message:
- print 'Error: %s'%message
- return 1
- cl = self.db.getclass(classname)
+ raise UsageError, message
+
+ # get the class
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'invalid class "%s"'%classname
+
properties = cl.getprops()
for key, value in props.items():
type = properties[key]
elif isinstance(type, hyperdb.Password):
props[key] = password.Password(value)
elif isinstance(type, hyperdb.Date):
- props[key] = date.Date(value)
+ try:
+ props[key] = date.Date(value)
+ except ValueError, message:
+ raise UsageError, '"%s": %s'%(value, message)
elif isinstance(type, hyperdb.Interval):
- props[key] = date.Interval(value)
+ try:
+ props[key] = date.Interval(value)
+ except ValueError, message:
+ raise UsageError, '"%s": %s'%(value, message)
elif isinstance(type, hyperdb.Link):
props[key] = value
elif isinstance(type, hyperdb.Multilink):
props[key] = value.split(',')
- apply(cl.set, (nodeid, ), props)
+
+ # try the set
+ try:
+ apply(cl.set, (nodeid, ), props)
+ except (TypeError, IndexError, ValueError), message:
+ raise UsageError, message
return 0
def do_find(self, args):
value may be either the nodeid of the linked node, or its key value.
'''
classname = args[0]
- cl = self.db.getclass(classname)
+ # get the class
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'invalid class "%s"'%classname
+
+ # TODO: handle > 1 argument
+ # handle the propname=value argument
+ if prop.find('=') == -1:
+ raise UsageError, 'argument "%s" not propname=value'%prop
+ try:
+ propname, value = args[1].split('=')
+ except (TypeError, ValueError):
+ raise UsageError, 'argument "%s" not propname=value'%prop
- # look up the linked-to class and get the nodeid that has the value
- propname, value = args[1].split('=')
+ # if the value isn't a number, look up the linked class to get the
+ # number
num_re = re.compile('^\d+$')
if not num_re.match(value):
- propcl = cl.properties[propname]
- if (not isinstance(propcl, hyperdb.Link) and not
+ # get the property
+ try:
+ property = cl.properties[propname]
+ except KeyError:
+ raise UsageError, '%s has no property "%s"'%(classname,
+ propname)
+
+ # make sure it's a link
+ if (not isinstance(property, hyperdb.Link) and not
isinstance(type, hyperdb.Multilink)):
- print 'You may only "find" link properties'
- return 1
- propcl = self.db.getclass(propcl.classname)
- value = propcl.lookup(value)
+ raise UsageError, 'You may only "find" link properties'
- # now do the find
- if self.comma_sep:
- print ','.join(apply(cl.find, (), {propname: value}))
- else:
- print apply(cl.find, (), {propname: value})
+ # get the linked-to class and look up the key property
+ link_class = self.db.getclass(property.classname)
+ try:
+ value = link_class.lookup(value)
+ except TypeError:
+ raise UsageError, '%s has no key property"'%link_class.classname
+ except KeyError:
+ raise UsageError, '%s has no entry "%s"'%(link_class.classname,
+ propname)
+
+ # now do the find
+ try:
+ if self.comma_sep:
+ print ','.join(apply(cl.find, (), {propname: value}))
+ else:
+ print apply(cl.find, (), {propname: value})
+ except KeyError:
+ raise UsageError, '%s has no property "%s"'%(classname,
+ propname)
+ except (ValueError, TypeError), message:
+ raise UsageError, message
return 0
def do_specification(self, args):
This lists the properties for a given class.
'''
classname = args[0]
- cl = self.db.getclass(classname)
+ # get the class
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'invalid class "%s"'%classname
+
+ # get the key property
keyprop = cl.getkey()
for key, value in cl.properties.items():
if keyprop == key:
from roundup import hyperdb
classname = args[0]
- cl = self.db.getclass(classname)
+
+ # get the class
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'invalid class "%s"'%classname
+
+ # now do a create
props = {}
properties = cl.getprops(protected = 0)
if len(args) == 1:
else:
# use the args
for prop in args[1:]:
- key, value = prop.split('=')
+ if prop.find('=') == -1:
+ raise UsageError, 'argument "%s" not propname=value'%prop
+ try:
+ key, value = prop.split('=')
+ except (TypeError, ValueError):
+ raise UsageError, 'argument "%s" not propname=value'%prop
props[key] = value
# convert types
for key in props.keys():
- type = properties[key]
+ # get the property
+ try:
+ type = properties[key]
+ except KeyError:
+ raise UsageError, '%s has no property "%s"'%(classname, key)
+
if isinstance(type, hyperdb.Date):
- props[key] = date.Date(value)
+ try:
+ props[key] = date.Date(value)
+ except ValueError, message:
+ raise UsageError, '"%s": %s'%(value, message)
elif isinstance(type, hyperdb.Interval):
- props[key] = date.Interval(value)
+ try:
+ props[key] = date.Interval(value)
+ except ValueError, message:
+ raise UsageError, '"%s": %s'%(value, message)
elif isinstance(type, hyperdb.Password):
props[key] = password.Password(value)
elif isinstance(type, hyperdb.Multilink):
props[key] = value.split(',')
+ # check for the key property
if cl.getkey() and not props.has_key(cl.getkey()):
- print "You must provide the '%s' property."%cl.getkey()
- else:
- print apply(cl.create, (), props)
+ raise UsageError, "you must provide the '%s' property."%cl.getkey()
+ # do the actual create
+ try:
+ print apply(cl.create, (), props)
+ except (TypeError, IndexError, ValueError), message:
+ raise UsageError, message
return 0
def do_list(self, args):
alphabetically.
'''
classname = args[0]
- cl = self.db.getclass(classname)
+
+ # get the class
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'invalid class "%s"'%classname
+
+ # figure the property
if len(args) > 1:
key = args[1]
else:
key = cl.labelprop()
+
if self.comma_sep:
print ','.join(cl.list())
else:
for nodeid in cl.list():
- value = cl.get(nodeid, key)
+ try:
+ value = cl.get(nodeid, key)
+ except KeyError:
+ raise UsageError, '%s has no property "%s"'%(classname, key)
print "%4s: %s"%(nodeid, value)
return 0
4 feature
'''
classname = args[0]
- cl = self.db.getclass(classname)
+
+ # get the class
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'invalid class "%s"'%classname
+
+ # figure the property names to display
if len(args) > 1:
prop_names = args[1].split(',')
else:
prop_names = cl.getprops().keys()
+
+ # now figure column widths
props = []
- for name in prop_names:
- if ':' in name:
- name, width = name.split(':')
- props.append((name, int(width)))
+ for spec in prop_names:
+ if ':' in spec:
+ try:
+ name, width = spec.split(':')
+ except (ValueError, TypeError):
+ raise UsageError, '"%s" not name:width'%spec
+ props.append((spec, int(width)))
else:
- props.append((name, len(name)))
+ props.append((spec, len(spec)))
+ # now display the heading
print ' '.join([string.capitalize(name) for name, width in props])
+
+ # and the table data
for nodeid in cl.list():
l = []
for name, width in props:
if name != 'id':
- value = str(cl.get(nodeid, name))
+ try:
+ value = str(cl.get(nodeid, name))
+ except KeyError:
+ raise UsageError, '%s has no property "%s"'%(classname,
+ name)
else:
value = str(nodeid)
f = '%%-%ds'%width
try:
classname, nodeid = roundupdb.splitDesignator(args[0])
except roundupdb.DesignatorError, message:
- print 'Error: %s'%message
- return 1
+ raise UsageError, message
+
# TODO: handle the -c option?
- print self.db.getclass(classname).history(nodeid)
+ try:
+ print self.db.getclass(classname).history(nodeid)
+ except KeyError:
+ raise UsageError, 'no such class "%s"'%classname
+ except IndexError:
+ raise UsageError, 'no such %s node "%s"'%(classname, nodeid)
return 0
def do_retire(self, args):
try:
classname, nodeid = roundupdb.splitDesignator(designator)
except roundupdb.DesignatorError, message:
- print 'Error: %s'%message
- return 1
- self.db.getclass(classname).retire(nodeid)
+ raise UsageError, message
+ try:
+ self.db.getclass(classname).retire(nodeid)
+ except KeyError:
+ raise UsageError, 'no such class "%s"'%classname
+ except IndexError:
+ raise UsageError, 'no such %s node "%s"'%(classname, nodeid)
return 0
def do_export(self, args):
# do all the classes specified
for classname in classes:
- cl = self.db.getclass(classname)
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'no such class "%s"'%classname
f = open(os.path.join(dir, classname+'.csv'), 'w')
f.write(string.join(cl.properties.keys(), ':') + '\n')
the old data.)
'''
if len(args) < 2:
- print do_import.__doc__
- return 1
+ raise UsageError, 'Not enough arguments supplied'
if csv is None:
- print 'Sorry, you need the csv module to use this function.'
- print 'Get it from: http://www.object-craft.com.au/projects/csv/'
- return 1
+ raise UsageError, \
+ 'Sorry, you need the csv module to use this function.\n'\
+ 'Get it from: http://www.object-craft.com.au/projects/csv/'
from roundup import hyperdb
# ensure that the properties and the CSV file headings match
- cl = self.db.getclass(args[0])
+ try:
+ cl = self.db.getclass(classname)
+ except KeyError:
+ raise UsageError, 'no such class "%s"'%classname
f = open(args[1])
p = csv.parser(field_sep=':')
file_props = p.parse(f.readline())
m.sort()
props.sort()
if m != props:
- print 'Import file doesn\'t define the same properties as "%s".'%args[0]
- return 1
+ raise UsageError, 'Import file doesn\'t define the same '\
+ 'properties as "%s".'%args[0]
# loop through the file and create a node for each entry
n = range(len(props))
return 1
# do the command
+ ret = 0
try:
- return function(args[1:])
- finally:
- self.db.close()
-
- return 1
+ ret = function(args[1:])
+ except UsageError, message:
+ print 'Error: %s'%message
+ print function.__doc__
+ ret = 1
+ except:
+ import traceback
+ traceback.print_exc()
+ ret = 1
+ return ret
def interactive(self, ws_re=re.compile(r'\s+')):
'''Run in an interactive mode
self.comma_sep = 1
# if no command - go interactive
+ ret = 0
if not args:
- return self.interactive()
-
- self.run_command(args)
+ self.interactive()
+ else:
+ ret = self.run_command(args)
+ self.db.close()
+ return ret
if __name__ == '__main__':
#
# $Log: not supported by cvs2svn $
+# Revision 1.41 2001/11/09 01:25:40 richard
+# Should parse with python 1.5.2 now.
+#
# Revision 1.40 2001/11/08 04:42:00 richard
# Expanded the already-abbreviated "initialise" and "specification" commands,
# and added a comment to the command help about the abbreviation.
diff --git a/roundup/hyperdb.py b/roundup/hyperdb.py
index 8c6ed345fcda4f734512c2d78ace2981f7a8bc92..49a6db60af35032e02da001eaaac64daa5cee031 100644 (file)
--- a/roundup/hyperdb.py
+++ b/roundup/hyperdb.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: hyperdb.py,v 1.29 2001-10-27 00:17:41 richard Exp $
+# $Id: hyperdb.py,v 1.30 2001-11-09 10:11:08 richard Exp $
# standard python modules
import cPickle, re, string
if not isinstance(prop, Link) and not isinstance(prop, Multilink):
raise TypeError, "'%s' not a Link/Multilink property"%propname
if not self.db.hasnode(prop.classname, nodeid):
- raise ValueError, '%s has no node %s'%(link_class, nodeid)
+ raise ValueError, '%s has no node %s'%(prop.classname, nodeid)
# ok, now do the find
cldb = self.db.getclassdb(self.classname)
#
# $Log: not supported by cvs2svn $
+# Revision 1.29 2001/10/27 00:17:41 richard
+# Made Class.stringFind() do caseless matching.
+#
# Revision 1.28 2001/10/21 04:44:50 richard
# bug #473124: UI inconsistency with Link fields.
# This also prompted me to fix a fairly long-standing usability issue -