Merge lp:~allenap/launchpad/config-dir into lp:launchpad

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 13972
Proposed branch: lp:~allenap/launchpad/config-dir
Merge into: lp:launchpad
Diff against target: 175 lines (+61/-24)
2 files modified
lib/canonical/config/__init__.py (+24/-10)
lib/canonical/config/tests/test_config.py (+37/-14)
To merge this branch: bzr merge lp:~allenap/launchpad/config-dir
Reviewer Review Type Date Requested Status
Abel Deuring (community) Approve
Review via email: mp+75737@code.launchpad.net

Commit message

[r=adeuring][no-qa] Add support for dir() and iter() to CanonicalConfig.

Description of the change

This adds support for dir() and iter() to CanonicalConfig. This means that it's easier, during development, to see what configuration keys are set, and to drill down. The dir() support is especially useful because it means that tab completion will work on configuration keys.

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

Look good. Just one suggestion:

+ def test_iter(self):
+ # iter(config) returns an iterator of config sections.
+ config = canonical.config.config
+ self.assertEqual(set(config._config), set(config))

The comment included, the word "config" appears eight times ;) Makes it a bit hard to understand what exact the test does. What about

sections = set(config._config)
self.assertEqual(sections, set(config))

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/config/__init__.py'
2--- lib/canonical/config/__init__.py 2011-02-25 17:28:21 +0000
3+++ lib/canonical/config/__init__.py 2011-09-16 14:33:25 +0000
4@@ -13,19 +13,20 @@
5 __metaclass__ = type
6
7
8+import logging
9 import os
10-import logging
11 import sys
12-from urlparse import urlparse, urlunparse
13+from urlparse import (
14+ urlparse,
15+ urlunparse,
16+ )
17
18+from lazr.config import ImplicitTypeSchema
19+from lazr.config.interfaces import ConfigErrors
20 import pkg_resources
21 import ZConfig
22
23-from lazr.config import ImplicitTypeSchema
24-from lazr.config.interfaces import ConfigErrors
25-
26 from canonical.launchpad.readonly import is_read_only
27-
28 from lp.services.osutils import open_for_writing
29
30
31@@ -259,7 +260,7 @@
32 if not ensureSlash:
33 return root_url.rstrip('/')
34 if not root_url.endswith('/'):
35- return root_url+'/'
36+ return root_url + '/'
37 return root_url
38
39 def __getattr__(self, name):
40@@ -278,6 +279,19 @@
41 self._getConfig()
42 return self._config[key]
43
44+ def __dir__(self):
45+ """List section names in addition to methods and variables."""
46+ self._getConfig()
47+ names = dir(self.__class__)
48+ names.extend(self.__dict__)
49+ names.extend(section.name for section in self._config)
50+ return names
51+
52+ def __iter__(self):
53+ """Iterate through configuration sections."""
54+ self._getConfig()
55+ return iter(self._config)
56+
57
58 config = CanonicalConfig()
59
60@@ -342,11 +356,11 @@
61 value = url(value)
62 scheme, location, path, parameters, query, fragment = urlparse(value)
63 if parameters:
64- raise ValueError, 'URL parameters not allowed'
65+ raise ValueError('URL parameters not allowed')
66 if query:
67- raise ValueError, 'URL query not allowed'
68+ raise ValueError('URL query not allowed')
69 if fragment:
70- raise ValueError, 'URL fragments not allowed'
71+ raise ValueError('URL fragments not allowed')
72 if not value.endswith('/'):
73 value = value + '/'
74 return value
75
76=== modified file 'lib/canonical/config/tests/test_config.py'
77--- lib/canonical/config/tests/test_config.py 2011-03-07 23:54:41 +0000
78+++ lib/canonical/config/tests/test_config.py 2011-09-16 14:33:25 +0000
79@@ -9,13 +9,17 @@
80
81 __metaclass__ = type
82
83-from doctest import DocTestSuite, NORMALIZE_WHITESPACE, ELLIPSIS
84+from doctest import (
85+ DocTestSuite,
86+ ELLIPSIS,
87+ NORMALIZE_WHITESPACE,
88+ )
89 import os
90-import pkg_resources
91 import unittest
92
93 from lazr.config import ConfigSchema
94 from lazr.config.interfaces import ConfigErrors
95+import pkg_resources
96 import ZConfig
97
98 import canonical.config
99@@ -34,8 +38,11 @@
100 def make_test(config_file, description):
101 def test_function():
102 root, handlers = ZConfig.loadConfig(schema, config_file)
103+ # Hack the config file name into test_function's __name__ so that the test
104+ # -vv output is more informative. Unfortunately, FunctionTestCase's
105+ # description argument doesn't do what we want.
106 test_function.__name__ = description
107- return test_function
108+ return unittest.FunctionTestCase(test_function)
109
110
111 def make_config_test(config_file, description):
112@@ -63,6 +70,24 @@
113 return LAZRConfigTestCase
114
115
116+class TestCanonicalConfig(unittest.TestCase):
117+
118+ def test_dir(self):
119+ # dir(config) returns methods, variables and section names.
120+ config = canonical.config.config
121+ names = set(dir(config))
122+ self.assertTrue(names.issuperset(dir(config.__class__)))
123+ self.assertTrue(names.issuperset(config.__dict__))
124+ section_names = set(section.name for section in config._config)
125+ self.assertTrue(names.issuperset(section_names))
126+
127+ def test_iter(self):
128+ # iter(config) returns an iterator of sections.
129+ config = canonical.config.config
130+ sections = set(config._config)
131+ self.assertEqual(sections, set(config))
132+
133+
134 def test_suite():
135 """Return a suite of canonical.conf and all conf files."""
136 # We know we are not using dirnames.
137@@ -72,29 +97,27 @@
138 'canonical.config',
139 optionflags=NORMALIZE_WHITESPACE | ELLIPSIS,
140 ))
141+ load_testcase = unittest.defaultTestLoader.loadTestsFromTestCase
142 # Add a test for every launchpad[.lazr].conf file in our tree.
143 for config_dir in canonical.config.CONFIG_ROOT_DIRS:
144- prefix_len = len(os.path.dirname(config_dir)) + 1
145 for dirpath, dirnames, filenames in os.walk(config_dir):
146 if os.path.basename(dirpath) in EXCLUDED_CONFIGS:
147+ del dirnames[:] # Don't look in subdirectories.
148 continue
149 for filename in filenames:
150 if filename == 'launchpad.conf':
151 config_file = os.path.join(dirpath, filename)
152- description = config_file[prefix_len:]
153- # Hack the config file name into the test_function's
154- # __name__ # so that the test -vv output is more
155- # informative. Unfortunately, FunctionTestCase's
156- # description argument doesn't do what we want.
157- suite.addTest(unittest.FunctionTestCase(
158- make_test(config_file, description)))
159+ description = os.path.relpath(config_file, config_dir)
160+ suite.addTest(make_test(config_file, description))
161 elif filename.endswith('-lazr.conf'):
162 # Test the lazr.config conf files.
163 config_file = os.path.join(dirpath, filename)
164- description = config_file[prefix_len:]
165- test = make_config_test(config_file, description)
166- suite.addTest(test('testConfig'))
167+ description = os.path.relpath(config_file, config_dir)
168+ testcase = make_config_test(config_file, description)
169+ suite.addTest(load_testcase(testcase))
170 else:
171 # This file is not a config that can be validated.
172 pass
173+ # Other tests.
174+ suite.addTest(load_testcase(TestCanonicalConfig))
175 return suite