← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~lifeless/python-oops-amqp/misc into lp:python-oops-amqp

 

Robert Collins has proposed merging lp:~lifeless/python-oops-amqp/misc into lp:python-oops-amqp.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~lifeless/python-oops-amqp/misc/+merge/119076

This updates the API to match current oops releases, and adds a useful trace helper.
-- 
https://code.launchpad.net/~lifeless/python-oops-amqp/misc/+merge/119076
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~lifeless/python-oops-amqp/misc into lp:python-oops-amqp.
=== added file 'Makefile'
--- Makefile	1970-01-01 00:00:00 +0000
+++ Makefile	2012-08-10 02:44:19 +0000
@@ -0,0 +1,13 @@
+all:
+
+bin/buildout: buildout.cfg versions.cfg setup.py download-cache eggs
+	./bootstrap.py \
+		--setup-source=download-cache/ez_setup.py \
+		--download-base=download-cache/dist --eggs=eggs
+
+
+download-cache:
+	bzr checkout --lightweight lp:lp-source-dependencies download-cache
+
+eggs:
+	mkdir eggs

=== modified file 'NEWS'
--- NEWS	2012-02-10 14:47:11 +0000
+++ NEWS	2012-08-10 02:44:19 +0000
@@ -3,6 +3,18 @@
 
 Changes and improvements to oops-amqp, grouped by release.
 
+NEXT
+----
+
+0.0.7
+-----
+
+* New script 'oops-amqp-trace' which will trace oops reports received on an
+  AMQP exchange. (Robert Collins)
+
+* Updated to support the new publisher API in oops 0.0.11 and above. This is
+  incompatible with older versions of oops. (Robert Collins)
+
 0.0.6
 -----
 

=== modified file 'README'
--- README	2011-12-08 10:52:36 +0000
+++ README	2012-08-10 02:44:19 +0000
@@ -29,7 +29,7 @@
 
 * bson
 
