← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:stormify-scriptactivity into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:stormify-scriptactivity into launchpad:master.

Commit message:
Convert ScriptActivity to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394785
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-scriptactivity into launchpad:master.
diff --git a/lib/lp/services/scripts/model/scriptactivity.py b/lib/lp/services/scripts/model/scriptactivity.py
index 46f76de..7984696 100644
--- a/lib/lp/services/scripts/model/scriptactivity.py
+++ b/lib/lp/services/scripts/model/scriptactivity.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -10,11 +10,17 @@ __all__ = [
 
 import socket
 
-from sqlobject import StringCol
+import pytz
+import six
+from storm.locals import (
+    DateTime,
+    Int,
+    Unicode,
+    )
 from zope.interface import implementer
 
-from lp.services.database.datetimecol import UtcDateTimeCol
-from lp.services.database.sqlbase import SQLBase
+from lp.services.database.interfaces import IStore
+from lp.services.database.stormbase import StormBase
 from lp.services.scripts.interfaces.scriptactivity import (
     IScriptActivity,
     IScriptActivitySet,
@@ -22,12 +28,22 @@ from lp.services.scripts.interfaces.scriptactivity import (
 
 
 @implementer(IScriptActivity)
-class ScriptActivity(SQLBase):
+class ScriptActivity(StormBase):
+
+    __storm_table__ = 'ScriptActivity'
+
+    id = Int(primary=True)
+    name = Unicode(allow_none=False)
+    hostname = Unicode(allow_none=False)
+    date_started = DateTime(tzinfo=pytz.UTC, allow_none=False)
+    date_completed = DateTime(tzinfo=pytz.UTC, allow_none=False)
 
-    name = StringCol(notNull=True)
-    hostname = StringCol(notNull=True)
-    date_started = UtcDateTimeCol(notNull=True)
-    date_completed = UtcDateTimeCol(notNull=True)
+    def __init__(self, name, hostname, date_started, date_completed):
+        super(ScriptActivity, self).__init__()
+        self.name = name
+        self.hostname = hostname
+        self.date_started = date_started
+        self.date_completed = date_completed
 
 
 @implementer(IScriptActivitySet)
@@ -38,11 +54,14 @@ class ScriptActivitySet:
         """See IScriptActivitySet"""
         if hostname is None:
             hostname = socket.gethostname()
-        return ScriptActivity(
-            name=name, hostname=hostname, date_started=date_started,
-            date_completed=date_completed)
+        activity = ScriptActivity(
+            name=six.ensure_text(name), hostname=six.ensure_text(hostname),
+            date_started=date_started, date_completed=date_completed)
+        IStore(ScriptActivity).add(activity)
+        return activity
 
     def getLastActivity(self, name):
         """See IScriptActivitySet"""
-        return ScriptActivity.selectFirstBy(
-            name=name, orderBy='-date_started')
+        rows = IStore(ScriptActivity).find(
+            ScriptActivity, name=six.ensure_text(name))
+        return rows.order_by(ScriptActivity.date_started).last()
diff --git a/lib/lp/services/statistics/model/statistics.py b/lib/lp/services/statistics/model/statistics.py
index 7cbb6cb..eabd9a2 100644
--- a/lib/lp/services/statistics/model/statistics.py
+++ b/lib/lp/services/statistics/model/statistics.py
@@ -175,7 +175,7 @@ class LaunchpadStatisticSet:
         ztm.commit()
         self.update('pofile_count', POFile.select().count())
         ztm.commit()
-        self.update('pomsgid_count', POMsgID.select().count())
+        self.update('pomsgid_count', IStore(POMsgID).find(POMsgID).count())
         ztm.commit()
         self.update('language_count', Language.select(
             "POFile.language=Language.id",
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 14e917d..f2b34ce 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -3247,7 +3247,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if potemplate is None:
             potemplate = self.makePOTemplate()
         if singular is None and plural is None:
-            singular = self.getUniqueString()
+            singular = self.getUniqueUnicode()
         if sequence is None:
             sequence = self.getUniqueInteger()
         potmsgset = potemplate.createMessageSetFromText(
@@ -3270,8 +3270,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
 
         if with_plural:
             if msgid is None:
-                msgid = self.getUniqueString()
-            plural = self.getUniqueString()
+                msgid = self.getUniqueUnicode()
+            plural = self.getUniqueUnicode()
         else:
             plural = None
 
diff --git a/lib/lp/translations/browser/tests/test_pofile_view.py b/lib/lp/translations/browser/tests/test_pofile_view.py
index f644302..659da63 100644
--- a/lib/lp/translations/browser/tests/test_pofile_view.py
+++ b/lib/lp/translations/browser/tests/test_pofile_view.py
@@ -368,7 +368,7 @@ class TestBrowser(BrowserTestCase):
             product.translationpermission = TranslationPermission.CLOSED
         # Add credits so that they show in the UI
         self.factory.makePOTMsgSet(
-            potemplate=pofile.potemplate, singular='translator-credits')
+            potemplate=pofile.potemplate, singular=u'translator-credits')
         browser = self.getViewBrowser(pofile)
         self.assertNotIn('This is a dummy translation', browser.contents)
         self.assertIn('(no translation yet)', browser.contents)
@@ -378,7 +378,7 @@ class TestBrowser(BrowserTestCase):
         pofile = self.factory.makePOFile()
         # Add credits so that they show in the UI
         self.factory.makePOTMsgSet(
-            potemplate=pofile.potemplate, singular='translator-credits')
+            potemplate=pofile.potemplate, singular=u'translator-credits')
         browser = self.getViewBrowser(pofile, no_login=True)
         self.assertTextMatchesExpressionIgnoreWhitespace(
             'To prevent privacy issues, this translation is not available to'
diff --git a/lib/lp/translations/browser/tests/test_translationmessage_view.py b/lib/lp/translations/browser/tests/test_translationmessage_view.py
index e7d7f88..233820e 100644
--- a/lib/lp/translations/browser/tests/test_translationmessage_view.py
+++ b/lib/lp/translations/browser/tests/test_translationmessage_view.py
@@ -158,7 +158,8 @@ class TestCurrentTranslationMessage_can_dismiss(TestCaseWithFactory):
         # If there is a suggestion on a plural message, it is dismissed
         # in yet a different place.
         self.potmsgset = self.factory.makePOTMsgSet(
-            self.potemplate, singular="msgid_singular", plural="msgid_plural")
+            self.potemplate,
+            singular=u"msgid_singular", plural=u"msgid_plural")
         message = self._makeTranslation(["singular_trans", "plural_trans"])
         self._makeTranslation(
             ["singular_sugg", "plural_sugg"], suggestion=True)
@@ -184,7 +185,8 @@ class TestCurrentTranslationMessage_can_dismiss(TestCaseWithFactory):
         # If there is a suggestion on a plural message, it is dismissed
         # in yet a different place.
         self.potmsgset = self.factory.makePOTMsgSet(
-            self.potemplate, singular="msgid_singular", plural="msgid_plural")
+            self.potemplate,
+            singular=u"msgid_singular", plural=u"msgid_plural")
         message = self._makeTranslation(["singular_trans", "plural_trans"])
         self._makeTranslation(["singular_new", "plural_new"], is_other=True)
         self._createView(message)
diff --git a/lib/lp/translations/browser/tests/translationmessage-views.txt b/lib/lp/translations/browser/tests/translationmessage-views.txt
index 670720d..68dccf8 100644
--- a/lib/lp/translations/browser/tests/translationmessage-views.txt
+++ b/lib/lp/translations/browser/tests/translationmessage-views.txt
@@ -157,15 +157,15 @@ We will use this helper function to simplify the test:
     # This is just an easy way to get different messages for all
     # available options to test.
     >>> from zope.security.proxy import removeSecurityProxy
-    >>> from lp.translations.model.pomsgid import POMsgID
-    >>> from sqlobject import SQLObjectNotFound
+    >>> from lp.app.errors import NotFoundError
     >>> from lp.services.propertycache import get_property_cache
+    >>> from lp.translations.model.pomsgid import POMsgID
     >>> def changeMsgID(new_msgid):
     ...     potmsgset = removeSecurityProxy(subview.context.potmsgset)
     ...     try:
-    ...         msgid = POMsgID.byMsgid(new_msgid)
-    ...     except SQLObjectNotFound:
-    ...         msgid = POMsgID(msgid=new_msgid)
+    ...         msgid = POMsgID.getByMsgid(new_msgid)
+    ...     except NotFoundError:
+    ...         msgid = POMsgID.new(new_msgid)
     ...     potmsgset.msgid_singular = msgid
     ...     del get_property_cache(potmsgset).singular_text
     ...     flush_database_updates()
diff --git a/lib/lp/translations/doc/pomsgid.txt b/lib/lp/translations/doc/pomsgid.txt
index 6d5c140..c53e535 100644
--- a/lib/lp/translations/doc/pomsgid.txt
+++ b/lib/lp/translations/doc/pomsgid.txt
@@ -1,15 +1,15 @@
-POMsgID.byMsgid()
-=================
+POMsgID.getByMsgid()
+====================
 
-Test that byMsgid is working:
+Test that getByMsgid is working:
 
 >>> from lp.translations.model.pomsgid import POMsgID
->>> created = POMsgID(msgid="This is a launchpad test")
->>> got = POMsgID.byMsgid("This is a launchpad test")
+>>> created = POMsgID.new("This is a launchpad test")
+>>> got = POMsgID.getByMsgid("This is a launchpad test")
 >>> got == created
 True
 
->>> created = POMsgID(msgid="This is a very \t\n\b'?'\\ odd test")
->>> got = POMsgID.byMsgid("This is a very \t\n\b'?'\\ odd test")
+>>> created = POMsgID.new("This is a very \t\n\b'?'\\ odd test")
+>>> got = POMsgID.getByMsgid("This is a very \t\n\b'?'\\ odd test")
 >>> got == created
 True
diff --git a/lib/lp/translations/doc/rosetta-translation.txt b/lib/lp/translations/doc/rosetta-translation.txt
index bf021c0..e7d8476 100644
--- a/lib/lp/translations/doc/rosetta-translation.txt
+++ b/lib/lp/translations/doc/rosetta-translation.txt
@@ -15,7 +15,7 @@ Get a PO template.
 Check that the PO template has a certain message ID.
 
     >>> from lp.translations.model.pomsgid import POMsgID
-    >>> pomsgid = POMsgID.byMsgid('evolution addressbook')
+    >>> pomsgid = POMsgID.getByMsgid('evolution addressbook')
     >>> template.hasMessageID(pomsgid, None)
     True
 
diff --git a/lib/lp/translations/model/pofile.py b/lib/lp/translations/model/pofile.py
index 92016e1..8179ee2 100644
--- a/lib/lp/translations/model/pofile.py
+++ b/lib/lp/translations/model/pofile.py
@@ -210,8 +210,7 @@ class POFileMixIn(RosettaStats):
             # To avoid seqscans on POMsgID table (which LIKE usually does),
             # we do ILIKE comparison on them in a subselect first filtered
             # by this POTemplate.
-            msgid_column = getattr(POTMsgSet, column_name)
-            msgid_columnID = getattr(POTMsgSet, column_name + "ID")
+            msgid_column_id = getattr(POTMsgSet, column_name + "_id")
             return Select(
                 POTMsgSet.id,
                 tables=(
@@ -223,12 +222,12 @@ class POFileMixIn(RosettaStats):
                             TranslationTemplateItem.potemplate ==
                                 self.potemplate))),
                 where=And(
-                    msgid_column != None,
-                    msgid_columnID.is_in(Select(
+                    msgid_column_id != None,
+                    msgid_column_id.is_in(Select(
                         POMsgID.id,
                         And(
                             POMsgID.id.is_in(Select(
-                                msgid_columnID,
+                                msgid_column_id,
                                 tables=(
                                     POTMsgSet,
                                     Join(
diff --git a/lib/lp/translations/model/pomsgid.py b/lib/lp/translations/model/pomsgid.py
index 9d83da3..18fcec4 100644
--- a/lib/lp/translations/model/pomsgid.py
+++ b/lib/lp/translations/model/pomsgid.py
@@ -4,39 +4,52 @@
 __metaclass__ = type
 __all__ = ['POMsgID']
 
-from sqlobject import (
-    SQLObjectNotFound,
-    StringCol,
+import six
+from storm.expr import Func
+from storm.locals import (
+    Int,
+    Unicode,
     )
 from zope.interface import implementer
 
-from lp.services.database.sqlbase import (
-    quote,
-    SQLBase,
-    )
+from lp.app.errors import NotFoundError
+from lp.services.database.interfaces import IStore
+from lp.services.database.stormbase import StormBase
 from lp.translations.interfaces.pomsgid import IPOMsgID
 
 
 @implementer(IPOMsgID)
-class POMsgID(SQLBase):
+class POMsgID(StormBase):
+
+    __storm_table__ = 'POMsgID'
+
+    id = Int(primary=True)
+    msgid = Unicode(name='msgid', allow_none=False)
 
-    _table = 'POMsgID'
+    def __init__(self, msgid):
+        super(POMsgID, self).__init__()
+        self.msgid = msgid
 
-    # alternateID is technically true, but we don't use it because this
-    # column is too large to be indexed.
-    msgid = StringCol(dbName='msgid', notNull=True, unique=True,
-        alternateID=False)
+    @classmethod
+    def new(cls, msgid):
+        """Return a new POMsgID object for the given msgid."""
+        pomsgid = cls(msgid)
+        IStore(cls).add(pomsgid)
+        return pomsgid
 
-    def byMsgid(cls, key):
-        """Return a POMsgID object for the given msgid."""
+    @classmethod
+    def getByMsgid(cls, key):
+        """Return a POMsgID object for the given msgid.
 
+        :raises NotFoundError: if the msgid is not found.
+        """
         # We can't search directly on msgid, because this database column
         # contains values too large to index. Instead we search on its
-        # hash, which *is* indexed
-        r = POMsgID.selectOne('sha1(msgid) = sha1(%s)' % quote(key))
+        # hash, which *is* indexed.
+        r = IStore(POMsgID).find(
+            POMsgID,
+            Func('sha1', POMsgID.msgid) ==
+                Func('sha1', six.ensure_text(key))).one()
         if r is None:
-            # To be 100% compatible with the alternateID behaviour, we should
-            # raise SQLObjectNotFound instead of KeyError
-            raise SQLObjectNotFound(key)
+            raise NotFoundError(six.ensure_str(key, errors='replace'))
         return r
-    byMsgid = classmethod(byMsgid)
diff --git a/lib/lp/translations/model/potemplate.py b/lib/lp/translations/model/potemplate.py
index 6098946..b0b5e2a 100644
--- a/lib/lp/translations/model/potemplate.py
+++ b/lib/lp/translations/model/potemplate.py
@@ -25,7 +25,6 @@ from sqlobject import (
     ForeignKey,
     IntCol,
     SQLMultipleJoin,
-    SQLObjectNotFound,
     StringCol,
     )
 from storm.expr import (
@@ -458,16 +457,16 @@ class POTemplate(SQLBase, RosettaStats):
         """See `IPOTemplate`."""
         # Find a message ID with the given text.
         try:
-            singular_msgid = POMsgID.byMsgid(singular_text)
-        except SQLObjectNotFound:
+            singular_msgid = POMsgID.getByMsgid(singular_text)
+        except NotFoundError:
             return None
 
         # Find a message ID for the plural string.
         plural_msgid = None
         if plural_text is not None:
             try:
-                plural_msgid = POMsgID.byMsgid(plural_text)
-            except SQLObjectNotFound:
+                plural_msgid = POMsgID.getByMsgid(plural_text)
+            except NotFoundError:
                 return None
 
         # Find a message set with the given message ID.
@@ -500,7 +499,7 @@ class POTemplate(SQLBase, RosettaStats):
         if prefetch:
             def prefetch_msgids(rows):
                 load_related(
-                    POMsgID, rows, ['msgid_singularID', 'msgid_pluralID'])
+                    POMsgID, rows, ['msgid_singular_id', 'msgid_plural_id'])
             return DecoratedResultSet(result, pre_iter_hook=prefetch_msgids)
         else:
             return result
@@ -864,12 +863,12 @@ class POTemplate(SQLBase, RosettaStats):
     def getOrCreatePOMsgID(text):
         """Creates or returns existing POMsgID for given `text`."""
         try:
-            msgid = POMsgID.byMsgid(text)
-        except SQLObjectNotFound:
+            msgid = POMsgID.getByMsgid(text)
+        except NotFoundError:
             # If there are no existing message ids, create a new one.
             # We do not need to check whether there is already a message set
             # with the given text in this template.
-            msgid = POMsgID(msgid=text)
+            msgid = POMsgID.new(text)
         return msgid
 
     def createMessageSetFromText(self, singular_text, plural_text,
@@ -1020,8 +1019,8 @@ class POTemplate(SQLBase, RosettaStats):
         Plural = ClassAlias(POMsgID)
 
         SingularJoin = LeftJoin(
-            Singular, Singular.id == POTMsgSet.msgid_singularID)
-        PluralJoin = LeftJoin(Plural, Plural.id == POTMsgSet.msgid_pluralID)
+            Singular, Singular.id == POTMsgSet.msgid_singular_id)
+        PluralJoin = LeftJoin(Plural, Plural.id == POTMsgSet.msgid_plural_id)
 
         source = Store.of(self).using(
             TranslationTemplateItem, POTMsgSet, SingularJoin, PluralJoin)
diff --git a/lib/lp/translations/model/potmsgset.py b/lib/lp/translations/model/potmsgset.py
index 649f6bd..c742d36 100644
--- a/lib/lp/translations/model/potmsgset.py
+++ b/lib/lp/translations/model/potmsgset.py
@@ -15,11 +15,7 @@ import logging
 import re
 
 import six
-from sqlobject import (
-    ForeignKey,
-    SQLObjectNotFound,
-    StringCol,
-    )
+from sqlobject import StringCol
 from storm.expr import (
     And,
     Coalesce,
@@ -33,6 +29,10 @@ from storm.expr import (
     Table,
     With,
     )
+from storm.locals import (
+    Int,
+    Reference,
+    )
 from storm.store import (
     EmptyResultSet,
     Store,
@@ -41,6 +41,7 @@ from zope.component import getUtility
 from zope.interface import implementer
 from zope.security.proxy import removeSecurityProxy
 
+from lp.app.errors import NotFoundError
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.services.config import config
 from lp.services.database.constants import DEFAULT
@@ -140,10 +141,11 @@ class POTMsgSet(SQLBase):
     _table = 'POTMsgSet'
 
     context = StringCol(dbName='context', notNull=False)
-    msgid_singular = ForeignKey(foreignKey='POMsgID', dbName='msgid_singular',
-        notNull=True)
-    msgid_plural = ForeignKey(foreignKey='POMsgID', dbName='msgid_plural',
-        notNull=False, default=DEFAULT)
+    msgid_singular_id = Int(name='msgid_singular', allow_none=False)
+    msgid_singular = Reference(msgid_singular_id, 'POMsgID.id')
+    msgid_plural_id = Int(
+        name='msgid_plural', allow_none=True, default=DEFAULT)
+    msgid_plural = Reference(msgid_plural_id, 'POMsgID.id')
     commenttext = StringCol(dbName='commenttext', notNull=False)
     filereferences = StringCol(dbName='filereferences', notNull=False)
     sourcecomment = StringCol(dbName='sourcecomment', notNull=False)
@@ -1200,9 +1202,9 @@ class POTMsgSet(SQLBase):
         else:
             # Store the given plural form.
             try:
-                pomsgid = POMsgID.byMsgid(plural_form_text)
-            except SQLObjectNotFound:
-                pomsgid = POMsgID(msgid=plural_form_text)
+                pomsgid = POMsgID.getByMsgid(plural_form_text)
+            except NotFoundError:
+                pomsgid = POMsgID.new(plural_form_text)
             self.msgid_plural = pomsgid
 
     def setTranslationCreditsToTranslated(self, pofile):
diff --git a/lib/lp/translations/scripts/tests/test_remove_translations.py b/lib/lp/translations/scripts/tests/test_remove_translations.py
index 46a59e9..dc16088 100644
--- a/lib/lp/translations/scripts/tests/test_remove_translations.py
+++ b/lib/lp/translations/scripts/tests/test_remove_translations.py
@@ -5,6 +5,8 @@
 
 """Test `remove_translations` and the `RemoveTranslations` script."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 import logging
diff --git a/lib/lp/translations/scripts/tests/test_translations_to_branch.py b/lib/lp/translations/scripts/tests/test_translations_to_branch.py
index 9095b74..2b5877e 100644
--- a/lib/lp/translations/scripts/tests/test_translations_to_branch.py
+++ b/lib/lp/translations/scripts/tests/test_translations_to_branch.py
@@ -73,7 +73,7 @@ class TestExportTranslationsToBranch(TestCaseWithFactory):
             path='po/messages.pot')
         template = removeSecurityProxy(template)
         potmsgset = self.factory.makePOTMsgSet(
-            template, singular='Hello World', sequence=1)
+            template, singular=u'Hello World', sequence=1)
         pofile = self.factory.makePOFile(
             'nl', potemplate=template, owner=product.owner)
         self.factory.makeCurrentTranslationMessage(
diff --git a/lib/lp/translations/tests/test_pofile.py b/lib/lp/translations/tests/test_pofile.py
index 034359b..04522b2 100644
--- a/lib/lp/translations/tests/test_pofile.py
+++ b/lib/lp/translations/tests/test_pofile.py
@@ -1881,7 +1881,7 @@ class TestPOFileStatistics(TestCaseWithFactory):
         # untranslated if at least one plural translation required
         # for the given language is missing.
         plural_potmsgset = self.factory.makePOTMsgSet(
-            self.potemplate, singular='singular-en', plural='plural-en')
+            self.potemplate, singular=u'singular-en', plural=u'plural-en')
         self.factory.makeCurrentTranslationMessage(
             pofile=self.pofile, potmsgset=plural_potmsgset,
             translations=['sr-singular', 'sr-plural-1'])
@@ -1895,7 +1895,7 @@ class TestPOFileStatistics(TestCaseWithFactory):
         # A translation requiring plural forms is considered to be
         # translated if all variants are translated.
         plural_potmsgset = self.factory.makePOTMsgSet(
-            self.potemplate, singular='singular-en', plural='plural-en')
+            self.potemplate, singular=u'singular-en', plural=u'plural-en')
         self.factory.makeCurrentTranslationMessage(
             pofile=self.pofile, potmsgset=plural_potmsgset,
             translations=['sr-singular', 'sr-plural-1', 'sr-plural-2'])
@@ -1925,7 +1925,7 @@ class TestPOFileStatistics(TestCaseWithFactory):
         # partially translated is considered to be untranslated, regardless
         # of any translations it may have on the other side.
         plural_potmsgset = self.factory.makePOTMsgSet(
-            self.potemplate, singular='singular-en', plural='plural-en')
+            self.potemplate, singular=u'singular-en', plural=u'plural-en')
         self.factory.makeCurrentTranslationMessage(
             pofile=self.pofile, potmsgset=plural_potmsgset,
             translations=['sr-singular', 'sr-plural-1'])
@@ -1966,7 +1966,7 @@ class TestPOFileStatistics(TestCaseWithFactory):
         # Instead, it's counted as translated on this side but not on
         # the other (newCount).
         plural_potmsgset = self.factory.makePOTMsgSet(
-            self.potemplate, singular='singular-en', plural='plural-en')
+            self.potemplate, singular=u'singular-en', plural=u'plural-en')
         self.factory.makeCurrentTranslationMessage(
             pofile=self.pofile, potmsgset=plural_potmsgset,
             translations=['sr-singular', 'sr-plural1', 'sr-plural2'])
@@ -1998,7 +1998,7 @@ class TestPOFileStatistics(TestCaseWithFactory):
         # A partial Translations that's current on both sides
         # counts as untranslated.
         plural_potmsgset = self.factory.makePOTMsgSet(
-            self.potemplate, singular='singular-en', plural='plural-en')
+            self.potemplate, singular=u'singular-en', plural=u'plural-en')
         self.factory.makeCurrentTranslationMessage(
             pofile=self.pofile, potmsgset=plural_potmsgset,
             translations=['sr-singular', 'sr-plural1'], current_other=True)
@@ -2056,10 +2056,10 @@ class TestPOFile(TestCaseWithFactory):
     # The sequence number 0 is put at the beginning of the data to verify that
     # it really gets sorted to the end.
     TEST_MESSAGES = [
-        {'msgid':'computer', 'string':'komputilo', 'sequence':0},
-        {'msgid':'mouse', 'string':'muso', 'sequence':0},
-        {'msgid':'Good morning', 'string':'Bonan matenon', 'sequence':2},
-        {'msgid':'Thank you', 'string':'Dankon', 'sequence':1},
+        {'msgid': u'computer', 'string': u'komputilo', 'sequence': 0},
+        {'msgid': u'mouse', 'string': u'muso', 'sequence': 0},
+        {'msgid': u'Good morning', 'string': u'Bonan matenon', 'sequence': 2},
+        {'msgid': u'Thank you', 'string': u'Dankon', 'sequence': 1},
         ]
     EXPECTED_SEQUENCE = [1, 2, 0, 0]
 
diff --git a/lib/lp/translations/tests/test_pofilestatsjob.py b/lib/lp/translations/tests/test_pofilestatsjob.py
index 8da8b38..2e879ee 100644
--- a/lib/lp/translations/tests/test_pofilestatsjob.py
+++ b/lib/lp/translations/tests/test_pofilestatsjob.py
@@ -45,7 +45,7 @@ class TestPOFileStatsJob(TestCaseWithFactory):
 
     def test_run(self):
         # Running a job causes the POFile statistics to be updated.
-        singular = self.factory.getUniqueString()
+        singular = self.factory.getUniqueUnicode()
         pofile = self.factory.makePOFile(side=TranslationSide.UPSTREAM)
         # Create a message so we have something to have statistics about.
         self.factory.makePOTMsgSet(pofile.potemplate, singular)
@@ -66,7 +66,7 @@ class TestPOFileStatsJob(TestCaseWithFactory):
         potemplate = self.factory.makePOTemplate(productseries=productseries)
         pofile = self.factory.makePOFile('en', potemplate)
         # Create a message so we have something to have statistics about.
-        singular = self.factory.getUniqueString()
+        singular = self.factory.getUniqueUnicode()
         self.factory.makePOTMsgSet(pofile.potemplate, singular)
         # The statistics are still at 0, even though there is a message.
         self.assertEqual(potemplate.messageCount(), 0)
@@ -209,7 +209,7 @@ class TestViaCelery(TestCaseWithFactory):
         self.useFixture(FeatureFixture(
             {'jobs.celery.enabled_classes': 'POFileStatsJob'}))
         # Running a job causes the POFile statistics to be updated.
-        singular = self.factory.getUniqueString()
+        singular = self.factory.getUniqueUnicode()
         pofile = self.factory.makePOFile(side=TranslationSide.UPSTREAM)
         # Create a message so we have something to have statistics about.
         self.factory.makePOTMsgSet(pofile.potemplate, singular)
diff --git a/lib/lp/translations/tests/test_potmsgset.py b/lib/lp/translations/tests/test_potmsgset.py
index 1049ffd..020529f 100644
--- a/lib/lp/translations/tests/test_potmsgset.py
+++ b/lib/lp/translations/tests/test_potmsgset.py
@@ -22,10 +22,7 @@ from lp.testing.layers import (
     ZopelessDatabaseLayer,
     )
 from lp.translations.interfaces.potemplate import IPOTemplateSet
-from lp.translations.interfaces.potmsgset import (
-    POTMsgSetInIncompatibleTemplatesError,
-    TranslationCreditsType,
-    )
+from lp.translations.interfaces.potmsgset import TranslationCreditsType
 from lp.translations.interfaces.side import (
     ITranslationSideTraitsSet,
     TranslationSide,
@@ -961,7 +958,7 @@ class TestPOTMsgSetText(TestCaseWithFactory):
 
     def test_singular_text_po(self):
         # Gettext PO format uses English strings as msgids.
-        english_msgid = self.factory.getUniqueString()
+        english_msgid = self.factory.getUniqueUnicode()
         potmsgset = self._makePOTMsgSet(
             english_msgid, TranslationFileFormat.PO)
         self.assertEqual(english_msgid, potmsgset.singular_text)
@@ -1258,7 +1255,7 @@ class TestPOTMsgSet_submitSuggestion(TestCaseWithFactory):
     def test_credits_message(self):
         # Suggestions for translation-credits messages are ignored.
         pofile, potmsgset = self._makePOFileAndPOTMsgSet(
-            msgid='translator-credits')
+            msgid=u'translator-credits')
         self.assertTrue(potmsgset.is_translation_credit)
         owner = pofile.potemplate.owner
         translation = {0: self.factory.getUniqueString()}
@@ -1270,7 +1267,7 @@ class TestPOTMsgSet_submitSuggestion(TestCaseWithFactory):
     def test_credits_karma(self):
         # No karma is assigned for suggestions on translation credits.
         pofile, potmsgset = self._makePOFileAndPOTMsgSet(
-            msgid='translator-credits')
+            msgid=u'translator-credits')
         self.assertTrue(potmsgset.is_translation_credit)
         owner = pofile.potemplate.owner
         translation = {0: self.factory.getUniqueString()}
@@ -1783,8 +1780,8 @@ class TestClone(TestCaseWithFactory):
         """Cloning a POTMsgSet should produce a near-identical copy."""
         msgset = self.factory.makePOTMsgSet(
             context=self.factory.getUniqueString('context'),
-            plural=self.factory.getUniqueString('plural'),
-            singular=self.factory.getUniqueString('singular'),
+            plural=self.factory.getUniqueUnicode('plural'),
+            singular=self.factory.getUniqueUnicode('singular'),
             commenttext=self.factory.getUniqueString('comment'),
             filereferences=self.factory.getUniqueString('filereferences'),
             sourcecomment=self.factory.getUniqueString('sourcecomment'),
diff --git a/lib/lp/translations/tests/test_shared_potemplate.py b/lib/lp/translations/tests/test_shared_potemplate.py
index 3e24c95..7483290 100644
--- a/lib/lp/translations/tests/test_shared_potemplate.py
+++ b/lib/lp/translations/tests/test_shared_potemplate.py
@@ -1,6 +1,8 @@
 # Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from storm.exceptions import DataError
@@ -137,7 +139,7 @@ class TestTranslationSharingPOTemplate(TestCaseWithFactory):
         naked_potemplate = removeSecurityProxy(self.devel_potemplate)
 
         # Let's create a new POTMsgSet.
-        singular_text = self.factory.getUniqueString()
+        singular_text = self.factory.getUniqueUnicode()
         msgid_singular = naked_potemplate.getOrCreatePOMsgID(singular_text)
         potmsgset = self.devel_potemplate.createPOTMsgSetFromMsgIDs(
             msgid_singular=msgid_singular)
@@ -157,7 +159,7 @@ class TestTranslationSharingPOTemplate(TestCaseWithFactory):
 
     def test_getOrCreateSharedPOTMsgSet(self):
         # Let's create a new POTMsgSet.
-        singular_text = self.factory.getUniqueString()
+        singular_text = self.factory.getUniqueUnicode()
         potmsgset = self.devel_potemplate.getOrCreateSharedPOTMsgSet(
             singular_text, None)
 
@@ -176,7 +178,7 @@ class TestTranslationSharingPOTemplate(TestCaseWithFactory):
     def test_getOrCreateSharedPOTMsgSet_initializes_file_references(self):
         # When creating a POTMsgSet, getOrCreateSharedPOTMsgSet
         # initializes its filereferences to initial_file_references.
-        singular = self.factory.getUniqueString()
+        singular = self.factory.getUniqueUnicode()
         file_references = self.factory.getUniqueString()
         potmsgset = self.devel_potemplate.getOrCreateSharedPOTMsgSet(
             singular, None, initial_file_references=file_references)
@@ -199,7 +201,7 @@ class TestTranslationSharingPOTemplate(TestCaseWithFactory):
     def test_getOrCreateSharedPOTMsgSet_initializes_source_comment(self):
         # When creating a POTMsgSet, getOrCreateSharedPOTMsgSet
         # initializes its sourcecomment to initial_source_comment.
-        singular = self.factory.getUniqueString()
+        singular = self.factory.getUniqueUnicode()
         source_comment = self.factory.getUniqueString()
         potmsgset = self.devel_potemplate.getOrCreateSharedPOTMsgSet(
             singular, None, initial_source_comment=source_comment)
diff --git a/lib/lp/translations/tests/test_suggestions.py b/lib/lp/translations/tests/test_suggestions.py
index 0dda49b..79c82e4 100644
--- a/lib/lp/translations/tests/test_suggestions.py
+++ b/lib/lp/translations/tests/test_suggestions.py
@@ -1,6 +1,8 @@
 # Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from datetime import (
diff --git a/lib/lp/translations/tests/test_translationmerger.py b/lib/lp/translations/tests/test_translationmerger.py
index 3ad1eb2..cd49d51 100644
--- a/lib/lp/translations/tests/test_translationmerger.py
+++ b/lib/lp/translations/tests/test_translationmerger.py
@@ -1,6 +1,8 @@
 # Copyright 2009, 2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 import gc
@@ -739,14 +741,14 @@ class TestSharingMigrationPerformance(TestCaseWithFactory,
         with StormStatementRecorder() as recorder:
             self.merger.mergePOTMsgSets()
         self.assertNoStatementsInvolvingTable(
-            POMsgID._table, recorder.statements)
+            POMsgID.__storm_table__, recorder.statements)
         self.assertNoStatementsInvolvingTable(
             POTranslation._table, recorder.statements)
 
         with StormStatementRecorder() as recorder:
             self.merger.mergeTranslationMessages()
         self.assertNoStatementsInvolvingTable(
-            POMsgID._table, recorder.statements)
+            POMsgID.__storm_table__, recorder.statements)
         self.assertNoStatementsInvolvingTable(
             POTranslation._table, recorder.statements)
 
diff --git a/lib/lp/translations/tests/test_translationmessage.py b/lib/lp/translations/tests/test_translationmessage.py
index 42b0879..8c35fe6 100644
--- a/lib/lp/translations/tests/test_translationmessage.py
+++ b/lib/lp/translations/tests/test_translationmessage.py
@@ -3,6 +3,8 @@
 
 """Unit tests for `TranslationMessage`."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from datetime import (
diff --git a/lib/lp/translations/tests/test_translationpackagingjob.py b/lib/lp/translations/tests/test_translationpackagingjob.py
index f47ebae..1bc8535 100644
--- a/lib/lp/translations/tests/test_translationpackagingjob.py
+++ b/lib/lp/translations/tests/test_translationpackagingjob.py
@@ -50,7 +50,7 @@ from lp.translations.tests.test_translationsplitter import (
 
 
 def make_translation_merge_job(factory, not_ubuntu=False):
-    singular = factory.getUniqueString()
+    singular = factory.getUniqueUnicode()
     upstream_pofile = factory.makePOFile(side=TranslationSide.UPSTREAM)
     upstream_potmsgset = factory.makePOTMsgSet(
         upstream_pofile.potemplate, singular)
diff --git a/lib/lp/translations/tests/test_translations_to_review.py b/lib/lp/translations/tests/test_translations_to_review.py
index 150d2e1..518a525 100644
--- a/lib/lp/translations/tests/test_translations_to_review.py
+++ b/lib/lp/translations/tests/test_translations_to_review.py
@@ -3,6 +3,8 @@
 
 """Test the choice of "translations to review" for a user."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
 from datetime import (
diff --git a/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py b/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py
index 16bfb62..0a9a38a 100644
--- a/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py
+++ b/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py
@@ -391,9 +391,9 @@ class GettextPOExporterTestCase(TestCaseWithFactory):
         # first of the two messages is exported.
         template = self.factory.makePOTemplate()
         self.factory.makePOTMsgSet(
-            template, singular='%d foo', plural='%d foos', sequence=1)
+            template, singular=u'%d foo', plural=u'%d foos', sequence=1)
         self.factory.makePOTMsgSet(
-            template, singular='%d foo', plural='%d foox', sequence=2)
+            template, singular=u'%d foo', plural=u'%d foox', sequence=2)
 
         exported_file = template.export()
 
@@ -417,9 +417,9 @@ class GettextPOExporterTestCase(TestCaseWithFactory):
         # non-obsolete one is exported.
         template = self.factory.makePOTemplate()
         obsolete_message = self.factory.makePOTMsgSet(
-            template, singular='%d goo', plural='%d goos', sequence=0)
+            template, singular=u'%d goo', plural=u'%d goos', sequence=0)
         current_message = self.factory.makePOTMsgSet(
-            template, singular='%d goo', plural='%d gooim', sequence=1)
+            template, singular=u'%d goo', plural=u'%d gooim', sequence=1)
 
         pofile = self.factory.makePOFile(
             potemplate=template, language_code='nl')
diff --git a/lib/lp/translations/utilities/translationmerger.py b/lib/lp/translations/utilities/translationmerger.py
index b507fb6..5805eb2 100644
--- a/lib/lp/translations/utilities/translationmerger.py
+++ b/lib/lp/translations/utilities/translationmerger.py
@@ -53,7 +53,7 @@ def get_potmsgset_key(potmsgset):
     """
     potmsgset = removeSecurityProxy(potmsgset)
     return (
-        potmsgset.msgid_singularID, potmsgset.msgid_pluralID,
+        potmsgset.msgid_singular_id, potmsgset.msgid_plural_id,
         potmsgset.context)