Skip to content

Commit

Permalink
Merge pull request #6003 from joshmoore/error-codes
Browse files Browse the repository at this point in the history
cli: error code discovery
  • Loading branch information
jburel authored Jun 7, 2019
2 parents b81e660 + f42d841 commit b9796dc
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 30 deletions.
54 changes: 54 additions & 0 deletions components/tools/OmeroPy/src/omero/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,34 @@ def _check_admin(*args, **kwargs):
return _admin_only


class Error(object):
"""
Wrapper for error messages which can be registered by an BaseControl
subclass in its _configure method. Example:
class MyControl(BaseControl):
def _configure(self, parser):
self.add_error("NAME", 100, "some message: %s")
...
def __call__(self, *args):
self.raise_error("NAME", "my text")
"""

def __init__(self, ctx, rcode, msg):
self.ctx = ctx
self.rcode = rcode
self.msg = msg

def die(self, *args):
"""
Call ctx.die passing the return code and the message for this instance
"""
self.ctx.die(self.rcode, self.msg % tuple(args))

def __str__(self):
return "Error(%d, '%s')" % (self.rcode, self.msg)


class BaseControl(object):
"""Controls get registered with a CLI instance on loadplugins().
Expand Down Expand Up @@ -681,6 +709,32 @@ def __init__(self, ctx=None, dir=OMERODIR):
self.ctx = ctx
if self.ctx is None:
self.ctx = Context() # Prevents unncessary stop_event creation
self.__errors = {}

def add_error(self, name, rcode, msg):
"""
Register an Error by name both for discovery via the ErrorsControl
as well as for raising an exception via raise_error.
"""
err = self.__errors.get(name)
if err is not None:
self.ctx.die(2, "Error already exists: %s (%s)" % (name, err))
self.__errors[name] = Error(self.ctx, rcode, msg)

def get_errors(self):
"""
Returns a mapping from name to Error object
"""
return dict(self.__errors)

def raise_error(self, name, *args):
"""
Call die on the named Error using the arguments to format the message
"""
err = self.__errors.get(name)
if err is None:
self.ctx.die(2, "Error doesn't exist: %s" % name)
err.die(*args)

def _isWindows(self):
p_s = platform.system()
Expand Down
64 changes: 39 additions & 25 deletions components/tools/OmeroPy/src/omero/plugins/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,34 @@ def _complete(self, text, line, begidx, endidx):
def _configure(self, parser):
sub = parser.sub()
self._add_diagnostics(parser, sub)
self.add_error(
"NOT_WINDOWS", 123,
"Not Windows")
self.add_error(
"SETUP", 200,
"Error during service user set up: (%s) %s")
self.add_error(
"RUNNING", 201,
"%s is already running. Use stop first")
self.add_error(
"NO_SERVICE", 202,
"%s service deleted.")
self.add_error(
"BAD_CONFIG", 300,
"Bad configuration: No IceGrid.Node.Data property")
self.add_error(
"WIN_CONFIG", 400, """
%s is not in this directory. Aborting...
Please see the installation instructions on modifying
the files for your installation (%s)
with bin\winconfig.bat
""")
self.add_error(
"NO_WIN32", 666,
"Could not import win32service and/or win32evtlogutil")
self.actions = {}

class Action(object):
Expand Down Expand Up @@ -553,8 +581,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):
policy_handle, sid_obj, ('SeServiceLogonRight',))
win32security.LsaClose(policy_handle)
except pywintypes.error, details:
self.ctx.die(200, "Error during service user set up:"
" (%s) %s" % (details[0], details[2]))
self.raise_error("SETUP", details[0], details[2])
if not pasw:
try:
pasw = config.as_map()["omero.windows.pass"]
Expand Down Expand Up @@ -583,8 +610,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):

# Then check if the server is already running
if 0 <= output.find("RUNNING"):
self.ctx.die(201, "%s is already running. Use stop first"
% svc_name)
self.raise_error("RUNNING", svc_name)

# Finally, try to start the service - delete if startup fails
hscm = win32service.OpenSCManager(
Expand All @@ -599,7 +625,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):
self.ctx.out("%s service startup failed: (%s) %s"
% (svc_name, details[0], details[2]))
win32service.DeleteService(hs)
self.ctx.die(202, "%s service deleted." % svc_name)
self.raise_error("NO_SERVICE", svc_name)
finally:
win32service.CloseServiceHandle(hs)
win32service.CloseServiceHandle(hscm)
Expand All @@ -617,20 +643,17 @@ def DumpRecord(record):
else:

def events(self, svc_name):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

def _query_service(self, svc_name):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

def _start_service(self, config, descript, svc_name, pasw, user):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

def _stop_service(self, svc_name):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

#
# End Windows Methods
#
Expand Down Expand Up @@ -695,15 +718,14 @@ def checkwindows(self, args):
"""
self.check_access(os.R_OK)
if not self._isWindows():
self.ctx.die(123, "Not Windows")
self.raise_error("NOT_WINDOWS")

