← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:py3-remaining-unicode into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:py3-remaining-unicode into launchpad:master.

Commit message:
Port remaining unicode() calls to Python 3

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

This unfortunately requires some contextual clues, because `six.text_type(b'foo')` returns `u'foo'` on Python 2 but `"b'foo'"` on Python 3, while `six.ensure_text` works on bytes or text but not on other types.  Use single-argument `six.text_type` in cases where we know that the argument is not bytes, `six.ensure_text` where we know the argument is either bytes or text, and something more elaborate otherwise.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-remaining-unicode into launchpad:master.
diff --git a/cronscripts/librarian-feed-swift.py b/cronscripts/librarian-feed-swift.py
index 3f7d902..de244c2 100755
--- a/cronscripts/librarian-feed-swift.py
+++ b/cronscripts/librarian-feed-swift.py
@@ -11,6 +11,8 @@ import _pythonpath
 
 import os
 
+import six
+
 from lp.services.database.interfaces import ISlaveStore
 from lp.services.librarian.model import LibraryFileContent
 from lp.services.librarianserver import swift
@@ -61,14 +63,14 @@ class LibrarianFeedSwift(LaunchpadCronScript):
                 SELECT MAX(id) FROM LibraryFileContent
                 WHERE datecreated < current_timestamp at time zone 'UTC'
                     - CAST(%s AS INTERVAL)
