Skip to content

Commit 2e32856

Browse files
committed
Multiple patches
- Add custom Exception for easier while-loop - About logging: separate setup & level-chang - Patch `_handle_login` again. Fix some bugs and remove unnecessary try-except - Patch `_main` and `main`. Talk is cheap, read the code for details.
1 parent 4a73a69 commit 2e32856

File tree

1 file changed

+132
-97
lines changed

1 file changed

+132
-97
lines changed

bugzilla/_mi.py

Lines changed: 132 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
# See the COPYING file in the top-level directory.
1515

1616
import datetime
17-
import errno
1817
import getpass
1918
import json
2019
import logging
@@ -28,8 +27,6 @@
2827

2928
import requests.exceptions
3029

31-
import bugzilla
32-
3330
import bugzilla
3431
from ._cli import _is_unittest_debug
3532
from ._cli import open_without_clobber
@@ -91,6 +88,7 @@
9188
HANDLE_LOGIN_Y = 0
9289
HANDLE_LOGIN_N = 1
9390

91+
class InterruptLoop(Exception): pass
9492

9593
################
9694
# Patch output #
@@ -122,6 +120,7 @@ def exit_patched(self, status=0, message=None):
122120
emsg = message + \
123121
"\nArgumentParser exit with status {}".format(status)
124122
self._print_message(emsg)
123+
raise InterruptLoop
125124

126125
class Bugzilla_patched(bugzilla.Bugzilla):
127126
""" Patch `bugzilla.Bugzilla` to fit MI
@@ -196,7 +195,7 @@ def interactive_login(self, user=None, password=None, force=False,
196195
# Util helpers #
197196
################
198197

199-
def setup_logging(debug, verbose):
198+
def setup_logging():
200199
""" Patch for redirecting log into file
201200
202201
Avoid interfering with the contents in
@@ -208,6 +207,9 @@ def setup_logging(debug, verbose):
208207
datefmt="%y.%m.%d %H:%M:%S"))
209208
log.addHandler(handler)
210209

210+
def level_logging(debug, verbose):
211+
""" Change log level on the fly
212+
"""
211213
if debug:
212214
log.setLevel(logging.DEBUG)
213215
elif verbose:
@@ -492,121 +494,154 @@ def _handle_login(opt, action, bz):
492494
password = getattr(opt, "pos_password", None) or opt.password
493495
use_key = getattr(opt, "api_key", False)
494496

495-
try:
496-
if use_key:
497-
bz.interactive_save_api_key()
498-
elif do_interactive_login:
499-
swrite(FLAG_HEAD_ILOGIN)
500-
if bz.api_key:
501-
swrite("You already have an API key configured for %s" % bz.url)
502-
swrite("There is no need to cache a login token. Exiting.")
503-
swrite(FLAG_TAIL_ILOGIN)
504-
sflush()
505-
return HANDLE_LOGIN_Y
506-
swrite("Logging into %s" % urllib.parse.urlparse(bz.url)[1])
497+
if use_key:
498+
bz.interactive_save_api_key()
499+
elif do_interactive_login:
500+
swrite(FLAG_HEAD_ILOGIN)
501+
if bz.api_key:
502+
swrite("You already have an API key configured for %s" % bz.url)
503+
swrite("There is no need to cache a login token. Exiting.")
507504
swrite(FLAG_TAIL_ILOGIN)
508505
sflush()
509-
bz.interactive_login(username, password,
510-
restrict_login=opt.restrict_login)
511-
except bugzilla.BugzillaError as e:
512-
swrite(FLAG_HEAD_EXCEPT)
513-
swrite("Hit bugzilla.BugzillaError: %s" % str(e))
514-
swrite(FLAG_TAIL_EXCEPT)
506+
raise InterruptLoop(HANDLE_LOGIN_Y)
507+
swrite("Logging into %s" % urllib.parse.urlparse(bz.url)[1])
508+
swrite(FLAG_TAIL_ILOGIN)
515509
sflush()
516-
return HANDLE_LOGIN_N
510+
bz.interactive_login(username, password,
511+
restrict_login=opt.restrict_login)
517512

