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

2020-12-02 Thread Colin Watson
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 

[Launchpad-reviewers] [Merge] ~cjwatson/launchpad:stormify-pomsgid into launchpad:master

2020-12-02 Thread Colin Watson
Colin Watson has proposed merging ~cjwatson/launchpad:stormify-pomsgid into 
launchpad:master.

Commit message:
Convert POMsgID to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394784
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~cjwatson/launchpad:stormify-pomsgid into launchpad:master.
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 

[Launchpad-reviewers] [Merge] ~pappacena/turnip:py3-as-default into turnip:master

2020-12-02 Thread Thiago F. Pappacena
Thiago F. Pappacena has proposed merging ~pappacena/turnip:py3-as-default into 
turnip:master.

Commit message:
Making python3 the default intepreter

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~pappacena/turnip/+git/turnip/+merge/394717
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~pappacena/turnip:py3-as-default into turnip:master.
diff --git a/.gitignore b/.gitignore
index 5698ab9..07be1e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ download-cache
 eggs
 env
 py3env
+py2env
 pip-cache
 *.egg*
 *.egg-info
diff --git a/Makefile b/Makefile
index 0b9d1b1..e0362ee 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 ENV := $(CURDIR)/env
-PY3_ENV := $(CURDIR)/py3env
+PY2_ENV := $(CURDIR)/py2env
 PIP_CACHE = $(CURDIR)/pip-cache
 
 PYTHON := $(ENV)/bin/python
@@ -11,6 +11,7 @@ FLAKE8 := $(ENV)/bin/flake8
 CELERY := $(ENV)/bin/celery
 PIP := $(ENV)/bin/pip
 VIRTUALENV := virtualenv
+VENV_ARGS := -p python3
 
 DEPENDENCIES_URL := https://git.launchpad.net/~canonical-launchpad-branches/turnip/+git/dependencies
 PIP_SOURCE_DIR := dependencies
@@ -73,17 +74,17 @@ bootstrap-test:
 run-test: $(ENV) bootstrap-test
 	$(PYTHON) -m unittest discover $(ARGS) turnip
 
-test: test-python2 test-python3
-
-test-python2:
-	$(MAKE) run-test VENV_ARGS="-p python2"
+test: test-python3 test-python2
 
 test-python3:
-	$(MAKE) run-test VENV_ARGS="-p python3" ENV="$(PY3_ENV)"
+	$(MAKE) run-test VENV_ARGS="-p python3"
+
+test-python2:
+	$(MAKE) run-test VENV_ARGS="-p python2" ENV="$(PY2_ENV)"
 
 clean:
 	find turnip -name '*.py[co]' -exec rm '{}' \;
-	rm -rf $(ENV) $(PY3_ENV) $(PIP_CACHE)
+	rm -rf $(ENV) $(PY2_ENV) $(PIP_CACHE)
 	rm -f turnip/version_info.py
 
 dist:
___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp


[Launchpad-reviewers] [Merge] ~cjwatson/launchpad:py3-raw-strings into launchpad:master

2020-12-02 Thread Colin Watson
Colin Watson has proposed merging ~cjwatson/launchpad:py3-raw-strings into 
launchpad:master.

Commit message:
Correct remaining spelling of \-escapes

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

Use raw strings or double-\, depending on whether it's more convenient for '\n' 
to mean a newline character or the two characters '\' 'n'.  This fixes a number 
of DeprecationWarnings with Python >= 3.6.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~cjwatson/launchpad:py3-raw-strings into launchpad:master.
diff --git a/database/schema/security.py b/database/schema/security.py
index b3bdbbf..254ad94 100755
--- a/database/schema/security.py
+++ b/database/schema/security.py
@@ -54,8 +54,8 @@ POSTGRES_ACL_MAP = {
 # PostgreSQL's putid emits an unquoted string if every character in the role
 # name isalnum or is _. Otherwise the name is enclosed in double quotes, and
 # any embedded double quotes are doubled.
-QUOTED_STRING_RE = '(?:([A-Za-z0-9_]+)|"([^"]*(?:""[^"]*)*)")?'
-ACLITEM_RE = re.compile('^%(qs)s=([\w*]*)/%(qs)s$' % {'qs': QUOTED_STRING_RE})
+QUOTED_STRING_RE = r'(?:([A-Za-z0-9_]+)|"([^"]*(?:""[^"]*)*)")?'
+ACLITEM_RE = re.compile(r'^%(qs)s=([\w*]*)/%(qs)s$' % {'qs': QUOTED_STRING_RE})
 
 
 def _split_postgres_aclitem(aclitem):
diff --git a/database/schema/upgrade.py b/database/schema/upgrade.py
index fa536ef..a6ddc0c 100755
--- a/database/schema/upgrade.py
+++ b/database/schema/upgrade.py
@@ -177,7 +177,7 @@ def get_patchlist(con):
 os.path.join(SCHEMA_DIR, 'patch--??-?.sql'))
 all_patch_files.sort()
 for patch_file in all_patch_files:
