Patch for Python 3 Support
by Ralph Bean
Hello all,
I created a patch to update python-bugzilla to work on both python 2
and python 3. The unit tests all pass under both py2.7 and py3.2.
I have tested the login and query commands by hand on py2.6, py2.7,
and py3.2.
http://threebean.org/patches/python-bugzilla-Python3-support.patch
The patch is against the master branch (7ca8a60). If I should
recreate it against another branch, please let me know.
I'm also willing to update and maintain a spec file supporting both
versions if and when that time comes.
Cheers-
-Ralph
10 years, 10 months
[PATCH 1/2] post_translation is called twice on query
by Don Zickus
I have been fighting with python bugzilla 0.7.0 and 0.8.0 environments for
awhile. Recently I noticed the rhbz_back_compat flag that would help unify
them until everything is straightened out upstream.
However, setting rhbz_back_compat to True caused my scripts which use 'flags'
to fail. The reason is the bugzilla.query command.
After querying for a list of bugs, they get post_translated and then returned
to the query command. The query command the re-initializes the the bugs with a
_Bug object which causes the post_translated bugs to be re-post_translated
again. This causes a fault with the 'flags' attribute as it goes from a list to a
string. :-(
The flow is:
bugzilla/base.py::query()
self._query
bugzilla/rhbugzilla.py::_query()
self.post_translation()
return [_Bug(...]
bugzilla/bug.py::__init__
self._update_dict()
self.bugzilla.post_translation()
^^^^OOPS
I don't know the reason the post_translation moved to begin with, but I am just
moving it back to where it was for now. This fixes my 'flags' issue.
Signed-off-by: Don Zickus <dzickus(a)redhat.com>
---
bugzilla/bug.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/bugzilla/bug.py b/bugzilla/bug.py
index 654d9a9..24bbc59 100644
--- a/bugzilla/bug.py
+++ b/bugzilla/bug.py
@@ -123,6 +123,7 @@ class _Bug(object):
# Use post_translation to convert getbug results to back compat values
q = {}
q["id"] = str(self.bug_id)
+ self.bugzilla.post_translation(q, r)
self._update_dict(r)
@@ -131,7 +132,6 @@ class _Bug(object):
Update internal dictionary, in a way that ensures no duplicate
entries are stored WRT field aliases
'''
- self.bugzilla.post_translation({}, newdict)
for newname, oldname in self.bugzilla.field_aliases:
if not oldname in newdict:
--
1.7.1
11 years, 1 month
[PATCH] Bug cloning
by Martin Cermak
Hi, I belive that cloning a bug is a useful feature. Below I'm
attaching a patch that should do the trick. I attempted to follow
the behaviour of Bugzilla's web UI:
----------------8<---------------------------------------------
diff --git a/bugzilla/base.py b/bugzilla/base.py
index 7748e2b..7e148c4 100644
--- a/bugzilla/base.py
+++ b/bugzilla/base.py
@@ -618,6 +618,17 @@ class BugzillaBase(object):
You may also add a "nomail":1 item, which will suppress email if set.'''
raise NotImplementedError
+ #---- Methods for cloning bugs.
+
+ def _clonebug(self,id,product=None,component=None,version=None):
+ '''IMPLEMENT ME: Clone the given bug. This is the raw call, and no data checking is
+ done here. That's up to the clonebug method.'''
+ raise NotImplementedError
+
+ def clonebug(self,product=None,component=None,version=None):
+ '''Clone the given bug.'''
+ return self.bugzilla._clonebug(self.bug_id,product,component,version)
+
#---- Methods for working with attachments
def _attachment_encode(self,fh):
@@ -1305,6 +1316,11 @@ class _Bug(object):
'''An alias for refresh()'''
self.refresh()
+ def clonebug(self,product=None,component=None,version=None):
+ '''Clone the given bug. This is the raw call, and no data checking is
+ done here. That's up to the clonebug method.'''
+ return self.bugzilla._clonebug(self.bug_id,product,component,version)
+
def setstatus(self,status,comment='',private=False,private_in_it=False,nomail=False):
'''Update the status for this bug report.
Valid values for status are listed in querydefaults['bug_status_list']
diff --git a/bugzilla/rhbugzilla.py b/bugzilla/rhbugzilla.py
index 4233d32..59fb418 100644
--- a/bugzilla/rhbugzilla.py
+++ b/bugzilla/rhbugzilla.py
@@ -130,6 +130,17 @@ class RHBugzilla(Bugzilla4):
requestee (as in: needinfo from smartguy(a)answers.com). Alas.'''
return self._proxy.bugzilla.updateFlags(id,flags)
+ #---- Methods for cloning bugs.
+
+ def _clonebug(self,id,product=None,component=None,version=None):
+ '''IMPLEMENT MEClone the given bug. This is the raw call, and no data checking is
+ done here. That's up to the clonebug method.'''
+ raise NotImplementedError
+
+ def clonebug(self,product=None,component=None,version=None):
+ '''Clone the given bug.'''
+ return self.bugzilla._clonebug(self.bug_id,product,component,version)
+
#---- Methods for working with attachments
# If your bugzilla wants attachments in something other than base64, you
@@ -281,6 +292,94 @@ class RHBugzilla(Bugzilla4):
return self._close_bug(id, update)
#return self._update_bug(id,update)
+ def _clonebug(self,id,product=None,component=None,version=None):
+ '''Clone this bug similarly as one can do it in the webui'''
+ origbug = self.getbug(id)
+ clone_message = '+++ This bug was initially created as a clone of Bug #%s +++\n\n' % (origbug.id)
+ isprivate = False
+ isadditional = False
+ for rec in origbug.longdescs:
+ if isadditional:
+ clone_message += '\n--- Additional comment from '
+ clone_message += rec['author']['realname']
+ clone_message += ' on '
+ clone_message += rec['time']
+ clone_message += ' EDT ---\n\n'
+ if 'extra_data' in rec.keys():
+ clone_message += '\n\n*** This bug has been marked as a duplicate of bug %s ***\n' % (rec['extra_data'])
+ else:
+ clone_message += rec['body'] + "\n"
+ isadditional = True
+ if rec['isprivate'] == 1:
+ isprivate = True
+ cc = origbug.cc
+ cc.append(origbug.reporter)
+ depends_on = str(origbug.bug_id) + ' '
+ depends_on += ' '.join([str(i) for i in origbug.dependson])
+ new_product = product
+ if new_product == None: new_product = origbug.product
+ new_component = component
+ if new_component == None: new_component = origbug.component
+ new_version = version
+ new_target_release = None
+ if new_version == None and new_product != origbug.product:
+ releases = self._proxy.Product.get({'names': new_product, 'include_fields': ['releases']})
+ prodinfo = releases['products'][0]['releases']
+ versions = [p['name'] for p in prodinfo]
+ new_version = versions[-1]
+ elif new_version == None:
+ new_target_release = origbug.target_release
+ new_version = origbug.version
+ kwargs = {
+ 'product': new_product,
+ 'component': new_component,
+ 'version': new_version,
+ 'op_sys': origbug.op_sys,
+ 'platform': origbug.platform,
+ 'summary': origbug.summary,
+ 'description': clone_message,
+ 'comment_is_private': isprivate,
+ 'priority': origbug.priority,
+ 'bug_severity': origbug.bug_severity,
+ 'depends_on': depends_on,
+ 'blocked': origbug.blocked,
+ 'whiteboard': origbug.whiteboard,
+ 'keywords': origbug.keywords,
+ 'cc': cc,
+ 'estimated_time': origbug.estimated_time,
+ 'remaining_time': origbug.remaining_time,
+ 'deadline': origbug.deadline,
+ 'url': origbug.bug_file_loc,
+ 'target_release': new_target_release
+ }
+ for key in kwargs.keys():
+ if kwargs[key] == None:
+ del kwargs[key]
+ newbug = self.createbug(**kwargs)
+ origgroups = [grp['name'] for grp in origbug.groups if grp['ison'] == 1]
+ update = dict(
+ cf_clone_of = str(origbug.id),
+ groups = dict(add = origgroups),
+ cf_qa_whiteboard = origbug.qa_whiteboard,
+ cf_cust_facing = origbug.cust_facing,
+ cf_devel_whiteboard = origbug.devel_whiteboard,
+ cf_internal_whiteboard = origbug.internal_whiteboard,
+ cf_build_id = origbug.build_id,
+ cf_partner = origbug.partner,
+ cf_verified = ['Any'],
+ cf_environment = origbug.cf_environment
+ )
+ changes = self._update_bug(newbug.id,update)
+ # external tracker references
+ extbugs = []
+ for eb in origbug.external_bugs:
+ neb = {}
+ neb['ext_bz_bug_id'] = eb['ext_bz_bug_id']
+ neb['ext_type_id'] = eb['type']['id']
+ extbugs.append(neb)
+ self._proxy.ExternalBugs.add_external_bug({'bug_ids': [newbug.id], 'external_bugs': extbugs})
+ return newbug
+
def _setassignee(self,id,**data):
'''Raw xmlrpc call to set one of the assignee fields on a bug.
changeAssignment($id, $data, $username, $password)
----------------8<---------------------------------------------
Martin
11 years, 1 month
[PATCH] modify: new option --dependson
by Don Zickus
A lot of our bugs are collected into higher level bugzillas. Having the
ability to add, remove, overwrite the --dependson field becomes necessary.
All the backend logic exists, just implement the front end logic.
The patch is pretty straightforward except for the duplicate code to handle
comma seperated lists.
The expected command is
bugzilla modify --dependons=123456,456789 987654
This would add bugs 123456 and 456789 to the depends on field for bz987654
Signed-off-by: Don Zickus <dzickus(a)redhat.com>
---
bin/bugzilla | 25 ++++++++++++++++++++++++-
1 files changed, 24 insertions(+), 1 deletions(-)
diff --git a/bin/bugzilla b/bin/bugzilla
index 3ec2be8..90282e8 100755
--- a/bin/bugzilla
+++ b/bin/bugzilla
@@ -279,6 +279,9 @@ def setup_action_parser(action):
'(Use a new option for each flag)')
p.add_option('--cc', action='append',
help='Add an email to the cc list')
+ p.add_option('--dependson', metavar='BUGID[, BUGID, ...]',
+ help=('Alter depends_on list. BUGID appends, '
+ '-BUGID removes, =BUGID overwrites'))
p.add_option('-F', '--fixed_in', metavar="VERSION",
help='"Fixed in version" field')
p.add_option("", "--whiteboard", metavar="TEXT",
@@ -732,12 +735,32 @@ def _do_modify(bz, opt, args):
add_val = val
return add_val, rm_val, set_val
+ def parse_triset_list(val):
+ val = val or ""
+ add_val = None
+ rm_val = None
+ set_val = None
+
+ if val.startswith("+"):
+ add_val = val[1:].split(",")
+ elif val.startswith("-"):
+ rm_val = val[1:].split(",")
+ elif val.startswith("="):
+ set_val = val[1:].split(",")
+ else:
+ add_val = val.split(",")
+ return add_val, rm_val, set_val
+
add_wb, rm_wb, set_wb = parse_triset(opt.whiteboard)
+ add_deps, rm_deps, set_deps = parse_triset_list(opt.dependson)
update = bz.build_update(
comment=opt.comment or None,
comment_private=opt.private or None,
cc_add=opt.cc and opt.cc[:] or None,
+ depends_on_add=add_deps or None,
+ depends_on_remove=rm_deps or None,
+ depends_on_set=set_deps or None,
qa_contact=opt.qa_contact or None,
assigned_to=opt.assignee or None,
status=status,
@@ -966,7 +989,7 @@ def main(bzinstance=None):
if not (opt.status or opt.close or
opt.assignee or opt.qa_contact or
opt.flag or opt.cc or opt.comment or
- opt.fixed_in or opt.whiteboard):
+ opt.fixed_in or opt.whiteboard or opt.dependson):
parser.error("'modify' command requires additional arguments")
if not args:
--
1.7.1
11 years, 1 month