518513
if opt.ensure_logged_in and not bz.logged_in:
519-
print("--ensure-logged-in passed but you aren't logged in to %s" %
520-
bz.url)
521-
return HANDLE_LOGIN_N
514+
swrite(FLAG_HEAD_ILOGIN)
515+
swrite("--ensure-logged-in passed but you aren't logged in to %s" % bz.url)
516+
swrite(FLAG_TAIL_ILOGIN)
517+
sflush()
518+
raise InterruptLoop(HANDLE_LOGIN_N)
522519

523520
if is_login_command:
524-
return HANDLE_LOGIN_Y
521+
raise InterruptLoop(HANDLE_LOGIN_Y)
525522

526523

527524
def _main(unittest_bz_instance):
525+
""" (Patched version)
526+
"""
527+
# init argparser & logger
528+
setup_logging()
528529
parser = setup_parser()
529-
opt = parser.parse_args()
530-
action = opt.command
531-
setup_logging(opt.debug, opt.verbose)
532-
533-
log.debug("Launched with command line: %s", " ".join(sys.argv))
534-
log.debug("Bugzilla module: %s", bugzilla)
535-
536-
if unittest_bz_instance:
537-
bz = unittest_bz_instance
538-
else:
539-
bz = _make_bz_instance(opt)
540-
541-
# Handle login options
542-
_handle_login(opt, action, bz)
530+
refresh = False
543531

532+
# main loop
533+
while True:
534+
swrite(FLAG_HEAD_ARGINF)
535+
swrite("ArgumentParser waiting")
536+
swrite(FLAG_TAIL_ARGINF)
537+
sflush()
544538

545-
###########################
546-
# Run the actual commands #
547-
###########################
548-
549-
if hasattr(opt, "outputformat"):
550-
if not opt.outputformat and opt.output not in ['raw', 'json', None]:
551-
opt.outputformat = _convert_to_outputformat(opt.output)
552-
553-
buglist = []
554-
if action == 'info':
555-
_do_info(bz, opt)
556-
557-
elif action == 'query':
558-
buglist = _do_query(bz, opt, parser)
559-
560-
elif action == 'new':
561-
buglist = _do_new(bz, opt, parser)
539+
try:
540+
NewCmd = sreadl().strip()
541+
if (NewCmd == "__REFRESH__"):
542+
refresh = True
543+
continue
544+
NewOpt = parser.parse_args(NewCmd.split())
545+
except InterruptLoop:
546+
continue
547+
level_logging(NewOpt.debug, NewOpt.verbose)
548+
log.debug("Launched with command line: %s", NewCmd)
549+
log.debug("Bugzilla module: %s", bugzilla)
550+
NewAct = NewOpt.command
562551

563-
elif action == 'attach':
564-
if opt.get or opt.getall:
565-
if opt.ids:
566-
parser.error("Bug IDs '%s' not used for "
567-
"getting attachments" % opt.ids)
568-
_do_get_attach(bz, opt)
552+
if unittest_bz_instance:
553+
bz = unittest_bz_instance
569554
else:
570-
_do_set_attach(bz, opt, parser)
555+
bz = _make_bz_instance(NewOpt, force_new=refresh)
556+
refresh = False
571557

572-
elif action == 'modify':
573-
_do_modify(bz, parser, opt)
574-
else: # pragma: no cover
575-
raise RuntimeError("Unexpected action '%s'" % action)
558+
try:
559+
# Handle login options
560+
_handle_login(NewOpt, NewAct, bz)
561+
except InterruptLoop:
562+
continue
563+
except Exception as E:
564+
# not BaseException to jump KeyboardInterrupt
565+
swrite(FLAG_HEAD_EXCEPT)
566+
swrite("Hit %s:\n%s" %(E.__class__.__name__,str(E)))
567+
swrite(FLAG_TAIL_EXCEPT)
568+
sflush()
569+
continue
576570

