Merge lp:~theiw/txstatsd/txstatsd-fix-860992 into lp:txstatsd

Proposed by Ian Wilkinson
Status: Merged
Approved by: Ian Wilkinson
Approved revision: 36
Merged at revision: 36
Proposed branch: lp:~theiw/txstatsd/txstatsd-fix-860992
Merge into: lp:txstatsd
Diff against target: 252 lines (+101/-59)
3 files modified
txstatsd/service.py (+85/-49)
txstatsd/tests/test_service.py (+15/-9)
txstatsd/version.py (+1/-1)
To merge this branch: bzr merge lp:~theiw/txstatsd/txstatsd-fix-860992
Reviewer Review Type Date Requested Status
Lucio Torre (community) Approve
Review via email: mp+77482@code.launchpad.net

Description of the change

OptionsGlue now properly renders the help text. Also, resolves a minor issue with conflicting options.

To post a comment you must log in.
Revision history for this message
Lucio Torre (lucio.torre) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'txstatsd/service.py'
2--- txstatsd/service.py 2011-09-02 16:14:10 +0000
3+++ txstatsd/service.py 2011-09-29 08:55:24 +0000
4@@ -1,4 +1,7 @@
5+
6+import getopt
7 import socket
8+import sys
9 import ConfigParser
10
11 from twisted.application.internet import TCPClient, UDPServer
12@@ -11,74 +14,103 @@
13 from txstatsd.metrics.metrics import Metrics
14 from txstatsd.server.processor import MessageProcessor
15 from txstatsd.server.configurableprocessor import ConfigurableMessageProcessor
16-from txstatsd.server.protocol import GraphiteClientFactory, StatsDServerProtocol
17+from txstatsd.server.protocol import (
18+ GraphiteClientFactory, StatsDServerProtocol)
19 from txstatsd.report import ReportingService
20
21-_unset = object()
22+
23+def accumulateClassList(classObj, attr, listObj,
24+ baseClass=None, excludeClass=None):
25+ """Accumulate all attributes of a given name in a class hierarchy
26+ into a single list.
27+
28+ Assuming all class attributes of this name are lists.
29+ """
30+ for base in classObj.__bases__:
31+ accumulateClassList(base, attr, listObj, excludeClass=excludeClass)
32+ if excludeClass != classObj:
33+ if baseClass is None or baseClass in classObj.__bases__:
34+ listObj.extend(classObj.__dict__.get(attr, []))
35
36
37 class OptionsGlue(usage.Options):
38- """
39- Extends usage.Options to also read parameters from a config file.
40- """
41-
42- optParameters = []
43+ """Extends usage.Options to also read parameters from a config file.
44+
45+ This is made far more complicated than should be necessary, as
46+ usage.Options does not appear to have been designed for extension.
47+ """
48+
49+ optParameters = [
50+ ["config", "c", None, "Config file to use."]
51+ ]
52
53 def __init__(self):
54- self.glue_defaults = {}
55- self._config_file = None
56- config = ["config", "c", None, "Config file to use."]
57-
58- new_params = []
59-
60- def process_parameter(parameter):
61- long, short, default, doc, paramType = util.padTo(5, parameter)
62- self.glue_defaults[long] = default
63- new_params.append(
64- [long, short, _unset, doc, paramType])
65-
66- for parameter in self.glue_parameters:
67+ parameters = []
68+ accumulateClassList(self.__class__, 'optParameters',
69+ parameters, excludeClass=OptionsGlue)
70+ for parameter in parameters:
71 if parameter[0] == "config" or parameter[1] == "c":
72 raise ValueError("the --config/-c parameter is reserved.")
73- process_parameter(parameter)
74- process_parameter(config)
75
76- # we need to change self.__class__.optParameters as usage.Options
77- # will collect the config from there, not self.optParameters:
78- # reflect.accumulateClassList(self.__class__, 'optParameters',
79- # parameters)
80- self.__class__.optParameters = new_params
81+ self.overridden_options = []
82
83 super(OptionsGlue, self).__init__()
84
85- def __getitem__(self, item):
86- result = super(OptionsGlue, self).__getitem__(item)
87- if result is not _unset:
88- return result
89-
90- fname = super(OptionsGlue, self).__getitem__("config")
91- if fname is not _unset:
92- self._config_file = ConfigParser.RawConfigParser()
93- self._config_file.read(fname)
94-
95- if self._config_file is not None:
96- try:
97- result = self._config_file.get("statsd", item)
98- except ConfigParser.NoOptionError:
99- pass
100+ def opt_config(self, config_path):
101+ self['config'] = config_path
102+
103+ opt_c = opt_config
104+
105+ def parseOptions(self, options=None):
106+ """Obtain overridden options."""
107+
108+ if options is None:
109+ options = sys.argv[1:]
110+ try:
111+ opts, args = getopt.getopt(options,
112+ self.shortOpt, self.longOpt)
113+ except getopt.error, e:
114+ raise usage.UsageError(str(e))
115+
116+ for opt, arg in opts:
117+ if opt[1] == '-':
118+ opt = opt[2:]
119 else:
120- if item in self._dispatch:
121- result = self._dispatch[item].coerce(result)
122- return result
123-
124- return self.glue_defaults[item]
125+ opt = opt[1:]
126+ self.overridden_options.append(opt)
127+
128+ super(OptionsGlue, self).parseOptions(options=options)
129+
130+ def postOptions(self):
131+ """Read the configuration file if one is provided."""
132+ if self['config'] is not None:
133+ config_file = ConfigParser.RawConfigParser()
134+ config_file.read(self['config'])
135+
136+ self.configure(config_file)
137+
138+ def overridden_option(self, opt):
139+ """Return whether this option was overridden."""
140+ return opt in self.overridden_options
141+
142+ def configure(self, config_file):
143+ """Read the configuration items, coercing types as required."""
144+ for name, value in config_file.items(self.config_section):
145+ # Overridden options have precedence
146+ if not self.overridden_option(name):
147+ # Options appends '=' when gathering the parameters
148+ if (name + '=') in self.longOpt:
149+ # Coerce the type if required
150+ if name in self._dispatch:
151+ value = self._dispatch[name].coerce(value)
152+ self[name] = value
153
154
155 class StatsDOptions(OptionsGlue):
156 """
157 The set of configuration settings for txStatsD.
158 """
159- glue_parameters = [
160+ optParameters = [
161 ["carbon-cache-host", "h", "localhost",
162 "The host where carbon cache is listening."],
163 ["carbon-cache-port", "p", 2003,
164@@ -87,7 +119,7 @@
165 "The UDP port where we will listen.", int],
166 ["flush-interval", "i", 60000,
167 "The number of milliseconds between each flush.", int],
168- ["prefix", "p", None,
169+ ["prefix", "x", None,
170 "Prefix to use when reporting stats.", str],
171 ["report", "r", None,
172 "Which additional stats to report {process|net|io|system}.", str],
173@@ -99,6 +131,10 @@
174 "Produce StatsD-compliant messages.", int]
175 ]
176
177+ def __init__(self):
178+ self.config_section = 'statsd'
179+ super(StatsDOptions, self).__init__()
180+
181
182 def createService(options):
183 """Create a txStatsD service."""
184
185=== modified file 'txstatsd/tests/test_service.py'
186--- txstatsd/tests/test_service.py 2011-09-01 09:45:05 +0000
187+++ txstatsd/tests/test_service.py 2011-09-29 08:55:24 +0000
188@@ -1,3 +1,4 @@
189+
190 import ConfigParser
191 import tempfile
192 from unittest import TestCase
193@@ -18,30 +19,30 @@
194 Defaults get passed over to the instance.
195 """
196 class TestOptions(service.OptionsGlue):
197- glue_parameters = [["test", "t", "default", "help"]]
198-
199+ optParameters = [["test", "t", "default", "help"]]
200+
201 o = TestOptions()
202 o.parseOptions([])
203 self.assertEquals("default", o["test"])
204-
205+
206 def test_set_parameter(self):
207 """
208 A parameter can be set from the command line
209 """
210 class TestOptions(service.OptionsGlue):
211- glue_parameters = [["test", "t", "default", "help"]]
212-
213+ optParameters = [["test", "t", "default", "help"]]
214+
215 o = TestOptions()
216 o.parseOptions(["--test", "notdefault"])
217 self.assertEquals("notdefault", o["test"])
218-
219+
220 def test_no_config_option(self):
221 """
222 A parameter can be set from the command line
223 """
224 class TestOptions(service.OptionsGlue):
225- glue_parameters = [["config", "c", "default", "help"]]
226-
227+ optParameters = [["config", "c", "default", "help"]]
228+
229 self.assertRaises(ValueError, lambda: TestOptions())
230
231 def get_file_parser(self, glue_parameters_config=None, **kwargs):
232@@ -63,7 +64,12 @@
233 f.flush()
234
235 class TestOptions(service.OptionsGlue):
236- glue_parameters = glue_parameters_config
237+ optParameters = glue_parameters_config
238+
239+ def __init__(self):
240+ self.config_section = 'statsd'
241+ super(TestOptions, self).__init__()
242+
243 return f, TestOptions()
244
245 def test_reads_from_config(self):
246
247=== modified file 'txstatsd/version.py'
248--- txstatsd/version.py 2011-09-14 12:01:10 +0000
249+++ txstatsd/version.py 2011-09-29 08:55:24 +0000
250@@ -1,1 +1,1 @@
251-txstatsd = "0.5.0"
252+txstatsd = "0.5.1"

Subscribers

People subscribed via source and target branches