From 0228ed0d2df9832a528311dcebb46755dd98bbd5 Mon Sep 17 00:00:00 2001 From: Stephen Zhuang Date: Sat, 12 Jan 2013 23:29:21 +0800 Subject: [PATCH 1/4] Call the registered main.default when no subcommand. --- komandr.py | 7 ++++++- tests.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/komandr.py b/komandr.py index 96f505b..b403c09 100644 --- a/komandr.py +++ b/komandr.py @@ -102,15 +102,20 @@ def execute(self, arg_list): :param type: list """ + default = getattr(self, 'default', None) + if not arg_list and default and callable(default): + return default() + arg_map = self.parser.parse_args(arg_list).__dict__ command = arg_map.pop(self._COMMAND_FLAG) return command(**arg_map) - def __call__(self): + def __call__(self, default=None): """Calls :py:func:``execute`` with :py:class:``sys.argv`` excluding script name which comes first. """ + self.default = default self.execute(sys.argv[1:]) main = prog() diff --git a/tests.py b/tests.py index 59687c0..08c149d 100644 --- a/tests.py +++ b/tests.py @@ -13,5 +13,22 @@ def foo(bar, baz=None): komandr.execute(['foo', '1', '--baz', '2'])) + def testMainDefault(self): + def foo(bar, baz=None): + return bar, baz + komandr.command(foo) + + # Without main.default a SystemExit exception would be raised. + with self.assertRaises(SystemExit) as ex: + komandr.execute([]) + + self.assertEqual(2, ex.exception.code) + + # With main.default=foo, `foo` would be called by default when no subcommand. + import functools + komandr.main.default = functools.partial(foo, bar='1', baz='2') + self.assertEqual(('1', '2'), komandr.execute([])) + + if __name__ == '__main__': unittest.main() From fcd320298c68550c67873da7cedb34744c42f74d Mon Sep 17 00:00:00 2001 From: Stephen Zhuang Date: Sat, 12 Jan 2013 23:36:23 +0800 Subject: [PATCH 2/4] Comment for the 'default' parameter --- komandr.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/komandr.py b/komandr.py index b403c09..4cb5a98 100644 --- a/komandr.py +++ b/komandr.py @@ -114,6 +114,8 @@ def __call__(self, default=None): """Calls :py:func:``execute`` with :py:class:``sys.argv`` excluding script name which comes first. + :param default: function called when no subcommand + :param type: function """ self.default = default self.execute(sys.argv[1:]) From d2529f0966e63e563f9e44c431dd715129d599a7 Mon Sep 17 00:00:00 2001 From: Stephen Zhuang Date: Sun, 13 Jan 2013 12:30:05 +0800 Subject: [PATCH 3/4] Specify the name, not the function call. --- komandr.py | 15 ++++++++------- tests.py | 20 ++++++++++++++------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/komandr.py b/komandr.py index 4cb5a98..1d771f4 100644 --- a/komandr.py +++ b/komandr.py @@ -102,22 +102,23 @@ def execute(self, arg_list): :param type: list """ - default = getattr(self, 'default', None) - if not arg_list and default and callable(default): - return default() + if getattr(self, 'default_subcommand', None): + subcommand = arg_list[0] if arg_list else '' + if not (subcommand and subcommand in self.subparsers.choices): + arg_list[:0] = [self.default_subcommand] arg_map = self.parser.parse_args(arg_list).__dict__ command = arg_map.pop(self._COMMAND_FLAG) return command(**arg_map) - def __call__(self, default=None): + def __call__(self, default_subcommand=None): """Calls :py:func:``execute`` with :py:class:``sys.argv`` excluding script name which comes first. - :param default: function called when no subcommand - :param type: function + :param default: name of subcommand called if no subcommand specified. + :param type: str """ - self.default = default + self.default_subcommand = default_subcommand self.execute(sys.argv[1:]) main = prog() diff --git a/tests.py b/tests.py index 08c149d..812bf7e 100644 --- a/tests.py +++ b/tests.py @@ -13,21 +13,29 @@ def foo(bar, baz=None): komandr.execute(['foo', '1', '--baz', '2'])) - def testMainDefault(self): + def testDefaultSubcommand(self): def foo(bar, baz=None): return bar, baz komandr.command(foo) - # Without main.default a SystemExit exception would be raised. + # Without main.default_subcommand a `SystemExit` exception would raise. with self.assertRaises(SystemExit) as ex: komandr.execute([]) self.assertEqual(2, ex.exception.code) - # With main.default=foo, `foo` would be called by default when no subcommand. - import functools - komandr.main.default = functools.partial(foo, bar='1', baz='2') - self.assertEqual(('1', '2'), komandr.execute([])) + # With main.default_subcommand='foo', subcommand named `foo` would be + # called by default when no subcommand. + komandr.main.default_subcommand = 'foo' + self.assertEqual(('1', '2'), komandr.execute(['1', '--baz', '2'])) + + def testDefaultSubcommandWithoutOptions(self): + def foo(): + return 'bar' + komandr.command(foo) + + komandr.main.default_subcommand = 'foo' + self.assertEqual('bar', komandr.execute([])) if __name__ == '__main__': From c5f6664735e8f036851fa4e623cb309482e4a845 Mon Sep 17 00:00:00 2001 From: Stephen Zhuang Date: Sun, 13 Jan 2013 16:03:29 +0800 Subject: [PATCH 4/4] Skip exist option strings of parser when using default subcommand. --- komandr.py | 8 ++++++-- tests.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/komandr.py b/komandr.py index 1d771f4..7cb52e0 100644 --- a/komandr.py +++ b/komandr.py @@ -7,7 +7,7 @@ import inspect import argparse from functools import wraps -from itertools import izip_longest +from itertools import izip_longest, chain class prog(object): @@ -104,7 +104,11 @@ def execute(self, arg_list): """ if getattr(self, 'default_subcommand', None): subcommand = arg_list[0] if arg_list else '' - if not (subcommand and subcommand in self.subparsers.choices): + action_option_strings = chain.from_iterable(i.option_strings + for i in self.parser._actions) + if not (subcommand + and subcommand in self.subparsers.choices + or subcommand in action_option_strings): arg_list[:0] = [self.default_subcommand] arg_map = self.parser.parse_args(arg_list).__dict__ diff --git a/tests.py b/tests.py index 812bf7e..bf0c7d0 100644 --- a/tests.py +++ b/tests.py @@ -1,10 +1,14 @@ # TODO: Add much more tests import unittest import komandr +from mock import patch class TestKomandr(unittest.TestCase): + def tearDown(self): + komandr.main.default_subcommand = None + def testCommand(self): def foo(bar, baz=None): return bar, baz @@ -37,6 +41,20 @@ def foo(): komandr.main.default_subcommand = 'foo' self.assertEqual('bar', komandr.execute([])) + def testOptionsNotTriggerDefaultSubcommand(self): + def foo(baz='bar'): + return baz + komandr.command(foo) + + komandr.main.default_subcommand = 'foo' + with patch.object(komandr.main.parser, 'print_help') as mock: + with self.assertRaises(SystemExit) as ex: + komandr.execute(['--help']) + + self.assertEqual(0, ex.exception.code) + self.assertTrue(mock.called) + + self.assertEqual('bar baz', komandr.execute(['--baz', 'bar baz'])) if __name__ == '__main__': unittest.main()