From 23b5b373ec18974bde6ce4dba006e81b8af1169f Mon Sep 17 00:00:00 2001
Message-Id: <23b5b373ec18974bde6ce4dba006e81b8af1169f.1362430417.git.crobinso@redhat.com>
From: Cole Robinson <crobinso@redhat.com>
Date: Tue, 26 Feb 2013 17:47:37 -0500
Subject: [PATCH] Fix uploading attachments as base64, and test it
Use xmlrpclib's base64 support, rather than a hand rolled one.
---
bugzilla/base.py | 47 +++++++++++++----------------------------
tests/rw_functional.py | 57 +++++++++++++++++++++++++-------------------------
2 files changed, 43 insertions(+), 61 deletions(-)
diff --git a/bugzilla/base.py b/bugzilla/base.py
index 60480dc..faddd0a 100644
--- a/bugzilla/base.py
+++ b/bugzilla/base.py
@@ -9,7 +9,6 @@
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
# the full text of the license.
-import base64
import cookielib
import os
import tempfile
@@ -834,14 +833,22 @@ class BugzillaBase(object):
# Methods for working with attachments #
########################################
+ def _attachment_uri(self, attachid):
+ '''Returns the URI for the given attachment ID.'''
+ att_uri = self.url.replace('xmlrpc.cgi', 'attachment.cgi')
+ att_uri = att_uri + '?id=%s' % attachid
+ return att_uri
+
def attachfile(self, idlist, attachfile, description, **kwargs):
'''
Attach a file to the given bug IDs. Returns the ID of the attachment
or raises xmlrpclib.Fault if something goes wrong.
+
attachfile may be a filename (which will be opened) or a file-like
object, which must provide a 'read' method. If it's not one of these,
this method will raise a TypeError.
description is the short description of this attachment.
+
Optional keyword args are as follows:
file_name: this will be used as the filename for the attachment.
REQUIRED if attachfile is a file-like object with no
@@ -851,9 +858,9 @@ class BugzillaBase(object):
is_private: Set to True if the attachment should be marked private.
is_patch: Set to True if the attachment is a patch.
content_type: The mime-type of the attached file. Defaults to
- application/octet-stream if not set. NOTE that text
- files will *not* be viewable in bugzilla unless you
- remember to set this to text/plain. So remember that!
+ application/octet-stream if not set. NOTE that text
+ files will *not* be viewable in bugzilla unless you
+ remember to set this to text/plain. So remember that!
Returns the list of attachment ids that were added. If only one
attachment was added, we return the single int ID for back compat
@@ -876,15 +883,14 @@ class BugzillaBase(object):
kwargs["file_name"] = kwargs.pop("filename")
kwargs['summary'] = description
+ kwargs['data'] = xmlrpclib.Binary(f.read())
+ kwargs['ids'] = self._listify(idlist)
+
if 'file_name' not in kwargs:
kwargs['file_name'] = os.path.basename(f.name)
-
if 'content_type' not in kwargs:
kwargs['content_type'] = 'application/octet-stream'
- kwargs['data'] = self._attachment_encode(f)
- kwargs['ids'] = self._listify(idlist)
-
ret = self._proxy.Bug.add_attachment(kwargs)
if "attachments" in ret:
@@ -916,31 +922,6 @@ class BugzillaBase(object):
# Hooray, now we have a file-like object with .read() and .name
return att
- def _attachment_uri(self, attachid):
- '''Returns the URI for the given attachment ID.'''
- att_uri = self.url.replace('xmlrpc.cgi', 'attachment.cgi')
- att_uri = att_uri + '?id=%s' % attachid
- return att_uri
-
- def _attachment_encode(self, fh):
- '''Return the contents of the file-like object fh in a form
- appropriate for attaching to a bug in bugzilla. This is the default
- encoding method, base64.'''
- # Read data in chunks so we don't end up with two copies of the file
- # in RAM.
-
- # base64 encoding wants input in multiples of 3
- chunksize = 3072
- data = ''
- chunk = fh.read(chunksize)
- while chunk:
- # we could use chunk.encode('base64') but that throws a newline
- # at the end of every output chunk, which increases the size of
- # the output.
- data = data + base64.b64encode(chunk)
- chunk = fh.read(chunksize)
- return data
-
def updateattachmentflags(self, bugid, attachid, flagname, **kwargs):
'''
Updates a flag for the given attachment ID.
diff --git a/tests/rw_functional.py b/tests/rw_functional.py
index 1b8997d..82e444f 100644
--- a/tests/rw_functional.py
+++ b/tests/rw_functional.py
@@ -357,43 +357,16 @@ class RHPartnerTest(BaseTest):
os.chdir("..")
os.system("rm -r %s" % tmpdir)
-
def _test8Attachments(self):
"""
Get and set attachments for a bug
"""
bz = self.bzclass(url=self.url, cookiefile=cf)
- getbugid = "663674"
+ getallbugid = "663674"
setbugid = "461686"
- attachid = "469147"
cmd = "bugzilla attach "
testfile = "../tests/data/bz-attach-get1.txt"
- # Get first attachment
- out = tests.clicomm(cmd + "--get %s" % attachid, bz).splitlines()
-
- # Expect format:
- # Wrote <filename>
- fname = out[2].split()[1].strip()
-
- self.assertEquals(len(out), 3)
- self.assertEquals(fname, "bugzilla-filename.patch")
- self.assertEquals(file(fname).read(),
- file(testfile).read())
-
- # Get all attachments
- getbug = bz.getbug(getbugid)
- numattach = len(getbug.attachments)
- out = tests.clicomm(cmd + "--getall %s" % getbugid, bz).splitlines()
-
- self.assertEquals(len(out), numattach + 2)
- fnames = [l.split(" ", 1)[1].strip() for l in out[2:]]
- self.assertEquals(len(fnames), numattach)
- for f in fnames:
- if not os.path.exists(f):
- raise AssertionError("filename '%s' not found" % f)
- os.unlink(f)
-
# Add attachment as CLI option
setbug = bz.getbug(setbugid)
orignumattach = len(setbug.attachments)
@@ -418,6 +391,7 @@ class RHPartnerTest(BaseTest):
self.assertEquals(setbug.attachments[-1]["description"], desc2)
self.assertEquals(setbug.attachments[-1]["id"],
int(out2.splitlines()[2].split()[2]))
+ attachid = setbug.attachments[-2]["id"]
# Set attachment flags
self.assertEquals(setbug.attachments[-1]["flags"], [])
@@ -435,6 +409,33 @@ class RHPartnerTest(BaseTest):
self.assertEquals(setbug.attachments[-1]["flags"], [])
+ # Get attachment, verify content
+ out = tests.clicomm(cmd + "--get %s" % attachid, bz).splitlines()
+
+ # Expect format:
+ # Wrote <filename>
+ fname = out[2].split()[1].strip()
+
+ self.assertEquals(len(out), 3)
+ self.assertEquals(fname, "bz-attach-get1.txt")
+ self.assertEquals(file(fname).read(),
+ file(testfile).read())
+ os.unlink(fname)
+
+ # Get all attachments
+ getbug = bz.getbug(getallbugid)
+ numattach = len(getbug.attachments)
+ out = tests.clicomm(cmd + "--getall %s" % getallbugid, bz).splitlines()
+
+ self.assertEquals(len(out), numattach + 2)
+ fnames = [l.split(" ", 1)[1].strip() for l in out[2:]]
+ self.assertEquals(len(fnames), numattach)
+ for f in fnames:
+ if not os.path.exists(f):
+ raise AssertionError("filename '%s' not found" % f)
+ os.unlink(f)
+
+
def test9Whiteboards(self):
bz = self.bzclass(url=self.url, cookiefile=cf)
bug_id = "663674"
--
1.8.1.4