← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~lifeless/launchpad/useoops into lp:launchpad

 

Robert Collins has proposed merging lp:~lifeless/launchpad/useoops into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~lifeless/launchpad/useoops/+merge/70995

Delete lp.services.log.uniquefileallocator and the ErrorReport.read method body - both are now in the lp:python-oops project.

To keep from having a --mega-- branch at the end I'm doing a bunch of small point releases as this work progresses.
-- 
https://code.launchpad.net/~lifeless/launchpad/useoops/+merge/70995
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~lifeless/launchpad/useoops into lp:launchpad.
=== modified file 'lib/canonical/launchpad/scripts/ftests/test_oops_prune.py'
--- lib/canonical/launchpad/scripts/ftests/test_oops_prune.py	2010-10-04 19:50:45 +0000
+++ lib/canonical/launchpad/scripts/ftests/test_oops_prune.py	2011-08-10 06:56:09 +0000
@@ -23,6 +23,7 @@
 import tempfile
 import unittest
 
+from oops import uniquefileallocator
 from pytz import UTC
 import transaction
 
@@ -36,7 +37,6 @@
     unwanted_oops_files,
     )
 from canonical.testing.layers import LaunchpadZopelessLayer
-from lp.services.log import uniquefileallocator
 
 
 class TestOopsPrune(unittest.TestCase):

=== modified file 'lib/canonical/launchpad/scripts/oops.py'
--- lib/canonical/launchpad/scripts/oops.py	2010-10-26 15:47:24 +0000
+++ lib/canonical/launchpad/scripts/oops.py	2011-08-10 06:56:09 +0000
@@ -18,12 +18,12 @@
 import os
 import re
 
+from ...oops import uniquefileallocator
 from pytz import utc
 
 from canonical.database.sqlbase import cursor
 from canonical.launchpad.webapp.dbpolicy import SlaveOnlyDatabasePolicy
 from lp.app.browser.stringformatter import FormattersAPI
-from lp.services.log import uniquefileallocator
 
 
 def referenced_oops():

=== modified file 'lib/canonical/launchpad/webapp/errorlog.py'
--- lib/canonical/launchpad/webapp/errorlog.py	2011-06-21 13:54:37 +0000
+++ lib/canonical/launchpad/webapp/errorlog.py	2011-08-10 06:56:09 +0000
@@ -20,6 +20,8 @@
 import urlparse
 
 from lazr.restful.utils import get_current_browser_request
+from oops.uniquefileallocator import UniqueFileAllocator
+import oops.serializer_rfc822
 import pytz
 from zope.component.interfaces import ObjectEvent
 from zope.error.interfaces import IErrorReportingUtility
@@ -45,7 +47,6 @@
 from canonical.launchpad.webapp.vhosts import allvhosts
 from canonical.lazr.utils import safe_hasattr
 from lp.app import versioninfo
-from lp.services.log.uniquefileallocator import UniqueFileAllocator
 from lp.services.timeline.requesttimeline import get_request_timeline
 
 
@@ -128,20 +129,6 @@
     return True
 
 
-def parse_iso8601_date(datestring):
-    """Parses a standard ISO 8601 format date, ignoring time zones.
-
-    Performs no validation whatsoever. It just plucks up to the first
-    7 numbers from the string and passes them to `datetime.datetime`,
-    so would in fact parse any string containing reasonable numbers.
-
-    This function can be replaced with `datetime.datetime.strptime()`
-    once we move to Python 2.5.
-    """
-    return datetime.datetime(
-        *(int(elem) for elem in re.findall('[0-9]+', datestring)[:7]))
-
-
 class ErrorReportEvent(ObjectEvent):
     """A new error report has been created."""
     implements(IErrorReportEvent)
@@ -204,54 +191,9 @@
 
     @classmethod
     def read(cls, fp):
