summaryrefslogtreecommitdiff
path: root/graphics/mol2chemfig/install/mol2chemfig-1.5/src/optionparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/mol2chemfig/install/mol2chemfig-1.5/src/optionparser.py')
-rw-r--r--graphics/mol2chemfig/install/mol2chemfig-1.5/src/optionparser.py458
1 files changed, 458 insertions, 0 deletions
diff --git a/graphics/mol2chemfig/install/mol2chemfig-1.5/src/optionparser.py b/graphics/mol2chemfig/install/mol2chemfig-1.5/src/optionparser.py
new file mode 100644
index 0000000000..5fc48d3d80
--- /dev/null
+++ b/graphics/mol2chemfig/install/mol2chemfig-1.5/src/optionparser.py
@@ -0,0 +1,458 @@
+'''
+parsing options with a nicer wrapper around getopt.
+Still throws getopt.GetoptError at runtime.
+
+Let's try to combine this with basic html form
+parsing, so that we can declare the options just
+once.
+
+Can we make this a bit more elegant by using
+class attributes and more subclassing than
+instantiation? The problem then is, of course,
+that client code will have to do both.
+'''
+
+import getopt, textwrap, re
+
+class OptionError(Exception):
+ pass
+
+class Option(object):
+ collapseWs = re.compile('\s+')
+
+ form_tag_template = "<!-- Option class needs to define a valid form tag template -->"
+
+ def __init__(self,
+ long_name,
+ short_name,
+ form_text=None,
+ key=None,
+ default=None,
+ valid_range=None,
+ help_text="""Our engineers deemed
+ it self-explanatory"""):
+
+ self.long_name = long_name
+ self.short_name = short_name
+ self.key = key or long_name
+ self.form_text = form_text or long_name
+
+ self.valid_range = valid_range # must precede assignment of self.default
+
+ if default is None:
+ default = self._default()
+ self.default = self.value = default
+
+ self.help_text = help_text
+
+
+ def _default(self):
+ return None
+
+
+ def validate_range(self, value):
+ '''
+ can be overridden if more general tests are needed
+ '''
+ return self.valid_range is None or value in self.valid_range
+
+
+ def validate(self, value):
+ success, converted = self._validate(value)
+ success = success and self.validate_range(converted)
+
+ if success:
+ self.value = converted
+ return True
+ return False
+
+
+ def validate_form_value(self, value):
+ '''
+ validation of option value received through a
+ web form. May need to be different from CLI,
+ but by default it's not.
+ '''
+ return self.validate(value)
+
+
+ def _validate(self, value):
+ '''
+ no-op default
+ '''
+ return True, value
+
+
+ def short_getopt(self):
+ '''
+ short option template for getopt
+ '''
+ return self.short_name + ':'
+
+
+ def long_getopt(self):
+ '''
+ long option template for getopt
+ '''
+ return self.long_name + '='
+
+
+ def format_help(self, indent=30, linewidth=80):
+ '''
+ format option and help text for console display
+ maybe we can generalize this for html somehow
+ '''
+ help_text = '%s (Default: %s)' % (self.help_text, self.default)
+ help_text = self.collapseWs.sub(' ', help_text.strip())
+
+ hwrap = textwrap.wrap(help_text,
+ width = linewidth,
+ initial_indent=' ' * indent,
+ subsequent_indent= ' ' * indent)
+
+ opts = '-%s, --%s' % (self.short_name, self.long_name)
+ hwrap[0] = opts.ljust(indent) + hwrap[0].lstrip()
+
+ return hwrap
+
+
+ def format_tag_value(self, value):
+ '''
+ format the default value for insertion into form tag
+ '''
+ if value is None:
+ return ''
+ return str(value)
+
+
+ def format_tag(self, value=None):
+ '''
+ render a html form tag
+ '''
+ value = value or self.default
+
+ values = dict(key=self.key, value=self.format_tag_value(value) )
+ tag = self.form_tag_template % values
+
+ return self.key, tag, self.form_text, self.help_text
+
+
+
+class BoolOption(Option):
+
+ form_tag_template = r'''<input type="checkbox" name="%(key)s" value="yes" %(value)s/>'''
+
+ def _default(self):
+ return False
+
+
+ def validate(self, value=None):
+ '''
+ value should be empty; we accept and discard it.
+ we simply switch the default value.
+ '''
+ self.value = not self.default
+ return True
+
+
+ def validate_form_value(self, value):
+ '''
+ if a value arrives through a web form, the box has been
+ ticked, so we set to True regardless of default. The passed
+ value itself is unimportant.
+ '''
+ self.value = True
+ return True
+
+
+ def short_getopt(self):
+ return self.short_name
+
+
+ def long_getopt(self):
+ return self.long_name
+
+
+ def format_tag_value(self, value):
+ if value is True:
+ return 'checked="checked"'
+ else:
+ return ''
+
+
+class SelectOption(Option):
+ '''
+ make a selection from a list of valid string values.
+ argument valid_range cannot be empty with this class.
+ '''
+ option_template = r'''<option value="%(option)s" %(selected)s>%(option)s</option>'''
+ field_template = '''<select name="%(key)s">\n%(options)s\n</select>'''
+
+ def _default(self):
+ '''
+ we stipulate that valid_range is not empty.
+ '''
+ try:
+ return self.valid_range[0]
+ except (TypeError, IndexError):
+ raise OptionError, 'valid_range does not supply default'
+
+
+ def _validate(self, value):
+ ''''
+ we enforce conversion to lowercase
+ '''
+ return True, value.lower()
+
+
+ def format_tag(self, value=None):
+
+ value = value or self.default
+
+ options = []
+
+ if not self.default in self.valid_range: # why am I doing this here?
+ raise OptionError, 'invalid default'
+
+ for option in self.valid_range:
+ if option == value:
+ selected = 'selected="selected"'
+ else:
+ selected = ''
+ options.append(self.option_template % dict(option=option, selected=selected))
+
+ option_string = '\n'.join(options)
+
+ tag = self.field_template % dict(options = option_string, key=self.key)
+ return self.key, tag, self.form_text, self.help_text
+
+
+class TypeOption(Option):
+ '''
+ coerces an input value to a type
+ '''
+ _type = int # example
+ _class_default = 0
+
+ form_tag_template = r'''<input type="text" name="%(key)s" value="%(value)s" size="8"/>'''
+
+ def _validate(self, value):
+ try:
+ converted = self._type(value)
+ return True, converted
+ except ValueError:
+ return False, value
+
+
+
+class IntOption(TypeOption):
+ _type = int
+
+
+class FloatOption(TypeOption):
+ _type = float
+
+
+class StringOption(TypeOption):
+ _type = str
+
+
+class RangeOption(Option):
+ '''
+ accept a string that can be parsed into one or more int ranges,
+ such as 5-6,7-19
+ these should be converted into [(5,6),(7,19)]
+ '''
+ outersep = ','
+ innersep = '-'
+ form_tag_template = r'''<input type="text" name="%(key)s" value="%(value)s" size="8"/>'''
+
+ def _validate(self, rawvalue):
+ ranges = []
+ outerfrags = rawvalue.split(self.outersep)
+
+ for frag in outerfrags:
+ innerfrags = frag.split(self.innersep)
+ if len(innerfrags) != 2:
+ return False, rawvalue
+ try:
+ ranges.append((int(innerfrags[0]), int(innerfrags[1])))
+ except ValueError:
+ return False, rawvalue
+
+ return True, ranges
+
+
+class OptionParser(object):
+ '''
+ collect and process options. the result will be contained in a dict.
+ '''
+ def __init__(self):
+ self._options = []
+ self._options_by_name = {}
+ self._options_by_key = {}
+
+
+ def append(self, option):
+ if option.short_name in self._options_by_name:
+ raise OptionError, "option name clash %s" % option.short_name
+ if option.long_name in self._options_by_name:
+ raise OptionError, "option name clash %s" % option_short_name
+
+ self._options_by_name[option.short_name] = option.key
+ self._options_by_name[option.long_name] = option.key
+ self._options_by_key[option.key] = option
+
+ # also maintain options ordered in a list
+ self._options.append(option)
+
+
+ def validKeys(self):
+ '''
+ required by the web form front end
+ '''
+ return self._option_by_key.keys()
+
+
+ def option_values(self):
+ '''
+ read current option values
+ '''
+ option_dict = {}
+
+ for option in self._options:
+ option_dict[option.key] = option.value
+
+ return option_dict
+
+
+ def process_form_fields(self, fields):
+ '''
+ process options received through the web form.
+ we don't look at the cargo data here at all.
+
+ what do we do about invalid options? puke? ignore?
+ create a list of warnings and then ignore.
+ '''
+ warnings = []
+
+ for key, value in fields.items():
+ option = self._options_by_key[key]
+ if not option.validate_form_value(value):
+ msg = 'Invalid value %s for option %s ignored' % (value, option.form_text)
+ warnings.append(msg)
+
+ return self.option_values(), warnings
+
+
+ def process_cli(self, rawinput):
+ '''
+ process input from the command line interface
+ - assemble template strings for getopt and run getopt
+ - pass the result back to each option
+ '''
+ try: # accept lists or strings
+ rawinput = rawinput.strip().split()
+ except AttributeError:
+ pass
+
+ shorts, longs = self.format_for_getopt()
+
+ opts, args = getopt.getopt(rawinput, shorts, longs)
+
+ for optname, value in opts:
+ key = self._options_by_name[optname.lstrip('-')]
+ option = self._options_by_key[key]
+
+ if not option.validate(value):
+ msg = ["rejected value '%s' for option %s" % (value, optname)]
+ msg.append('Option usage:')
+ msg.extend(option.format_help())
+ raise OptionError, '\n'.join(msg)
+
+ return self.option_values(), args
+
+
+ def format_for_getopt(self):
+ shorts = ''.join([option.short_getopt() for option in self._options])
+ longs = [option.long_getopt() for option in self._options]
+
+ return shorts, longs
+
+
+ def format_for_lua(self):
+ '''
+ with lua, we use dumb option parsing. we only provide enough
+ information for lua to distinguish between options with and
+ without arguments.
+ '''
+ bools = [opt for opt in self._options if isinstance(opt, BoolOption)]
+ shorts = [nb.short_name for nb in bools]
+ return ''.join(shorts)
+
+
+ def format_help(self, indent=25, linewidth=70, separator=None):
+ '''
+ just ask the options to render themselves
+ '''
+ output = []
+
+ for option in self._options:
+ output.extend(option.format_help(indent, linewidth))
+
+ if separator is not None:
+ output.append(separator)
+
+ return '\n'.join(output)
+
+
+ def form_tags(self):
+ '''
+ collect the html for each option
+ '''
+ return [opt.format_tag() for opt in self._options]
+
+
+if __name__ == '__main__': # test it
+
+ p = OptionParser()
+
+ p.append(BoolOption(
+ 'absolute',
+ 'a',
+ # default=True,
+ help_text = 'not relative. what happens if we choose to use a really, really, really excessively long help text here?'))
+
+ p.append(IntOption(
+ 'count',
+ 'c',
+ default=5,
+ valid_range=range(10),
+ help_text="how many apples to buy"))
+
+ p.append(StringOption(
+ 'party',
+ 'p',
+ default="NDP",
+ help_text="what party to choose"))
+
+ p.append(FloatOption(
+ 'diameter',
+ 'd',
+ default=3.14,
+ help_text='how big it is'))
+
+ p.append(StringOption(
+ 'candy',
+ 'n',
+ default='chocolate'))
+
+
+ rawinput = "-a -c 6 -p LP alpha beta gamma"
+ options, args = p.process_cli(rawinput)
+
+ print 'options', options
+ print 'args', args
+ print
+ print p.format_help()
+ print p.form_tags()