Skip to content

Commit 4021245

Browse files
committed
bugzilla: Rework output formatting
So that all options just build an outputformat string. This allows us to centralize parameter->string and parameter->include_fields conversions.
1 parent f8cb541 commit 4021245

4 files changed

Lines changed: 129 additions & 129 deletions

File tree

bin/bugzilla

Lines changed: 101 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -475,34 +475,30 @@ def do_query(bz, opt, parser):
475475
setattr(opt, optname, val.split(","))
476476

477477
include_fields = None
478-
# To optimize speed and reduce network traffic through lookups, we
479-
# specifically tell bugzilla the exact data we want. This allows us
480-
# make one call to output the data, rather than have a follow-on
481-
# getbug() to grab more info than what the default has.
482-
# Testing has shown this to be a _huge_ time saver. Unfortunately,
483-
# this list and the output format have to be in _sync_. Otherwise,
484-
# you lose speed by doing a look-up for each new output element.
485-
if opt.output == 'oneline':
486-
include_fields = ['bug_id', 'bug_status', 'assigned_to',
487-
'component', 'target_milestone', 'short_desc', 'flags',
488-
'keywords', 'blockedby']
489-
490-
elif opt.output == 'normal':
491-
include_fields = ['bug_id', 'bug_status', 'assigned_to',
492-
'short_desc']
493-
494-
elif opt.output == 'raw':
478+
if opt.output == 'raw':
495479
# 'raw' always does a getbug() call anyways, so just ask for ID back
496480
include_fields = ['id']
497481

498-
if opt.outputformat:
482+
elif opt.outputformat:
499483
include_fields = []
500484
for fieldname, rest in format_field_re.findall(opt.outputformat):
501485
if fieldname == "whiteboard" and rest:
502486
fieldname = rest + "_" + fieldname
503487
elif fieldname == "flag":
504488
fieldname = "flags"
505-
include_fields.append(fieldname)
489+
elif fieldname == "cve":
490+
fieldname = ["keywords", "blocks"]
491+
elif fieldname == "__unicode__":
492+
# Needs to be in sync with bug.__unicode__
493+
fieldname = ["id", "status", "assigned_to", "summary"]
494+
495+
flist = type(fieldname) is list and fieldname or [fieldname]
496+
for f in flist:
497+
if f not in include_fields:
498+
include_fields.append(f)
499+
500+
if include_fields is not None:
501+
include_fields.sort()
506502

