Merge lp:~elachuni/txstatsd/straight into lp:txstatsd

Proposed by Anthony Lenton
Status: Merged
Approved by: Anthony Lenton
Approved revision: 101
Merged at revision: 99
Proposed branch: lp:~elachuni/txstatsd/straight
Merge into: lp:txstatsd
Diff against target: 318 lines (+169/-109)
3 files modified
txstatsd/client.py (+11/-109)
txstatsd/protocol.py (+131/-0)
txstatsd/tests/test_client.py (+27/-0)
To merge this branch: bzr merge lp:~elachuni/txstatsd/straight
Reviewer Review Type Date Requested Status
Sidnei da Silva Approve
Review via email: mp+114879@code.launchpad.net

Commit message

Made it possible to use the non-twisted client code even if twisted is unavailable.

Description of the change

This branch makes it possible to import the non-twisted client code without having twisted installed.

It also adds a test that ensures this can be installed, by marking everything twisted-ish as unavailable and then reloading the txstats client and metrics modules.

The code works as intended afaict, and the test fails if you add a random import twisted.something anywhere in txstatsd.client or txstatsd.metrics, but I'm still unsure if it's the best way to test. I tried a couple of permutations with import hooks, but that was even messier.

To post a comment you must log in.
Revision history for this message
Sidnei da Silva (sidnei) wrote :

[1] Please add the license header to protocol.py

[2] The restore_modules cleanup should reload() the modules again after restoring the unloaded twisted modules.

Once you fix those, set a commit message and flip the MP to approved, and tarmac will take care of landing it.

Thanks!

review: Approve
Revision history for this message
Anthony Lenton (elachuni) wrote :

Thanks Sidnei!
Fixes pushed up in rev. 100, marking as approved

Revision history for this message
Ubuntu One Server Tarmac Bot (ubuntuone-server-tarmac) wrote :
Download full text (19.6 KiB)

The attempt to merge lp:~elachuni/txstatsd/straight into lp:txstatsd failed. Below is the output from the failed tests.

txstatsd.tests.metrics.test_distinct
  TestDistinct
    test_all ... [OK]
  TestDistinctMetricReporter
    test_reports ... [OK]
  TestHash
    test_chi_square ... [SKIPPED]
    test_hash_chars ... [OK]
  TestPlugin
    test_factory ... [OK]
  TestZeros
    test_zeros ... [OK]
txstatsd.tests.metrics.test_histogrammetric
  TestHistogramReporterMetric
    test_histogram_histogram ... [OK]
    test_histogram_of_numbers_1_through_10000 ... [OK]
    test_histogram_with_zero_recorded_values ... [OK]
txstatsd.tests.metrics.test_metermetric
  TestDeriveMetricReporter
    test_fastpoll ... [OK]
    test_interface ... [OK]
txstatsd.tests.metrics.test_sli
  TestConditions
    test_above ... [OK]
    test_above_linear ... [OK]
    test_below ... [OK]
    test_below_linear ... [OK]
    test_between ... [OK]
  TestFactory
    test_configure ... [OK]
    test_configure_linear ... [OK]
  TestMetric
    test_clear ... [OK]
    test_count_all ... [OK]
    test_count_error ... [OK]
    test_count_threshold ... [OK]
    test_reports ... [OK]
  TestMetricLinear
    test_count_threshold ... [OK]
  TestParsing
    test_parse ... [OK]
txstatsd.tests.metrics.test_timermetric
  TestBlankTimerMetric
    test_count ... [OK]
    test_max ... [OK]
    test_mean ... [OK]
    test_min ... [OK]
    test_no_values ... [OK]
    test_percentiles ... [OK]
    test_rate ... [OK]
    test_std_dev ... [OK]
  TestTim...

lp:~elachuni/txstatsd/straight updated
101. By Anthony Lenton

