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
=== modified file 'lib/canonical/config/__init__.py'
--- lib/canonical/config/__init__.py 2011-02-25 17:28:21 +0000
+++ lib/canonical/config/__init__.py 2011-09-16 14:33:25 +0000
@@ -13,19 +13,20 @@
13__metaclass__ = type13__metaclass__ = type
1414
1515
16import logging
16import os17import os
17import logging
18import sys18import sys
19from urlparse import urlparse, urlunparse19from urlparse import (
20 urlparse,
21 urlunparse,
22 )
2023
24from lazr.config import ImplicitTypeSchema
25from lazr.config.interfaces import ConfigErrors
21import pkg_resources26import pkg_resources
22import ZConfig27import ZConfig
2328
24from lazr.config import ImplicitTypeSchema
25from lazr.config.interfaces import ConfigErrors
26
27from canonical.launchpad.readonly import is_read_only29from canonical.launchpad.readonly import is_read_only
28
29from lp.services.osutils import open_for_writing30from lp.services.osutils import open_for_writing
3031
3132
@@ -259,7 +260,7 @@
259 if not ensureSlash:260 if not ensureSlash:
260 return root_url.rstrip('/')261 return root_url.rstrip('/')
261 if not root_url.endswith('/'):262 if not root_url.endswith('/'):
262 return root_url+'/'263 return root_url + '/'
263 return root_url264 return root_url
264265
265 def __getattr__(self, name):266 def __getattr__(self, name):
@@ -278,6 +279,19 @@
278 self._getConfig()279 self._getConfig()
279 return self._config[key]280 return self._config[key]
280281
282 def __dir__(self):
283 """List section names in addition to methods and variables."""
284 self._getConfig()
285 names = dir(self.__class__)
286 names.extend(self.__dict__)
287 names.extend(section.name for section in self._config)
288 return names
289
290 def __iter__(self):
291 """Iterate through configuration sections."""
292 self._getConfig()
293 return iter(self._config)
294
281295
282config = CanonicalConfig()296config = CanonicalConfig()
283297
@@ -342,11 +356,11 @@
342 value = url(value)356 value = url(value)
343 scheme, location, path, parameters, query, fragment = urlparse(value)357 scheme, location, path, parameters, query, fragment = urlparse(value)
344 if parameters:358 if parameters:
345 raise ValueError, 'URL parameters not allowed'359 raise ValueError('URL parameters not allowed')
346 if query:360 if query:
347 raise ValueError, 'URL query not allowed'361 raise ValueError('URL query not allowed')
348 if fragment:362 if fragment:
349 raise ValueError, 'URL fragments not allowed'363 raise ValueError('URL fragments not allowed')
350 if not value.endswith('/'):364 if not value.endswith('/'):
351 value = value + '/'365 value = value + '/'
352 return value366 return value
353367
=== modified file 'lib/canonical/config/tests/test_config.py'
--- lib/canonical/config/tests/test_config.py 2011-03-07 23:54:41 +0000
+++ lib/canonical/config/tests/test_config.py 2011-09-16 14:33:25 +0000
@@ -9,13 +9,17 @@
99
10__metaclass__ = type10__metaclass__ = type
1111
12from doctest import DocTestSuite, NORMALIZE_WHITESPACE, ELLIPSIS12from doctest import (
13 DocTestSuite,
14 ELLIPSIS,
15 NORMALIZE_WHITESPACE,
16 )
13import os17import os
14import pkg_resources
15import unittest18import unittest
1619
17from lazr.config import ConfigSchema20from lazr.config import ConfigSchema
18from lazr.config.interfaces import ConfigErrors21from lazr.config.interfaces import ConfigErrors
22import pkg_resources
19import ZConfig23import ZConfig
2024
21import canonical.config25import canonical.config
@@ -34,8 +38,11 @@
34def make_test(config_file, description):38def make_test(config_file, description):
35 def test_function():39 def test_function():
36 root, handlers = ZConfig.loadConfig(schema, config_file)40 root, handlers = ZConfig.loadConfig(schema, config_file)
41 # Hack the config file name into test_function's __name__ so that the test
42 # -vv output is more informative. Unfortunately, FunctionTestCase's
43 # description argument doesn't do what we want.
37 test_function.__name__ = description44 test_function.__name__ = description
38 return test_function45 return unittest.FunctionTestCase(test_function)
3946
4047
41def make_config_test(config_file, description):48def make_config_test(config_file, description):
@@ -63,6 +70,24 @@
63 return LAZRConfigTestCase70 return LAZRConfigTestCase
6471
6572
73class TestCanonicalConfig(unittest.TestCase):
74
75 def test_dir(self):
76 # dir(config) returns methods, variables and section names.
77 config = canonical.config.config
78 names = set(dir(config))
79 self.assertTrue(names.issuperset(dir(config.__class__)))
80 self.assertTrue(names.issuperset(config.__dict__))
81 section_names = set(section.name for section in config._config)
82 self.assertTrue(names.issuperset(section_names))
83
84 def test_iter(self):
85 # iter(config) returns an iterator of sections.
86 config = canonical.config.config
87 sections = set(config._config)
88 self.assertEqual(sections, set(config))
89
90
66def test_suite():91def test_suite():
67 """Return a suite of canonical.conf and all conf files."""92 """Return a suite of canonical.conf and all conf files."""
68 # We know we are not using dirnames.93 # We know we are not using dirnames.
@@ -72,29 +97,27 @@
72 'canonical.config',97 'canonical.config',
73 optionflags=NORMALIZE_WHITESPACE | ELLIPSIS,98 optionflags=NORMALIZE_WHITESPACE | ELLIPSIS,
74 ))99 ))
100 load_testcase = unittest.defaultTestLoader.loadTestsFromTestCase
75 # Add a test for every launchpad[.lazr].conf file in our tree.101 # Add a test for every launchpad[.lazr].conf file in our tree.
76 for config_dir in canonical.config.CONFIG_ROOT_DIRS:102 for config_dir in canonical.config.CONFIG_ROOT_DIRS:
77 prefix_len = len(os.path.dirname(config_dir)) + 1
78 for dirpath, dirnames, filenames in os.walk(config_dir):103 for dirpath, dirnames, filenames in os.walk(config_dir):
79 if os.path.basename(dirpath) in EXCLUDED_CONFIGS:104 if os.path.basename(dirpath) in EXCLUDED_CONFIGS:
105 del dirnames[:] # Don't look in subdirectories.
80 continue106 continue
81 for filename in filenames:107 for filename in filenames:
82 if filename == 'launchpad.conf':108 if filename == 'launchpad.conf':
83 config_file = os.path.join(dirpath, filename)109 config_file = os.path.join(dirpath, filename)
84 description = config_file[prefix_len:]110 description = os.path.relpath(config_file, config_dir)
85 # Hack the config file name into the test_function's111 suite.addTest(make_test(config_file, description))
86 # __name__ # so that the test -vv output is more
87 # informative. Unfortunately, FunctionTestCase's
88 # description argument doesn't do what we want.
89 suite.addTest(unittest.FunctionTestCase(
90 make_test(config_file, description)))
91 elif filename.endswith('-lazr.conf'):112 elif filename.endswith('-lazr.conf'):
92 # Test the lazr.config conf files.113 # Test the lazr.config conf files.
93 config_file = os.path.join(dirpath, filename)114 config_file = os.path.join(dirpath, filename)
94 description = config_file[prefix_len:]115 description = os.path.relpath(config_file, config_dir)
95 test = make_config_test(config_file, description)116 testcase = make_config_test(config_file, description)
96 suite.addTest(test('testConfig'))117 suite.addTest(load_testcase(testcase))
97 else:118 else:
98 # This file is not a config that can be validated.119 # This file is not a config that can be validated.
99 pass120 pass
121 # Other tests.
122 suite.addTest(load_testcase(TestCanonicalConfig))
100 return suite123 return suite