Skip to content

Commit 27bea9e

Browse files
committed
cli: error code discovery
Suggested by Damir, a few additional methods on the BaseControl class permit discovering all errors codes for available plugins. Example output for the state of this commit: $ bin/omero errors 123 admin NOT_WINDOWS 'Not Windows' 200 admin SETUP 'Error during service user set up: (%s) %s' 201 admin RUNNING '%s is already running. Use stop first' 201 server NO_CONFIG 'No --Ice.Config provided' 202 admin NO_SERVICE '%s service deleted.' 300 admin BAD_CONFIG 'Bad configuration: No IceGrid.Node.Data property' 400 admin WIN_CONFIG '%s is not in this directory. Aborting... ...' 666 admin NO_WIN32 'Could not import win32service and/or win32evtlogutil' see: https://www.openmicroscopy.org/community/viewtopic.php?f=6&t=8676&p=20525#p20525
1 parent dace95d commit 27bea9e

File tree

4 files changed

+132
-26
lines changed

4 files changed

+132
-26
lines changed

components/tools/OmeroPy/src/omero/cli.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,34 @@ def _check_admin(*args, **kwargs):
650650
return _admin_only
651651

652652

653+
class Error(object):
654+
"""
655+
Wrapper for error messages which can be registered by an BaseControl
656+
subclass in its _configure method. Example:
657+
658+
class MyControl(BaseControl):
659+
def _configure(self, parser):
660+
self.add_error("NAME", 100, "some message: %s")
661+
...
662+
def __call__(self, *args):
663+
self.raise_error("NAME", "my text")
664+
"""
665+
666+
def __init__(self, ctx, rcode, msg):
667+
self.ctx = ctx
668+
self.rcode = rcode
669+
self.msg = msg
670+
671+
def die(self, *args):
672+
"""
673+
Call ctx.die passing the return code and the message for this instance
674+
"""
675+
self.ctx.die(self.rcode, self.msg % tuple(args))
676+
677+
def __str__(self):
678+
return "Error(%d, '%s')" % (self.rcode, self.msg)
679+
680+
653681
class BaseControl(object):
654682
"""Controls get registered with a CLI instance on loadplugins().
655683
@@ -679,6 +707,32 @@ def __init__(self, ctx=None, dir=OMERODIR):
679707
self.ctx = ctx
680708
if self.ctx is None:
681709
self.ctx = Context() # Prevents unncessary stop_event creation
710+
self.__errors = {}
711+
712+
def add_error(self, name, rcode, msg):
713+
"""
714+
Register an Error by name both for discovery via the ErrorsControl
715+
as well as for raising an exception via raise_error.
716+
"""
717+
err = self.__errors.get(name)
718+
if err is not None:
719+
self.ctx.die(2, "Error already exists: %s (%s)" % (name, err))
720+
self.__errors[name] = Error(self.ctx, rcode, msg)
721+
722+
def get_errors(self):
723+
"""
724+
Returns a mapping from name to Error object
725+
"""
726+
return dict(self.__errors)
727+
728+
def raise_error(self, name, *args):
729+
"""
730+
Call die on the named Error using the arguments to format the message
731+
"""
732+
err = self.__errors.get(name)
733+
if err is None:
734+
self.ctx.die(2, "Error doesn't exist: %s" % name)
735+
err.die(*args)
682736

683737
def _isWindows(self):
684738
p_s = platform.system()

components/tools/OmeroPy/src/omero/plugins/admin.py

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,34 @@ def _complete(self, text, line, begidx, endidx):
102102
def _configure(self, parser):
103103
sub = parser.sub()
104104
self._add_diagnostics(parser, sub)
105+
self.add_error(
106+
"NOT_WINDOWS", 123,
107+
"Not Windows")
108+
self.add_error(
109+
"SETUP", 200,
110+
"Error during service user set up: (%s) %s")
111+
self.add_error(
112+
"RUNNING", 201,
113+
"%s is already running. Use stop first")
114+
self.add_error(
115+
"NO_SERVICE", 202,
116+
"%s service deleted.")
117+
self.add_error(
118+
"BAD_CONFIG", 300,
119+
"Bad configuration: No IceGrid.Node.Data property")
120+
self.add_error(
121+
"WIN_CONFIG", 400, """
122+
123+
%s is not in this directory. Aborting...
124+
125+
Please see the installation instructions on modifying
126+
the files for your installation (%s)
127+
with bin\winconfig.bat
128+
129+
""")
130+
self.add_error(
131+
"NO_WIN32", 666,
132+
"Could not import win32service and/or win32evtlogutil")
105133
self.actions = {}
106134

107135
class Action(object):
@@ -551,8 +579,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):
551579
policy_handle, sid_obj, ('SeServiceLogonRight',))
552580
win32security.LsaClose(policy_handle)
553581
except pywintypes.error, details:
554-
self.ctx.die(200, "Error during service user set up:"
555-
" (%s) %s" % (details[0], details[2]))
582+
self.raise_error("SETUP", details[0], details[2])
556583
if not pasw:
557584
try:
558585
pasw = config.as_map()["omero.windows.pass"]
@@ -581,8 +608,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):
581608

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

587613
# Finally, try to start the service - delete if startup fails
588614
hscm = win32service.OpenSCManager(
@@ -597,7 +623,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):
597623
self.ctx.out("%s service startup failed: (%s) %s"
598624
% (svc_name, details[0], details[2]))
599625
win32service.DeleteService(hs)
600-
self.ctx.die(202, "%s service deleted." % svc_name)
626+
self.raise_error("NO_SERVICE", svc_name)
601627
finally:
602628
win32service.CloseServiceHandle(hs)
603629
win32service.CloseServiceHandle(hscm)
@@ -615,20 +641,17 @@ def DumpRecord(record):
615641
else:
616642

617643
def events(self, svc_name):
618-
self.ctx.die(
619-
666, "Could not import win32service and/or win32evtlogutil")
644+
self.raise_error("NO_WIN32")
620645

621646
def _query_service(self, svc_name):
622-
self.ctx.die(
623-
666, "Could not import win32service and/or win32evtlogutil")
647+
self.raise_error("NO_WIN32")
624648

625649
def _start_service(self, config, descript, svc_name, pasw, user):
626-
self.ctx.die(
627-
666, "Could not import win32service and/or win32evtlogutil")
650+
self.raise_error("NO_WIN32")
628651

629652
def _stop_service(self, svc_name):
630-
self.ctx.die(
631-
666, "Could not import win32service and/or win32evtlogutil")
653+
self.raise_error("NO_WIN32")
654+
632655
#
633656
# End Windows Methods
634657
#
@@ -693,15 +716,14 @@ def checkwindows(self, args):
693716
"""
694717
self.check_access(os.R_OK)
695718
if not self._isWindows():
696-
self.ctx.die(123, "Not Windows")
719+
self.raise_error("NOT_WINDOWS")
697720

