Show More
@@ -40,6 +40,7 b' class ConfigLoaderError(ConfigError):' | |||||
40 | #----------------------------------------------------------------------------- |
|
40 | #----------------------------------------------------------------------------- | |
41 | # Argparse fix |
|
41 | # Argparse fix | |
42 | #----------------------------------------------------------------------------- |
|
42 | #----------------------------------------------------------------------------- | |
|
43 | ||||
43 | # Unfortunately argparse by default prints help messages to stderr instead of |
|
44 | # Unfortunately argparse by default prints help messages to stderr instead of | |
44 | # stdout. This makes it annoying to capture long help screens at the command |
|
45 | # stdout. This makes it annoying to capture long help screens at the command | |
45 | # line, since one must know how to pipe stderr, which many users don't know how |
|
46 | # line, since one must know how to pipe stderr, which many users don't know how | |
@@ -200,10 +201,13 b' class ConfigLoader(object):' | |||||
200 | self.config = Config() |
|
201 | self.config = Config() | |
201 |
|
202 | |||
202 | def load_config(self): |
|
203 | def load_config(self): | |
203 |
"""Load a config from somewhere, return a |
|
204 | """Load a config from somewhere, return a :class:`Config` instance. | |
204 |
|
205 | |||
205 | Usually, this will cause self.config to be set and then returned. |
|
206 | Usually, this will cause self.config to be set and then returned. | |
|
207 | However, in most cases, :meth:`ConfigLoader.clear` should be called | |||
|
208 | to erase any previous state. | |||
206 | """ |
|
209 | """ | |
|
210 | self.clear() | |||
207 | return self.config |
|
211 | return self.config | |
208 |
|
212 | |||
209 |
|
213 | |||
@@ -242,6 +246,7 b' class PyFileConfigLoader(FileConfigLoader):' | |||||
242 |
|
246 | |||
243 | def load_config(self): |
|
247 | def load_config(self): | |
244 | """Load the config from a file and return it as a Struct.""" |
|
248 | """Load the config from a file and return it as a Struct.""" | |
|
249 | self.clear() | |||
245 | self._find_file() |
|
250 | self._find_file() | |
246 | self._read_file_as_dict() |
|
251 | self._read_file_as_dict() | |
247 | self._convert_to_config() |
|
252 | self._convert_to_config() | |
@@ -292,20 +297,10 b' class CommandLineConfigLoader(ConfigLoader):' | |||||
292 | """ |
|
297 | """ | |
293 |
|
298 | |||
294 |
|
299 | |||
295 | class __NoConfigDefault(object): pass |
|
|||
296 | NoConfigDefault = __NoConfigDefault() |
|
|||
297 |
|
||||
298 |
|
||||
299 | class ArgParseConfigLoader(CommandLineConfigLoader): |
|
300 | class ArgParseConfigLoader(CommandLineConfigLoader): | |
300 | #: Global default for arguments (see argparse docs for details) |
|
|||
301 | argument_default = NoConfigDefault |
|
|||
302 |
|
||||
303 | def __init__(self, argv=None, arguments=(), *args, **kw): |
|
|||
304 | """Create a config loader for use with argparse. |
|
|||
305 |
|
301 | |||
306 | With the exception of ``argv`` and ``arguments``, other args and kwargs |
|
302 | def __init__(self, argv=None, arguments=(), *parser_args, **parser_kw): | |
307 | arguments here are passed onto the constructor of |
|
303 | """Create a config loader for use with argparse. | |
308 | :class:`argparse.ArgumentParser`. |
|
|||
309 |
|
304 | |||
310 | Parameters |
|
305 | Parameters | |
311 | ---------- |
|
306 | ---------- | |
@@ -315,18 +310,27 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
315 | sys.argv[1:] is used. |
|
310 | sys.argv[1:] is used. | |
316 |
|
311 | |||
317 | arguments : optional, tuple |
|
312 | arguments : optional, tuple | |
318 | Description of valid command-line arguments, to be called in sequence |
|
313 | A tuple of two element tuples each having the form (args, kwargs). | |
319 | with parser.add_argument() to configure the parser. |
|
314 | Each such pair is passed to parser.add_argument(*args, **kwargs) | |
|
315 | in sequence to configure the parser. | |||
|
316 | ||||
|
317 | parser_args : tuple | |||
|
318 | A tuple of positional arguments that will be passed to the | |||
|
319 | constructor of :class:`argparse.ArgumentParser`. | |||
|
320 | ||||
|
321 | parser_kw : dict | |||
|
322 | A tuple of keyword arguments that will be passed to the | |||
|
323 | constructor of :class:`argparse.ArgumentParser`. | |||
320 | """ |
|
324 | """ | |
321 | super(CommandLineConfigLoader, self).__init__() |
|
325 | super(CommandLineConfigLoader, self).__init__() | |
322 | if argv == None: |
|
326 | if argv == None: | |
323 | argv = sys.argv[1:] |
|
327 | argv = sys.argv[1:] | |
324 | self.argv = argv |
|
328 | self.argv = argv | |
325 | self.arguments = arguments |
|
329 | self.arguments = arguments | |
326 | self.args = args |
|
330 | self.parser_args = parser_args | |
327 |
kwargs = dict(argument_default=se |
|
331 | kwargs = dict(argument_default=argparse.SUPPRESS) | |
328 | kwargs.update(kw) |
|
332 | kwargs.update(parser_kw) | |
329 | self.kw = kwargs |
|
333 | self.parser_kw = kwargs | |
330 |
|
334 | |||
331 | def load_config(self, args=None): |
|
335 | def load_config(self, args=None): | |
332 | """Parse command line arguments and return as a Struct. |
|
336 | """Parse command line arguments and return as a Struct. | |
@@ -335,10 +339,10 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
335 | ---------- |
|
339 | ---------- | |
336 |
|
340 | |||
337 | args : optional, list |
|
341 | args : optional, list | |
338 |
If given, a list with the structure of sys.argv[1:] to parse |
|
342 | If given, a list with the structure of sys.argv[1:] to parse | |
339 |
from. |
|
343 | arguments from. If not given, the instance's self.argv attribute | |
340 | construction time) is used.""" |
|
344 | (given at construction time) is used.""" | |
341 |
|
345 | self.clear() | ||
342 | if args is None: |
|
346 | if args is None: | |
343 | args = self.argv |
|
347 | args = self.argv | |
344 | self._create_parser() |
|
348 | self._create_parser() | |
@@ -353,13 +357,17 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
353 | return [] |
|
357 | return [] | |
354 |
|
358 | |||
355 | def _create_parser(self): |
|
359 | def _create_parser(self): | |
356 | self.parser = ArgumentParser(*self.args, **self.kw) |
|
360 | self.parser = ArgumentParser(*self.parser_args, **self.parser_kw) | |
357 | self._add_arguments() |
|
361 | self._add_arguments() | |
358 | self._add_other_arguments() |
|
362 | self._add_other_arguments() | |
359 |
|
363 | |||
360 | def _add_arguments(self): |
|
364 | def _add_arguments(self): | |
361 | for argument in self.arguments: |
|
365 | for argument in self.arguments: | |
362 | self.parser.add_argument(*argument[0],**argument[1]) |
|
366 | # Remove any defaults in case people add them. We can't have | |
|
367 | # command line default because all default are determined by | |||
|
368 | # traited class attributes. | |||
|
369 | argument[1].pop('default', None) | |||
|
370 | self.parser.add_argument(*argument[0], **argument[1]) | |||
363 |
|
371 | |||
364 | def _add_other_arguments(self): |
|
372 | def _add_other_arguments(self): | |
365 | """Meant for subclasses to add their own arguments.""" |
|
373 | """Meant for subclasses to add their own arguments.""" | |
@@ -372,6 +380,7 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||||
372 | def _convert_to_config(self): |
|
380 | def _convert_to_config(self): | |
373 | """self.parsed_data->self.config""" |
|
381 | """self.parsed_data->self.config""" | |
374 | for k, v in vars(self.parsed_data).items(): |
|
382 | for k, v in vars(self.parsed_data).items(): | |
375 | if v is not NoConfigDefault: |
|
383 | exec_str = 'self.config.' + k + '= v' | |
376 | exec_str = 'self.config.' + k + '= v' |
|
384 | exec exec_str in locals(), globals() | |
377 | exec exec_str in locals(), globals() |
|
385 | ||
|
386 |
@@ -62,22 +62,26 b' class TestPyFileCL(TestCase):' | |||||
62 | self.assertEquals(config.D.C.value, 'hi there') |
|
62 | self.assertEquals(config.D.C.value, 'hi there') | |
63 |
|
63 | |||
64 |
|
64 | |||
|
65 | arguments = ( | |||
|
66 | (('-f','--foo'), dict(dest='Global.foo', type=str)), | |||
|
67 | (('-b',), dict(dest='MyClass.bar', type=int)), | |||
|
68 | (('-n',), dict(dest='n', action='store_true')), | |||
|
69 | (('Global.bam',), dict(type=str)) | |||
|
70 | ) | |||
|
71 | ||||
65 | class TestArgParseCL(TestCase): |
|
72 | class TestArgParseCL(TestCase): | |
66 |
|
73 | |||
67 | def test_basic(self): |
|
74 | def test_basic(self): | |
68 |
|
||||
69 | arguments = ( |
|
|||
70 | (('-f','--foo'), dict(dest='Global.foo', type=str)), |
|
|||
71 | (('-b',), dict(dest='MyClass.bar', type=int)), |
|
|||
72 | (('-n',), dict(dest='n', action='store_true')), |
|
|||
73 | (('Global.bam',), dict(type=str)) |
|
|||
74 | ) |
|
|||
75 | cl = ArgParseConfigLoader(arguments=arguments) |
|
75 | cl = ArgParseConfigLoader(arguments=arguments) | |
76 | config = cl.load_config('-f hi -b 10 -n wow'.split()) |
|
76 | config = cl.load_config('-f hi -b 10 -n wow'.split()) | |
77 | self.assertEquals(config.Global.foo, 'hi') |
|
77 | self.assertEquals(config.Global.foo, 'hi') | |
78 | self.assertEquals(config.MyClass.bar, 10) |
|
78 | self.assertEquals(config.MyClass.bar, 10) | |
79 | self.assertEquals(config.n, True) |
|
79 | self.assertEquals(config.n, True) | |
80 | self.assertEquals(config.Global.bam, 'wow') |
|
80 | self.assertEquals(config.Global.bam, 'wow') | |
|
81 | config = cl.load_config(['wow']) | |||
|
82 | self.assertEquals(config.keys(), ['Global']) | |||
|
83 | self.assertEquals(config.Global.keys(), ['bam']) | |||
|
84 | self.assertEquals(config.Global.bam, 'wow') | |||
81 |
|
85 | |||
82 | def test_add_arguments(self): |
|
86 | def test_add_arguments(self): | |
83 |
|
87 | |||
@@ -97,6 +101,18 b' class TestArgParseCL(TestCase):' | |||||
97 | self.assertEquals(config.subparser_name, '1') |
|
101 | self.assertEquals(config.subparser_name, '1') | |
98 | self.assertEquals(config.Global.x, 'frobble') |
|
102 | self.assertEquals(config.Global.x, 'frobble') | |
99 |
|
103 | |||
|
104 | def test_argv(self): | |||
|
105 | cl = ArgParseConfigLoader( | |||
|
106 | argv='-f hi -b 10 -n wow'.split(), | |||
|
107 | arguments=arguments | |||
|
108 | ) | |||
|
109 | config = cl.load_config() | |||
|
110 | self.assertEquals(config.Global.foo, 'hi') | |||
|
111 | self.assertEquals(config.MyClass.bar, 10) | |||
|
112 | self.assertEquals(config.n, True) | |||
|
113 | self.assertEquals(config.Global.bam, 'wow') | |||
|
114 | ||||
|
115 | ||||
100 | class TestConfig(TestCase): |
|
116 | class TestConfig(TestCase): | |
101 |
|
117 | |||
102 | def test_setget(self): |
|
118 | def test_setget(self): |
@@ -32,8 +32,7 b' from IPython.core.application import Application' | |||||
32 | from IPython.core.iplib import InteractiveShell |
|
32 | from IPython.core.iplib import InteractiveShell | |
33 | from IPython.config.loader import ( |
|
33 | from IPython.config.loader import ( | |
34 | Config, |
|
34 | Config, | |
35 |
PyFileConfigLoader |
|
35 | PyFileConfigLoader | |
36 | # NoConfigDefault, |
|
|||
37 | ) |
|
36 | ) | |
38 | from IPython.lib import inputhook |
|
37 | from IPython.lib import inputhook | |
39 | from IPython.utils.path import filefind, get_ipython_dir |
|
38 | from IPython.utils.path import filefind, get_ipython_dir |
@@ -23,8 +23,8 b" if os.name=='posix':" | |||||
23 | from twisted.scripts._twistd_unix import daemonize |
|
23 | from twisted.scripts._twistd_unix import daemonize | |
24 |
|
24 | |||
25 | from IPython.core import release |
|
25 | from IPython.core import release | |
26 | from IPython.external.argparse import ArgumentParser |
|
26 | from IPython.external.argparse import ArgumentParser, SUPPRESS | |
27 |
from IPython.config.loader import ArgParseConfigLoader |
|
27 | from IPython.config.loader import ArgParseConfigLoader | |
28 | from IPython.utils.importstring import import_item |
|
28 | from IPython.utils.importstring import import_item | |
29 |
|
29 | |||
30 | from IPython.kernel.clusterdir import ( |
|
30 | from IPython.kernel.clusterdir import ( | |
@@ -56,7 +56,7 b' class IPClusterCLLoader(ArgParseConfigLoader):' | |||||
56 | def _add_other_arguments(self): |
|
56 | def _add_other_arguments(self): | |
57 | # This has all the common options that all subcommands use |
|
57 | # This has all the common options that all subcommands use | |
58 | parent_parser1 = ArgumentParser(add_help=False, |
|
58 | parent_parser1 = ArgumentParser(add_help=False, | |
59 |
argument_default= |
|
59 | argument_default=SUPPRESS) | |
60 | parent_parser1.add_argument('--ipython-dir', |
|
60 | parent_parser1.add_argument('--ipython-dir', | |
61 | dest='Global.ipython_dir',type=unicode, |
|
61 | dest='Global.ipython_dir',type=unicode, | |
62 | help='Set to override default location of Global.ipython_dir.', |
|
62 | help='Set to override default location of Global.ipython_dir.', | |
@@ -68,7 +68,7 b' class IPClusterCLLoader(ArgParseConfigLoader):' | |||||
68 |
|
68 | |||
69 | # This has all the common options that other subcommands use |
|
69 | # This has all the common options that other subcommands use | |
70 | parent_parser2 = ArgumentParser(add_help=False, |
|
70 | parent_parser2 = ArgumentParser(add_help=False, | |
71 |
argument_default= |
|
71 | argument_default=SUPPRESS) | |
72 | parent_parser2.add_argument('-p','--profile', |
|
72 | parent_parser2.add_argument('-p','--profile', | |
73 | dest='Global.profile',type=unicode, |
|
73 | dest='Global.profile',type=unicode, | |
74 | help='The string name of the profile to be used. This determines ' |
|
74 | help='The string name of the profile to be used. This determines ' | |
@@ -112,7 +112,6 b' class IPClusterCLLoader(ArgParseConfigLoader):' | |||||
112 | parser_create.add_argument( |
|
112 | parser_create.add_argument( | |
113 | '--reset-config', |
|
113 | '--reset-config', | |
114 | dest='Global.reset_config', action='store_true', |
|
114 | dest='Global.reset_config', action='store_true', | |
115 | default=NoConfigDefault, |
|
|||
116 | help='Recopy the default config files to the cluster directory. ' |
|
115 | help='Recopy the default config files to the cluster directory. ' | |
117 | 'You will loose any modifications you have made to these files.' |
|
116 | 'You will loose any modifications you have made to these files.' | |
118 | ) |
|
117 | ) |
General Comments 0
You need to be logged in to leave comments.
Login now