-        msg = rfc822.Message(fp)
-        id = msg.getheader('oops-id')
-        exc_type = msg.getheader('exception-type')
-        exc_value = msg.getheader('exception-value')
-        date = parse_iso8601_date(msg.getheader('date'))
-        pageid = msg.getheader('page-id')
-        username = msg.getheader('user')
-        url = msg.getheader('url')
-        duration = int(float(msg.getheader('duration', '-1')))
-        informational = msg.getheader('informational')
-
-        # Explicitly use an iterator so we can process the file
-        # sequentially. In most instances the iterator will actually
-        # be the file object passed in because file objects should
-        # support iteration.
-        lines = iter(msg.fp)
-
-        # Request variables until the first blank line.
-        req_vars = []
-        for line in lines:
-            line = line.strip()
-            if line == '':
-                break
-            key, value = line.split('=', 1)
-            req_vars.append((urllib.unquote(key), urllib.unquote(value)))
-
-        # Statements until the next blank line.
-        statements = []
-        for line in lines:
-            line = line.strip()
-            if line == '':
-                break
-            match = re.match(
-                r'^(\d+)-(\d+)(?:@([\w-]+))?\s+(.*)', line)
-            assert match is not None, (
-                "Unable to interpret oops line: %s" % line)
-            start, end, db_id, statement = match.groups()
-            if db_id is not None:
-                db_id = intern(db_id)  # This string is repeated lots.
-            statements.append(
-                (int(start), int(end), db_id, statement))
-
-        # The rest is traceback.
-        tb_text = ''.join(lines)
-
-        return cls(id, exc_type, exc_value, date, pageid, tb_text,
-                   username, url, duration, req_vars, statements,
-                   informational)
+        # Deprecated: use the oops module directly now, when possible.
+        report = oops.serializer_rfc822.read(fp)
+        return cls(**report)
 
 
 class ErrorReportingUtility:

=== modified file 'lib/canonical/launchpad/webapp/tests/test_errorlog.py'
--- lib/canonical/launchpad/webapp/tests/test_errorlog.py	2011-06-20 18:08:45 +0000
+++ lib/canonical/launchpad/webapp/tests/test_errorlog.py	2011-08-10 06:56:09 +0000
@@ -20,6 +20,7 @@
 
 from lazr.batchnavigator.interfaces import InvalidBatchSizeError
 from lazr.restful.declarations import error_status
+from oops.uniquefileallocator import UniqueFileAllocator
 import pytz
 import testtools
 from zope.app.publication.tests.test_zopepublication import (
@@ -51,13 +52,12 @@
     GoneError,
     TranslationUnavailable,
     )
-from lp.services.log.uniquefileallocator import UniqueFileAllocator
 from lp.services.osutils import remove_tree
 from lp.testing import TestCase
 from lp_sitecustomize import customize_get_converter
 
 
-UTC = pytz.timezone('UTC')
+UTC = pytz.utc
 
 
 class ArbitraryException(Exception):
@@ -141,6 +141,7 @@
 
     def test_read(self):
         """Test ErrorReport.read()."""