507503
built_query = bz.build_query(
508504
product=getattr(opt, "product", None),
@@ -588,113 +584,102 @@ def _do_info(bz, opt):
588584
break
589585

590586

591-
def _format_output(bz, opt, buglist):
592-
if opt.outputformat:
593-
def bug_field(matchobj):
594-
# whiteboard and flag allow doing
595-
# %{whiteboard:devel} and %{flag:needinfo}
596-
# That's what 'rest' matches
597-
(fieldname, rest) = matchobj.groups()
587+
def _convert_to_outputformat(output):
588+
fmt = ""
598589

599-
if fieldname == "whiteboard" and rest:
600-
fieldname = rest + "_" + fieldname
590+
if output == "normal":
591+
fmt = "%{__unicode__}"
601592

602-
if fieldname == "flag":
603-
val = b.get_flag_status(rest)
604-
else:
605-
val = getattr(b, fieldname, "")
593+
elif output == "ids":
594+
fmt = "%{id}"
606595

607-
if type(val) is list:
608-
val = ','.join(val)
596+
elif output == 'full':
597+
fmt += "%{__unicode__}\n"
598+
fmt += "CC: %{cc}\n"
599+
fmt += "Blocked: %{blocks}\n"
600+
fmt += "Depends: %{depends_on}\n"
601+
fmt += "%{comments}\n"
609602

610-
return str(val)
603+
elif output == 'extra':
604+
fmt += "%{__unicode__}\n"
605+
fmt += " +Keywords: %{keywords}\n"
606+
fmt += " +QA Whiteboard: %{qa_whiteboard}\n"
607+
fmt += " +Status Whiteboard: %{status_whiteboard}\n"
608+
fmt += " +Devel Whiteboard: %{devel_whiteboard}\n"
611609

612-
for b in buglist:
613-
print format_field_re.sub(bug_field, opt.outputformat)
610+
elif output == 'oneline':
611+
fmt += "#%{bug_id} %{status} %{assigned_to} %{component}\t"
612+
fmt += "[%{target_milestone}] %{flags} %{cve}"
614613

615-
elif opt.output == 'ids':
616-
for b in buglist:
617-
print b.bug_id
618-
619-
elif opt.output == 'full':
620-
fullbuglist = bz.getbugs([b.bug_id for b in buglist])
621-
for b in fullbuglist:
622-
print b
623-
624-
if hasattr(b, "cc"):
625-
print "CC: %s" % " ".join(b.cc)
626-
if hasattr(b, "blocked"):
627-
print "Blocked: %s" % " ".join([str(i)
628-
for i in b.blocked or []])
629-
if hasattr(b, "dependson"):
630-
print ("Depends: %s" %
631-
" ".join([str(i) for i in b.dependson or []]))
632-
633-
for c in getattr(b, "longdescs", []):
634-
print to_encoding(u"* %s - %s:\n%s\n" % (c['time'],
635-
c['author'], c['text']))
636-
637-
elif opt.output == 'normal':
638-
for b in buglist:
639-
print b
640-
641-
elif opt.output == 'extra':
642-
print "Grabbing 'extra' bug information. This could take a moment."
643-
fullbuglist = bz.getbugs([b.bug_id for b in buglist])
644-
for b in fullbuglist:
645-
print b
646-
if hasattr(b, 'keywords') and b.keywords:
647-
print to_encoding(u" +Keywords: %s" % b.keywords)
648-
if hasattr(b, 'qa_whiteboard') and b.qa_whiteboard:
649-
print to_encoding(u" +QA Whiteboard: %s" % b.qa_whiteboard)
650-
if hasattr(b, 'status_whiteboard') and b.status_whiteboard:
651-
print to_encoding(u" +Status Whiteboard: %s" %
652-
b.status_whiteboard)
653-
if hasattr(b, 'devel_whiteboard') and b.devel_whiteboard:
654-
print to_encoding(u" +Devel Whiteboard: %s" %
655-
b.devel_whiteboard)
656-
print "\nBugs listed: ", len(buglist)
657-
658-
elif opt.output == 'oneline':
614+
else:
615+
raise RuntimeError("Unknown output type '%s'" % opt.output)
616+
617+
return fmt
618+
619+
620+
def _format_output(bz, opt, buglist):
621+
if opt.output == 'raw':
622+
buglist = bz.getbugs([b.bug_id for b in buglist])
659623
for b in buglist:
660-
cve = ""
661-
flags = ""
662-
if hasattr(b, "flags"):
663-
for flag in b.flags:
664-
flags = " ".join([f["name"] + f["status"]
665-
for f in b.flags])
666-
667-
keywords = getattr(b, "keywords", "")
668-
if type(keywords) is list:
669-
keywords = " ".join(keywords)
670-
671-
# grab CVE from keywords and blockers
672-
if keywords.find("Security") != -1 and b.blockedby:
673-
for bl in str(b.blockedby).split(','):
674-
cvebug = bz.getbug(bl)
675-
for cb in cvebug.alias:
676-
if cb.find("CVE") != -1:
677-
cve += cb + " "
678-
679-
# bugzilla.redhat.com has component as a list
680-
if type(b.component) == list:
681-
b.component = ','.join(b.component)
682-
print to_encoding(u"#%s %8s %22s %s\t[%s] %s %s" %
683-
(b.bug_id, b.bug_status, b.assigned_to, b.component,
684-
b.target_milestone, flags, cve))
685-
686-
elif opt.output == 'raw':
687-
fullbuglist = bz.getbugs([b.bug_id for b in buglist])
688-
for b in fullbuglist:
689624
print "Bugzilla %s: " % b.bug_id
690625
for a in dir(b):
691626
if a.startswith("__") and a.endswith("__"):
692627
continue
693628
print to_encoding(u"ATTRIBUTE[%s]: %s" % (a, getattr(b, a)))
694629
print "\n\n"
630+
return
695631

696-
else:
697-
raise RuntimeError("Unknown output type '%s'" % opt.output)
632+
def bug_field(matchobj):
633+
# whiteboard and flag allow doing
634+
# %{whiteboard:devel} and %{flag:needinfo}
635+
# That's what 'rest' matches
636+
(fieldname, rest) = matchobj.groups()
637+
638+
if fieldname == "whiteboard" and rest:
639+
fieldname = rest + "_" + fieldname
640+
641+
if fieldname == "flag" and rest:
642+
val = b.get_flag_status(rest)
643+
644+
elif fieldname == "flags":
645+
val = ",".join([f["name"] + f["status"]
646+
for f in getattr(b, "flags", [])])
647+
648+
elif fieldname == "cve":
649+
cves = []
650+
for key in getattr(b, "keywords", []):
651+
# grab CVE from keywords and blockers
652+
if key.find("Security") == -1:
653+
continue
654+
for bl in b.blocks:
655+
cvebug = bz.getbug(bl)
656+
for cb in cvebug.alias:
657+
if cb.find("CVE") == -1:
658+
continue
659+
if cb.strip() not in cves:
660+
cves.append(cb)
661+
val = ",".join(cves)
662+
663+
elif fieldname == "comments":
664+
val = ""
665+
for c in getattr(b, "comments", []):
666+
val += ("\n* %s - %s:\n%s\n" %
667+
(c['time'], c['author'], c['text']))
668+
669+
elif fieldname == "__unicode__":
670+
val = unicode(b)
671+
else:
672+
val = getattr(b, fieldname, "")
673+
674+
if type(val) is list:
675+
val = ','.join(val)
676+
elif type(val) is int:
677+
val = str(val)
678+
679+
return to_encoding(val)
680+
681+
for b in buglist:
682+
print format_field_re.sub(bug_field, opt.outputformat)
698683

699684

700685
def _do_new(bz, opt):
@@ -914,6 +899,10 @@ def main(bzinstance=None):
914899
# Run the actual commands #
915900
###########################
916901

902+
if hasattr(opt, "outputformat"):
903+
if not opt.outputformat and opt.output not in ['raw', None]:
904+
opt.outputformat = _convert_to_outputformat(opt.output)
905+
917906
buglist = []
918907
if action == 'info':
919908
if args:

bugzilla/base.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,18 @@ def url_to_query(url):
134134
(ignore, ignore, path,
135135
ignore, query, ignore) = urlparse.urlparse(url)
136136

137-
if os.path.basename(path) in ('buglist.cgi', 'query.cgi'):
138-
for (k, v) in urlparse.parse_qsl(query):
139-
if k not in q:
140-
q[k] = v
141-
elif isinstance(q[k], list):
142-
q[k].append(v)
137+
if os.path.basename(path) not in ('buglist.cgi', 'query.cgi'):
138+
return {}
139+
140+
for (k, v) in urlparse.parse_qsl(query):
141+
if k not in q:
142+
q[k] = v
143+
elif isinstance(q[k], list):
144+
q[k].append(v)
143145
else:
144146
oldv = q[k]
145147
q[k] = [oldv, v]
148+
146149
return q
147150

148151

tests/query.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,14 @@ def testBooleanChart(self):
138138
class BZ4Test(BZ34Test):
139139
bz = bz4
140140

141-
_default_includes = ['assigned_to', 'summary', 'status', 'id']
141+
_default_includes = ['assigned_to', 'id', 'status', 'summary']
142142

143143
_basic_query_out = BZ34Test._basic_query_out.copy()
144144
_basic_query_out["include_fields"] = _default_includes
145145

146146
_oneline_out = BZ34Test._oneline_out.copy()
147-
_oneline_out["include_fields"] = ['assigned_to', 'component',
148-
'target_milestone', 'flags', 'keywords', 'summary', 'status', 'id',
149-
'blocks']
147+
_oneline_out["include_fields"] = ['assigned_to', 'blocks', 'component',
148+
'flags', 'keywords', 'status', 'target_milestone', 'id']
150149

151150
_output_format_out = BZ34Test._output_format_out.copy()
152151
_output_format_out["include_fields"] = ['product', 'summary',
@@ -193,7 +192,7 @@ class RHBZTest(BZ4Test):
193192
'emailtype3': 'substring', 'emailtype4': 'substring',
194193
'emailcc1': True, 'emailassigned_to2': True,
195194
'emailreporter3': True, 'emailqa_contact4': True,
196-
'include_fields': ['assigned_to', 'summary', 'status', 'id'],
195+
'include_fields': BZ4Test._default_includes,
197196
'query_format': 'advanced'}
198197
_booleans_out = {'value2-0-0': 'baz foo', 'value0-0-0': '123456',
199198
'type3-0-1': 'substring', 'value1-1-0': 'devel_ack', 'type0-0-0':
@@ -204,13 +203,13 @@ class RHBZTest(BZ4Test):
204203
'substring', 'type1-0-0': 'substring', 'field1-1-0':
205204
'flagtypes.name', 'negate2': 1, 'field2-0-0':
206205
'cf_qa_whiteboard', 'type3-0-0': 'substring', 'field0-0-0':
207-
'blocked', 'include_fields': ['assigned_to', 'summary', 'status',
208-
'id'], 'query_format': 'advanced'}
206+
'blocked', 'include_fields': BZ4Test._default_includes,
207+
'query_format': 'advanced'}
209208
_booleans_chart_out = {'value1-0-1': 'wee', 'value2-0-0': 'yargh',
210209
'field2-0-0': 'foo', 'value0-0-0': 'Partner', 'type0-0-0':
211210
'substring', 'type2-0-0': 'bar', 'field1-0-1': 'foo', 'field1-0-0':
212211
'foo', 'value1-0-0': 'baz', 'field0-1-0': 'keywords', 'field0-0-0':
213212
'keywords', 'type1-0-0': 'bar', 'type1-0-1': 'bar', 'negate2': 1,
214213
'type0-1-0': 'notsubstring', 'value0-1-0': 'OtherQA',
215-
'include_fields': ['assigned_to', 'summary', 'status', 'id'],
214+
'include_fields': BZ4Test._default_includes,
216215
'query_format': 'advanced'}