import Ice
key = "IceGrid.Node.Data"
properties = Ice.createProperties([self._icecfg()])
nodedata = properties.getProperty(key)
if not nodedata:
self.ctx.die(300,
"Bad configuration: No IceGrid.Node.Data property")
self.raise_error("BAD_CONFIG")
nodepath = path(nodedata)
pp = nodepath.parpath(self.ctx.dir)
if pp:
Expand All @@ -715,15 +737,7 @@ def checkwindows(self, args):
count = win_set_path(dir=self.ctx.dir)
if count:
return
self.ctx.die(400, """
%s is not in this directory. Aborting...
Please see the installation instructions on modifying
the files for your installation (%s)
with bin\winconfig.bat
""" % (nodedata, self.ctx.dir))
self.raise_error("WIN_CONFIG", nodedata, self.ctx.dir)

##############################################
#
Expand Down
37 changes: 37 additions & 0 deletions components/tools/OmeroPy/src/omero/plugins/basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import sys

from collections import defaultdict

from omero_ext.argparse import FileType

from omero.cli import BaseControl
Expand Down Expand Up @@ -240,9 +242,44 @@ def __call__(self, args):
elif args.topic:
self.print_single_command_or_topic(args)


class ErrorsControl(BaseControl):

def _configure(self, parser):
parser.set_defaults(func=self.__call__)
parser.add_argument("--length", default=50, type=int,
help="Length of message to print")
parser.add_argument("plugins", nargs="*", default=(),
help="Limit to these plugins; otherwise all")

def __call__(self, args):
arranged = defaultdict(lambda: defaultdict(
lambda: defaultdict(list)))
for name, control in self.ctx.controls.items():
if not args.plugins or name in args.plugins:
combined = []
if hasattr(control, "get_errors"):
combined.extend(control.get_errors().items())
combined.sort(lambda a, b: cmp(a[1].rcode, b[1].rcode))
for key, err in combined:
arranged[err.rcode][name][key].append(err)

for rcode, names in sorted(arranged.items()):
for name, keys in sorted(names.items()):
for key, errors in sorted(keys.items()):
for err in errors:
msg = err.msg
if len(msg) > (args.length+1):
msg = msg[:args.length] + "..."
msg = msg.replace("\n", " ")
msg = msg.strip()
t = (err.rcode, name, key, msg)
self.ctx.out("%5d\t%10s\t%10s\t'%s'" % t)

controls = {
"help": (HelpControl, "Syntax help for all commands"),
"quit": (QuitControl, "Quit application"),
"errors": (ErrorsControl, "Display all plugin error codes"),
"shell": (ShellControl, """Starts an IPython interpreter session
All arguments not understood vi %(prog)s will be passed to the shell.
Expand Down
13 changes: 9 additions & 4 deletions components/tools/OmeroPy/src/omero/plugins/hql.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ def _configure(self, parser):
parser.add_limit_arguments()
parser.add_style_argument()
parser.add_login_arguments()
self.add_error("NO_QUIET", 67,
"Can't ask for query with --quiet option")
self.add_error("NOT_ADMIN", 53,
("SecurityViolation: Current user is not an"
" admin and cannot use '--admin'"))
self.add_error("BAD_QUERY", 52, "Bad query: %s")

def __call__(self, args):
if args.query:
self.hql(args)
else:
if self.ctx.isquiet:
self.ctx.die(67, "Can't ask for query with --quiet option")
self.raise_error("NO_QUIET")
while True:
args.query = self.ctx.input("Enter query:")
if not args.query:
Expand Down Expand Up @@ -246,13 +252,12 @@ def project(self, querySvc, queryStr, params, ice_map):
return rv
except omero.SecurityViolation, sv:
if "omero.group" in ice_map:
self.ctx.die(53, "SecurityViolation: Current user is not an"
" admin and cannot use '--admin'")
self.raise_error("NOT_ADMIN")
else:
self.ctx.die(54, "SecurityViolation: %s" % sv)
except omero.QueryException, qe:
self.ctx.set("last.hql.rv", [])
self.ctx.die(52, "Bad query: %s" % qe.message)
self.raise_error("BAD_QUERY", qe.message)

try:
register("hql", HqlControl, HELP)
Expand Down
3 changes: 2 additions & 1 deletion components/tools/OmeroPy/src/omero/plugins/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def _configure(self, parser):
parser.add(sub, self.indexer, help="Start OMERO.indexer")
# web = parser.add(sub, self.web, help = "Start OMERO.web")
# web.add_argument("arg", nargs="*")
self.add_error("NO_CONFIG", 201, "No --Ice.Config provided")

def _prop(self, data, key):
return data.properties.getProperty("omero."+key)
Expand All @@ -43,7 +44,7 @@ def _checkIceConfig(self, args):
try:
args["--Ice.Config"]
except KeyError:
self.ctx.die(201, "No --Ice.Config provided")
self.raise_error("NO_CONFIG")
pre = []
post = []
for arg in args.args:
Expand Down

0 comments on commit b9796dc

Please sign in to comment.