Command line errors cleaned up and made more consistent

This commit is contained in:
Jim Paris 2013-01-16 16:52:43 -05:00
parent f0c2a64ae3
commit 87b43e5d04
11 changed files with 62 additions and 38 deletions

View File

@ -24,10 +24,16 @@ subcmd_mods = {}
for cmd in subcommands:
subcmd_mods[cmd] = __import__("nilmdb.cmdline." + cmd, fromlist = [ cmd ])
class JimArgumentParser(argparse.ArgumentParser):
def error(self, message):
self.print_usage(sys.stderr)
self.exit(2, sprintf("error: %s\n", message))
class Cmdline(object):
def __init__(self, argv):
self.argv = argv
self.client = None
def arg_time(self, toparse):
"""Parse a time string argument"""
@ -93,8 +99,8 @@ class Cmdline(object):
version_string = sprintf("nilmtool %s, client library %s",
version, nilmdb.Client.client_version)
self.parser = argparse.ArgumentParser(add_help = False,
formatter_class = def_form)
self.parser = JimArgumentParser(add_help = False,
formatter_class = def_form)
group = self.parser.add_argument_group("General options")
group.add_argument("-h", "--help", action='help',
@ -119,7 +125,8 @@ class Cmdline(object):
def die(self, formatstr, *args):
fprintf(sys.stderr, formatstr + "\n", *args)
self.client.close()
if self.client:
self.client.close()
sys.exit(-1)
def run(self):
@ -141,7 +148,7 @@ class Cmdline(object):
try:
server_version = self.client.version()
except nilmdb.client.Error as e:
self.die("Error connecting to server: %s", str(e))
self.die("error connecting to server: %s", str(e))
# Now dispatch client request to appropriate function. Parser
# should have ensured that we don't have any unknown commands

View File

@ -24,4 +24,4 @@ def cmd_create(self):
try:
self.client.stream_create(self.args.path, self.args.layout)
except nilmdb.client.ClientError as e:
self.die("Error creating stream: %s", str(e))
self.die("error creating stream: %s", str(e))

View File

@ -22,4 +22,4 @@ def cmd_destroy(self):
try:
self.client.stream_destroy(self.args.path)
except nilmdb.client.ClientError as e:
self.die("Error deleting stream: %s", str(e))
self.die("error destroying stream: %s", str(e))

View File

@ -39,7 +39,7 @@ def cmd_extract_verify(self):
def cmd_extract(self):
streams = self.client.stream_list(self.args.path)
if len(streams) != 1:
self.die("Error getting stream info for path %s", self.args.path)
self.die("error getting stream info for path %s", self.args.path)
layout = streams[0][1]
if self.args.annotate:

View File

@ -51,12 +51,12 @@ def cmd_insert(self):
# Find requested stream
streams = self.client.stream_list(self.args.path)
if len(streams) != 1:
self.die("Error getting stream info for path %s", self.args.path)
self.die("error getting stream info for path %s", self.args.path)
layout = streams[0][1]
if self.args.start and len(self.args.file) != 1:
self.die("--start can only be used with one input file, for now")
self.die("error: --start can only be used with one input file")
for filename in self.args.file:
if filename == '-':
@ -65,7 +65,7 @@ def cmd_insert(self):
try:
infile = open(filename, "r")
except IOError:
self.die("Error opening input file %s", filename)
self.die("error opening input file %s", filename)
# Build a timestamper for this file
if self.args.none:
@ -77,11 +77,11 @@ def cmd_insert(self):
try:
start = self.parse_time(filename)
except ValueError:
self.die("Error extracting time from filename '%s'",
self.die("error extracting time from filename '%s'",
filename)
if not self.args.rate:
self.die("Need to specify --rate")
self.die("error: --rate is needed, but was not specified")
rate = self.args.rate
ts = nilmdb.timestamper.TimestamperRate(infile, start, rate)
@ -100,6 +100,6 @@ def cmd_insert(self):
# ugly bracketed ranges of 16-digit numbers and a mangled URL.
# Need to consider adding something like e.prettyprint()
# that is smarter about the contents of the error.
self.die("Error inserting data: %s", str(e))
self.die("error inserting data: %s", str(e))
return

View File

@ -43,21 +43,21 @@ def cmd_metadata(self):
for keyval in keyvals:
kv = keyval.split('=')
if len(kv) != 2 or kv[0] == "":
self.die("Error parsing key=value argument '%s'", keyval)
self.die("error parsing key=value argument '%s'", keyval)
data[kv[0]] = kv[1]
# Make the call
try:
handler(self.args.path, data)
except nilmdb.client.ClientError as e:
self.die("Error setting/updating metadata: %s", str(e))
self.die("error setting/updating metadata: %s", str(e))
else:
# Get (or unspecified)
keys = self.args.get or None
try:
data = self.client.stream_get_metadata(self.args.path, keys)
except nilmdb.client.ClientError as e:
self.die("Error getting metadata: %s", str(e))
self.die("error getting metadata: %s", str(e))
for key, value in sorted(data.items()):
# Omit nonexistant keys
if value is None:

View File

@ -33,15 +33,11 @@ def cmd_remove_verify(self):
self.parser.error("start is after end")
def cmd_remove(self):
streams = self.client.stream_list(self.args.path)
if len(streams) != 1:
self.die("Error getting stream info for path %s", self.args.path)
try:
count = self.client.stream_remove(self.args.path,
self.args.start, self.args.end)
except nilmdb.client.ClientError as e: # pragma: no cover (shouldn't happen)
self.die("Error removing data: %s", str(e))
except nilmdb.client.ClientError as e:
self.die("error removing data: %s", str(e))
if self.args.count:
printf("%d\n", count)

View File

@ -26,12 +26,19 @@ class Error(Exception):
self.url = url # URL we were requesting
self.traceback = traceback # server traceback, if available
def __str__(self):
s = sprintf("[%s]", self.status)
if self.message:
s += sprintf(" %s", self.message)
if self.traceback: # pragma: no cover
s += sprintf("\nServer traceback:\n%s", self.traceback)
return s
def __repr__(self): # pragma: no cover
s = sprintf("[%s]", self.status)
if self.message:
s += sprintf(" %s", self.message)
if self.url:
s += sprintf(" (%s)", self.url)
if self.traceback: # pragma: no cover
if self.traceback:
s += sprintf("\nServer traceback:\n%s", self.traceback)
return s
class ClientError(Error):

View File

@ -80,7 +80,7 @@ _sql_schema_updates = {
class NilmDBError(Exception):
"""Base exception for NilmDB errors"""
def __init__(self, message = "Unspecified error"):
Exception.__init__(self, self.__class__.__name__ + ": " + message)
Exception.__init__(self, message)
class StreamError(NilmDBError):
pass
@ -484,8 +484,8 @@ class NilmDB(object):
than actually fetching the data. It is not limited by
max_results.
"""
table = self.data.getnode(path)
stream_id = self._stream_id(path)
table = self.data.getnode(path)
intervals = self._get_intervals(stream_id)
requested = Interval(start or 0, end or 1e12)
result = []
@ -530,8 +530,8 @@ class NilmDB(object):
are truncated or split appropriately. Returns the number of
data points removed.
"""
table = self.data.getnode(path)
stream_id = self._stream_id(path)
table = self.data.getnode(path)
intervals = self._get_intervals(stream_id)
to_remove = Interval(start or 0, end or 1e12)
removed = 0

View File

@ -63,7 +63,7 @@ def exception_to_httperror(*expected):
try:
return func(*args, **kwargs)
except expected as e:
message = sprintf("%s: %s", type(e).__name__, str(e))
message = sprintf("%s", str(e))
raise cherrypy.HTTPError("400 Bad Request", message)
return wrapper
return decorator
@ -213,7 +213,7 @@ class Stream(NilmApp):
parser.parse(body)
except nilmdb.layout.ParserError as e:
raise cherrypy.HTTPError("400 Bad Request",
"Error parsing input data: " +
"error parsing input data: " +
e.message)
if (not parser.min_timestamp or not parser.max_timestamp or

View File

@ -10,6 +10,7 @@ from nose.tools import assert_raises
import itertools
import datetime_tz
import os
import re
import shutil
import sys
import threading
@ -97,14 +98,24 @@ class TestCmdline(object):
self.dump()
eq_(self.exitcode, 0)
def fail(self, arg_string, infile = None, exitcode = None):
def fail(self, arg_string, infile = None,
exitcode = None, require_error = True):
self.run(arg_string, infile)
if exitcode is not None and self.exitcode != exitcode:
# Wrong exit code
self.dump()
eq_(self.exitcode, exitcode)
if self.exitcode == 0:
# Success, when we wanted failure
self.dump()
ne_(self.exitcode, 0)
# Make sure the output contains the word "error" at the
# beginning of a line, but only if an exitcode wasn't
# specified.
if require_error and not re.search("^error",
self.captured, re.MULTILINE):
raise AssertionError("command failed, but output doesn't "
"contain the string 'error'")
def contain(self, checkstring):
in_(checkstring, self.captured)
@ -339,13 +350,13 @@ class TestCmdline(object):
self.ok("insert --help")
self.fail("insert /foo/bar baz qwer")
self.contain("Error getting stream info")
self.contain("error getting stream info")
self.fail("insert /newton/prep baz qwer")
self.match("Error opening input file baz\n")
self.match("error opening input file baz\n")
self.fail("insert /newton/prep")
self.contain("Error extracting time")
self.contain("error extracting time")
self.fail("insert --start 19801205 /newton/prep 1 2 3 4")
self.contain("--start can only be used with one input file")
@ -386,7 +397,7 @@ class TestCmdline(object):
os.environ['TZ'] = "UTC"
self.fail("insert --rate 120 /newton/raw "
"tests/data/prep-20120323T1004")
self.contain("Error parsing input data")
self.contain("error parsing input data")
# empty data does nothing
self.ok("insert --rate 120 --start '03/23/2012 06:05:00' /newton/prep "
@ -432,7 +443,7 @@ class TestCmdline(object):
def test_08_extract(self):
# nonexistent stream
self.fail("extract /no/such/foo --start 2000-01-01 --end 2020-01-01")
self.contain("Error getting stream info")
self.contain("error getting stream info")
# reversed range
self.fail("extract -a /newton/prep --start 2020-01-01 --end 2000-01-01")
@ -441,15 +452,18 @@ class TestCmdline(object):
# empty ranges return error 2
self.fail("extract -a /newton/prep " +
"--start '23 Mar 2012 10:00:30' " +
"--end '23 Mar 2012 10:00:30'", exitcode = 2)
"--end '23 Mar 2012 10:00:30'",
exitcode = 2, require_error = False)
self.contain("no data")
self.fail("extract -a /newton/prep " +
"--start '23 Mar 2012 10:00:30.000001' " +
"--end '23 Mar 2012 10:00:30.000001'", exitcode = 2)
"--end '23 Mar 2012 10:00:30.000001'",
exitcode = 2, require_error = False)
self.contain("no data")
self.fail("extract -a /newton/prep " +
"--start '23 Mar 2022 10:00:30' " +
"--end '23 Mar 2022 10:00:30'", exitcode = 2)
"--end '23 Mar 2022 10:00:30'",
exitcode = 2, require_error = False)
self.contain("no data")
# but are ok if we're just counting results
@ -502,7 +516,7 @@ class TestCmdline(object):
# Try nonexistent stream
self.fail("remove /no/such/foo --start 2000-01-01 --end 2020-01-01")
self.contain("Error getting stream info")
self.contain("No stream at path")
self.fail("remove /newton/prep --start 2020-01-01 --end 2000-01-01")
self.contain("start is after end")