summaryrefslogtreecommitdiff
path: root/cmd2/pyscript_bridge.py
diff options
context:
space:
mode:
Diffstat (limited to 'cmd2/pyscript_bridge.py')
-rw-r--r--cmd2/pyscript_bridge.py107
1 files changed, 107 insertions, 0 deletions
diff --git a/cmd2/pyscript_bridge.py b/cmd2/pyscript_bridge.py
new file mode 100644
index 00000000..88e12bfb
--- /dev/null
+++ b/cmd2/pyscript_bridge.py
@@ -0,0 +1,107 @@
+"""Bridges calls made inside of pyscript with the Cmd2 host app while maintaining a reasonable
+degree of isolation between the two"""
+
+import argparse
+
+class ArgparseFunctor:
+ def __init__(self, cmd2_app, item, parser):
+ self._cmd2_app = cmd2_app
+ self._item = item
+ self._parser = parser
+
+ self._args = {}
+ self.__current_subcommand_parser = parser
+
+ def __getattr__(self, item):
+ # look for sub-command
+ for action in self.__current_subcommand_parser._actions:
+ if not action.option_strings and isinstance(action, argparse._SubParsersAction):
+ if item in action.choices:
+ # item matches the a sub-command, save our position in argparse,
+ # save the sub-command, return self to allow next level of traversal
+ self.__current_subcommand_parser = action.choices[item]
+ self._args[action.dest] = item
+ return self
+ return super().__getatttr__(item)
+
+ def __call__(self, *args, **kwargs):
+ next_pos_index = 0
+
+ has_subcommand = False
+ consumed_kw = []
+ for action in self.__current_subcommand_parser._actions:
+ # is this a flag option?
+ if action.option_strings:
+ if action.dest in kwargs:
+ self._args[action.dest] = kwargs[action.dest]
+ consumed_kw.append(action.dest)
+ else:
+ if not isinstance(action, argparse._SubParsersAction):
+ if next_pos_index < len(args):
+ self._args[action.dest] = args[next_pos_index]
+ next_pos_index += 1
+ else:
+ has_subcommand = True
+
+ for kw in kwargs:
+ if kw not in consumed_kw:
+ raise TypeError('{}() got an unexpected keyword argument \'{}\''.format(
+ self.__current_subcommand_parser.prog, kw))
+
+ if has_subcommand:
+ return self
+ else:
+ return self._run()
+
+ def _run(self):
+ # look up command function
+ func = getattr(self._cmd2_app, 'do_' + self._item)
+
+ # reconstruct the cmd2 command from the python call
+ cmd_str = ['']
+
+ def traverse_parser(parser):
+ for action in parser._actions:
+ # was something provided for the argument
+ if action.dest in self._args:
+ # was the argument a flag?
+ # TODO: Handle 'narg' and 'append' options
+ if action.option_strings:
+ cmd_str[0] += '"{}" "{}" '.format(action.option_strings[0], self._args[action.dest])
+ else:
+ cmd_str[0] += '"{}" '.format(self._args[action.dest])
+
+ if isinstance(action, argparse._SubParsersAction):
+ traverse_parser(action.choices[self._args[action.dest]])
+ traverse_parser(self._parser)
+
+ func(cmd_str[0])
+ return self._cmd2_app._last_result
+
+
+class PyscriptBridge(object):
+ def __init__(self, cmd2_app):
+ self._cmd2_app = cmd2_app
+ self._last_result = None
+
+ def __getattr__(self, item: str):
+ commands = self._cmd2_app.get_all_commands()
+ if item in commands:
+ func = getattr(self._cmd2_app, 'do_' + item)
+
+ try:
+ parser = getattr(func, 'argparser')
+ except AttributeError:
+ def wrap_func(args=''):
+ func(args)
+ return self._cmd2_app._last_result
+ return wrap_func
+ else:
+ return ArgparseFunctor(self._cmd2_app, item, parser)
+
+ return super().__getattr__(item)
+
+ def __call__(self, args):
+ self._cmd2_app.onecmd_plus_hooks(args + '\n')
+ self._last_result = self._cmd2_app._last_result
+ return self._cmd2_app._last_result