-m = re.search('patch-(\d+)-(\d+)-(\d).sql$', patch_file)
+m = re.search(r'patch-(\d+)-(\d+)-(\d).sql$', patch_file)
 if m is None:
 log.fatal('Invalid patch filename %s' % repr(patch_file))
 raise SystemExit(1)
diff --git a/lib/contrib/apachelog.py b/lib/contrib/apachelog.py
index 48a30d9..20c1a3c 100644
--- a/lib/contrib/apachelog.py
+++ b/lib/contrib/apachelog.py
@@ -148,7 +148,7 @@ class parser:
 
 self._names.append(self.alias(element))
 
-subpattern = '(\S*)'
+subpattern = r'(\S*)'
 
 if hasquotes:
 if element == '%r' or findreferreragent.search(element):
diff --git a/lib/contrib/glock.py b/lib/contrib/glock.py
index d1ced09..fb835e2 100644
--- a/lib/contrib/glock.py
+++ b/lib/contrib/glock.py
@@ -101,7 +101,7 @@ class GlobalLock:
std module msvcrt.locking(), because global lock is OK, but
blocks also for 2 calls from the same thread!
 '''
-RE_ERROR_MSG = re.compile ("^\[Errno ([0-9]+)\]")
+RE_ERROR_MSG = re.compile (r"^\[Errno ([0-9]+)\]")
 
 def __init__(self, fpath, lockInitially=False, logger=None):
 ''' Creates (or opens) a global lock.
diff --git a/lib/lp/answers/browser/question.py b/lib/lp/answers/browser/question.py
index 491ff16..b039552 100644
--- a/lib/lp/answers/browser/question.py
+++ b/lib/lp/answers/browser/question.py
@@ -1187,7 +1187,7 @@ class SearchAllQuestionsView(SearchQuestionsView):
 
 display_target_column = True
 # Match contiguous digits, optionally prefixed with a '#'.
-id_pattern = re.compile('^#?(\d+)$')
+id_pattern = re.compile(r'^#?(\d+)$')
 
 @property
 def pageheading(self):
diff --git a/lib/lp/answers/tests/test_faq_webservice.py b/lib/lp/answers/tests/test_faq_webservice.py
index dc1e1e7..33ad24b 100644
--- a/lib/lp/answers/tests/test_faq_webservice.py
+++ b/lib/lp/answers/tests/test_faq_webservice.py
@@ -46,8 +46,9 @@ class TestFAQWebService(TestCaseWithFactory):
 "title": Equals("Nothing works"),
 "keywords": Equals("foo bar"),
 "content": Equals("It is all broken."),