-                """, (unicode(self.options.start_since),)).get_one()[0]
+                """, (six.text_type(self.options.start_since),)).get_one()[0]
 
         if self.options.end_at:
             self.options.end = ISlaveStore(LibraryFileContent).execute("""
                 SELECT MAX(id) FROM LibraryFileContent
                 WHERE datecreated < current_timestamp at time zone 'UTC'
                     - CAST(%s AS INTERVAL)
-                """, (unicode(self.options.end_at),)).get_one()[0]
+                """, (six.text_type(self.options.end_at),)).get_one()[0]
 
         if self.options.ids and (self.options.start or self.options.end):
             self.parser.error(
diff --git a/lib/lp/answers/model/question.py b/lib/lp/answers/model/question.py
index 31c4ed7..d44bd58 100644
--- a/lib/lp/answers/model/question.py
+++ b/lib/lp/answers/model/question.py
@@ -27,6 +27,7 @@ from lazr.lifecycle.event import (
     )
 from lazr.lifecycle.snapshot import Snapshot
 import pytz
+import six
 from sqlobject import (
     ForeignKey,
     SQLMultipleJoin,
@@ -678,7 +679,7 @@ class Question(SQLBase, BugLinkTargetMixin):
         from lp.bugs.model.bug import Bug
         bug_ids = [
             int(id) for _, id in getUtility(IXRefSet).findFrom(
-                (u'question', unicode(self.id)), types=[u'bug'])]
+                (u'question', six.text_type(self.id)), types=[u'bug'])]
         return list(sorted(
             bulk.load(Bug, bug_ids), key=operator.attrgetter('id')))
 
@@ -689,13 +690,14 @@ class Question(SQLBase, BugLinkTargetMixin):
             props = {}
         # XXX: Should set creator.
         getUtility(IXRefSet).create(
-            {(u'question', unicode(self.id)):
-                {(u'bug', unicode(bug.id)): props}})
+            {(u'question', six.text_type(self.id)):
+                {(u'bug', six.text_type(bug.id)): props}})
 
     def deleteBugLink(self, bug):
         """See BugLinkTargetMixin."""
         getUtility(IXRefSet).delete(
-            {(u'question', unicode(self.id)): [(u'bug', unicode(bug.id))]})
+            {(u'question', six.text_type(self.id)):
+                [(u'bug', six.text_type(bug.id))]})
 
     def setCommentVisibility(self, user, comment_number, visible):
         """See `IQuestion`."""
diff --git a/lib/lp/app/browser/doc/launchpad-search-pages.txt b/lib/lp/app/browser/doc/launchpad-search-pages.txt
index d3ff7f3..83347e4 100644
--- a/lib/lp/app/browser/doc/launchpad-search-pages.txt
+++ b/lib/lp/app/browser/doc/launchpad-search-pages.txt
@@ -5,6 +5,7 @@ Users can search for Launchpad objects and pages from the search form
 located on all pages. The search is performed and displayed by the
 LaunchpadSearchView.
 
+    >>> import six
     >>> from zope.component import getMultiAdapter, getUtility
     >>> from lp.services.webapp.interfaces import ILaunchpadRoot
     >>> from lp.services.webapp.servers import LaunchpadTestRequest
@@ -460,7 +461,7 @@ maximum number of results that can be returned per request.
     >>> len(pages)
     20
     >>> for page in pages[0:5]:
-    ...     unicode(page.title)
+    ...     six.ensure_text(page.title)
     u'Launchpad Bugs'
     u'Bugs in Ubuntu Linux'
     u'Bugs related to Sample Person'
@@ -496,7 +497,7 @@ is 20. That is because there were only 25 matching pages.
     >>> len(pages)
     5
     >>> for page in pages:
-    ...     unicode(page.title)
+    ...     six.ensure_text(page.title)
     u'...Bug... #2 in Ubuntu Hoary: \u201cBlackhole Trash folder\u201d'
     u'...Bug... #2 in mozilla-firefox (Debian): ...Blackhole Trash folder...'
     u'...Bug... #3 in mozilla-firefox (Debian): \u201cBug Title Test\u201d'
diff --git a/lib/lp/archiveuploader/tests/test_uploadprocessor.py b/lib/lp/archiveuploader/tests/test_uploadprocessor.py
index 1a23cea..b469bd4 100644
--- a/lib/lp/archiveuploader/tests/test_uploadprocessor.py
+++ b/lib/lp/archiveuploader/tests/test_uploadprocessor.py
@@ -17,6 +17,7 @@ from StringIO import StringIO
 import tempfile
 
 from fixtures import MonkeyPatch
+import six
 from storm.locals import Store
 from zope.component import (
     getGlobalSiteManager,
@@ -204,9 +205,9 @@ class TestUploadProcessorBase(TestCaseWithFactory):
         """Publish a single package that is currently NEW in the queue."""
         self.switchToAdmin()
 
-        packagename = unicode(packagename)
+        packagename = six.ensure_text(packagename)
         if version is not None:
-            version = unicode(version)
+            version = six.ensure_text(version)
         queue_items = self.breezy.getPackageUploads(
             status=PackageUploadStatus.NEW, name=packagename,
             version=version, exact_match=True, archive=archive)
diff --git a/lib/lp/blueprints/browser/specification.py b/lib/lp/blueprints/browser/specification.py
index d39b591..6dfd039 100644
--- a/lib/lp/blueprints/browser/specification.py
+++ b/lib/lp/blueprints/browser/specification.py
@@ -54,6 +54,7 @@ from lazr.restful.interfaces import (
     IJSONRequestCache,
     IWebServiceClientRequest,
     )
+import six
 from zope import component
 from zope.component import getUtility
 from zope.error.interfaces import IErrorReportingUtility
@@ -1376,10 +1377,10 @@ def to_DOT_ID(value):
     u'"foo \\" bar \\n"'
 
     """
-    if isinstance(value, str):
-        unitext = unicode(value, encoding='ascii')
+    if isinstance(value, bytes):
+        unitext = six.ensure_text(value, encoding='ascii')
     else:
-        unitext = unicode(value)
+        unitext = six.text_type(value)
     output = unitext.replace(u'"', u'\\"')
     output = output.replace(u'\n', u'\\n')
     return u'"%s"' % output
diff --git a/lib/lp/blueprints/model/specification.py b/lib/lp/blueprints/model/specification.py
index 4b37d10..7ac13e5 100644
--- a/lib/lp/blueprints/model/specification.py
+++ b/lib/lp/blueprints/model/specification.py
@@ -15,6 +15,7 @@ import operator
 
 from lazr.lifecycle.event import ObjectCreatedEvent
 from lazr.lifecycle.objectdelta import ObjectDelta
+import six
 from sqlobject import (
     BoolCol,
     ForeignKey,
@@ -794,7 +795,7 @@ class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
         from lp.bugs.model.bug import Bug
         bug_ids = [
             int(id) for _, id in getUtility(IXRefSet).findFrom(
-                (u'specification', unicode(self.id)), types=[u'bug'])]
+                (u'specification', six.text_type(self.id)), types=[u'bug'])]
         return list(sorted(
             bulk.load(Bug, bug_ids), key=operator.attrgetter('id')))
 