698721
import Ice
699722
key = "IceGrid.Node.Data"
700723
properties = Ice.createProperties([self._icecfg()])
701724
nodedata = properties.getProperty(key)
702725
if not nodedata:
703-
self.ctx.die(300,
704-
"Bad configuration: No IceGrid.Node.Data property")
726+
self.raise_error("BAD_CONFIG")
705727
nodepath = path(nodedata)
706728
pp = nodepath.parpath(self.ctx.dir)
707729
if pp:
@@ -713,15 +735,7 @@ def checkwindows(self, args):
713735
count = win_set_path(dir=self.ctx.dir)
714736
if count:
715737
return
716-
self.ctx.die(400, """
717-
718-
%s is not in this directory. Aborting...
719-
720-
Please see the installation instructions on modifying
721-
the files for your installation (%s)
722-
with bin\winconfig.bat
723-
724-
""" % (nodedata, self.ctx.dir))
738+
self.raise_error("WIN_CONFIG", nodedata, self.ctx.dir)
725739

726740
##############################################
727741
#

components/tools/OmeroPy/src/omero/plugins/basics.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import sys
2222

23+
from collections import defaultdict
24+
2325
from omero_ext.argparse import FileType
2426

2527
from omero.cli import BaseControl
@@ -240,9 +242,44 @@ def __call__(self, args):
240242
elif args.topic:
241243
self.print_single_command_or_topic(args)
242244

245+
246+
class ErrorsControl(BaseControl):
247+
248+
def _configure(self, parser):
249+
parser.set_defaults(func=self.__call__)
250+
parser.add_argument("--length", default=50, type=int,
251+
help="Length of message to print")
252+
parser.add_argument("plugins", nargs="*", default=(),
253+
help="Limit to these plugins; otherwise all")
254+
255+
def __call__(self, args):
256+
arranged = defaultdict(lambda: defaultdict(
257+
lambda: defaultdict(list)))
258+
for name, control in self.ctx.controls.items():
259+
if not args.plugins or name in args.plugins:
260+
combined = []
261+
if hasattr(control, "get_errors"):
262+
combined.extend(control.get_errors().items())
263+
combined.sort(lambda a, b: cmp(a[1].rcode, b[1].rcode))
264+
for key, err in combined:
265+
arranged[err.rcode][name][key].append(err)
266+
267+
for rcode, names in sorted(arranged.items()):
268+
for name, keys in sorted(names.items()):
269+
for key, errors in sorted(keys.items()):
270+
for err in errors:
271+
msg = err.msg
272+
if len(msg) > (args.length+1):
273+
msg = msg[:args.length] + "..."
274+
msg = msg.replace("\n", " ")
275+
msg = msg.strip()
276+
t = (err.rcode, name, key, msg)
277+
self.ctx.out("%5d\t%10s\t%10s\t'%s'" % t)
278+
243279
controls = {
244280
"help": (HelpControl, "Syntax help for all commands"),
245281
"quit": (QuitControl, "Quit application"),
282+
"errors": (ErrorsControl, "Display all plugin error codes"),
246283
"shell": (ShellControl, """Starts an IPython interpreter session
247284
248285
All arguments not understood vi %(prog)s will be passed to the shell.

components/tools/OmeroPy/src/omero/plugins/server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def _configure(self, parser):
3434
parser.add(sub, self.indexer, help="Start OMERO.indexer")
3535
# web = parser.add(sub, self.web, help = "Start OMERO.web")
3636
# web.add_argument("arg", nargs="*")
37+
self.add_error("NO_CONFIG", 201, "No --Ice.Config provided")
3738

3839
def _prop(self, data, key):
3940
return data.properties.getProperty("omero."+key)
@@ -43,7 +44,7 @@ def _checkIceConfig(self, args):
4344
try:
4445
args["--Ice.Config"]
4546
except KeyError:
46-
self.ctx.die(201, "No --Ice.Config provided")
47+
self.raise_error("NO_CONFIG")
4748
pre = []
4849
post = []
4950
for arg in args.args:

0 commit comments

Comments
 (0)