-"date_created": MatchesRegex("\d\d\d\d-\d\d-\d\dT.*"),
-"date_last_updated": MatchesRegex("\d\d\d\d-\d\d-\d\dT.*"),
+"date_created": MatchesRegex(r"\d\d\d\d-\d\d-\d\dT.*"),
+"date_last_updated": MatchesRegex(
+r"\d\d\d\d-\d\d-\d\dT.*"),
 "last_updated_by_link": Contains(
 "/devel/~%s" % faq.owner.name),
 "target_link": Contains(
diff --git a/lib/lp/app/browser/launchpad.py b/lib/lp/app/browser/launchpad.py
index 4c1eb59..7388562 100644
--- a/lib/lp/app/browser/launchpad.py
+++ b/lib/lp/app/browser/launchpad.py
@@ -1012,7 +1012,7 @@ class LaunchpadRootNavigation(Navigation):
 return None
 
 # If the request is for a bug then redirect straight to that bug.
-bug_match = re.match("/bugs/(\d+)$", self.request['PATH_INFO'])
+bug_match = re.match(r"/bugs/(\d+)$", self.request['PATH_INFO'])
 if bug_match:
 

[Launchpad-reviewers] [Merge] ~twom/launchpad:source-target-typo into launchpad:master

2020-12-02 Thread Tom Wardill
Tom Wardill has proposed merging ~twom/launchpad:source-target-typo into 
launchpad:master.

Commit message:
Fix typo in prerequisite branch explanation

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~twom/launchpad/+git/launchpad/+merge/394765
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~twom/launchpad:source-target-typo into launchpad:master.
diff --git a/lib/lp/code/browser/gitref.py b/lib/lp/code/browser/gitref.py
index 5edce2c..ff01a72 100644
--- a/lib/lp/code/browser/gitref.py
+++ b/lib/lp/code/browser/gitref.py
@@ -250,7 +250,7 @@ class GitRefRegisterMergeProposalSchema(Interface):
 
 prerequisite_git_ref = copy_field(
 IBranchMergeProposal['prerequisite_git_ref'], required=False,
-description=_("If the target branch is based on a different branch, "
+description=_("If the source branch is based on a different branch, "
   "you can add this as a prerequisite. "
   "The changes from that branch will not show "
   "in the diff."))
___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp


[Launchpad-reviewers] [Merge] ~cjwatson/launchpad:stormify-xpph-queries into launchpad:master

2020-12-02 Thread Colin Watson
Colin Watson has proposed merging ~cjwatson/launchpad:stormify-xpph-queries 
into launchpad:master.

Commit message:
Convert remaining publishing history queries to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394764
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~cjwatson/launchpad:stormify-xpph-queries into launchpad:master.
diff --git a/lib/lp/archiveuploader/tests/test_ppauploadprocessor.py b/lib/lp/archiveuploader/tests/test_ppauploadprocessor.py
index 2be7342..899a85e 100644
--- a/lib/lp/archiveuploader/tests/test_ppauploadprocessor.py
+++ b/lib/lp/archiveuploader/tests/test_ppauploadprocessor.py
@@ -28,6 +28,7 @@ from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.services.config import config
 from lp.services.database.constants import UTC_NOW
+from lp.services.database.interfaces import IStore
 from lp.services.librarian.interfaces import ILibraryFileAliasSet
 from lp.soyuz.enums import (
 PackagePublishingStatus,
@@ -329,7 +330,8 @@ class TestPPAUploadProcessor(TestPPAUploadProcessorBase):
 
 for binary_package in build.binarypackages:
 self.assertEqual(binary_package.component.name, "universe")
-[binary_pub] = BinaryPackagePublishingHistory.selectBy(
+[binary_pub] = IStore(BinaryPackagePublishingHistory).find(
+BinaryPackagePublishingHistory,
 binarypackagerelease=binary_package,
 archive=self.name16.archive)
 self.assertEqual(binary_pub.component.name, "main")
diff --git a/lib/lp/archiveuploader/tests/test_uploadprocessor.py b/lib/lp/archiveuploader/tests/test_uploadprocessor.py
index 6fb09fb..91ec622 100644
--- a/lib/lp/archiveuploader/tests/test_uploadprocessor.py
+++ b/lib/lp/archiveuploader/tests/test_uploadprocessor.py
@@ -63,6 +63,7 @@ from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
 from lp.registry.model.sourcepackagename import SourcePackageName
 from lp.services.config import config
 from lp.services.database.constants import UTC_NOW
+from lp.services.database.interfaces import IStore
 from lp.services.librarian.interfaces import ILibraryFileAliasSet
 from lp.services.log.logger import (
 BufferLogger,
@@ -1022,8 +1023,9 @@ class TestUploadProcessor(TestUploadProcessorBase):
 self.publishPackage("foocomm", "1.0-1", archive=partner_archive)
 
 # Check the publishing record's archive and component.
-foocomm_spph = SourcePackagePublishingHistory.selectOneBy(
-sourcepackagerelease=foocomm_spr)
+foocomm_spph = IStore(SourcePackagePublishingHistory).find(
+SourcePackagePublishingHistory,
+sourcepackagerelease=foocomm_spr).one()
 self.assertEqual(foocomm_spph.archive.description,
 'Partner archive')
 self.assertEqual(foocomm_spph.component.name,
@@ -1066,8 +1068,9 @@ class TestUploadProcessor(TestUploadProcessorBase):
 self.publishPackage("foocomm", "1.0-1", source=False)
 
 # Check the publishing record's archive and component.
-foocomm_bpph = BinaryPackagePublishingHistory.selectOneBy(
-binarypackagerelease=foocomm_bpr)
+foocomm_bpph = IStore(BinaryPackagePublishingHistory).find(
+BinaryPackagePublishingHistory,
+binarypackagerelease=foocomm_bpr).one()
 self.assertEqual(foocomm_bpph.archive.description,
 'Partner archive')
 self.assertEqual(foocomm_bpph.component.name,
diff --git a/lib/lp/registry/doc/sourcepackage.txt b/lib/lp/registry/doc/sourcepackage.txt
index e494a68..c8a6508 100644
--- a/lib/lp/registry/doc/sourcepackage.txt
+++ b/lib/lp/registry/doc/sourcepackage.txt
@@ -127,13 +127,14 @@ upload, even though it gets changed in the publishing record.
 PENDING main
 PUBLISHED main
 
+>>> from lp.services.database.interfaces import IStore
 >>> from lp.services.database.sqlbase import flush_database_caches
 >>> from lp.soyuz.model.component import Component
 >>> from lp.soyuz.model.publishing import (
 ... SourcePackagePublishingHistory)
 
->>> latest_publishing = SourcePackagePublishingHistory.get(
-... publishing_history[-1].id)
+>>> latest_publishing = IStore(SourcePackagePublishingHistory).get(
+... SourcePackagePublishingHistory, publishing_history.last().id)
 >>> universe = Component.byName('universe')
 >>> latest_publishing.component = universe
 >>> flush_database_caches()
diff --git a/lib/lp/registry/model/distributionmirror.py b/lib/lp/registry/model/distributionmirror.py
index 323fb26..11f2b13 100644
--- a/lib/lp/registry/model/distributionmirror.py
+++ b/lib/lp/registry/model/distributionmirror.py
@@ -835,32 +835,35 @@ class 

[Launchpad-reviewers] [Merge] ~twom/launchpad:questions-about-storm into launchpad:master

2020-12-02 Thread Tom Wardill
Tom Wardill has proposed merging ~twom/launchpad:questions-about-storm into 
launchpad:master.

Commit message:
Port Question to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~twom/launchpad/+git/launchpad/+merge/394763
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~twom/launchpad:questions-about-storm into launchpad:master.
diff --git a/lib/lp/answers/doc/workflow.txt b/lib/lp/answers/doc/workflow.txt
index 3cabebb..4d2116e 100644
--- a/lib/lp/answers/doc/workflow.txt
+++ b/lib/lp/answers/doc/workflow.txt
@@ -592,7 +592,7 @@ Users without launchpad.Moderator privileges cannot set the assignee.
 >>> question.assignee = sample_person
 Traceback (most recent call last):
   ...
-Unauthorized: (, 'assignee', 'launchpad.Append')
+Unauthorized: (, 'assignee', 'launchpad.Append')
 
 
 Events
diff --git a/lib/lp/answers/model/question.py b/lib/lp/answers/model/question.py
index 9c51e88..fd85013 100644
--- a/lib/lp/answers/model/question.py
+++ b/lib/lp/answers/model/question.py
@@ -13,7 +13,7 @@ __all__ = [
 'QuestionTargetMixin',
 ]
 
-from datetime import datetime
+from datetime import datetime, timedelta
 from email.utils import make_msgid
 import operator
 
@@ -26,18 +26,18 @@ from lazr.lifecycle.event import (
 ObjectModifiedEvent,
 )
 from lazr.lifecycle.snapshot import Snapshot
+from lp.services.database.stormbase import StormBase
 import pytz
 import six
-from sqlobject import (
-ForeignKey,
-SQLMultipleJoin,
-SQLObjectNotFound,
-StringCol,
+from storm.expr import (
+Alias,
+LeftJoin,
 )
-from storm.expr import LeftJoin
 from storm.locals import (
 And,
 ClassAlias,
+Count,
+DateTime,
 Desc,
 Int,
 Join,
@@ -46,6 +46,7 @@ from storm.locals import (
 Reference,
 Select,
 Store,
+Unicode,
 )
 from storm.references import ReferenceSet
 from zope.component import getUtility
@@ -89,6 +90,7 @@ from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.bugs.interfaces.buglink import IBugLinkTarget
 from lp.bugs.interfaces.bugtask import BugTaskStatus
 from lp.bugs.model.buglinktarget import BugLinkTargetMixin
+from lp.bugs.model.bugtask import BugTask
 from lp.registry.interfaces.distribution import (
 IDistribution,
 IDistributionSet,
@@ -111,16 +113,10 @@ from lp.services.database.constants import (
 DEFAULT,
 UTC_NOW,
 )
-from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.decoratedresultset import DecoratedResultSet
-from lp.services.database.enumcol import EnumCol
+from lp.services.database.enumcol import DBEnum
 from lp.services.database.interfaces import IStore
 from lp.services.database.nl_search import nl_phrase_search
-from lp.services.database.sqlbase import (
-cursor,
-SQLBase,
-sqlvalues,
-)
 from lp.services.database.stormexpr import (
 fti_search,
 rank_by_fti,
@@ -137,6 +133,7 @@ from lp.services.worlddata.helpers import is_english_variant
 from lp.services.worlddata.interfaces.language import ILanguage
 from lp.services.worlddata.model.language import Language
 from lp.services.xref.interfaces import IXRefSet
+from lp.services.xref.model import XRef
 
 
 class notify_question_modified:
@@ -177,50 +174,50 @@ class notify_question_modified:
 
 
 @implementer(IQuestion, IBugLinkTarget)
-class Question(SQLBase, BugLinkTargetMixin):
+class Question(StormBase, BugLinkTargetMixin):
 """See `IQuestion`."""
 
-_table = 'Question'
+__storm_table__ = 'Question'
 _defaultOrder = ['-priority', 'datecreated']
 
 # db field names
-owner = ForeignKey(
-dbName='owner', foreignKey='Person',
-storm_validator=validate_public_person, notNull=True)
-title = StringCol(notNull=True)
-description = StringCol(notNull=True)
-language = ForeignKey(
-dbName='language', notNull=True, foreignKey='Language')
-status = EnumCol(
-schema=QuestionStatus, notNull=True, default=QuestionStatus.OPEN)
-priority = EnumCol(
-schema=QuestionPriority, notNull=True,
-default=QuestionPriority.NORMAL)
-assignee = ForeignKey(
-dbName='assignee', notNull=False, foreignKey='Person',
-storm_validator=validate_public_person, default=None)
-answerer = ForeignKey(
-dbName='answerer', notNull=False, foreignKey='Person',
-storm_validator=validate_public_person, default=None)
+id = Int(primary=True)
+owner_id = Int(name='owner', allow_none=False,
+   validator=validate_public_person)
+owner = Reference(owner_id, 'Person.id')
+title = Unicode(allow_none=False)
+description = Unicode(allow_none=False)
+language_id = Int(name="language", allow_none=False)
+language = Reference(language_id, 'Language.id')
+status = DBEnum(name="status", enum=QuestionStatus, 

Re: [Launchpad-reviewers] [Merge] ~pappacena/launchpad:mirror-prober-untwist-db-calls into launchpad:master

2020-12-02 Thread Colin Watson
Review: Approve

This is pretty difficult to follow, but I'm OK with it if it works.

However, did you consider instead using storm.twisted.transact for this?  It's 
designed for this sort of thing, and it might be possible to use it to make 
things significantly easier to follow.
-- 
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/394494
Your team Launchpad code reviewers is subscribed to branch 
~pappacena/launchpad:mirror-prober-untwist-db-calls.

___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp


[Launchpad-reviewers] [Merge] ~cjwatson/launchpad:stormify-potmsgset-queries into launchpad:master

2020-12-02 Thread Colin Watson
Colin Watson has proposed merging 
~cjwatson/launchpad:stormify-potmsgset-queries into launchpad:master.

Commit message:
Convert queries in lp.translations.model.potmsgset to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394757
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~cjwatson/launchpad:stormify-potmsgset-queries into launchpad:master.
diff --git a/lib/lp/translations/model/potmsgset.py b/lib/lp/translations/model/potmsgset.py
index f256384..649f6bd 100644
--- a/lib/lp/translations/model/potmsgset.py
+++ b/lib/lp/translations/model/potmsgset.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2018 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
@@ -21,10 +21,17 @@ from sqlobject import (
 StringCol,
 )
 from storm.expr import (
+And,
 Coalesce,
+Column,
 Desc,
+Join,
+Not,
 Or,
+Select,
 SQL,
+Table,
+With,
 )
 from storm.store import (
 EmptyResultSet,
@@ -39,11 +46,14 @@ from lp.services.config import config
 from lp.services.database.constants import DEFAULT
 from lp.services.database.interfaces import IStore
 from lp.services.database.sqlbase import (
-cursor,
-quote,
 SQLBase,
 sqlvalues,
 )
+from lp.services.database.stormexpr import (
+IsTrue,
+NullsFirst,
+NullsLast,
+)
 from lp.services.helpers import shortlist
 from lp.services.propertycache import get_property_cache
 from lp.translations.interfaces.potmsgset import (
@@ -55,9 +65,6 @@ from lp.translations.interfaces.side import (
 ITranslationSideTraitsSet,
 TranslationSide,
 )
-from lp.translations.interfaces.translationfileformat import (
-TranslationFileFormat,
-)
 from lp.translations.interfaces.translationimporter import (
 ITranslationImporter,
 )
@@ -163,6 +170,8 @@ class POTMsgSet(SQLBase):
 of all the source_file_format values.  Otherwise, it should be
 a `TranslationFileFormat` value.
 """
+# Circular import.
+from lp.translations.model.potemplate import POTemplate
 
 translation_importer = getUtility(ITranslationImporter)
 
@@ -175,19 +184,18 @@ class POTMsgSet(SQLBase):
 
 # Now let's find all the source_file_formats for all the
 # POTemplates this POTMsgSet is part of.
-query = """
-   SELECT DISTINCT POTemplate.source_file_format
- FROM TranslationTemplateItem
-  JOIN POTemplate
-ON POTemplate.id = TranslationTemplateItem.potemplate
- WHERE TranslationTemplateItem.potmsgset = %s""" % (
-sqlvalues(self))
-cur = cursor()
-cur.execute(query)
-source_file_formats = cur.fetchall()
-for source_file_format, in source_file_formats:
+origin = [
+TranslationTemplateItem,
+Join(
+POTemplate,
+TranslationTemplateItem.potemplate == POTemplate.id),
+]
+source_file_formats = IStore(POTemplate).using(*origin).find(
+POTemplate.source_file_format,
+TranslationTemplateItem.potmsgset == self).config(distinct=True)
+for source_file_format in source_file_formats:
 format = translation_importer.getTranslationFormatImporter(
-TranslationFileFormat.items[source_file_format])
+source_file_format)
 format_uses_english_msgids = not format.uses_source_string_msgids
 
 if uses_english_msgids is None:
@@ -326,15 +334,13 @@ class POTMsgSet(SQLBase):
 include_dismissed=False,
 include_unreviewed=True):
 """See `IPOTMsgSet`."""
-query = """
-is_current_ubuntu IS NOT TRUE AND
-is_current_upstream IS NOT TRUE AND
-potmsgset = %s AND
-language = %s
-""" % sqlvalues(self, language)
-msgstr_clause = make_plurals_sql_fragment(
-"msgstr%(form)d IS NOT NULL", "OR")
-query += " AND (%s)" % msgstr_clause
+clauses = [
+Not(IsTrue(TranslationMessage.is_current_ubuntu)),
+Not(IsTrue(TranslationMessage.is_current_upstream)),
+TranslationMessage.potmsgset == self,
+TranslationMessage.language == language,
+SQL(make_plurals_sql_fragment("msgstr%(form)d IS NOT NULL", "OR")),
+]
 if include_dismissed != include_unreviewed:
 current = self.getCurrentTranslation(
 potemplate, language, potemplate.translation_side)
@@ -344,10 +350,10 @@ class POTMsgSet(SQLBase):
 else:
  

[Launchpad-reviewers] [Merge] lp:~cjwatson/lpbuildbot/reduce-timeout into lp:lpbuildbot

2020-12-02 Thread noreply
The proposal to merge lp:~cjwatson/lpbuildbot/reduce-timeout into lp:lpbuildbot 
has been updated.

Status: Needs review => Merged

For more details, see:
https://code.launchpad.net/~cjwatson/lpbuildbot/reduce-timeout/+merge/394746
-- 
Your team Launchpad code reviewers is subscribed to branch lp:lpbuildbot.

___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-reviewers] [Merge] lp:~cjwatson/lpbuildbot/reduce-timeout into lp:lpbuildbot

2020-12-02 Thread Tom Wardill
Review: Approve


-- 
https://code.launchpad.net/~cjwatson/lpbuildbot/reduce-timeout/+merge/394746
Your team Launchpad code reviewers is subscribed to branch lp:lpbuildbot.

___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp


[Launchpad-reviewers] [Merge] lp:~cjwatson/lpbuildbot/reduce-timeout into lp:lpbuildbot

2020-12-02 Thread Colin Watson
Colin Watson has proposed merging lp:~cjwatson/lpbuildbot/reduce-timeout into 
lp:lpbuildbot.

Commit message:
Reduce test timeout to two hours.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/lpbuildbot/reduce-timeout/+merge/394746

Successful runs typically take around 75 minutes at the moment, and never close 
to two hours.  Reduce the timeout so that if a run gets stuck then we don't 
have to wait as long for buildbot to clean it up automatically.

(This does mean that local deployments on slower hardware may need to tweak 
this.)
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
lp:~cjwatson/lpbuildbot/reduce-timeout into lp:lpbuildbot.
=== modified file 'master.cfg'
--- master.cfg	2020-10-09 10:44:56 +
+++ master.cfg	2020-12-02 12:06:17 +
@@ -137,7 +137,7 @@
 addEnvironmentPrepSteps(f, tree, container, variables=variables)
 f.addStep(
 lpbuildbot.test.Test(
-timeout=6*60*60,
+timeout=2*60*60,
 env={
 'TEMP': "{}/devel/temp".format(tree),
 'LP_LXD_CONTAINER': container,

___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp


[Launchpad-reviewers] [Merge] ~cjwatson/launchpad:oci-recipe-retry-build into launchpad:master

2020-12-02 Thread Colin Watson
Colin Watson has proposed merging ~cjwatson/launchpad:oci-recipe-retry-build 
into launchpad:master.

Commit message:
Add support for retrying OCI recipe builds

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394745
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~cjwatson/launchpad:oci-recipe-retry-build into launchpad:master.
diff --git a/lib/lp/oci/browser/configure.zcml b/lib/lp/oci/browser/configure.zcml
index 0aa6da8..6c254fb 100644
--- a/lib/lp/oci/browser/configure.zcml
+++ b/lib/lp/oci/browser/configure.zcml
@@ -103,6 +103,12 @@
 template="../templates/ocirecipebuild-index.pt" />
 
+
   
+  
   
diff --git a/lib/lp/oci/templates/ocirecipebuild-retry.pt b/lib/lp/oci/templates/ocirecipebuild-retry.pt
new file mode 100644
index 000..ba08004
--- /dev/null
+++ b/lib/lp/oci/templates/ocirecipebuild-retry.pt
@@ -0,0 +1,28 @@
+http://www.w3.org/1999/xhtml;
+  xmlns:tal="http://xml.zope.org/namespaces/tal;
+  xmlns:metal="http://xml.zope.org/namespaces/metal;
+  xmlns:i18n="http://xml.zope.org/namespaces/i18n;
+  metal:use-macro="view/macro:page/main_only"
+  i18n:domain="launchpad">
+
+
+  
+
+  
+
+  The status of  is
+  .
+
+Retrying this build will destroy its history and logs.
+
+  By default, this build will be retried only after other pending
+  builds; please contact a build daemon administrator if you need
+  special treatment.
+
+  
+
+  
+
+
+
diff --git a/lib/lp/oci/tests/test_ocirecipebuild.py b/lib/lp/oci/tests/test_ocirecipebuild.py
index 1dbd170..4f558f3 100644
--- a/lib/lp/oci/tests/test_ocirecipebuild.py
+++ b/lib/lp/oci/tests/test_ocirecipebuild.py
@@ -5,9 +5,13 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from datetime import timedelta
+from datetime import (
+datetime,
+timedelta,
+)
 
 from fixtures import FakeLogger
+import pytz
 import six
 from testtools.matchers import (
 ContainsDict,
@@ -47,6 +51,7 @@ from lp.services.webapp.publisher import canonical_url
 from lp.services.webhooks.testing import LogsScheduledWebhooks
 from lp.testing import (
 admin_logged_in,
+person_logged_in,
 StormStatementRecorder,
 TestCaseWithFactory,
 )
@@ -144,6 +149,30 @@ class TestOCIRecipeBuild(OCIConfigHelperMixin, TestCaseWithFactory):
 self.build.getLayerFileByDigest,
 'missing')
 
+def test_can_be_retried(self):
+ok_cases = [
+BuildStatus.FAILEDTOBUILD,
+BuildStatus.MANUALDEPWAIT,
+BuildStatus.CHROOTWAIT,
+BuildStatus.FAILEDTOUPLOAD,
+BuildStatus.CANCELLED,
+BuildStatus.SUPERSEDED,
+]
+for status in BuildStatus.items:
+build = self.factory.makeOCIRecipeBuild(status=status)
+if status in ok_cases:
+self.assertTrue(build.can_be_retried)
+else:
+self.assertFalse(build.can_be_retried)
+
+def test_can_be_retried_obsolete_series(self):
+# Builds for obsolete series cannot be retried.
+distroseries = self.factory.makeDistroSeries(
+status=SeriesStatus.OBSOLETE)
+das = self.factory.makeDistroArchSeries(distroseries=distroseries)
+build = self.factory.makeOCIRecipeBuild(distro_arch_series=das)
+self.assertFalse(build.can_be_retried)
+
 def test_can_be_cancelled(self):
 # For all states that can be cancelled, can_be_cancelled returns True.
 ok_cases = [
@@ -156,6 +185,22 @@ class TestOCIRecipeBuild(OCIConfigHelperMixin, TestCaseWithFactory):
 else:
 self.assertFalse(self.build.can_be_cancelled)
 
+def test_retry_resets_state(self):
+# Retrying a build resets most of the state attributes, but does
+# not modify the first dispatch time.
+now = datetime.now(pytz.UTC)
+build = self.factory.makeOCIRecipeBuild()
+build.updateStatus(BuildStatus.BUILDING, date_started=now)
+build.updateStatus(BuildStatus.FAILEDTOBUILD)
+build.gotFailure()
+with person_logged_in(build.recipe.owner):
+build.retry()
+self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
+self.assertEqual(now, build.date_first_dispatched)
+self.assertIsNone(build.log)
+self.assertIsNone(build.upload_log)
+self.assertEqual(0, build.failure_count)
+
 def test_cancel_not_in_progress(self):
 # The cancel() method for a pending build leaves it in the CANCELLED
 # state.
___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : 

[Launchpad-reviewers] [Merge] ~cjwatson/launchpad:py3-processwithtimeout into launchpad:master

2020-12-02 Thread Colin Watson
Colin Watson has proposed merging ~cjwatson/launchpad:py3-processwithtimeout 
into launchpad:master.

Commit message:
Port ProcessWithTimeout to io.BytesIO

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394741
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of 
~cjwatson/launchpad:py3-processwithtimeout into launchpad:master.
diff --git a/lib/lp/buildmaster/interactor.py b/lib/lp/buildmaster/interactor.py
index be6bb60..589c1f3 100644
--- a/lib/lp/buildmaster/interactor.py
+++ b/lib/lp/buildmaster/interactor.py
@@ -15,6 +15,7 @@ import sys
 import traceback
 
 from ampoule.pool import ProcessPool
+import six
 from six.moves.urllib.parse import urlparse
 import transaction
 from twisted.internet import (
@@ -389,7 +390,8 @@ class BuilderInteractor(object):
 def got_resume_bad(failure):
 stdout, stderr, code = failure.value
 raise CannotResumeHost(
-"Resuming failed:\nOUT:\n%s\nERR:\n%s\n" % (stdout, stderr))
+"Resuming failed:\nOUT:\n%s\nERR:\n%s\n" %
+(six.ensure_str(stdout), six.ensure_str(stderr)))
 
 return d.addCallback(got_resume_ok).addErrback(got_resume_bad)
 
diff --git a/lib/lp/buildmaster/tests/test_interactor.py b/lib/lp/buildmaster/tests/test_interactor.py
index 0847014..c1f4c6d 100644
--- a/lib/lp/buildmaster/tests/test_interactor.py
+++ b/lib/lp/buildmaster/tests/test_interactor.py
@@ -16,6 +16,7 @@ import signal
 import tempfile
 
 from lpbuildd.builder import BuilderStatus
+import six
 from six.moves import xmlrpc_client
 from testtools.matchers import ContainsAll
 from testtools.testcase import ExpectedException
@@ -161,7 +162,7 @@ class TestBuilderInteractor(TestCase):
 url="http://crackle.ppa/;, virtualized=True, vm_host="pop"))
 
 def got_resume(output):
-self.assertEqual(('snap crackle pop', ''), output)
+self.assertEqual((b'snap crackle pop', b''), output)
 return d.addCallback(got_resume)
 
 def test_resumeSlaveHost_command_failed(self):
@@ -635,7 +636,7 @@ class TestSlave(TestCase):
 # XXX: JonathanLange 2010-09-23: We should instead pass the
 # expected vm_host into the client slave. Not doing this now,
 # since the SlaveHelper is being moved around.
-self.assertEqual("%s\n" % slave._vm_host, out)
+self.assertEqual("%s\n" % slave._vm_host, six.ensure_str(out))
 
 def test_resumeHost_failure(self):
 # On a failed resume, 'resumeHost' fires the returned deferred
diff --git a/lib/lp/services/twistedsupport/processmonitor.py b/lib/lp/services/twistedsupport/processmonitor.py
index 1dbbe98..c4f2b87 100644
--- a/lib/lp/services/twistedsupport/processmonitor.py
+++ b/lib/lp/services/twistedsupport/processmonitor.py
@@ -12,8 +12,8 @@ __all__ = [
 ]
 
 
+import io
 import os
-import StringIO
 
 from twisted.internet import (
 defer,
@@ -278,8 +278,8 @@ class ProcessWithTimeout(ProcessProtocol, TimeoutMixin):
 self._deferred = deferred
 self._clock = clock
 self._timeout = timeout
-self._out_buf = StringIO.StringIO()
-self._err_buf = StringIO.StringIO()
+self._out_buf = io.BytesIO()
+self._err_buf = io.BytesIO()
 self._process_transport = None
 
 # outReceived and errReceived are callback methods on
___
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : launchpad-reviewers@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp