|
|
@@ -0,0 +1,180 @@
|
|
|
+import sys, inspect, pkgutil
|
|
|
+import re
|
|
|
+import FlatCAMApp
|
|
|
+
|
|
|
+
|
|
|
+class TclCommand(object):
|
|
|
+
|
|
|
+ app=None
|
|
|
+
|
|
|
+ # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
|
|
+ aliases = None
|
|
|
+
|
|
|
+ # dictionary of types from Tcl command: args = {'name': str}, this is for value without optionname
|
|
|
+ arg_names = {'name': str}
|
|
|
+
|
|
|
+ # dictionary of types from Tcl command: types = {'outname': str} , this is for options like -optionname value
|
|
|
+ option_types = {}
|
|
|
+
|
|
|
+ # array of mandatory options for current Tcl command: required = {'name','outname'}
|
|
|
+ required = ['name']
|
|
|
+
|
|
|
+ # structured help for current command
|
|
|
+ help = {
|
|
|
+ 'main': "undefined help.",
|
|
|
+ 'args': {
|
|
|
+ 'argumentname': 'undefined help.',
|
|
|
+ 'optionname': 'undefined help.'
|
|
|
+ },
|
|
|
+ 'examples' : []
|
|
|
+ }
|
|
|
+
|
|
|
+ def __init__(self, app):
|
|
|
+ self.app=app
|
|
|
+
|
|
|
+ def get_decorated_help(self):
|
|
|
+ """
|
|
|
+ Decorate help for TCL console output.
|
|
|
+
|
|
|
+ :return: decorated help from structue
|
|
|
+ """
|
|
|
+
|
|
|
+ def get_decorated_command(alias):
|
|
|
+ command_string = []
|
|
|
+ for key, value in reversed(self.help['args'].items()):
|
|
|
+ command_string.append(get_decorated_argument(key, value, True))
|
|
|
+ return "> " + alias + " " + " ".join(command_string)
|
|
|
+
|
|
|
+ def get_decorated_argument(key, value, in_command=False):
|
|
|
+ option_symbol = ''
|
|
|
+ if key in self.arg_names:
|
|
|
+ type=self.arg_names[key]
|
|
|
+ in_command_name = "<" + str(type.__name__) + ">"
|
|
|
+ else:
|
|
|
+ option_symbol = '-'
|
|
|
+ type=self.option_types[key]
|
|
|
+ in_command_name = option_symbol + key + " <" + str(type.__name__) + ">"
|
|
|
+ if in_command:
|
|
|
+ if key in self.required:
|
|
|
+ return in_command_name
|
|
|
+ else:
|
|
|
+ return '[' + in_command_name + "]"
|
|
|
+ else:
|
|
|
+ if key in self.required:
|
|
|
+ return "\t" + option_symbol + key + " <" + str(type.__name__) + ">: " + value
|
|
|
+ else:
|
|
|
+ return "\t[" + option_symbol + key + " <" + str(type.__name__) + ">: " + value+"]"
|
|
|
+
|
|
|
+ def get_decorated_example(example):
|
|
|
+ example_string = ''
|
|
|
+ return "todo" + example_string
|
|
|
+
|
|
|
+ help_string=[self.help['main']]
|
|
|
+ for alias in self.aliases:
|
|
|
+ help_string.append(get_decorated_command(alias))
|
|
|
+
|
|
|
+ for key, value in reversed(self.help['args'].items()):
|
|
|
+ help_string.append(get_decorated_argument(key, value))
|
|
|
+
|
|
|
+ for example in self.help['examples']:
|
|
|
+ help_string.append(get_decorated_example(example))
|
|
|
+
|
|
|
+ return "\n".join(help_string)
|
|
|
+
|
|
|
+ def parse_arguments(self, args):
|
|
|
+ """
|
|
|
+ Pre-processes arguments to detect '-keyword value' pairs into dictionary
|
|
|
+ and standalone parameters into list.
|
|
|
+
|
|
|
+ This is copy from FlatCAMApp.setup_shell().h() just for accesibility, original should be removed after all commands will be converted
|
|
|
+ """
|
|
|
+
|
|
|
+ options = {}
|
|
|
+ arguments = []
|
|
|
+ n = len(args)
|
|
|
+ name = None
|
|
|
+ for i in range(n):
|
|
|
+ match = re.search(r'^-([a-zA-Z].*)', args[i])
|
|
|
+ if match:
|
|
|
+ assert name is None
|
|
|
+ name = match.group(1)
|
|
|
+ continue
|
|
|
+
|
|
|
+ if name is None:
|
|
|
+ arguments.append(args[i])
|
|
|
+ else:
|
|
|
+ options[name] = args[i]
|
|
|
+ name = None
|
|
|
+
|
|
|
+ return arguments, options
|
|
|
+
|
|
|
+ def check_args(self, args):
|
|
|
+ """
|
|
|
+ Check arguments and options for right types
|
|
|
+
|
|
|
+ :param args: arguments from tcl to check
|
|
|
+ :return:
|
|
|
+ """
|
|
|
+
|
|
|
+ arguments, options = self.parse_arguments(args)
|
|
|
+
|
|
|
+ named_args={}
|
|
|
+ unnamed_args=[]
|
|
|
+
|
|
|
+ # check arguments
|
|
|
+ idx=0
|
|
|
+ arg_names_reversed=self.arg_names.items()
|
|
|
+ for argument in arguments:
|
|
|
+ if len(self.arg_names) > idx:
|
|
|
+ key, type = arg_names_reversed[len(self.arg_names)-idx-1]
|
|
|
+ try:
|
|
|
+ named_args[key] = type(argument)
|
|
|
+ except Exception, e:
|
|
|
+ self.app.raiseTclError("Cannot cast named argument '%s' to type %s." % (key, type))
|
|
|
+ else:
|
|
|
+ unnamed_args.append(argument)
|
|
|
+ idx += 1
|
|
|
+
|
|
|
+ # check otions
|
|
|
+ for key in options:
|
|
|
+ if key not in self.option_types:
|
|
|
+ self.app.raiseTclError('Unknown parameter: %s' % key)
|
|
|
+ try:
|
|
|
+ named_args[key] = self.option_types[key](options[key])
|
|
|
+ except Exception, e:
|
|
|
+ self.app.raiseTclError("Cannot cast argument '-%s' to type %s." % (key, self.option_types[key]))
|
|
|
+
|
|
|
+ # check required arguments
|
|
|
+ for key in self.required:
|
|
|
+ if key not in named_args:
|
|
|
+ self.app.raiseTclError("Missing required argument '%s'." % (key))
|
|
|
+
|
|
|
+ return named_args, unnamed_args
|
|
|
+
|
|
|
+ def execute_wrapper(self, *args):
|
|
|
+ """
|
|
|
+ Command which is called by tcl console when current commands aliases are hit.
|
|
|
+ Main catch(except) is implemented here.
|
|
|
+ This method should be reimplemented only when initial checking sequence differs
|
|
|
+
|
|
|
+ :param args: arguments passed from tcl command console
|
|
|
+ :return: None, output text or exception
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ args, unnamed_args = self.check_args(args)
|
|
|
+ return self.execute(args, unnamed_args)
|
|
|
+ except Exception as unknown:
|
|
|
+ self.app.raiseTclUnknownError(unknown)
|
|
|
+
|
|
|
+ def execute(self, args, unnamed_args):
|
|
|
+ """
|
|
|
+ Direct execute of command, this method should be implemented in each descendant.
|
|
|
+ No main catch should be implemented here.
|
|
|
+
|
|
|
+ :param args: array of known named arguments and options
|
|
|
+ :param unnamed_args: array of other values which were passed into command
|
|
|
+ without -somename and we do not have them in known arg_names
|
|
|
+ :return: None, output text or exception
|
|
|
+ """
|
|
|
+
|
|
|
+ raise NotImplementedError("Please Implement this method")
|