Avoid using assertIsNone in tests, as the trial version on tarmac doesn't have that available.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'txstatsd/client.py'
--- txstatsd/client.py 2012-06-28 17:29:26 +0000
+++ txstatsd/client.py 2012-07-13 18:01:22 +0000
@@ -21,118 +21,20 @@
2121
22import socket22import socket
2323
24from twisted.internet.defer import inlineCallbacks, returnValue24try:
25from twisted.internet.protocol import DatagramProtocol25 import twisted
26from twisted.python import log26except ImportError:
2727 # If twisted is missing, still provide the non-twisted client
28 pass
29else:
30 from txstatsd.protocol import (
31 StatsDClientProtocol,
32 TwistedStatsDClient,
33 )
34
28from txstatsd.hashing import ConsistentHashRing35from txstatsd.hashing import ConsistentHashRing
2936
3037
31class StatsDClientProtocol(DatagramProtocol):
32 """A Twisted-based implementation of the StatsD client protocol.
33
34 Data is sent via UDP to a StatsD server for aggregation.
35 """
36
37 def __init__(self, client):
38 self.client = client
39
40 def startProtocol(self):
41 """Connect to destination host."""
42 self.client.connect(self.transport)
43
44 def stopProtocol(self):
45 """Connection was lost."""
46 self.client.disconnect()
47
48
49class TwistedStatsDClient(object):
50
51 def __init__(self, host, port,
52 connect_callback=None,
53 disconnect_callback=None,
54 resolver_errback=None):
55 """
56 Build a connection that reports to the endpoint (on C{host} and
57 C{port}) using UDP.
58
59 @param host: The StatsD server host.
60 @param port: The StatsD server port.
61 @param resolver_errback: The errback to invoke should
62 issues occur resolving the supplied C{host}.
63 @param connect_callback: The callback to invoke on connection.
64 @param disconnect_callback: The callback to invoke on disconnection.
65 """
66 from twisted.internet import reactor
67
68 self.reactor = reactor
69
70 @inlineCallbacks
71 def resolve(host):
72 self.host = yield reactor.resolve(host)
73 returnValue(self.host)
74
75 self.original_host = host
76 self.host = None
77 self.resolver = resolve(host)
78 if resolver_errback is None:
79 self.resolver.addErrback(log.err)
80 else:
81 self.resolver.addErrback(resolver_errback)
82
83 self.port = port
84 self.connect_callback = connect_callback
85 self.disconnect_callback = disconnect_callback
86
87 self.transport = None
88
89 def __str__(self):
90 return "%s:%d" % (self.original_host, self.port)
91
92 @inlineCallbacks
93 def connect(self, transport=None):
94 """Connect to the StatsD server."""
95 host = yield self.resolver
96 if host is not None:
97 self.transport = transport
98 if self.transport is not None:
99 if self.connect_callback is not None:
100 self.connect_callback()
101
102 def disconnect(self):
103 """Disconnect from the StatsD server."""
104 if self.disconnect_callback is not None:
105 self.disconnect_callback()
106 self.transport = None
107
108 def write(self, data, callback=None):
109 """Send the metric to the StatsD server.
110
111 @param data: The data to be sent.
112 @param callback: The callback to which the result should be sent.
113 B{Note}: The C{callback} will be called in the C{reactor}
114 thread, and not in the thread of the original caller.
115 """
116 self.reactor.callFromThread(self._write, data, callback)
117
118 def _write(self, data, callback):
119 """Send the metric to the StatsD server.
120
121 @param data: The data to be sent.
122 @param callback: The callback to which the result should be sent.
123 @raise twisted.internet.error.MessageLengthError: If the size of data
124 is too large.
125 """
126 if self.host is not None and self.transport is not None:
127 try:
128 bytes_sent = self.transport.write(data, (self.host, self.port))
129 if callback is not None:
130 callback(bytes_sent)
131 except (OverflowError, TypeError, socket.error, socket.gaierror):
132 if callback is not None:
133 callback(None)
134
135
136class UdpStatsDClient(object):38class UdpStatsDClient(object):
13739
138 def __init__(self, host=None, port=None):40 def __init__(self, host=None, port=None):
13941
=== added file 'txstatsd/protocol.py'
--- txstatsd/protocol.py 1970-01-01 00:00:00 +0000
+++ txstatsd/protocol.py 2012-07-13 18:01:22 +0000
@@ -0,0 +1,131 @@
1# Copyright (C) 2011-2012 Canonical Services Ltd
2#
3# Permission is hereby granted, free of charge, to any person obtaining
4# a copy of this software and associated documentation files (the
5# "Software"), to deal in the Software without restriction, including
6# without limitation the rights to use, copy, modify, merge, publish,
7# distribute, sublicense, and/or sell copies of the Software, and to
8# permit persons to whom the Software is furnished to do so, subject to
9# the following conditions:
10#
11# The above copyright notice and this permission notice shall be
12# included in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22import socket
23
24from twisted.internet.defer import inlineCallbacks, returnValue
25from twisted.internet.protocol import DatagramProtocol
26from twisted.python import log
27
28
29class StatsDClientProtocol(DatagramProtocol):
30 """A Twisted-based implementation of the StatsD client protocol.
31
32 Data is sent via UDP to a StatsD server for aggregation.
33 """
34
35 def __init__(self, client):
36 self.client = client
37
38 def startProtocol(self):
39 """Connect to destination host."""
40 self.client.connect(self.transport)
41
42 def stopProtocol(self):
43 """Connection was lost."""
44 self.client.disconnect()
45
46
47class TwistedStatsDClient(object):
48
49 def __init__(self, host, port,
50 connect_callback=None,
51 disconnect_callback=None,
52 resolver_errback=None):
53 """
54 Build a connection that reports to the endpoint (on C{host} and
55 C{port}) using UDP.
56
57 @param host: The StatsD server host.
58 @param port: The StatsD server port.
59 @param resolver_errback: The errback to invoke should
60 issues occur resolving the supplied C{host}.
61 @param connect_callback: The callback to invoke on connection.
62 @param disconnect_callback: The callback to invoke on disconnection.
63 """
64 from twisted.internet import reactor
65
66 self.reactor = reactor
67
68 @inlineCallbacks
69 def resolve(host):
70 self.host = yield reactor.resolve(host)
71 returnValue(self.host)
72
73 self.original_host = host
74 self.host = None
75 self.resolver = resolve(host)
76 if resolver_errback is None:
77 self.resolver.addErrback(log.err)
78 else:
79 self.resolver.addErrback(resolver_errback)
80
81 self.port = port
82 self.connect_callback = connect_callback
83 self.disconnect_callback = disconnect_callback
84
85 self.transport = None
86
87 def __str__(self):
88 return "%s:%d" % (self.original_host, self.port)
89
90 @inlineCallbacks
91 def connect(self, transport=None):
92 """Connect to the StatsD server."""
93 host = yield self.resolver
94 if host is not None:
95 self.transport = transport
96 if self.transport is not None:
97 if self.connect_callback is not None:
98 self.connect_callback()
99
100 def disconnect(self):
101 """Disconnect from the StatsD server."""
102 if self.disconnect_callback is not None:
103 self.disconnect_callback()
104 self.transport = None
105
106 def write(self, data, callback=None):
107 """Send the metric to the StatsD server.
108
109 @param data: The data to be sent.
110 @param callback: The callback to which the result should be sent.
111 B{Note}: The C{callback} will be called in the C{reactor}
112 thread, and not in the thread of the original caller.
113 """
114 self.reactor.callFromThread(self._write, data, callback)
115
116 def _write(self, data, callback):
117 """Send the metric to the StatsD server.
118
119 @param data: The data to be sent.
120 @param callback: The callback to which the result should be sent.
121 @raise twisted.internet.error.MessageLengthError: If the size of data
122 is too large.
123 """
124 if self.host is not None and self.transport is not None:
125 try:
126 bytes_sent = self.transport.write(data, (self.host, self.port))
127 if callback is not None:
128 callback(bytes_sent)
129 except (OverflowError, TypeError, socket.error, socket.gaierror):
130 if callback is not None:
131 callback(None)
0132
=== modified file 'txstatsd/tests/test_client.py'
--- txstatsd/tests/test_client.py 2012-06-28 17:29:26 +0000
+++ txstatsd/tests/test_client.py 2012-07-13 18:01:22 +0000
@@ -20,11 +20,15 @@
20# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.20# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21"""Tests for the various client classes."""21"""Tests for the various client classes."""
2222
23import sys
23from twisted.internet import reactor24from twisted.internet import reactor
24from twisted.internet.defer import inlineCallbacks, Deferred25from twisted.internet.defer import inlineCallbacks, Deferred
25from twisted.python import log26from twisted.python import log
26from twisted.trial.unittest import TestCase27from twisted.trial.unittest import TestCase
2728
29import txstatsd.client
30import txstatsd.metrics.metric
31import txstatsd.metrics.metrics
28from txstatsd.metrics.metric import Metric32from txstatsd.metrics.metric import Metric
29from txstatsd.client import (33from txstatsd.client import (
30 StatsDClientProtocol, TwistedStatsDClient, UdpStatsDClient,34 StatsDClientProtocol, TwistedStatsDClient, UdpStatsDClient,
@@ -128,6 +132,29 @@
128 # setblocking(0) is the same as settimeout(0.0).132 # setblocking(0) is the same as settimeout(0.0).
129 self.assertEqual(client.socket.gettimeout(), 0.0)133 self.assertEqual(client.socket.gettimeout(), 0.0)
130134
135 def test_udp_client_can_be_imported_without_twisted(self):
136 """Ensure that the twisted-less client can be used without twisted."""
137 unloaded = [(name, mod) for (name, mod) in sys.modules.items()
138 if 'twisted' in name]
139 def restore_modules():
140 for name, mod in unloaded:
141 sys.modules[name] = mod
142 reload(txstatsd.client)
143 reload(txstatsd.metrics.metrics)
144 reload(txstatsd.metrics.metric)
145 self.addCleanup(restore_modules)
146
147 # Mark everything twistedish as unavailable
148 for name, mod in unloaded:
149 sys.modules[name] = None
150
151 reload(txstatsd.client)
152 reload(txstatsd.metrics.metrics)
153 reload(txstatsd.metrics.metric)
154 for mod in sys.modules:
155 if 'twisted' in mod:
156 self.assertTrue(sys.modules[mod] is None)
157
131158
132class TestConsistentHashingClient(TestCase):159class TestConsistentHashingClient(TestCase):
133160

Subscribers

People subscribed via source and target branches