diff --git a/src/cli/arghandler.py b/src/cli/arghandler.py index e0bea8b51..0080396c0 100644 --- a/src/cli/arghandler.py +++ b/src/cli/arghandler.py @@ -52,6 +52,89 @@ from gen.plug import BasePluginManager from gen.plug.report import CATEGORY_BOOK, CATEGORY_CODE from cli.plug import cl_report +#------------------------------------------------------------------------- +# +# private functions +# +#------------------------------------------------------------------------- +def _split_options(options_str): + """ + Split the options for the action. + + Rules: + * Entries in the list of options are separated by commas without spaces + between entries + * List values must be inclosed in brackets ("[" and "]") + * Entries within a list value are separated by commas + * Text values (as a value or as entries in a list) do not have to be + enclosed in quotes unless they include commas or quotation marks. + * Text containing double quotes must be contained in single quotes + * Text containing single quotes must be contained in double quotes + * Text cannot include both single and double quotes + + Examples: + * Multiple options specified: + report -p 'name=ancestor_chart,father_disp=["$n born $b"]' + * Using text with commas and quotes: + title="This is some text with ,s and 's" + title='This is some text with ,s and "s' + * Using a list of text + textlist=[row1,row2,"row3 with ' and ,"] + """ + name = "" + value = "" + parsing_value = False + in_quotes = False + in_list = False + quote_type = "" + options_str_dict = {} + + for char in options_str: + if not parsing_value: + # Parsing the name of the option + if char == "=": + #print char, "This value ends the name" + parsing_value = True + else: + #print char, "This value is part of the name" + name += char + else: + # Parsing the value of the option + if value == "" and char == '[': + #print char, "This character begins a list" + in_list = True + value += char + elif in_list == True and char == ']': + #print char, "This character ends the list" + in_list = False + value += char + elif not in_quotes and ( char == '"' or char == "'"): + #print char, "This character starts a quoted string" + in_quotes = True + quote_type = char + value += char + elif in_quotes and char == quote_type: + #print char, "This character ends a quoted string" + in_quotes = False + value += char + elif not in_quotes and not in_list and char == ",": + #print char, "This character ends the value of the option" + options_str_dict[name] = value + name = "" + value = "" + parsing_value = False + in_quotes = False + in_list = False + else: + #print char, "This character is part of the value" + value += char + + if parsing_value and not in_quotes and not in_list: + # Add the last option + options_str_dict[name] = value + + return options_str_dict + #------------------------------------------------------------------------- # ArgHandler #------------------------------------------------------------------------- @@ -465,8 +548,7 @@ class ArgHandler(object): pmgr = BasePluginManager.get_instance() if action == "report": try: - options_str_dict = dict( [ tuple(chunk.split('=')) - for chunk in options_str.split(',') ] ) + options_str_dict = _split_options(options_str) except: options_str_dict = {} print >> sys.stderr, "Ignoring invalid options string." diff --git a/src/cli/plug/__init__.py b/src/cli/plug/__init__.py index 24d725a0d..0d3a34a70 100644 --- a/src/cli/plug/__init__.py +++ b/src/cli/plug/__init__.py @@ -67,6 +67,71 @@ from cli.grampscli import CLIManager # Private Functions # #------------------------------------------------------------------------ +def _convert_str_to_match_type(str_val, type_val): + """ + Returns a value representing str_val that is the same type as type_val. + """ + str_val = str_val.strip() + ret_type = type(type_val) + + if ret_type in (str, unicode): + if ( str_val.startswith("'") and str_val.endswith("'") ) or \ + ( str_val.startswith('"') and str_val.endswith('"') ): + # Remove enclosing quotes + return unicode(str_val[1:-1]) + else: + return unicode(str_val) + + elif ret_type == int: + if str_val.isdigit(): + return int(str_val) + else: + print "%s is not an integer number" % str_val + return 0 + + elif ret_type == float: + if str_val.isdecimal(): + return float(ret_type) + else: + print "%s is not a decimal number" % str_val + return 0.0 + + elif ret_type == bool: + if str_val == str(True): + return True + return False + + elif ret_type == list: + ret_val = [] + if not ( str_val.startswith("[") and str_val.endswith("]") ): + print "%s is not a list" % str_val + return ret_val + + entry = "" + quote_type = None + + # Search through characters between the brackets + for char in str_val[1:-1]: + if (char == "'" or char == '"') and quote_type == None: + # This character starts a string + quote_type = char + elif char == quote_type: + # This character ends a string + quote_type = None + elif quote_type == None and char == ",": + # This character ends an entry + ret_val.append(entry.strip()) + entry = "" + quote_type = None + else: + entry += char + + if entry != "": + # Add the last entry + ret_val.append(entry.strip()) + + return ret_val + def _validate_options(options, dbase): """ Validate all options by making sure that their values are consistent with @@ -291,13 +356,15 @@ class CommandLineReport(object): elif isinstance(option, NumberOption): self.options_help[name].append("A number") elif isinstance(option, BooleanOption): - self.options_help[name].append(["False\tno", "True\tyes"]) + self.options_help[name].append(["False", "True"]) elif isinstance(option, DestinationOption): self.options_help[name].append("A file system path") elif isinstance(option, StringOption): self.options_help[name].append("Any text") elif isinstance(option, TextOption): - self.options_help[name].append("Any text") + self.options_help[name].append( + "A list of text values. Each entry in the list " + "represents one line of text." ) elif isinstance(option, EnumeratedListOption): ilist = [] for (value, description) in option.get_items(): @@ -320,8 +387,10 @@ class CommandLineReport(object): menu_opt_names = menu.get_all_option_names() for opt in self.options_str_dict: if opt in self.options_dict: - converter = Utils.get_type_converter(self.options_dict[opt]) - self.options_dict[opt] = converter(self.options_str_dict[opt]) + self.options_dict[opt] = \ + _convert_str_to_match_type(self.options_str_dict[opt], + self.options_dict[opt]) + self.option_class.handler.options_dict[opt] = \ self.options_dict[opt]