-* oops (http://pypi.python.org/pypi/oops)
+* oops (http://pypi.python.org/pypi/oops) 0.0.11 or newer.
 
 * amqplib
 
@@ -63,7 +63,7 @@
 Provide the publisher to your OOPS config::
 
   >>> config = oops.Config()
-  >>> config.publishers.append(publisher)
+  >>> config.publisher = publisher
 
 Any oops published via that config will now be sent via amqp.
 
@@ -82,14 +82,14 @@
 the publication failed. To prevent losing the OOPS its a good idea to have a 
 fallback publisher - either another AMQP publisher (to a different server) or
 one that spools locally (where you can pick up the OOPSes via rsync or some
-other mechanism. Using the oops standard helper publish_new_only will let you
-wrap the fallback publisher so that it only gets invoked if the primary
+other mechanism. Using the oops standard helper publish_with_fallback will let
+you wrap the fallback publisher so that it only gets invoked if the primary
 method failed::
 
   >>> fallback_factory = partial(amqp.Connection, host="otherserver:5672",
   ...     userid="guest", password="guest", virtual_host="/", insist=False)
   >>> fallback_publisher = oops_amqp.Publisher(fallback_factory, "oopses", "")
-  >>> config.publishers.append(publish_new_only(fallback_publisher))
+  >>> config.publisher = publish_with_fallback(publisher, fallback_publisher)
 
 Receiving from AMQP
 +++++++++++++++++++
@@ -106,7 +106,7 @@
 
   >>> publisher = oops_datedir_repo.DateDirRepo('.', inherit_id=True)
   >>> config = oops.Config()
-  >>> config.publishers.append(publisher.publish)
+  >>> config.publisher = publisher.publish
   >>> receiver = oops_amqp.Receiver(config, factory, "my queue")
   >>> receiver.run_forever()
 

=== modified file 'oops_amqp/publisher.py'
--- oops_amqp/publisher.py	2012-02-09 23:13:45 +0000
+++ oops_amqp/publisher.py	2012-08-10 02:44:19 +0000
@@ -88,7 +88,7 @@
         message.properties["delivery_mode"] = 2
         channel = self.get_channel()
         if channel is None:
-            return None
+            return []
         try:
             channel.basic_publish(
                 message, self.exchange_name, routing_key=self.routing_key)
@@ -96,7 +96,7 @@
             self.channels.channel = None
             if is_amqplib_connection_error(e):
                 # Could not connect / interrupted connection
-                return None
+                return []
             # Unknown error mode : don't hide it.
             raise
-        return report['id']
+        return [report['id']]

=== modified file 'oops_amqp/tests/test_publisher.py'
--- oops_amqp/tests/test_publisher.py	2012-02-09 23:13:45 +0000
+++ oops_amqp/tests/test_publisher.py	2012-08-10 02:44:19 +0000
@@ -40,9 +40,9 @@
         reference_oops = {'id': 'kept', 'akey': 'avalue'}
         oops = dict(reference_oops)
         expected_id = 'kept'
-        oops_id = publisher(oops)
+        oops_ids = publisher(oops)
         # Publication returns the oops ID allocated.
-        self.assertEqual(expected_id, oops_id)
+        self.assertEqual([expected_id], oops_ids)
         # The oops should not be altered by publication.
         self.assertEqual(reference_oops, oops)
         # The received OOPS should have the ID embedded and be a bson dict.
@@ -66,14 +66,14 @@
         oops = dict(reference_oops)
         id_bson = md5(bson.dumps(oops)).hexdigest()
         expected_id = "OOPS-%s" % id_bson
-        oops_id = publisher(oops)
+        oops_ids = publisher(oops)
         # Publication returns the oops ID allocated.
-        self.assertEqual(expected_id, oops_id)
+        self.assertEqual([expected_id], oops_ids)
         # The oops should not be altered by publication.
         self.assertEqual(reference_oops, oops)
         # The received OOPS should have the ID embedded and be a bson dict.
         expected_oops = dict(reference_oops)
-        expected_oops['id'] = oops_id
+        expected_oops['id'] = oops_ids[0]
         def check_oops(msg):
             self.assertEqual(expected_oops, bson.loads(msg.body))
             channel.basic_ack(msg.delivery_tag)
@@ -98,11 +98,11 @@
             publisher = Publisher(
                 self.connection_factory, queue.exchange_name, "")
             oops = {'akey': 42}
-            self.assertEqual(None, publisher(oops))
+            self.assertEqual([], publisher(oops))
         finally:
             self.rabbit.runner._start()
             queue.channel = self.connection_factory().channel()
-        self.assertNotEqual(None, publisher(oops))
+        self.assertNotEqual([], publisher(oops))
 
     def test_publish_amqp_down_after_use(self):
         # If amqp goes down after its been successfully used, None is returned
@@ -119,9 +119,9 @@
         # release.
         self.rabbit.runner._stop()
         try:
-            self.assertEqual(None, publisher(oops))
+            self.assertEqual([], publisher(oops))
         finally:
             self.rabbit.runner._start()
             queue.channel = self.connection_factory().channel()
-        self.assertNotEqual(None, publisher(oops))
+        self.assertNotEqual([], publisher(oops))
 

=== modified file 'oops_amqp/tests/test_receiver.py'
--- oops_amqp/tests/test_receiver.py	2012-02-09 23:13:45 +0000
+++ oops_amqp/tests/test_receiver.py	2012-08-10 02:44:19 +0000
@@ -39,7 +39,7 @@
         reports = []
         def capture(report):
             reports.append(report)
-            return report['id']
+            return [report['id']]
         expected_report = {'id': 'foo', 'otherkey': 42}
         message = amqp.Message(bson.dumps(expected_report))
         channel = self.useFixture(
@@ -51,7 +51,7 @@
         channel.basic_publish(
             amqp.Message(sentinel), queue.exchange_name, routing_key="")
         config = Config()
-        config.publishers.append(capture)
+        config.publisher = capture
         receiver = Receiver(config, self.connection_factory, queue.queue_name)
         receiver.sentinel = sentinel
         receiver.run_forever()
@@ -62,7 +62,7 @@
         reports = []
         def capture(report):
             reports.append(report)
-            return report['id']
+            return [report['id']]
         expected_report = {'id': 'foo', 'otherkey': 42}
         message = amqp.Message(bson.dumps(expected_report))
         channel = self.useFixture(
@@ -71,7 +71,7 @@
         channel.basic_publish(
             message, queue.exchange_name, routing_key="")
         config = Config()
-        config.publishers.append(capture)
+        config.publisher = capture
         # We don't want to loop forever: patch the channel so that after one
         # call to wait (which will get our injected message) the loop will shut
         # down.
@@ -134,7 +134,7 @@
         reports = []
         def capture(report):
             reports.append(report)
-            return report['id']
+            return [report['id']]
         expected_report = {'id': 'foo', 'otherkey': 42}
         message = amqp.Message(bson.dumps(expected_report))
         channel = self.useFixture(
@@ -142,7 +142,7 @@
         queue = self.useFixture(QueueFixture(channel, self.getUniqueString))
         channel.basic_publish(message, queue.exchange_name, routing_key="")
         config = Config()
-        config.publishers.append(capture)
+        config.publisher = capture
         state = {}
         def error_once(func):
             def wrapped(*args, **kwargs):

=== added file 'oops_amqp/trace.py'
--- oops_amqp/trace.py	1970-01-01 00:00:00 +0000
+++ oops_amqp/trace.py	2012-08-10 02:44:19 +0000
@@ -0,0 +1,74 @@
+# Copyright (c) 2012, Canonical Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 only.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# GNU Lesser General Public License version 3 (see the file LICENSE).
+
+"""Trace OOPS reports coming from an AMQP queue."""
+
+from functools import partial
+import sys
+import optparse
+from textwrap import dedent
+
+import amqplib.client_0_8 as amqp
+import oops
+import oops_amqp
+
+import anybson as bson
+
+
+def main(argv=None):
+    if argv is None:
+        argv=sys.argv
+    usage = dedent("""\
+        %prog [options]
+
+        The following options must be supplied:
+         --host
+
+        e.g.
+        oops-amqp-trace --host "localhost:3472"
+
+        If you do not have a persistent queue, you should run this script
+        before generating oopses, as AMQP will discard messages with no
+        consumers.
+        """)
+    description = "Load OOPS reports into oops-tools from AMQP."
+    parser = optparse.OptionParser(
+        description=description, usage=usage)
+    parser.add_option('--host', help="AQMP host / host:port.")
+    parser.add_option('--username', help="AQMP username.", default="guest")
+    parser.add_option('--password', help="AQMP password.", default="guest")
+    parser.add_option('--vhost', help="AMQP vhost.", default="/")
+    parser.add_option('--exchange', help="AMQP exchange name.", default="oopses")
+    options, args = parser.parse_args(argv[1:])
+    def needed(optname):
+        if getattr(options, optname, None) is None:
+            raise ValueError('option "%s" must be supplied' % optname)
+    needed('host')
+    factory = partial(
+        amqp.Connection, host=options.host, userid=options.username,
+        password=options.password, virtual_host=options.vhost)
+    connection = factory()
+    channel = connection.channel()
+    channel.exchange_declare(options.exchange, type="fanout", durable=False,
+        auto_delete=True)
+    queue = channel.queue_declare(durable=False, auto_delete=True)[0]
+    channel.queue_bind(queue, options.exchange)
+    config = oops.Config()
+    config.publisher = oops.pprint_to_stream(sys.stdout)
+    receiver = oops_amqp.Receiver(config, factory, queue)
+    try:
+        receiver.run_forever()
+    except KeyboardInterrupt:
+        pass

=== modified file 'setup.py'
--- setup.py	2012-02-10 14:47:11 +0000
+++ setup.py	2012-08-10 02:44:19 +0000
@@ -51,4 +51,8 @@
               'testtools',
               ]
           ),
+        entry_points=dict(
+            console_scripts=[ # `console_scripts` is a magic name to setuptools
+                'oops-amqp-trace = oops_amqp.trace:main',
+                ]),
       )

=== modified file 'versions.cfg'
--- versions.cfg	2011-10-10 21:49:50 +0000
+++ versions.cfg	2012-08-10 02:44:19 +0000
@@ -6,7 +6,8 @@
 bson = 0.3.2
 fixtures = 0.3.6
 iso8601 = 0.1.4
-oops = 0.0.6
+oops = 0.0.13
+pymongo = 2.1.1
 pytz = 2010o
 rabbitfixture = 0.3.2
 setuptools = 0.6c11