+        # Note: this exists to test the compatibility thunk only.
         fp = StringIO.StringIO(dedent("""\
             Oops-Id: OOPS-A0001
             Exception-Type: NotFound
@@ -163,7 +164,8 @@
         self.assertEqual(entry.id, 'OOPS-A0001')
         self.assertEqual(entry.type, 'NotFound')
         self.assertEqual(entry.value, 'error message')
-        self.assertEqual(entry.time, datetime.datetime(2005, 4, 1))
+        self.assertEqual(
+                entry.time, datetime.datetime(2005, 4, 1, tzinfo=UTC))
         self.assertEqual(entry.pageid, 'IFoo:+foo-template')
         self.assertEqual(entry.tb_text, 'traceback-text')
         self.assertEqual(entry.username, 'Sample User')
@@ -183,46 +185,6 @@
             entry.db_statements[1],
             (5, 10, 'store_b', 'SELECT 2'))
 
-    def test_read_no_store_id(self):
-        """Test ErrorReport.read() for old logs with no store_id."""
-        fp = StringIO.StringIO(dedent("""\
-            Oops-Id: OOPS-A0001
-            Exception-Type: NotFound
-            Exception-Value: error message
-            Date: 2005-04-01T00:00:00+00:00
-            Page-Id: IFoo:+foo-template
-            User: Sample User
-            URL: http://localhost:9000/foo
-            Duration: 42
-
-            HTTP_USER_AGENT=Mozilla/5.0
-            HTTP_REFERER=http://localhost:9000/
-            name%3Dfoo=hello%0Aworld
-
-            00001-00005 SELECT 1
-            00005-00010 SELECT 2
-
-            traceback-text"""))
-        entry = ErrorReport.read(fp)
-        self.assertEqual(entry.id, 'OOPS-A0001')
-        self.assertEqual(entry.type, 'NotFound')
-        self.assertEqual(entry.value, 'error message')
-        self.assertEqual(entry.time, datetime.datetime(2005, 4, 1))
-        self.assertEqual(entry.pageid, 'IFoo:+foo-template')
-        self.assertEqual(entry.tb_text, 'traceback-text')
-        self.assertEqual(entry.username, 'Sample User')
-        self.assertEqual(entry.url, 'http://localhost:9000/foo')
-        self.assertEqual(entry.duration, 42)
-        self.assertEqual(len(entry.req_vars), 3)
-        self.assertEqual(entry.req_vars[0], ('HTTP_USER_AGENT',
-                                             'Mozilla/5.0'))
-        self.assertEqual(entry.req_vars[1], ('HTTP_REFERER',
-                                             'http://localhost:9000/'))
-        self.assertEqual(entry.req_vars[2], ('name=foo', 'hello\nworld'))
-        self.assertEqual(len(entry.db_statements), 2)
-        self.assertEqual(entry.db_statements[0], (1, 5, None, 'SELECT 1'))
-        self.assertEqual(entry.db_statements[1], (5, 10, None, 'SELECT 2'))
-
 
 class TestErrorReportingUtility(testtools.TestCase):
 

=== removed file 'lib/lp/services/log/tests/test_uniquefileallocator.py'
--- lib/lp/services/log/tests/test_uniquefileallocator.py	2010-10-26 15:47:24 +0000
+++ lib/lp/services/log/tests/test_uniquefileallocator.py	1970-01-01 00:00:00 +0000
@@ -1,156 +0,0 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Tests for the unique log naming facility."""
-
-__metaclass__ = type
-
-import datetime
-import os
-import shutil
-import stat
-import tempfile
-import unittest
-
-import pytz
-import testtools
-
-from lp.services.log.uniquefileallocator import UniqueFileAllocator
-
-
-UTC = pytz.timezone('UTC')
-
-
-class TestUniqueFileAllocator(testtools.TestCase):
-
-    def setUp(self):
-        super(TestUniqueFileAllocator, self).setUp()
-        tempdir = tempfile.mkdtemp()
-        self._tempdir = tempdir
-        self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True)
-
-    def test_setToken(self):
-        namer = UniqueFileAllocator("/any-old/path/", 'OOPS', 'T')
-        self.assertEqual('T', namer.get_log_infix())
-
-        # Some scripts will append a string token to the prefix.
-        namer.setToken('CW')
-        self.assertEqual('TCW', namer.get_log_infix())
-
-        # Some scripts run multiple processes and append a string number
-        # to the prefix.
-        namer.setToken('1')
-        self.assertEqual('T1', namer.get_log_infix())
-
-    def assertUniqueFileAllocator(self, namer, now, expected_id,
-        expected_last_id, expected_suffix, expected_lastdir):
-        logid, filename = namer.newId(now)
-        self.assertEqual(logid, expected_id)
-        self.assertEqual(filename,
-            os.path.join(namer._output_root, expected_suffix))
-        self.assertEqual(namer._last_serial, expected_last_id)
-        self.assertEqual(namer._last_output_dir,
-            os.path.join(namer._output_root, expected_lastdir))
-
-    def test_newId(self):
-        # TODO: This should return an id, fileobj instead of a file name, to
-        # reduce races with threads that are slow to use what they asked for,
-        # when combined with configuration changes causing disk scans. That
-        # would also permit using a completely stubbed out file system,
-        # reducing IO in tests that use UniqueFileAllocator (such as all the
-        # pagetests in Launchpad. At that point an interface to obtain a
-        # factory of UniqueFileAllocator's would be useful to parameterise the
-        # entire test suite.
-        namer = UniqueFileAllocator(self._tempdir, 'OOPS', 'T')
-        # first name of the day
-        self.assertUniqueFileAllocator(namer,
-            datetime.datetime(2006, 04, 01, 00, 30, 00, tzinfo=UTC),
-            'OOPS-91T1', 1, '2006-04-01/01800.T1', '2006-04-01')
-        # second name of the day
-        self.assertUniqueFileAllocator(namer,
-            datetime.datetime(2006, 04, 01, 12, 00, 00, tzinfo=UTC),
-            'OOPS-91T2', 2, '2006-04-01/43200.T2', '2006-04-01')
-
-        # first name of the following day sets a new dir and the id starts
-        # over.
-        self.assertUniqueFileAllocator(namer,
-            datetime.datetime(2006, 04, 02, 00, 30, 00, tzinfo=UTC),
-            'OOPS-92T1', 1, '2006-04-02/01800.T1', '2006-04-02')
-
-        # Setting a token inserts the token into the filename.
-        namer.setToken('YYY')
-        logid, filename = namer.newId(
-            datetime.datetime(2006, 04, 02, 00, 30, 00, tzinfo=UTC))
-        self.assertEqual(logid, 'OOPS-92TYYY2')
-
-        # Setting a type controls the log id:
-        namer.setToken('')
-        namer._log_type = "PROFILE"
-        logid, filename = namer.newId(
-            datetime.datetime(2006, 04, 02, 00, 30, 00, tzinfo=UTC))
-        self.assertEqual(logid, 'PROFILE-92T3')
-
-        # Native timestamps are not permitted - UTC only.
-        now = datetime.datetime(2006, 04, 02, 00, 30, 00)
-        self.assertRaises(ValueError, namer.newId, now)
-
-    def test_changeErrorDir(self):
-        """Test changing the log output dir."""
-        namer = UniqueFileAllocator(self._tempdir, 'OOPS', 'T')
-
-        # First an id in the original error directory.
-        self.assertUniqueFileAllocator(namer,
-            datetime.datetime(2006, 04, 01, 00, 30, 00, tzinfo=UTC),
-            'OOPS-91T1', 1, '2006-04-01/01800.T1', '2006-04-01')
-
-        # UniqueFileAllocator uses the _output_root attribute to get the
-        # current output directory.
-        new_output_dir = tempfile.mkdtemp()
-        self.addCleanup(shutil.rmtree, new_output_dir, ignore_errors=True)
-        namer._output_root = new_output_dir
-
-        # Now an id on the same day, in the new directory.
-        now = datetime.datetime(2006, 04, 01, 12, 00, 00, tzinfo=UTC)
-        log_id, filename = namer.newId(now)
-
-        # Since it's a new directory, with no previous logs, the id is 1
-        # again, rather than 2.
-        self.assertEqual(log_id, 'OOPS-91T1')
-        self.assertEqual(namer._last_serial, 1)
-        self.assertEqual(namer._last_output_dir,
-            os.path.join(new_output_dir, '2006-04-01'))
-
-    def test_findHighestSerial(self):
-        namer = UniqueFileAllocator(self._tempdir, "OOPS", "T")
-        # Creates the dir using now as the timestamp.
-        output_dir = namer.output_dir()
-        # write some files, in non-serial order.
-        open(os.path.join(output_dir, '12343.T1'), 'w').close()
-        open(os.path.join(output_dir, '12342.T2'), 'w').close()
-        open(os.path.join(output_dir, '12345.T3'), 'w').close()
-        open(os.path.join(output_dir, '1234567.T0010'), 'w').close()
-        open(os.path.join(output_dir, '12346.A42'), 'w').close()
-        open(os.path.join(output_dir, '12346.B100'), 'w').close()
-        # The namer should figure out the right highest serial.
-        self.assertEqual(namer._findHighestSerial(output_dir), 10)
-
-    def test_output_dir_permission(self):
-        # Set up default dir creation mode to rwx------.
-        umask_permission = stat.S_IRWXG | stat.S_IRWXO
-        old_umask = os.umask(umask_permission)
-        namer = UniqueFileAllocator(self._tempdir, "OOPS", "T")
-        output_dir = namer.output_dir()
-        st = os.stat(output_dir)
-        # Permission we want here is: rwxr-xr-x
-        wanted_permission = (
-            stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH |
-            stat.S_IXOTH)
-        # Get only the permission bits for this directory.
-        dir_permission = stat.S_IMODE(st.st_mode)
-        self.assertEqual(dir_permission, wanted_permission)
-        # Restore the umask to the original value.
-        ignored = os.umask(old_umask)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromName(__name__)

=== modified file 'setup.py'
--- setup.py	2011-08-10 01:00:53 +0000
+++ setup.py	2011-08-10 06:56:09 +0000
@@ -56,6 +56,7 @@
         'mercurial',
         'mocker',
         'oauth',
+        'oops',
         'paramiko',
         'psycopg2',
         'python-memcached',

=== modified file 'versions.cfg'
--- versions.cfg	2011-08-10 01:00:53 +0000
+++ versions.cfg	2011-08-10 06:56:09 +0000
@@ -25,6 +25,7 @@
 grokcore.component = 1.6
 httplib2 = 0.6.0
 ipython = 0.9.1
+iso8601 = 0.1.4
 Jinja2 = 2.2
 keyring = 0.6.2
 launchpadlib = 1.9.9
@@ -48,6 +49,7 @@
 mocker = 0.10.1
 mozrunner = 1.3.4
 oauth = 1.0
+oops = 0.0.1
 paramiko = 1.7.4
 Paste = 1.7.2
 PasteDeploy = 1.3.3


Follow ups