577-
# If we're doing new/query/modify, output our results
578-
if action in ['new', 'query']:
579-
_format_output(bz, opt, buglist)
571+
if hasattr(NewOpt, "outputformat"):
572+
if not NewOpt.outputformat and NewOpt.output not in ['raw', 'json', None]:
573+
NewOpt.outputformat = _convert_to_outputformat(NewOpt.output)
574+
buglist = []
575+
try:
576+
if NewAct == 'info':
577+
_do_info(bz, NewOpt)
578+
579+
elif NewAct == 'query':
580+
buglist = _do_query(bz, NewOpt, parser)
581+
582+
elif NewAct == 'new':
583+
buglist = _do_new(bz, NewOpt, parser)
584+
585+
elif NewAct == 'attach':
586+
if NewOpt.get or NewOpt.getall:
587+
if NewOpt.ids:
588+
parser.error("Bug IDs '%s' not used for "
589+
"getting attachments" % NewOpt.ids)
590+
_do_get_attach(bz, NewOpt)
591+
else:
592+
_do_set_attach(bz, NewOpt, parser)
593+
594+
elif NewAct == 'modify':
595+
_do_modify(bz, parser, NewOpt)
596+
else:
597+
continue
580598

599+
# If we're doing new/query/modify, output our results
600+
if NewAct in ['new', 'query']:
601+
_format_output(bz, NewOpt, buglist)
602+
except InterruptLoop:
603+
continue
604+
except (xmlrpc.client.Fault, bugzilla.BugzillaError) as e:
605+
swrite(FLAG_HEAD_EXCEPT)
606+
swrite("Server error - %s: %s" %(e.__class__.__name__,str(e)))
607+
swrite(FLAG_TAIL_EXCEPT)
608+
refresh = True
609+
continue
610+
except requests.exceptions.SSLError as e:
611+
# Give SSL recommendations
612+
swrite(FLAG_HEAD_EXCEPT)
613+
swrite("SSL error: %s" % e)
614+
swrite("\nIf you trust the remote server, you can work "
615+
"around this error with `--nosslverify`")
616+
swrite(FLAG_TAIL_EXCEPT)
617+
refresh = True
618+
continue
619+
except (socket.error,
620+
requests.exceptions.HTTPError,
621+
requests.exceptions.ConnectionError,
622+
requests.exceptions.InvalidURL,
623+
xmlrpc.client.ProtocolError) as e:
624+
swrite(FLAG_HEAD_EXCEPT)
625+
swrite("Connection lost/failed - %s: %s" %(e.__class__.__name__,str(e)))
626+
swrite(FLAG_TAIL_EXCEPT)
627+
refresh = True
628+
continue
581629

582630
def main(unittest_bz_instance=None):
631+
""" (Patched version)
632+
"""
583633
try:
584-
try:
585-
return _main(unittest_bz_instance)
586-
except (Exception, KeyboardInterrupt):
587-
log.debug("", exc_info=True)
588-
raise
634+
_main(unittest_bz_instance)
589635
except KeyboardInterrupt:
590-
print("\nExited at user request.")
591-
sys.exit(1)
592-
except (xmlrpc.client.Fault, bugzilla.BugzillaError) as e:
593-
print("\nServer error: %s" % str(e))
594-
sys.exit(3)
595-
except requests.exceptions.SSLError as e:
596-
# Give SSL recommendations
597-
print("SSL error: %s" % e)
598-
print("\nIf you trust the remote server, you can work "
599-
"around this error with:\n"
600-
" bugzilla --nosslverify ...")
601-
sys.exit(4)
602-
except (socket.error,
603-
requests.exceptions.HTTPError,
604-
requests.exceptions.ConnectionError,
605-
requests.exceptions.InvalidURL,
606-
xmlrpc.client.ProtocolError) as e:
607-
print("\nConnection lost/failed: %s" % str(e))
608-
sys.exit(2)
636+
swrite(FLAG_HEAD_STRING)
637+
swrite("Exited at user request")
638+
swrite(FLAG_HEAD_STRING)
639+
sflush()
640+
sys.exit(0)
641+
except BaseException:
642+
log.debug("", exc_info=True)
643+
raise
609644

610645

611646
def mi():
612-
main()
647+
main()

0 commit comments

Comments
 (0)