tests/ro_functional.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# -*- encoding: utf-8 -*-
2+
13
#
24
# Copyright Red Hat, Inc. 2012
35
#
@@ -71,7 +73,7 @@ def _testQuery(self, args, mincount, expectbug):
7173
if expectexc:
7274
return
7375

74-
self.assertTrue(len(out) >= mincount)
76+
self.assertTrue(len(out.splitlines()) >= mincount)
7577
self.assertTrue(any([l.startswith("#" + expectbug)
7678
for l in out.splitlines()]))
7779

@@ -183,14 +185,14 @@ class RHTest(BaseTest):
183185
test7 = lambda s: BaseTest._testQueryRaw(s, "307471", 70,
184186
"ATTRIBUTE[whiteboard]: bzcl34nup")
185187
test8 = lambda s: BaseTest._testQueryOneline(s, "785016",
186-
"[---] fedora-review+ fedora-cvs+")
188+
"[---] fedora-review+,fedora-cvs+")
187189
test9 = lambda s: BaseTest._testQueryExtra(s, "307471",
188190
" +Status Whiteboard: bzcl34nup")
189191
test10 = lambda s: BaseTest._testQueryFormat(s,
190192
"--bug_id 307471 --outputformat=\"id=%{bug_id} "
191193
"sw=%{whiteboard:status} needinfo=%{flag:needinfo} "
192194
"sum=%{summary}\"",
193-
"id=307471 sw= bzcl34nup needinfo=None")
195+
"id=307471 sw= bzcl34nup needinfo= ")
194196
test11 = lambda s: BaseTest._testQueryURL(s,
195197
"https://bugzilla.redhat.com/buglist.cgi?f1=creation_ts"
196198
"&list_id=973582&o1=greaterthaneq&classification=Fedora&"
@@ -202,6 +204,13 @@ class RHTest(BaseTest):
202204
"sw=%{whiteboard:status} flag=%{flag:fedora-review} "
203205
"sum=%{summary}\"",
204206
"id=785016 sw= flag=+")
207+
# Unicode in this bugs summary
208+
test13 = lambda s: BaseTest._testQueryFormat(s,
209+
"--bug_id 522796 --outputformat \"%{summary}\"",
210+
"V34 — system")
211+
# CVE bug
212+
test14 = lambda s: BaseTest._testQueryOneline(s, "720784",
213+
" CVE-2011-2527")
205214

206215
def testQueryFixedIn(self):
207216
out = self.clicomm("query --fixed_in anaconda-15.29-1")

0 commit comments

Comments
 (0)