← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:py3-bug-export into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:py3-bug-export into launchpad:master.

Commit message:
Port bug-export.py to Python 3

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/399018

We need to use io.BytesIO, adjust for Python 3's base64 and xml.etree.ElementTree changes, and use sys.stdout.buffer where appropriate.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-bug-export into launchpad:master.
diff --git a/lib/lp/bugs/doc/bug-export.txt b/lib/lp/bugs/doc/bug-export.txt
index 3df8a3b..b0736e2 100644
--- a/lib/lp/bugs/doc/bug-export.txt
+++ b/lib/lp/bugs/doc/bug-export.txt
@@ -19,13 +19,13 @@ Exporting one bug
 We will export bug #1 in the context of Firefox.  First some initial
 setup:
 
+    >>> import io
     >>> import sys
     >>> try:
     ...     import xml.etree.cElementTree as ET
     ... except ImportError:
     ...     import cElementTree as ET
     >>> from zope.component import getUtility
-    >>> from cStringIO import StringIO
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.interfaces.product import IProductSet
@@ -44,7 +44,9 @@ Now we serialise it as XML, and print it:
 
     >>> node = serialise_bugtask(bugtask)
     >>> tree = ET.ElementTree(node)
-    >>> tree.write(sys.stdout)
+    >>> output = io.BytesIO()
+    >>> tree.write(output)
+    >>> print(output.getvalue().decode('UTF-8'))
     <bug id="1">
     <private>False</private>
     <security_related>False</security_related>
@@ -93,7 +95,8 @@ bugs for a product.  The export_bugtasks() function does this by
 successively serialising each of the tasks for that product.
 
     >>> import transaction
-    >>> export_bugtasks(transaction, firefox, sys.stdout)
+    >>> export_bugtasks(
+    ...     transaction, firefox, getattr(sys.stdout, 'buffer', sys.stdout))
     <launchpad-bugs xmlns="https://launchpad.net/xmlns/2006/bugs";>
     <bug id="1">
     ...
@@ -131,13 +134,11 @@ Attachments are included in the XML dump.  First add an attachment to
 bug #1.  We need to commit here so that the librarian can later serve
 the file when we later serialise the bug:
 
-    >>> from io import BytesIO
-
     >>> login('test@xxxxxxxxxxxxx')
     >>> bug4 = getUtility(IBugSet).get(4)
     >>> sampleperson = getUtility(IPersonSet).getByEmail('test@xxxxxxxxxxxxx')
     >>> bug4.addAttachment(
-    ...     sampleperson, BytesIO(b'Hello World'), 'Added attachment',
+    ...     sampleperson, io.BytesIO(b'Hello World'), 'Added attachment',
     ...     'hello.txt', description='"Hello World" attachment')
     <BugAttachment ...>
 
@@ -181,14 +182,14 @@ script.  To test this, we'll make a bug private:
 
 Now we'll do a dump not including private bugs:
 
-    >>> output = StringIO()
+    >>> output = io.BytesIO()
     >>> export_bugtasks(transaction, firefox, output)
-    >>> '<bug id="4">' in output.getvalue()
+    >>> b'<bug id="4">' in output.getvalue()
     False
 
 However, bug #4 will appear in the export if we include private bugs:
 
-    >>> output = StringIO()
+    >>> output = io.BytesIO()
     >>> export_bugtasks(transaction, firefox, output, include_private=True)
-    >>> '<bug id="4">' in output.getvalue()
+    >>> b'<bug id="4">' in output.getvalue()
     True
diff --git a/lib/lp/bugs/scripts/bugexport.py b/lib/lp/bugs/scripts/bugexport.py
index 34873f1..de9fb66 100644
--- a/lib/lp/bugs/scripts/bugexport.py
+++ b/lib/lp/bugs/scripts/bugexport.py
@@ -8,7 +8,7 @@ __all__ = [
     ]
 
 import base64
-
+import sys
 
 try:
     import xml.etree.cElementTree as ET
@@ -16,15 +16,23 @@ except ImportError:
     import cElementTree as ET
 
 from zope.component import getUtility
+
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
-from lp.services.librarian.browser import ProxiedLibraryFileAlias
+from lp.bugs.browser.bugtask import get_comments_for_bugtask
 from lp.bugs.interfaces.bugtask import IBugTaskSet
 from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
-from lp.bugs.browser.bugtask import get_comments_for_bugtask
+from lp.services.librarian.browser import ProxiedLibraryFileAlias
+
 
 BUGS_XMLNS = 'https://launchpad.net/xmlns/2006/bugs'
 
 
+if sys.version_info[0] >= 3:
+    encodebytes = base64.encodebytes
+else:
+    encodebytes = base64.encodestring
+
+
 def addnode(parent, elementname, content, **attrs):
     node = ET.SubElement(parent, elementname, attrs)
     node.text = content
@@ -95,7 +103,7 @@ def serialise_bugtask(bugtask):
                     attachment.libraryfile.mimetype)
             # Attach the attachment file contents, base 64 encoded.
             addnode(attachment_node, 'contents',
-                    base64.encodestring(attachment.libraryfile.read()))
+                    encodebytes(attachment.libraryfile.read()).decode('ASCII'))
 
     return bug_node
 
@@ -110,13 +118,14 @@ def export_bugtasks(ztm, bugtarget, output, include_private=False):
     ids = [task.id for task in bugtarget.searchTasks(
         BugTaskSearchParams(user=user, omit_dupes=False, orderby='id'))]
     bugtaskset = getUtility(IBugTaskSet)
-    output.write('<launchpad-bugs xmlns="%s">\n' % BUGS_XMLNS)
+    output.write(
+        ('<launchpad-bugs xmlns="%s">\n' % BUGS_XMLNS).encode('UTF-8'))
     for count, taskid in enumerate(ids):
         task = bugtaskset.get(taskid)
         tree = ET.ElementTree(serialise_bugtask(task))
-        tree.write(output)
+        tree.write(output, encoding="UTF-8", xml_declaration=False)
         # Periodically abort the transaction so that we don't lock
         # everyone else out.
         if count % 100:
             ztm.abort()
-    output.write('</launchpad-bugs>\n')
+    output.write(b'</launchpad-bugs>\n')
diff --git a/scripts/bug-export.py b/scripts/bug-export.py
index b06daba..15bf58e 100755
--- a/scripts/bug-export.py
+++ b/scripts/bug-export.py
@@ -33,9 +33,10 @@ class BugExportScript(LaunchpadScript):
     def main(self):
         if self.options.product is None:
             self.parser.error('No product specified')
-        output = sys.stdout
         if self.options.output is not None:
             output = open(self.options.output, 'wb')
+        else:
+            output = getattr(sys.stdout, 'buffer', sys.stdout)
 
         product = getUtility(IProductSet).getByName(self.options.product)
         if product is None: