← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
Port lp.services.profile to Python 3

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/386528
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-profile into launchpad:master.
diff --git a/lib/lp/services/profile/mem.py b/lib/lp/services/profile/mem.py
index 73ea820..42deda6 100644
--- a/lib/lp/services/profile/mem.py
+++ b/lib/lp/services/profile/mem.py
@@ -16,6 +16,8 @@ and improve APIs as needed.
 
 """
 
+from __future__ import absolute_import, print_function
+
 __metatype__ = type
 __all__ = [
     'classesWithMostRefs',
@@ -94,15 +96,15 @@ def dump_garbage():
     """
 
     # force collection
-    print "\nGARBAGE:"
+    print("\nGARBAGE:")
     gc.collect()
 
-    print "\nGARBAGE OBJECTS:"
+    print("\nGARBAGE OBJECTS:")
     for x in gc.garbage:
         s = str(x)
         if len(s) > 80:
             s = s[:80]
-        print type(x), "\n  ", s
+        print(type(x), "\n  ", s)
 
 
 # This is spiv's reference count code, under 'MIT Licence if I'm pressed'.
@@ -183,7 +185,7 @@ def deltaCounts(counts1, counts2, n=30):
 def printCounts(counts, file=None):
     for c, obj in counts:
         if file is None:
-            print c, obj
+            print(c, obj)
         else:
             file.write("%s %s\n" % (c, obj))
 
diff --git a/lib/lp/services/profile/profile.py b/lib/lp/services/profile/profile.py
index 400f96e..e38de21 100644
--- a/lib/lp/services/profile/profile.py
+++ b/lib/lp/services/profile/profile.py
@@ -18,11 +18,11 @@ import heapq
 import os
 import pstats
 import re
-import StringIO
 import sys
 import threading
 
 from breezy import lsprof
+import six
 import oops_datedir_repo.serializer_rfc822
 from zope.component import (
     adapter,
@@ -339,13 +339,14 @@ def end_request(event):
         if is_html and 'show' in actions:
             # Generate rfc822 OOPS result (might be nice to have an html
             # serializer..).
-            template_context['oops'] = ''.join(
-                oops_datedir_repo.serializer_rfc822.to_chunks(oops_report))
+            template_context['oops'] = b''.join(
+                oops_datedir_repo.serializer_rfc822.to_chunks(
+                    oops_report)).decode('UTF-8', 'replace')
             # Generate profile summaries.
             prof_stats.strip_dirs()
             for name in ('time', 'cumulative', 'calls'):
                 prof_stats.sort(name)
-                f = StringIO.StringIO()
+                f = six.StringIO()
                 prof_stats.pprint(file=f)
                 template_context[name] = f.getvalue()
         template_context['profile_count'] = prof_stats.count
@@ -457,9 +458,8 @@ def end_request(event):
                 rank=step['python_rank'],
                 cls=step['python_class']))
         # Identify the repeated Python calls that generated SQL.
-        triggers = triggers.items()
-        triggers.sort(key=lambda x: len(x[1]))
-        triggers.reverse()
+        triggers = sorted(
+            triggers.items(), key=lambda x: len(x[1]), reverse=True)
         top_triggers = []
         for (key, ixs) in triggers:
             if len(ixs) == 1:
@@ -491,11 +491,12 @@ def end_request(event):
         except Exception:
             error = ''.join(format_exception(*sys.exc_info(), as_html=True))
             added_html = (
-                '<div class="profiling_info">' + error + '</div>')
+                '<div class="profiling_info">' + error +
+                '</div>').encode(encoding)
         existing_html = request.response.consumeBody()
         e_start, e_close_body, e_end = existing_html.rpartition(
-            '</body>')
-        new_html = ''.join(
+            b'</body>')
+        new_html = b''.join(
             (e_start, added_html, e_close_body, e_end))
         request.response.setResult(new_html)
 
diff --git a/lib/lp/services/profile/profiling.txt b/lib/lp/services/profile/profiling.txt
index 844698b..7ba0ac7 100644
--- a/lib/lp/services/profile/profiling.txt
+++ b/lib/lp/services/profile/profiling.txt
@@ -72,7 +72,7 @@ When the environment isn't set, no profile is created.
     >>> del os.environ['PROFILE_PAGETESTS_REQUESTS']
 
     >>> PageTestLayer.setUp()
-    >>> print PageTestLayer.profiler
+    >>> print(PageTestLayer.profiler)
     None
 
 And no stats file is written when the layer is torn down.
@@ -123,7 +123,7 @@ The feature has two modes.
     smoke test.  The unit tests verify further functionality.
 
     >>> response = http('GET /++profile++ HTTP/1.0')
-    >>> '<h1>Profiling Information</h1>' in response.getBody()
+    >>> b'<h1>Profiling Information</h1>' in response.getBody()
     True
 
 -   It can be configured to profile all requests, indiscriminately.  To turn
@@ -199,11 +199,11 @@ log file.
     ...     (timestamp, page_id, oops_id, duration,
     ...      start_vss, start_rss, end_vss, end_rss) = (
     ...         memory_profile_fh.readline().split())
-    >>> print timestamp
+    >>> print(timestamp)
     20...
-    >>> print oops_id
+    >>> print(oops_id)
     -
-    >>> print page_id
+    >>> print(page_id)
     RootObject:index.html
 
 ..  ReST comment: this is clean up for the work done above.
diff --git a/lib/lp/services/profile/tests.py b/lib/lp/services/profile/tests.py
index 5bfcd81..b299a83 100644
--- a/lib/lp/services/profile/tests.py
+++ b/lib/lp/services/profile/tests.py
@@ -394,7 +394,7 @@ class BaseRequestEndHandlerTest(BaseTest):
 
     def getAddedResponse(self, request,
                          start=EXAMPLE_HTML_START, end=EXAMPLE_HTML_END):
-        output = request.response.consumeBody()
+        output = request.response.consumeBody().decode('UTF-8')
         return output[len(start):-len(end)]
 
     def getMemoryLog(self):
@@ -590,7 +590,7 @@ class TestBothProfilersRequestEndHandler(BaseRequestEndHandlerTest):
         # to verify that these two files are different.
         data = []
         for filename in self.getAllProfilePaths():
-            with open(filename) as f:
+            with open(filename, 'rb') as f:
                 data.append(f.read())
         self.assertEqual(2, len(data))
         self.assertNotEqual(data[0], data[1])
@@ -797,7 +797,8 @@ def test_suite():
     suite = unittest.TestSuite()
 
     doctest = LayeredDocFileSuite(
-        './profiling.txt', setUp=setUp, tearDown=tearDown,
+        './profiling.txt',
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=LaunchpadFunctionalLayer, stdout_logging_level=logging.WARNING)
     suite.addTest(doctest)
     suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))