@@ -804,14 +805,14 @@ class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
             props = {}
         # XXX: Should set creator.
         getUtility(IXRefSet).create(
-            {(u'specification', unicode(self.id)):
-                {(u'bug', unicode(bug.id)): props}})
+            {(u'specification', six.text_type(self.id)):
+                {(u'bug', six.text_type(bug.id)): props}})
 
     def deleteBugLink(self, bug):
         """See BugLinkTargetMixin."""
         getUtility(IXRefSet).delete(
-            {(u'specification', unicode(self.id)):
-                [(u'bug', unicode(bug.id))]})
+            {(u'specification', six.text_type(self.id)):
+                [(u'bug', six.text_type(bug.id))]})
 
     # sprint linking
     def linkSprint(self, sprint, user):
diff --git a/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt b/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt
index 4c7ffb2..af5e43f 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt
@@ -74,7 +74,8 @@ a "proposed" goal.
 The spec will not show up immediately as a Grumpy goal since it must
 first be approved.
 
-  >>> result = unicode(http(r"""
+  >>> import six
+  >>> result = six.text_type(http(r"""
   ... GET /ubuntu/hoary/+specs HTTP/1.1
   ... """))
   >>> '<td>CD Media Integrity Check' not in result
diff --git a/lib/lp/buildmaster/model/packagebuild.py b/lib/lp/buildmaster/model/packagebuild.py
index b4d6070..9b84aee 100644
--- a/lib/lp/buildmaster/model/packagebuild.py
+++ b/lib/lp/buildmaster/model/packagebuild.py
@@ -58,7 +58,8 @@ class PackageBuildMixin(BuildFarmJobMixin):
 
         if (status == BuildStatus.MANUALDEPWAIT and slave_status is not None
             and slave_status.get('dependencies') is not None):
-            self.dependencies = unicode(slave_status.get('dependencies'))
+            self.dependencies = six.ensure_text(
+                slave_status.get('dependencies'))
         else:
             self.dependencies = None
 
diff --git a/lib/lp/codehosting/inmemory.py b/lib/lp/codehosting/inmemory.py
index 2b704fd..a387cdb 100644
--- a/lib/lp/codehosting/inmemory.py
+++ b/lib/lp/codehosting/inmemory.py
@@ -433,7 +433,7 @@ class FakeObjectFactory(ObjectFactory):
         else:
             sourcepackagename = sourcepackage.sourcepackagename
             distroseries = sourcepackage.distroseries
-        IBranch['name'].validate(unicode(name))
+        IBranch['name'].validate(six.ensure_text(name))
         branch = FakeBranch(
             branch_type, name=name, owner=owner, url=url,
             stacked_on=stacked_on, product=product,
diff --git a/lib/lp/scripts/garbo.py b/lib/lp/scripts/garbo.py
index 09a64ed..6e0feac 100644
--- a/lib/lp/scripts/garbo.py
+++ b/lib/lp/scripts/garbo.py
@@ -32,6 +32,7 @@ import iso8601
 from psycopg2 import IntegrityError
 import pytz
 import simplejson
+import six
 from storm.expr import (
     And,
     Cast,
@@ -152,7 +153,7 @@ def load_garbo_job_state(job_name):
     # Load the json state data for the given job name.
     job_data = IMasterStore(Person).execute(
         "SELECT json_data FROM GarboJobState WHERE name = ?",
-        params=(unicode(job_name),)).get_one()
+        params=(six.ensure_text(job_name),)).get_one()
     if job_data:
         return simplejson.loads(job_data[0])
     return None
@@ -164,11 +165,12 @@ def save_garbo_job_state(job_name, job_data):
     json_data = simplejson.dumps(job_data, ensure_ascii=False)
     result = store.execute(
         "UPDATE GarboJobState SET json_data = ? WHERE name = ?",
-        params=(json_data, unicode(job_name)))
+        params=(json_data, six.ensure_text(job_name)))
     if result.rowcount == 0:
         store.execute(
         "INSERT INTO GarboJobState(name, json_data) "
-        "VALUES (?, ?)", params=(unicode(job_name), unicode(json_data)))
+        "VALUES (?, ?)",
+        params=(six.ensure_text(job_name), six.ensure_text(json_data)))
 
 
 class BulkPruner(TunableLoop):
diff --git a/lib/lp/snappy/browser/snap.py b/lib/lp/snappy/browser/snap.py
index bda9483..c4c8c8b 100644
--- a/lib/lp/snappy/browser/snap.py
+++ b/lib/lp/snappy/browser/snap.py
@@ -23,6 +23,7 @@ from lazr.restful.interface import (
     copy_field,
     use_template,
     )
+import six
 from six.moves.urllib.parse import urlencode
 from zope.component import getUtility
 from zope.error.interfaces import IErrorReportingUtility
@@ -837,7 +838,7 @@ class SnapAuthorizeView(LaunchpadEditFormView):
                 ])
             return login_url
         except CannotAuthorizeStoreUploads as e:
-            request.response.addInfoNotification(unicode(e))
+            request.response.addInfoNotification(six.text_type(e))
             request.response.redirect(canonical_url(snap))
             return
 
diff --git a/lib/lp/snappy/model/snapbuild.py b/lib/lp/snappy/model/snapbuild.py
index 6971e83..69cbcfe 100644
--- a/lib/lp/snappy/model/snapbuild.py
+++ b/lib/lp/snappy/model/snapbuild.py
@@ -13,6 +13,7 @@ from datetime import timedelta
 from operator import attrgetter
 
 import pytz
+import six
 from storm.expr import (
     Column,
     Table,
@@ -378,7 +379,7 @@ class SnapBuild(PackageBuildMixin, Storm):
         if slave_status is not None:
             revision_id = slave_status.get("revision_id")
             if revision_id is not None:
-                self.revision_id = unicode(revision_id)
+                self.revision_id = six.ensure_text(revision_id)
         if status != old_status:
             notify(SnapBuildStatusChangedEvent(self))
 
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 0cb90a9..7df8b84 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -1840,7 +1840,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             paths = [self.getUniqueUnicode('refs/heads/path')]
         refs_info = {
             path: {
-                u"sha1": unicode(
+                u"sha1": six.ensure_text(
                     hashlib.sha1(path.encode('utf-8')).hexdigest()),
                 u"type": GitObjectType.COMMIT,
                 }
@@ -3798,7 +3798,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             creator = self.makePerson()
 
         if version is None:
-            version = unicode(self.getUniqueInteger()) + 'version'
+            version = six.text_type(self.getUniqueInteger()) + 'version'
 
         if copyright is None:
             copyright = self.getUniqueString()
@@ -4563,7 +4563,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         """Create a new `PlainPackageCopyJob`."""
         if package_name is None and package_version is None:
             package_name = self.makeSourcePackageName().name
-            package_version = unicode(self.getUniqueInteger()) + 'version'
+            package_version = (
+                six.text_type(self.getUniqueInteger()) + 'version')
         if source_archive is None:
             source_archive = self.makeArchive()
         if target_archive is None:
diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
index 949dae6..71045f0 100644
--- a/lib/lp/testing/pages.py
+++ b/lib/lp/testing/pages.py
@@ -405,7 +405,7 @@ def extract_text(content, extract_image_text=False, skip_tags=None,
         if type(node) in IGNORED_ELEMENTS:
             continue
         elif isinstance(node, CData):
-            result.append(unicode(node))
+            result.append(six.text_type(node))
         elif isinstance(node, NavigableString):
             result.append(node.format_string(node, formatter=formatter))
         else:
diff --git a/scripts/generate-access-token.py b/scripts/generate-access-token.py
index 403b0f5..55d7acd 100755
--- a/scripts/generate-access-token.py
+++ b/scripts/generate-access-token.py
@@ -10,6 +10,7 @@ import _pythonpath
 
 import sys
 
+import six
 from zope.component import getUtility
 
 from lp.registry.interfaces.person import IPersonSet
@@ -45,7 +46,7 @@ class AccessTokenGenerator(LaunchpadScript):
             self.parser.error('No username supplied')
         username = self.args[0]
 
-        key = unicode(self.options.consumer_name)
+        key = six.ensure_text(self.options.consumer_name)
         consumer = getUtility(IOAuthConsumerSet).new(key, u'')
         request_token, _ = consumer.newRequestToken()