From d251326a5e3f9605045e7f7ba87805bfc0e337b1 Mon Sep 17 00:00:00 2001 From: Pawel Szostek <pawel.szostek@cern.ch> Date: Wed, 27 Apr 2011 13:41:15 +0000 Subject: [PATCH] Mistypes, finished-up home-grown configuration parser, corrected fetch-makefile generation, partial transformation of remote synthesis precodure --- synthesis/cfgparse2.py | 1762 ---------------------------------- synthesis/cfgparse3.py | 1773 ----------------------------------- synthesis/configparser.py | 106 ++- synthesis/connection.py | 49 +- synthesis/depend.py | 60 +- synthesis/global_mod.py | 2 - synthesis/hdlmake.py | 90 +- synthesis/helper_classes.py | 8 + synthesis/module.py | 55 +- synthesis/msg.py | 3 + 10 files changed, 222 insertions(+), 3686 deletions(-) delete mode 100644 synthesis/cfgparse2.py delete mode 100644 synthesis/cfgparse3.py diff --git a/synthesis/cfgparse2.py b/synthesis/cfgparse2.py deleted file mode 100644 index c66e581b..00000000 --- a/synthesis/cfgparse2.py +++ /dev/null @@ -1,1762 +0,0 @@ -"""cfgparse - a powerful, extensible, and easy-to-use configuration parser. - -By Dan Gass <dan.gass@gmail.com> - -If you have problems with this module, please file bugs through the Source -Forge project page: - http://sourceforge.net/projects/cfgparse -""" - -# @future use option note when get error -# @future print file/section/linenumber information when checks fail -# @future - check type='choice' and choices=[] one must have the other -# @future -- do we have a command line --cfgcheck option that expands all configuration and checks all possible keys? - -__version__ = "1.3" - -__all__ = [] - -__copyright__ = """ -Copyright (c) 2004 by Daniel M. Gass. All rights reserved. -Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved. -Copyright (c) 2002-2004 Python Software Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import ConfigParser as _ConfigParser -import cStringIO -import os -import re -import sys -import textwrap - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# U T I L I T Y -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -# <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> -try: - from gettext import gettext as _ -except ImportError: - _ = lambda arg: arg - -class HelpFormatter: - """ - Abstract base class for formatting option help. ConfigParser - instances should use one of the HelpFormatter subclasses for - formatting help; by default IndentedHelpFormatter is used. - - Instance attributes: - parser : OptionParser - the controlling OptionParser instance - indent_increment : int - the number of columns to indent per nesting level - max_help_position : int - the maximum starting column for option help text - help_position : int - the calculated starting column for option help text; - initially the same as the maximum - width : int - total number of columns for output (pass None to constructor for - this value to be taken from the $COLUMNS environment variable) - level : int - current indentation level - current_indent : int - current indentation level (in columns) - help_width : int - number of columns available for option help text (calculated) - default_tag : str - text to replace with each option's default value, "%default" - by default. Set to false value to disable default value expansion. - option_strings : { Option : str } - maps Option instances to the snippet of help text explaining - the syntax of that option, e.g. "option=VALUE" - """ - - NO_DEFAULT_VALUE = "none" - - def __init__(self, - indent_increment, - max_help_position, - width, - short_first): - self.parser = None - self.indent_increment = indent_increment - self.help_position = self.max_help_position = max_help_position - if width is None: - try: - width = int(os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width -= 2 - self.width = width - self.current_indent = 0 - self.level = 0 - self.help_width = None # computed later - self.short_first = short_first - self.default_tag = "%default" - self.option_strings = {} - self._short_opt_fmt = "%s %s" - self._long_opt_fmt = "%s=%s" - - def set_parser(self, parser): - self.parser = parser - - def indent(self): - self.current_indent += self.indent_increment - self.level += 1 - - def dedent(self): - self.current_indent -= self.indent_increment - assert self.current_indent >= 0, "Indent decreased below 0." - self.level -= 1 - - def format_usage(self, usage): - raise NotImplementedError, "subclasses must implement" - - def format_heading(self, heading): - raise NotImplementedError, "subclasses must implement" - - def format_description(self, description): - if not description: - return "" - desc_width = self.width - self.current_indent - indent = " "*self.current_indent - return textwrap.fill(description, - desc_width, - initial_indent=indent, - subsequent_indent=indent) + "\n" - - def expand_default(self, option): - if self.parser is None or not self.default_tag: - return option.help - - try: - default_value = option.default - except AttributeError: - default_value = None - - if default_value is NO_DEFAULT or default_value is None: - default_value = self.NO_DEFAULT_VALUE - - return option.help.replace(self.default_tag, str(default_value)) - - def format_option(self, option): - # The help for each option consists of two parts: - # * the opt strings and metavars - # eg. ("option=VALUE") - # * the user-supplied help string - # eg. ("turn on expert mode", "read data from FILENAME") - # - # If possible, we write both of these on the same line: - # option=VALUE explanation of some option - # - # But if the opt string list is too long, we put the help - # string on a second line, indented to the same column it would - # start in if it fit on the first line. - # thisoption=WAY_TO_LONG - # explanation of the long option - result = [] - opts = self.option_strings[option] - opt_width = self.help_position - self.current_indent - 2 - if len(opts) > opt_width: - opts = "%*s%s\n" % (self.current_indent, "", opts) - indent_first = self.help_position - else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) - indent_first = 0 - result.append(opts) - if option.help: - help_text = self.expand_default(option) - help_lines = textwrap.wrap(help_text, self.help_width) - result.append("%*s%s\n" % (indent_first, "", help_lines[0])) - result.extend(["%*s%s\n" % (self.help_position, "", line) - for line in help_lines[1:]]) - elif opts[-1] != "\n": - result.append("\n") - return "".join(result) - - def store_option_strings(self, parser): - self.indent() - max_len = 0 - for opt in parser.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.indent() - for group in parser.option_groups: - for opt in group.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.dedent() - self.dedent() - self.help_position = min(max_len + 2, self.max_help_position) - self.help_width = self.width - self.help_position - - def format_option_strings(self, option): - """Return a comma-separated list of option strings & metavariables.""" - metavar = option.metavar or option.dest.upper() - return '%s=%s' % (option.name,metavar) - -class IndentedHelpFormatter (HelpFormatter): - """Format help with indented section bodies. - """ - # NOTE optparse - def __init__(self, - indent_increment=2, - max_help_position=24, - width=None, - short_first=1): - HelpFormatter.__init__( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return _("usage: %s\n") % usage - - def format_heading(self, heading): - return "%*s%s:\n" % (self.current_indent, "", heading) - -class TitledHelpFormatter (HelpFormatter): - """Format help with underlined section headers. - """ - # NOTE optparse - def __init__(self, - indent_increment=0, - max_help_position=24, - width=None, - short_first=0): - HelpFormatter.__init__ ( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return "%s %s\n" % (self.format_heading(_("Usage")), usage) - - def format_heading(self, heading): - return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading)) - -SUPPRESS_HELP = "SUPPRESS"+"HELP" -NO_DEFAULT = ("NO", "DEFAULT") - -def _repr(self): - return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) -# </borrowed> - -NOTHING_FOUND = ("NOTHING","FOUND") - -def split_keys(keys): - """Returns list of keys resulting from keys argument. - - --- NO KEYS --- - >>> split_keys( None ) - [] - >>> split_keys( [] ) - [] - - --- STRINGS --- - >>> split_keys( "key1" ) - ['key1'] - >>> split_keys( "key1,key2" ) - ['key1', 'key2'] - >>> split_keys( "key1.key2" ) - ['key1', 'key2'] - >>> split_keys( "key1.key2,key3" ) - ['key1', 'key2', 'key3'] - - --- LISTS --- - >>> split_keys( ['key1'] ) # single item - ['key1'] - >>> split_keys( ['key1','key2'] ) # multiple items - ['key1', 'key2'] - - --- QUOTING --- - These tests check that quotes can be used to protect '.' and ','. - >>> split_keys( "'some.key1','some,key2'" ) # single ticks work - ['some.key1', 'some,key2'] - >>> split_keys( '"some,key1","some.key2"' ) # double ticks work - ['some,key1', 'some.key2'] - >>> split_keys( '"some,key1",some.key2' ) # must quote everything - Traceback (most recent call last): - ConfigParserError: Keys not quoted properly. Quotes must surround all keys. - >>> split_keys( "some,key1,'some.key2'" ) # must quote everything - Traceback (most recent call last): - ConfigParserError: Keys not quoted properly. Quotes must surround all keys. - >>> split_keys( "key1,'some.key2'.key3" ) # must quote everything - Traceback (most recent call last): - ConfigParserError: Keys not quoted properly. Quotes must surround all keys. - >>> split_keys('DEFAULT') - [] - >>> split_keys(['DEFAULT']) - [] - """ - if (keys is None) or (keys == 'DEFAULT') or (keys == ['DEFAULT']): - return [] - try: - keys = keys.replace('"',"'") - if "'" in keys: - keys = keys.strip("'").split("','") - for key in keys: - if "'" in key: - IMPROPER_QUOTES = "Keys not quoted properly. Quotes must surround all keys." - raise ConfigParserUserError(IMPROPER_QUOTES) - else: - keys = keys.replace('.',',').split(',') - except AttributeError: - pass - return keys - -def join_keys(keys,sep=','): - """ - >>> join_keys(['key1']) - 'key1' - >>> join_keys(['key1','key2']) - 'key1,key2' - >>> join_keys(['key1','key2'],'.') - 'key1.key2' - >>> join_keys(['key.1','key2'],'.') - "'key.1','key2'" - >>> join_keys(['key,1','key2'],'.') - "'key,1','key2'" - >>> join_keys([]) - 'DEFAULT' - """ - if not keys: - return 'DEFAULT' - mash = ''.join(keys) - if '.' in mash or ',' in mash: - quote = "'" - sep = quote + ',' + quote - return quote + sep.join(keys) + quote - return sep.join(keys) - -def split_paths(paths): - """Returns list of paths resulting from paths argument. - - --- NO KEYS --- - >>> split_paths( None ) - [] - >>> split_paths( [] ) - [] - - --- STRINGS --- - >>> split_paths( "path1" ) - ['path1'] - >>> split_paths( os.path.pathsep.join(["path1","path2"]) ) - ['path1', 'path2'] - >>> split_paths( "path.1,path.2" ) - ['path.1', 'path.2'] - - --- LISTS --- - >>> split_paths( ['path1'] ) # single item - ['path1'] - >>> split_paths( ['path1','path2'] ) # multiple items - ['path1', 'path2'] - """ - if paths is None: - return [] - try: - return paths.replace(',',os.path.pathsep).split(os.path.pathsep) - except AttributeError: - return paths - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# E X C E P T I O N S -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class ConfigParserError(Exception): - """Exception associated with the cfgparse module""" - pass - -class ConfigParserAppError(Exception): - """Exception due to application programming error""" - pass - -class ConfigParserUserError(Exception): - """Exception due to user error""" - pass - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# K E Y S -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class Keys(object): - """Prioritizes and stores default configuration keys. - - This class is used to store default configuration keys for an instance - of the Config class. The different sources of keys are supported (stored) - by the following methods of this class: - - add_cfg_keys -- configuration file specified default keys - add_cmd_keys -- command line option keys - add_env_keys -- environment variable keys - - The 'get' method returns a combined list of keys in the following order: - application keys (passed when calling 'get' method) - command line keys - environment variable keys - configuration file keys - a 'DEFAULT' key (always present) - - Setup: modify os module to fake out environment variable gets - >>> _getenv = os.getenv - >>> def getenv(variable,default): - ... if variable == 'VAR12': - ... return 'env1,env2' - ... elif variable == 'VAR3': - ... return 'env3' - ... elif variable == 'VAR4': - ... return 'env4' - ... else: - ... return default - >>> os.getenv = getenv - - Case 1: normal string lists of keys - >>> k = Keys() - >>> k.add_env_keys('VAR12,VAR3') # this has effect - >>> k.add_env_keys('VAR_NONE') # no effect - >>> k.add_cfg_keys('cfg1,cfg2') - >>> k.add_cmd_keys('cmd1.cmd2') - >>> k.add_env_keys('VAR12') # no effect (already read) - >>> k.get('app1,app2') - ['app1', 'app2', 'cmd1', 'cmd2', 'env1', 'env2', 'env3', 'cfg1', 'cfg2', 'DEFAULT'] - - Case 2: extend lists using other key input flavors just to make sure each - method uses split_keys() - >>> k.add_env_keys(['VAR4']) # this has effect - >>> k.add_cfg_keys(['cfg3']) - >>> k.add_cmd_keys(['cmd3']) - >>> k.get(['app']) - ['app', 'cmd1', 'cmd2', 'cmd3', 'env1', 'env2', 'env3', 'env4', 'cfg1', 'cfg2', 'cfg3', 'DEFAULT'] - - Teardown: restore os module - >>> os.getenv = _getenv - """ - - def __init__(self): - """Initialize Keys Instance""" - self.cmd_keys = [] - self.env_keys = [] - self.cfg_keys = [] - self.env_vars = [] - - def __repr__(self): - """Return string representation of object""" - return ','.join(self.get([])) - - def add_cmd_keys(self,keys): - """Store keys from command line interface - - keys -- list of keys (typically from the command line) to store. May - be a list of keys or a string with keys separated by commas. - Use any value which evaluates False when no keys. - """ - self.cmd_keys.extend(split_keys(keys)) - - def add_env_keys(self,variables): - """Store keys from invoking shell's environment variable - - variable -- (string) environment variable name from which to obtain - keys to store - """ - variables = split_keys(variables) - for variable in variables: - # only add keys from shell environment variable if we haven't already - if variable not in self.env_vars: - # save key variable name so we can't add same keys twice - self.env_vars.append(variable) - # if shell environment variable has a option save it - keys = os.getenv(variable,None) - if keys is not None: - self.env_keys.extend(split_keys(keys)) - - def add_cfg_keys(self,keys): - """Store keys from user's configuration file. - - keys -- list of keys (from user's configuration file) to store. May - be a list of keys or a string with keys separated by comma. - Use any value which evaluates False when no keys. - """ - self.cfg_keys.extend(split_keys(keys)) - - def get(self,keys=None): - """Return prioritized list of stored configuration keys - - keys -- list of differentiator keys. May be a list of keys or a string - with keys separated by commas. Use any valid which evaluates - False when no keys. - """ - keys = split_keys(keys) - return (keys + self.cmd_keys + self.env_keys + self.cfg_keys + - ['DEFAULT']) - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N V A L U E -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class Value(object): - """Used to store option settings in the blended option dictionary. - Needed to be able to tie the option setting back to the configuration - file for better error reporting and for round trip get/set/write - capability.""" - def __init__(self,value,parent,section_keys): - """Initializes instance.""" - self.value = value - self.parent = parent - self.section_keys = section_keys - - def set(self,value): - """Sets option setting to 'value' argument passed in. - - By default configuration file parsers to do not support round trip. - If they do they should subclass Value() and override this method - """ - self.value = value - - def get_roots(self): - return ["File: %s" % self.parent.get_filename(), - "Section: [%s]" % join_keys(self.section_keys,'.')] - - def __str__(self): - """Returns string representation of the setting.""" - return str(self.value) - - __repr__ = _repr - - def get(self): - """Returns the option setting.""" - return self.value - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class Option(object): - """Options added to configuration parser are an instance of this class.""" - - def __init__(self,**kwargs): - """Instance initializer. - Arguments: - kwargs -- dictionary with keys parser,name,type,default,help,check,keys, - choices (see add_option of OptionContainer) - """ - self.__dict__.update(kwargs) - if self.dest is None: - self.dest = self.name - if self.type not in self.conversions: - TYPE_DOES_NOT_EXIST = "type '%s' is not valid" % self.type - raise ConfigParserAppError(TYPE_DOES_NOT_EXIST) - # help cross reference for options partnered with OptionParser - self._help_xref = "" - self.note = None - - def __str__(self): - """Returns string representation of the option.""" - return self.name - - __repr__ = _repr - - def add_note(self,note): - """Adds 'note' argument text to configuration parser help text and - to error messages associated with this option.""" - self.parser.add_note(note) - self.note = note - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Get - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def _get(self,keys): - # Read any pending configuration files at the top level in order to - # pick up user's default keys in those files. - self.parser.parse_pending_cfg([]) - - keys = split_keys(keys) + self.keys - keys = self.parser.keys.get(keys) - - # Get the option's dictionary from the configuration parser so that - # any pending configuration files that are needed are read. - option = self.parser.get_option_dict(self.name,keys) - - # When keys are given as an argument, we don't have the constraints - # of an exact match like a section. Instead we use the default - # key list (highest priority key first) to walk the option - # dictionary. At each level of the dictionary we look for the - # highest priority key and if it exists we move down a level - # otherwise the remaining keys are checked in order of priority. - - def walk_option(option): - if option.__class__ is dict: - for key in keys: - if key in option: - v = walk_option(option[key]) - if v.__class__ is not dict: - return v - if isinstance(option,Value): - return option - else: - return NOTHING_FOUND - - return walk_option(option) - - def get(self,keys=[],errors=None): - """Returns option value associated with 'keys' argument or options - option value using 'keys' argument (in combination with other keys). - - keys -- name of keys to obtain option value from - - Note: - If option is partnered with an optparse option and that option - is present, the optparse option will take priority and be returned. - """ - - warnings = [] - option = NOTHING_FOUND - - def convert(value,option_help): - # Do conversion to the type application specified - value,warning = self.conversions[self.type](self,value) - # Do final check using application check function if supplied - if not warning and self.check is not None: - value,warning = self.check(value) - if warning: - try: - warnings.extend(option_help.get_roots()) - except AttributeError: - warnings.append(option_help) - warnings.append(warning) - return value - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Priority 1: Get option from optparse partner (command line) - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - if self.dest in self.parser.optpar_option_partners: - option = getattr(self.parser.optparser_options,self.dest,None) - if option is None: - option = NOTHING_FOUND - else: - option = convert(option,'command line option') - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Priority 2: Get a default option - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - if option is NOTHING_FOUND and not warnings: - - option = self._get(keys) - - if option is not NOTHING_FOUND: - value = option.get() - option = convert(value,option) - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Priority 3: Use default specified when adding the option - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - if option is NOTHING_FOUND and not warnings: - if self.default is not NO_DEFAULT: - option = self.default - else: - warnings.append('No valid default found.') - keys = split_keys(keys) + self.keys - keys = self.parser.keys.get(keys) - warnings.append('keys=%s' % ','.join(keys)) - - if warnings: - lines = ['Option: %s' % self.name] + warnings - lines = '\n'.join(lines) + '\n' - if errors is not None: - errors.append(lines) - else: - # not coming back - self.parser.write_errors([lines]) - - return option - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Conversions - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def convert_choice(self,value): - if value in self.choices: - return value,None - else: - choices = str(self.choices).strip('[]()') - warning = "Invalid choice '%s', must be one of: %s" % (value,choices) - return None,warning - - def convert_complex(self,value): - try: - return complex(value),None - except ValueError: - return None,"Cannot convert '%s' to a complex number" % (value) - - def convert_float(self,value): - try: - return float(value),None - except ValueError: - return None,"Cannot convert '%s' to a float" % (value) - - def convert_int(self,value): - try: - return int(value),None - except ValueError: - return None,"Cannot convert '%s' to an integer" % (value) - - def convert_long(self,value): - try: - return long(value),None - except ValueError: - return None,"Cannot convert '%s' to a long" % (value) - - def convert_string(self,value): - try: - return str(value),None - except ValueError: - return None,"Cannot convert '%s' to a string" % (value) - - def convert_nothing(self,value): - return value,None - - conversions = { - 'choice' : convert_choice, - 'complex' : convert_complex, - 'float' : convert_float, - 'int' : convert_int, - 'long' : convert_long, - 'string' : convert_string, - None : convert_nothing, - } - - def set(self,value,cfgfile=None,keys=None): - value = str(value) - if cfgfile: - if keys is not None: - keys = split_keys(keys) - else: - keys = self.keys - cfgfile.set_option(self.name,value,keys,self.help) - else: - keys = split_keys(keys) + self.keys - keys = self.parser.keys.get(keys) - - option = self._get(keys) - - if option is NOTHING_FOUND: - NOTHING_TO_SET = '\n'.join([ - 'ERROR: No option found', - 'option name: %s' % self.name, - 'keys: %s' % keys]) - raise ConfigParserUserError(NOTHING_TO_SET) - else: - option.set(value) - cfgfile = option.parent - return cfgfile - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N C O N T A I N E R B A S E C L A S S E S -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class OptionContainer(object): - - OptionClass = Option - - def __init__(self,description): - self.option_list = [] - self.set_description(description) - - def set_description(self, description): - self.description = description - - def get_description(self): - return self.description - - def add_option(self,name,help=None,type=None,choices=None,dest=None,metavar=None,default=NO_DEFAULT,check=None,keys=None): - """ - name -- configuration file option name (used same as optparse) - type -- choices similar to optparse (used same as optparse) - default -- default value (used same as optparse) - help -- help string (used same as optparse) - dest -- option database attribute name (used same as optparse) - check -- check function - keys -- name of keys to obtain option from - choices -- list of choices (used same as optparse) - metavar -- FUTURE - """ - if dest is None: - dest = name - - kwargs = { - 'parser' : self.parser, - 'name' : name, - 'type' : type, - 'help' : help, - 'dest' : dest, - 'check' : check, - 'keys' : split_keys(keys), - 'choices' : choices, - 'metavar' : metavar, - 'default' : default} - - option = self.OptionClass(**kwargs) - self.parser.master_option_list.append(option) - self.parser.master_option_dict[dest] = option - self.option_list.append(option) - return option - - # <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> - - def format_option_help(self, formatter): - if not self.option_list: - return "" - result = [] - for option in self.option_list: - if not option.help == SUPPRESS_HELP: - result.append(formatter.format_option(option)) - return "".join(result) - - def format_description(self, formatter): - return formatter.format_description(self.get_description()) - - def format_help(self, formatter): - result = [] - if self.description: - result.append(self.format_description(formatter)) - if self.option_list: - result.append(self.format_option_help(formatter)) - return "\n".join(result) - # </borrowed> - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N G R O U P -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class OptionGroup(OptionContainer): - def __init__(self,parser,title,description): - OptionContainer.__init__(self,description) - self.parser = parser - self.title = title - - # <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> - def set_title (self, title): - self.title = title - - # -- Help-formatting methods --------------------------------------- - - def format_help (self, formatter): - result = formatter.format_heading(self.title) - formatter.indent() - result += OptionContainer.format_help(self, formatter) - formatter.dedent() - return result - # </borrowed> - - -class ConfigFile(object): - def __init__(self,cfgfile,content,keys,parent,parser): - - p,n = os.path.split(cfgfile) - try: - p = os.path.join(parent.path,p) - except AttributeError: - pass - p = os.path.abspath(p) - - self.path = p - self.filename = n - self.content = content - self.underkeys = keys - self.parent = parent - self.parser = parser - - self.parsed = False - - def get_filename(self): - return os.path.join(self.path,self.filename) - - def __str__(self): - return os.path.join(self.path,self.filename) - - __repr__ = _repr - - def get_as_str(self): - content = self.content - if content is None: - cfgfile = os.path.join(self.path,self.filename) - f = open(cfgfile) - content = f.read() - f.close() - else: - try: - content = self.content.read() - except AttributeError: - pass - return content - - def parse_if_unparsed(self): - if not self.parsed: - - # The parent is important in that it is used error messages but more - # importantly when getting ready to read a configuration script we - # must set the current directory of the parent so relative path - # specification of any configuration file works out. - try: - parent_path = self.parent.path - except AttributeError: - parent_path = os.getcwd() - - # Save so we can restore after we are done - cwd = os.getcwd() - - # Make sure file is present - cfgfile = os.path.join(self.path,self.filename) - if not self.content and not os.path.exists(cfgfile): - lines = ["File not found: '%s'" % cfgfile] - # remember, top level configuration file parent is just the - # current working directory when ConfigParser was instantiated - # FUTURE parent info in here too - FILE_NOT_FOUND = '\n'.join(lines) - raise ConfigParserUserError(FILE_NOT_FOUND) - - # Change the current working directory to where the configuration - # script is (to accomodate Python configuraton scripts so that it - # can use os.getcwd() to determine its location). - os.chdir(self.path) - - self.parse() - - # Restore - os.chdir(cwd) - - # Mark it as done so it isn't parsed twice - self.parsed = True - - -class ConfigFilePy(ConfigFile): - - def parse(self): - - underkeys = self.underkeys - parser = self.parser - - # Parsing can be easy! - options = {} - if self.content is None: - cfgfile = os.path.join(self.path,self.filename) - execfile(cfgfile,options) - else: - exec self.get_as_str() in options - - # Update the keys. "KEYS_VARIABLE" option used to specify the - # environment variable that holds additional default keys, if - # present get keys from the environment using it. If reading - # configuration file that is being included under a key, don't - # bother with its keys because it would get too confusing. - if not underkeys: - try: - parser.keys.add_env_keys(options['KEYS_VARIABLE']) - del options['KEYS_VARIABLE'] - except KeyError: - pass - try: - parser.keys.add_cfg_keys(options['KEYS']) - del options['KEYS'] - except KeyError: - pass - - try: - CONFIG_FILES = options['CONFIG_FILES'] - del options['CONFIG_FILES'] - except KeyError: - CONFIG_FILES = None - - def merge_option(value,section_keys): - if value.__class__ is dict: - for key in value: - merge_option(value[key],section_keys+[key]) - else: - valueobj = Value(value,self,section_keys) - parser.merge_option(name,valueobj,underkeys+section_keys) - - # Merge all options that don't start with "_" into the options - for name,value in options.items(): - if not name.startswith('_'): - merge_option(value,[]) - - # Process the "CONFIG_FILES" option used to merge in configuration - # from other files. Users specify a comma separated (string) - # listing of configuration file names or a dictionary of (and - # optionally - of dictionaries of) file name strings. - def add_files(value,under): - if isinstance(value,dict): - for k,v in value.iteritems(): - add_files(v,under+[k]) - else: - for cf in split_paths(value): - parser.add_file(cf,keys=under,parent=self) - - if CONFIG_FILES: - add_files(CONFIG_FILES,underkeys) - -class ConfigFileIni(ConfigFile): - - def _read(self): - - try: - self._already_read - return - except AttributeError: - self._already_read = True - - underkeys = self.underkeys - option_parser = self.parser - marker_fmt = '{{{%s-(?P<id>\d+)}}}' - _self = self - - class BaseClass(object): - def __init__(self,matchobj): - self.text = matchobj.group('text') - self.num = len(self.objects) - self.objects.append(self) - def restore(self): - return self.text - - class Line(BaseClass): - pass - - class Block(BaseClass): - objects = [] - find_re = re.compile('<b>(?P<text>.*?)</b>',re.DOTALL) - - class Verbatim(BaseClass): - objects = [] - find_re = re.compile('<v>(?P<text>.*?)</v>',re.DOTALL) - - class Comment(BaseClass): - objects = [] - find_re = re.compile('(?P<text>[ \t]*[#;].*)') - - class OptionPair(Value): - # OptionPair must subclass Value() because it is being submitted into the - # parser options dictionary (all options in the dictionary must be a - # subclass of Value). All things equal this would subclass BaseClass() - # but it implements all that functionality anyways. - objects = [] - find_re = re.compile('(?P<indent>[ \t]*)(?P<name>.+?)(?P<sep>\s*=\s*)(?P<value>.*)') - section = None - def __init__(self,matchobj): - section_keys = OptionPair.section.keys - # get the name right - name = matchobj.group('name') - self.extended_name = name - if '[' in name: - name,keys = name.strip(']').split('[') - section_keys = section_keys + split_keys(keys) - self.name = name - self.indent = matchobj.group('indent') - self.sep = matchobj.group('sep') - value = matchobj.group('value') - self.valueplus = value - try: - value = restore(Comment,value) - value = restore(Block,value) - value = restore(Verbatim,value) - self.linenum = re.findall(marker_fmt % 'Line',value)[0] - except IndexError: - self.linenum = 'new' - self.num = len(self.objects) - self.objects.append(self) - self.section = OptionPair.section - self.section.options[self.extended_name] = self - # The following are needed for Value() functionality - self.parent = _self - self.section_keys = section_keys - # self.value will be set later (can't now because value may contain - # interpolations which cannot be expanded until all options are read - # for this section - def get_roots(self): - return Value.get_roots(self) + ["Line: %s" % self.linenum] - def set(self,value): - Value.set(self,value) - value = [value] - def hit(matchobj): - value.append(matchobj.group(0)) - regexp = re.compile(marker_fmt % 'Comment') - regexp.sub(hit,self.valueplus) - self.valueplus = ''.join(value) - def restore(self): - return ''.join([self.extended_name,self.sep,self.valueplus]) - def expand(self,levellist=[]): - levellist = levellist + [self.name] - if len(levellist) > 10: - lines = self.get_roots() - lines.append("Interpolation: %s" % ' << '.join(levellist)) - lines.append("Maximum nesting level exceeded.") - MAX_NESTING_LEVEL_EXCEEDED = '\n' + '\n'.join(lines) - raise ConfigParserUserError(MAX_NESTING_LEVEL_EXCEEDED) - try: - return self.value - except AttributeError: - pass - value = remove(Comment,self.valueplus) - value = remove(Line,value) - value = restore(Block,value.strip(' \t')) - value = remove(Line,value) - # @future [ABSPATH] value = value.replace('%(ABSPATH(','%_(ABSPATH(') - regexp = re.compile('%\((?P<name>.*?)\)s') - def hit(matchobj): - name = matchobj.group('name') - try: - return self.section.options[name].expand(levellist) - except KeyError: - try: - return Section.default.options[name].expand(levellist) - except KeyError: - lines = self.get_roots() - lines.append("Interpolation: %s" % ' << '.join(levellist+[name])) - lines.append("'%s' not found in section or in [DEFAULT]." % name) - INTERPOLATION_ERROR = '\n' + '\n'.join(lines) - raise ConfigParserUserError(INTERPOLATION_ERROR) - value = regexp.sub(hit,value) - # @future [ABSPATH] regexp = re.compile(r'%_\(ABSPATH\((.*?)\)\)s') - # def hit(matchobj): - # return os.path.abspath(matchobj.group(1)) - # value = regexp.sub(hit,value) - self.value = remove(Line,restore(Verbatim,value)) - return self.value - - class Section(BaseClass): - objects = [] - find_re = re.compile('(?P<text>\n\[(?P<name>.*?)\].*?(?=\n\[))',re.DOTALL) - default = None - def __init__(self,matchobj): - self.options = {} - name = matchobj.group('name') - self.name = name - keys = split_keys(name) - if keys == ['DEFAULT']: - keys = [] - if not keys: - Section.default = self - self.keys = keys - OptionPair.section = self - self.text = collapse(OptionPair,matchobj.group(0)) - self.num = len(self.objects) - self.objects.append(self) - def add_option(self,name,value,help): - if help: - helplines = textwrap.fill(help,77).split('\n') - lines = ['# %s' % line for line in helplines] - else: - lines = [] - lines.append('%s = %s' % (name,value)) - OptionPair.section = self - block = collapse(Comment,'\n'+'\n'.join(lines)) - self.text += collapse(OptionPair,block) - option = OptionPair.objects[-1] - underkeys = _self.underkeys + option.section_keys - if not underkeys: - underkeys = ['DEFAULT'] - # submit new value to parser - _self.parser.merge_option(name,option,underkeys) - return option - - def collapse(SubClass,text): - marker_fmt = '{{{%s-%%d}}}' % (SubClass.__name__) - def hit(matchobj): - return marker_fmt % SubClass(matchobj).num - return SubClass.find_re.sub(hit,text) - - def remove(SubClass,text): - return re.compile(marker_fmt % SubClass.__name__).sub('',text) - - def restore(SubClass,text): - def hit(matchobj): - return SubClass.objects[int(matchobj.group('id'))].restore() - return re.compile(marker_fmt % SubClass.__name__).sub(hit,text) - - text = self.get_as_str() - lines = [] - i = 1 - for line in text.split('\n'): - lines.append(line + ('{{{Line-%d}}}' % i)) - i += 1 - text = '\n'.join(lines) - text = '\n[DEFAULT]\n#_START_MARKER_\n%s\n[' % text - text = collapse(Block,text) - text = collapse(Verbatim,text) - text = collapse(Comment,text) - text = collapse(Section,text) - - self._OptionPair = OptionPair - self._Line = Line - self._Comment = Comment - self._Block = Block - self._Verbatim = Verbatim - self._Section = Section - self._collapse = collapse - self._restore = restore - self._remove = remove - self.text = text - - def parse(self): - self._read() - for option in self._OptionPair.objects: - name = option.name - underkeys = self.underkeys + option.section_keys - value = option.expand() - if name == '<include>': - for cf in split_paths(value): - self.parser.add_file(cf,keys=underkeys,parent=self) - continue - if not underkeys: - if name == '<keys>': - self.parser.keys.add_cfg_keys(split_keys(value)) - continue - if name == '<keys_variable>': - self.parser.keys.add_env_keys(value) - continue - underkeys = ['DEFAULT'] - # call expand method to get .value attribute set - self.parser.merge_option(name,option,underkeys) - - def set_option(self,name,value,keys=None,help=None): - self._read() - value = str(value) - keys = split_keys(keys) - found = False - for option in self._OptionPair.objects: - if name==option.name and keys == option.section_keys: - option.set(value) - found = True - if not found: - Section = self._Section - for section in Section.objects: - if keys == section.keys: - found = True - section.add_option(name,value,help) - if not found: - block = '\n\n[%s]\n\n[\n' % join_keys(keys,'.') - self.text = '%s%s' % (self.text[:-2],self._collapse(Section,block)) - section = self._Section.objects[-1] - section.add_option(name,value,help) - - def write(self,file): - self._read() - - restore = self._restore - content = self.text - content = restore(self._Section,content) - content = restore(self._OptionPair,content) + '\n' - content = restore(self._Comment,content) - content = restore(self._Block,content) - content = restore(self._Verbatim,content) - content = self._remove(self._Line,content) - content = content.split('\n#_START_MARKER_\n')[1][:-3] - - try: - file.write(content) - except AttributeError: - f = open(file,'w') - f.write(content) - f.close() - - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# C O N F I G U R A T I O N P A R S E R -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class ConfigParser(OptionContainer): - - KeysClass = Keys - OptionGroupClass = OptionGroup - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Initializer - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def __init__(self,description=None,allow_py=False,formatter=None,exception=False): - """ - description -- Introductory text placed above configuration option - help text. - allow_py -- Set to True to allow Python based configuraton files - to be executed. Defaults to False. Enabling this feature - poses a potential security hole for your application. - formatter -- Controls configuration option help text style. Set - to either the IndentedHelpFormatter or TitledHelpFormatter - classes found in cfgparse module (or a subclass of either). - exception -- set to True to raise ConfigParserUserError exception - when configuration error occurs (due to user error). Omitting - or setting to False will write error message to sys.stderr. - Set to an exception class to raise that exception when a user - configuration file error occurs. - """ - OptionContainer.__init__(self,description) - - self.exception = exception - - # Needed because shares same base class as an option group - # (option group constructor initializes parser using an arg). - self.parser = self - - self.option_dicts = {} - - self.option_groups = [] - self.optpar_option_partners = {} - - self.master_option_list = [] - self.master_option_dict = {} - - if formatter is None: - formatter = IndentedHelpFormatter() - self.formatter = formatter - self.formatter.set_parser(self) - - self.notes = [] - - # Set up database of option selection keys - self.keys = self.KeysClass() - - # Create database to store information on those configuration files - # to be read later (configuration files under keys are not read until - # all of the keys in which it is under are active. - self._pending = [] - - # Since it introduces a security risk, only allow Python based - # configuration files if application explicitly sets this True. - self.allow_python_cfg = allow_py - - self.optparse_dests = {} - - def add_optparse_keys_option(self,option_group,switches=('-k','--keys'),dest='cfgparse_keys',help="Comma separated list of configuration keys."): - """Adds configuration file keys list option to optparse object.""" - self.optparse_dests['keys'] = dest - option_group.add_option(dest=dest,metavar='LIST',help=help,*switches) - - def add_optparse_files_option(self,option_group,switches=('--cfgfiles',),dest='cfgparse_files',help="Comma separated list of configuration files."): - """Adds configuration file list option to optparse object.""" - self.optparse_dests['files'] = dest - option_group.add_option(dest=dest,metavar='LIST',help=help,*switches) - - def add_optparse_help_option(self,option_group,switches=('--cfghelp',),dest='cfgparse_help',help="Show configuration file help and exit."): - """Adds configuration file help option to optparse object.""" - self.optparse_dests['help'] = dest - option_group.add_option(dest=dest,action='store_true',help=help,*switches) - - def add_env_file(self,var,keys=[]): - """Adds configuration file specified by environment variable setting. - Arguments: - var -- name of environment variable holding configuration file name - keys -- section key list to place configuration file options settings under - """ - # Add configuration files specified by an environment variable - # if application script specified it. (Don't read right away, - # rather hold them in pending database until they are needed - # because adding options causes option_dicts to be set with a - # default for the option destination. - f = os.getenv(var,None) - if f: - f = self.add_file(cfgfile=f,keys=keys) - else: - f = None - return f - - def get_option(self,dest): - """Returns option object previously added. - Arguments: - dest -- destination attribute name of option - """ - opt = self.master_option_dict.get(dest,None) - if opt is None: - OPTION_NOT_FOUND = '\n'.join([ - 'ERROR: No option found', - 'option dest: %s' % dest]) - raise ConfigParserAppError(OPTION_NOT_FOUND) - return opt - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Adding Option Groups (adding options handled by base class) - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def add_option_group(self,title,description=None): - option_group = self.OptionGroupClass(self,title,description) - self.option_groups.append(option_group) - return option_group - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Pending Configuration - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def get_option_dict(self,name,keys): - self.parse_pending_cfg(keys) - return self.option_dicts.get(name,NOTHING_FOUND) - - def parse_pending_cfg(self,keys=[],read_all=False): - """ - read_all -- Set to True to read all configuration files up front. - Defaults to reading "on the fly" as the configuration files are - needed.""" - # Read any pending configuration files that could possibly effect the - # setting about to be retrieved. Note, it was decided that if the - # pending configuration file's under keys are all in the key list - # computed above it will be installed right away. The other - # alternative is to try retrieving the setting with the highest - # priority key alone (first reading any pending configuration files - # that are under that key), then if that fails try retrieving the - # setting with the top two highest priority keys (again first reading - # any pending configuration files that are under either or both of the - # keys), and repeating this process for each key in the list until a - # setting is found. This would have the benefit of only reading - # pending configuration files when it is absolutely necessary but at - # cost of performance and more difficult to explain how it works. - keys = split_keys(keys) - d = [] - while self._pending: - underkeys,cfgfileobj = self._pending.pop(0) - underkeys = split_keys(underkeys) - parse_it = read_all - if not parse_it: - for key in underkeys: - if key not in keys: - d.append((underkeys,cfgfileobj)) - break - else: - parse_it = True - if parse_it: - cfgfileobj.parse_if_unparsed() - self._pending = d - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - FileClasses = {'py' : ConfigFilePy, 'ini': ConfigFileIni, 'default' : ConfigFileIni} - - def add_file(self,cfgfile=None,content=None,type=None,keys=[],parent=None): - """ - Adds configuration file to parser. Note, file is not read until parse - or get_option method is called (and even then it may not be read if any - keys in the keys list are not in the keys being used to parse). - - cfgfile -- either the filename or a file stream, defaults to None (see table below) - content -- either file contents string or file stream, defaults to None (see table below) - type -- set to either 'py', 'ini', or None (default) to control file parser used. - When set to None, filename extension is used to determine parser to use. 'py' - interprets the file as Python code. Otherwise the 'ini' style parser is used. - keys -- key list that all options in the configuration file will - be placed under when the file is read. - parent -- Not intended to be used by the public - - The following table summarizes the legal combinations of the cfgfile and - arguments and resulting file name and contents utilized. - - cfgfile content result (when configuration is parsed) - -------- ------- ----------------------------------------------------------- - filename None file is opened and contents read - stream None stream contents are read, filename is stream name attribute - filename stream stream contents are read, filename is cfgfile argument - filename string both filename and content are used as is - """ - - if isinstance(cfgfile,str): - n = cfgfile - c = content - elif hasattr(cfgfile,'name'): - n = cfgfile.name - c = cfgfile - elif isinstance(content,str): - n = 'heredoc' - c = content - elif hasattr(content,'read'): - n = 'stream' - c = content - else: - ARGUMENT_CONFICT = "Illegal combination of 'cfgfile' and 'content' arguments" - raise ConfigParserAppError(ARGUMENT_CONFICT) - - uk = split_keys(keys) - - if type is None: - # calculate type (lower case extension) - type = os.path.splitext(n)[1][1:].lower() - - if type == 'py' and not self.allow_python_cfg: - return None - - FileClass = self.FileClasses.get(type) - if FileClass is None: - FileClass = self.FileClasses['default'] - - fileobj = FileClass(cfgfile=n,content=c,keys=uk,parent=parent,parser=self) - self._pending.append((uk,fileobj)) - return fileobj - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Read Configuration File Utilities - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def merge_option(self,name,value,keys): - """ Merge value (under keys) into options dictionary. - - name -- option name - value -- value to assign option (may be a dictionary or a option) - keys -- list of keys to place value in options dict under - """ - - # Add the option name to the key list so we can start walking at - # the top level of the options dictionary. - keys = [name] + keys - option_dicts = self.option_dicts - # Move through the options dictionary using the keys we are - # supposed to place the value under creating dictionaries and keys - # that aren't already present. - for key in keys[:-1]: - if key in option_dicts: - if option_dicts[key].__class__ is not dict: - option_dicts[key] = dict(DEFAULT=option_dicts[key]) - else: - option_dicts[key]={} - option_dicts = option_dicts[key] - option_dicts[keys[-1]] = value - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Option Parsing (not configuration file parsing) - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def write_errors(self,errors): - CFGPARSE_USER_ERROR = '\n' + '\n'.join(errors) - if not self.exception: - sys.stderr.write("ERROR: Configuration File Parser\n") - sys.stderr.write(CFGPARSE_USER_ERROR) - self.system_exit() - else: - if self.exception is True: - UserError = ConfigParserUserError - else: - UserError = self.exception - raise UserError(CFGPARSE_USER_ERROR) - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Option Parsing - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def parse(self,optparser=None,args=None,read_all=False): - """Partners the option parser and the configuration parser together - read_all -- Set to True to read all configuration files up front. - Defaults to reading "on the fly" as the configuration files are - needed. - """ - - if optparser: - # Marry up type, help, choices attributes between option parser and - # configuration parser options. - self.marry_options(optparser) - - # Parse command line arguments - options, args = optparser.parse_args(args) - self.optparser_options = options - - # generate help if requested from the command line - help_dest = self.optparse_dests.get('help') - if help_dest and getattr(options,help_dest): - self.print_help() - self.system_exit() - - # add command line keys - keys_dest = self.optparse_dests.get('keys') - if keys_dest: - self.keys.add_cmd_keys(getattr(options,keys_dest)) - - # add command line configuration files (must hold it as other configuration - # files may be pending that should be read first) - files_dest = self.optparse_dests.get('files') - if files_dest: - cfgfiles = getattr(options,files_dest) - for cf in split_paths(cfgfiles): - self.add_file(cfgfile=cf,keys=[]) - else: - class ConfigOptions(object): - pass - options = ConfigOptions() - - self.parse_pending_cfg([],read_all) - - # Go through each option in the configuration options and add them - # to options object. - errors = [] - for option in self.master_option_list: - setattr(options,option.dest,option.get(errors=errors)) - if errors: - self.write_errors(errors) - - if optparser: - return options, args - else: - return options - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def marry_options(self,optparser): - # create mapping: dest -> [optpar options] - optpar_lookup = {} - for option in optparser.option_list: - if option.dest: - optpar_lookup.setdefault(option.dest,[]).append(option) - for group in optparser.option_groups: - for option in group.option_list: - if option.dest: - optpar_lookup.setdefault(option.dest,[]).append(option) - self.optpar_option_partners = optpar_lookup - - # we are guarenteed no duplicate destinations with a configuration parser - for cfgpar_option in self.master_option_list: - optpar_options = optpar_lookup.get(cfgpar_option.dest,[]) - for optpar_option in optpar_options: - for attrname in ['metavar','type','choices','help']: - cfgpar_attr = getattr(cfgpar_option,attrname) - optpar_attr = getattr(optpar_option,attrname) - if cfgpar_attr is None: - cfgpar_attr = optpar_attr - setattr(cfgpar_option,attrname,cfgpar_attr) - if cfgpar_option.help == SUPPRESS_HELP: - continue - try: - # remove anything we've added previously - optpar_option.help = optpar_option.help.replace(optpar_option._cfgparse_help,'') - except AttributeError: - # must not have modified it previously - pass - help = " See also '%s' option in configuration file help." % (cfgpar_option.name) - if not optpar_option.help: - help = help.strip() - optpar_option.help = '' - optpar_option.help = optpar_option.help + help - optpar_option._cfgparse_help = help - if cfgpar_option.help == SUPPRESS_HELP: - continue - if cfgpar_option.help is None: - cfgpar_option.help = '' - try: - # remove anything we've added previously - cfgpar_option.help = cfgpar_option.help.replace(cfgpar_option._help_xref,'') - except AttributeError: - # must not have modified it previously - pass - switches = '/'.join([str(o) for o in optpar_options]) - if switches: - help = " See also '%s' command line switch." % (switches) - if not cfgpar_option.help: - help = help.strip() - cfgpar_option.help = cfgpar_option.help + help - cfgpar_option._help_xref = help - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def marry_attribute(self,attrname,cfgpar_option,optpar_option): - cfgpar_attr = getattr(cfgpar_option,attrname) - optpar_attr = getattr(optpar_option,attrname) - if cfgpar_attr is None: - cfgpar_attr = optpar_attr - setattr(cfgpar_option,attrname,cfgpar_attr) - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Help - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - # <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> - def format_option_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - formatter.store_option_strings(self) - result = [] - result.append(formatter.format_heading(_("Configuration file options"))) - formatter.indent() - if self.option_list: - result.append(OptionContainer.format_option_help(self, formatter)) - result.append("\n") - for group in self.option_groups: - result.append(group.format_help(formatter)) - result.append("\n") - formatter.dedent() - # Drop the last "\n", or the header if no options or option groups: - return "".join(result[:-1]) - - def format_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - result = [] - if self.description: - result.append(self.format_description(formatter) + "\n") - result.append(self.format_option_help(formatter)) - return "".join(result) - - def print_help(self, file=None): - """print_help(file : file = stdout) - - Print an extended help message, listing all options and any - help text provided with them, to 'file' (default stdout). - """ - if file is None: - file = sys.stdout - - file.write(self.format_help()) - - if self.notes: - file.write('\nNotes:\n%s\n'%'\n'.join(self.notes)) - - # </borrowed> - - def add_note(self,note): - self.notes.append(note) - - def system_exit(self): - sys.exit() - - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() - diff --git a/synthesis/cfgparse3.py b/synthesis/cfgparse3.py deleted file mode 100644 index c4651521..00000000 --- a/synthesis/cfgparse3.py +++ /dev/null @@ -1,1773 +0,0 @@ -# -*- coding: utf-8 -*- -"""cfgparse - a powerful, extensible, and easy-to-use configuration parser. - -By Dan Gass <dan.gass@gmail.com> - -If you have problems with this module, please file bugs through the Source -Forge project page: - http://sourceforge.net/projects/cfgparse -""" - -# @future use option note when get error -# @future print file/section/linenumber information when checks fail -# @future - check type='choice' and choices=[] one must have the other -# @future -- do we have a command line --cfgcheck option that expands all configuration and checks all possible keys? - -__version__ = "1.3" - -__all__ = [] - -__copyright__ = """ -Copyright (c) 2004 by Daniel M. Gass. All rights reserved. -Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved. -Copyright (c) 2002-2004 Python Software Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import sys -if sys.version_info > (3,0): - import configparser as _ConfigParser -else: - import ConfigParser as _ConfigParser -import io -import os -import re -import textwrap - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# U T I L I T Y -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -# <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> -try: - from gettext import gettext as _ -except ImportError: - _ = lambda arg: arg - -class HelpFormatter: - """ - Abstract base class for formatting option help. ConfigParser - instances should use one of the HelpFormatter subclasses for - formatting help; by default IndentedHelpFormatter is used. - - Instance attributes: - parser : OptionParser - the controlling OptionParser instance - indent_increment : int - the number of columns to indent per nesting level - max_help_position : int - the maximum starting column for option help text - help_position : int - the calculated starting column for option help text; - initially the same as the maximum - width : int - total number of columns for output (pass None to constructor for - this value to be taken from the $COLUMNS environment variable) - level : int - current indentation level - current_indent : int - current indentation level (in columns) - help_width : int - number of columns available for option help text (calculated) - default_tag : str - text to replace with each option's default value, "%default" - by default. Set to false value to disable default value expansion. - option_strings : { Option : str } - maps Option instances to the snippet of help text explaining - the syntax of that option, e.g. "option=VALUE" - """ - - NO_DEFAULT_VALUE = "none" - - def __init__(self, - indent_increment, - max_help_position, - width, - short_first): - self.parser = None - self.indent_increment = indent_increment - self.help_position = self.max_help_position = max_help_position - if width is None: - try: - width = int(os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width -= 2 - self.width = width - self.current_indent = 0 - self.level = 0 - self.help_width = None # computed later - self.short_first = short_first - self.default_tag = "%default" - self.option_strings = {} - self._short_opt_fmt = "%s %s" - self._long_opt_fmt = "%s=%s" - - def set_parser(self, parser): - self.parser = parser - - def indent(self): - self.current_indent += self.indent_increment - self.level += 1 - - def dedent(self): - self.current_indent -= self.indent_increment - assert self.current_indent >= 0, "Indent decreased below 0." - self.level -= 1 - - def format_usage(self, usage): - raise NotImplementedError("subclasses must implement") - - def format_heading(self, heading): - raise NotImplementedError("subclasses must implement") - - def format_description(self, description): - if not description: - return "" - desc_width = self.width - self.current_indent - indent = " "*self.current_indent - return textwrap.fill(description, - desc_width, - initial_indent=indent, - subsequent_indent=indent) + "\n" - - def expand_default(self, option): - if self.parser is None or not self.default_tag: - return option.help - - try: - default_value = option.default - except AttributeError: - default_value = None - - if default_value is NO_DEFAULT or default_value is None: - default_value = self.NO_DEFAULT_VALUE - - return option.help.replace(self.default_tag, str(default_value)) - - def format_option(self, option): - # The help for each option consists of two parts: - # * the opt strings and metavars - # eg. ("option=VALUE") - # * the user-supplied help string - # eg. ("turn on expert mode", "read data from FILENAME") - # - # If possible, we write both of these on the same line: - # option=VALUE explanation of some option - # - # But if the opt string list is too long, we put the help - # string on a second line, indented to the same column it would - # start in if it fit on the first line. - # thisoption=WAY_TO_LONG - # explanation of the long option - result = [] - opts = self.option_strings[option] - opt_width = self.help_position - self.current_indent - 2 - if len(opts) > opt_width: - opts = "%*s%s\n" % (self.current_indent, "", opts) - indent_first = self.help_position - else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) - indent_first = 0 - result.append(opts) - if option.help: - help_text = self.expand_default(option) - help_lines = textwrap.wrap(help_text, self.help_width) - result.append("%*s%s\n" % (indent_first, "", help_lines[0])) - result.extend(["%*s%s\n" % (self.help_position, "", line) - for line in help_lines[1:]]) - elif opts[-1] != "\n": - result.append("\n") - return "".join(result) - - def store_option_strings(self, parser): - self.indent() - max_len = 0 - for opt in parser.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.indent() - for group in parser.option_groups: - for opt in group.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.dedent() - self.dedent() - self.help_position = min(max_len + 2, self.max_help_position) - self.help_width = self.width - self.help_position - - def format_option_strings(self, option): - """Return a comma-separated list of option strings & metavariables.""" - metavar = option.metavar or option.dest.upper() - return '%s=%s' % (option.name,metavar) - -class IndentedHelpFormatter (HelpFormatter): - """Format help with indented section bodies. - """ - # NOTE optparse - def __init__(self, - indent_increment=2, - max_help_position=24, - width=None, - short_first=1): - HelpFormatter.__init__( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return _("usage: %s\n") % usage - - def format_heading(self, heading): - return "%*s%s:\n" % (self.current_indent, "", heading) - -class TitledHelpFormatter (HelpFormatter): - """Format help with underlined section headers. - """ - # NOTE optparse - def __init__(self, - indent_increment=0, - max_help_position=24, - width=None, - short_first=0): - HelpFormatter.__init__ ( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return "%s %s\n" % (self.format_heading(_("Usage")), usage) - - def format_heading(self, heading): - return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading)) - -SUPPRESS_HELP = "SUPPRESS"+"HELP" -NO_DEFAULT = ("NO", "DEFAULT") - -def _repr(self): - return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) -# </borrowed> - -NOTHING_FOUND = ("NOTHING","FOUND") - -def split_keys(keys): - """Returns list of keys resulting from keys argument. - - --- NO KEYS --- - >>> split_keys( None ) - [] - >>> split_keys( [] ) - [] - - --- STRINGS --- - >>> split_keys( "key1" ) - ['key1'] - >>> split_keys( "key1,key2" ) - ['key1', 'key2'] - >>> split_keys( "key1.key2" ) - ['key1', 'key2'] - >>> split_keys( "key1.key2,key3" ) - ['key1', 'key2', 'key3'] - - --- LISTS --- - >>> split_keys( ['key1'] ) # single item - ['key1'] - >>> split_keys( ['key1','key2'] ) # multiple items - ['key1', 'key2'] - - --- QUOTING --- - These tests check that quotes can be used to protect '.' and ','. - >>> split_keys( "'some.key1','some,key2'" ) # single ticks work - ['some.key1', 'some,key2'] - >>> split_keys( '"some,key1","some.key2"' ) # double ticks work - ['some,key1', 'some.key2'] - >>> split_keys( '"some,key1",some.key2' ) # must quote everything - Traceback (most recent call last): - ConfigParserError: Keys not quoted properly. Quotes must surround all keys. - >>> split_keys( "some,key1,'some.key2'" ) # must quote everything - Traceback (most recent call last): - ConfigParserError: Keys not quoted properly. Quotes must surround all keys. - >>> split_keys( "key1,'some.key2'.key3" ) # must quote everything - Traceback (most recent call last): - ConfigParserError: Keys not quoted properly. Quotes must surround all keys. - >>> split_keys('DEFAULT') - [] - >>> split_keys(['DEFAULT']) - [] - """ - if (keys is None) or (keys == 'DEFAULT') or (keys == ['DEFAULT']): - return [] - try: - keys = keys.replace('"',"'") - if "'" in keys: - keys = keys.strip("'").split("','") - for key in keys: - if "'" in key: - IMPROPER_QUOTES = "Keys not quoted properly. Quotes must surround all keys." - raise ConfigParserUserError(IMPROPER_QUOTES) - else: - keys = keys.replace('.',',').split(',') - except AttributeError: - pass - return keys - -def join_keys(keys,sep=','): - """ - >>> join_keys(['key1']) - 'key1' - >>> join_keys(['key1','key2']) - 'key1,key2' - >>> join_keys(['key1','key2'],'.') - 'key1.key2' - >>> join_keys(['key.1','key2'],'.') - "'key.1','key2'" - >>> join_keys(['key,1','key2'],'.') - "'key,1','key2'" - >>> join_keys([]) - 'DEFAULT' - """ - if not keys: - return 'DEFAULT' - mash = ''.join(keys) - if '.' in mash or ',' in mash: - quote = "'" - sep = quote + ',' + quote - return quote + sep.join(keys) + quote - return sep.join(keys) - -def split_paths(paths): - """Returns list of paths resulting from paths argument. - - --- NO KEYS --- - >>> split_paths( None ) - [] - >>> split_paths( [] ) - [] - - --- STRINGS --- - >>> split_paths( "path1" ) - ['path1'] - >>> split_paths( os.path.pathsep.join(["path1","path2"]) ) - ['path1', 'path2'] - >>> split_paths( "path.1,path.2" ) - ['path.1', 'path.2'] - - --- LISTS --- - >>> split_paths( ['path1'] ) # single item - ['path1'] - >>> split_paths( ['path1','path2'] ) # multiple items - ['path1', 'path2'] - """ - if paths is None: - return [] - try: - return paths.replace(',',os.path.pathsep).split(os.path.pathsep) - except AttributeError: - return paths - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# E X C E P T I O N S -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class ConfigParserError(Exception): - """Exception associated with the cfgparse module""" - pass - -class ConfigParserAppError(Exception): - """Exception due to application programming error""" - pass - -class ConfigParserUserError(Exception): - """Exception due to user error""" - pass - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# K E Y S -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class Keys(object): - """Prioritizes and stores default configuration keys. - - This class is used to store default configuration keys for an instance - of the Config class. The different sources of keys are supported (stored) - by the following methods of this class: - - add_cfg_keys -- configuration file specified default keys - add_cmd_keys -- command line option keys - add_env_keys -- environment variable keys - - The 'get' method returns a combined list of keys in the following order: - application keys (passed when calling 'get' method) - command line keys - environment variable keys - configuration file keys - a 'DEFAULT' key (always present) - - Setup: modify os module to fake out environment variable gets - >>> _getenv = os.getenv - >>> def getenv(variable,default): - ... if variable == 'VAR12': - ... return 'env1,env2' - ... elif variable == 'VAR3': - ... return 'env3' - ... elif variable == 'VAR4': - ... return 'env4' - ... else: - ... return default - >>> os.getenv = getenv - - Case 1: normal string lists of keys - >>> k = Keys() - >>> k.add_env_keys('VAR12,VAR3') # this has effect - >>> k.add_env_keys('VAR_NONE') # no effect - >>> k.add_cfg_keys('cfg1,cfg2') - >>> k.add_cmd_keys('cmd1.cmd2') - >>> k.add_env_keys('VAR12') # no effect (already read) - >>> k.get('app1,app2') - ['app1', 'app2', 'cmd1', 'cmd2', 'env1', 'env2', 'env3', 'cfg1', 'cfg2', 'DEFAULT'] - - Case 2: extend lists using other key input flavors just to make sure each - method uses split_keys() - >>> k.add_env_keys(['VAR4']) # this has effect - >>> k.add_cfg_keys(['cfg3']) - >>> k.add_cmd_keys(['cmd3']) - >>> k.get(['app']) - ['app', 'cmd1', 'cmd2', 'cmd3', 'env1', 'env2', 'env3', 'env4', 'cfg1', 'cfg2', 'cfg3', 'DEFAULT'] - - Teardown: restore os module - >>> os.getenv = _getenv - """ - - def __init__(self): - """Initialize Keys Instance""" - self.cmd_keys = [] - self.env_keys = [] - self.cfg_keys = [] - self.env_vars = [] - - def __repr__(self): - """Return string representation of object""" - return ','.join(self.get([])) - - def add_cmd_keys(self,keys): - """Store keys from command line interface - - keys -- list of keys (typically from the command line) to store. May - be a list of keys or a string with keys separated by commas. - Use any value which evaluates False when no keys. - """ - self.cmd_keys.extend(split_keys(keys)) - - def add_env_keys(self,variables): - """Store keys from invoking shell's environment variable - - variable -- (string) environment variable name from which to obtain - keys to store - """ - variables = split_keys(variables) - for variable in variables: - # only add keys from shell environment variable if we haven't already - if variable not in self.env_vars: - # save key variable name so we can't add same keys twice - self.env_vars.append(variable) - # if shell environment variable has a option save it - keys = os.getenv(variable,None) - if keys is not None: - self.env_keys.extend(split_keys(keys)) - - def add_cfg_keys(self,keys): - """Store keys from user's configuration file. - - keys -- list of keys (from user's configuration file) to store. May - be a list of keys or a string with keys separated by comma. - Use any value which evaluates False when no keys. - """ - self.cfg_keys.extend(split_keys(keys)) - - def get(self,keys=None): - """Return prioritized list of stored configuration keys - - keys -- list of differentiator keys. May be a list of keys or a string - with keys separated by commas. Use any valid which evaluates - False when no keys. - """ - keys = split_keys(keys) - return (keys + self.cmd_keys + self.env_keys + self.cfg_keys + - ['DEFAULT']) - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N V A L U E -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class Value(object): - """Used to store option settings in the blended option dictionary. - Needed to be able to tie the option setting back to the configuration - file for better error reporting and for round trip get/set/write - capability.""" - def __init__(self,value,parent,section_keys): - """Initializes instance.""" - self.value = value - self.parent = parent - self.section_keys = section_keys - - def set(self,value): - """Sets option setting to 'value' argument passed in. - - By default configuration file parsers to do not support round trip. - If they do they should subclass Value() and override this method - """ - self.value = value - - def get_roots(self): - return ["File: %s" % self.parent.get_filename(), - "Section: [%s]" % join_keys(self.section_keys,'.')] - - def __str__(self): - """Returns string representation of the setting.""" - return str(self.value) - - __repr__ = _repr - - def get(self): - """Returns the option setting.""" - return self.value - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class Option(object): - """Options added to configuration parser are an instance of this class.""" - - def __init__(self,**kwargs): - """Instance initializer. - Arguments: - kwargs -- dictionary with keys parser,name,type,default,help,check,keys, - choices (see add_option of OptionContainer) - """ - self.__dict__.update(kwargs) - if self.dest is None: - self.dest = self.name - if self.type not in self.conversions: - TYPE_DOES_NOT_EXIST = "type '%s' is not valid" % self.type - raise ConfigParserAppError(TYPE_DOES_NOT_EXIST) - # help cross reference for options partnered with OptionParser - self._help_xref = "" - self.note = None - - def __str__(self): - """Returns string representation of the option.""" - return self.name - - __repr__ = _repr - - def add_note(self,note): - """Adds 'note' argument text to configuration parser help text and - to error messages associated with this option.""" - self.parser.add_note(note) - self.note = note - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Get - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def _get(self,keys): - # Read any pending configuration files at the top level in order to - # pick up user's default keys in those files. - self.parser.parse_pending_cfg([]) - - keys = split_keys(keys) + self.keys - keys = self.parser.keys.get(keys) - - # Get the option's dictionary from the configuration parser so that - # any pending configuration files that are needed are read. - option = self.parser.get_option_dict(self.name,keys) - - # When keys are given as an argument, we don't have the constraints - # of an exact match like a section. Instead we use the default - # key list (highest priority key first) to walk the option - # dictionary. At each level of the dictionary we look for the - # highest priority key and if it exists we move down a level - # otherwise the remaining keys are checked in order of priority. - - def walk_option(option): - if option.__class__ is dict: - for key in keys: - if key in option: - v = walk_option(option[key]) - if v.__class__ is not dict: - return v - if isinstance(option,Value): - return option - else: - return NOTHING_FOUND - - return walk_option(option) - - def get(self,keys=[],errors=None): - """Returns option value associated with 'keys' argument or options - option value using 'keys' argument (in combination with other keys). - - keys -- name of keys to obtain option value from - - Note: - If option is partnered with an optparse option and that option - is present, the optparse option will take priority and be returned. - """ - - warnings = [] - option = NOTHING_FOUND - - def convert(value,option_help): - # Do conversion to the type application specified - value,warning = self.conversions[self.type](self,value) - # Do final check using application check function if supplied - if not warning and self.check is not None: - value,warning = self.check(value) - if warning: - try: - warnings.extend(option_help.get_roots()) - except AttributeError: - warnings.append(option_help) - warnings.append(warning) - return value - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Priority 1: Get option from optparse partner (command line) - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - if self.dest in self.parser.optpar_option_partners: - option = getattr(self.parser.optparser_options,self.dest,None) - if option is None: - option = NOTHING_FOUND - else: - option = convert(option,'command line option') - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Priority 2: Get a default option - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - if option is NOTHING_FOUND and not warnings: - - option = self._get(keys) - - if option is not NOTHING_FOUND: - value = option.get() - option = convert(value,option) - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Priority 3: Use default specified when adding the option - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - if option is NOTHING_FOUND and not warnings: - if self.default is not NO_DEFAULT: - option = self.default - else: - warnings.append('No valid default found.') - keys = split_keys(keys) + self.keys - keys = self.parser.keys.get(keys) - warnings.append('keys=%s' % ','.join(keys)) - - if warnings: - lines = ['Option: %s' % self.name] + warnings - lines = '\n'.join(lines) + '\n' - if errors is not None: - errors.append(lines) - else: - # not coming back - self.parser.write_errors([lines]) - - return option - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Conversions - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def convert_choice(self,value): - if value in self.choices: - return value,None - else: - choices = str(self.choices).strip('[]()') - warning = "Invalid choice '%s', must be one of: %s" % (value,choices) - return None,warning - - def convert_complex(self,value): - try: - return complex(value),None - except ValueError: - return None,"Cannot convert '%s' to a complex number" % (value) - - def convert_float(self,value): - try: - return float(value),None - except ValueError: - return None,"Cannot convert '%s' to a float" % (value) - - def convert_int(self,value): - try: - return int(value),None - except ValueError: - return None,"Cannot convert '%s' to an integer" % (value) - - def convert_long(self,value): - try: - return int(value),None - except ValueError: - return None,"Cannot convert '%s' to a long" % (value) - - def convert_string(self,value): - try: - return str(value),None - except ValueError: - return None,"Cannot convert '%s' to a string" % (value) - - def convert_nothing(self,value): - return value,None - - conversions = { - 'choice' : convert_choice, - 'complex' : convert_complex, - 'float' : convert_float, - 'int' : convert_int, - 'long' : convert_long, - 'string' : convert_string, - None : convert_nothing, - } - - def set(self,value,cfgfile=None,keys=None): - value = str(value) - if cfgfile: - if keys is not None: - keys = split_keys(keys) - else: - keys = self.keys - cfgfile.set_option(self.name,value,keys,self.help) - else: - keys = split_keys(keys) + self.keys - keys = self.parser.keys.get(keys) - - option = self._get(keys) - - if option is NOTHING_FOUND: - NOTHING_TO_SET = '\n'.join([ - 'ERROR: No option found', - 'option name: %s' % self.name, - 'keys: %s' % keys]) - raise ConfigParserUserError(NOTHING_TO_SET) - else: - option.set(value) - cfgfile = option.parent - return cfgfile - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N C O N T A I N E R B A S E C L A S S E S -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class OptionContainer(object): - - OptionClass = Option - - def __init__(self,description): - self.option_list = [] - self.set_description(description) - - def set_description(self, description): - self.description = description - - def get_description(self): - return self.description - - def add_option(self,name,help=None,type=None,choices=None,dest=None,metavar=None,default=NO_DEFAULT,check=None,keys=None): - """ - name -- configuration file option name (used same as optparse) - type -- choices similar to optparse (used same as optparse) - default -- default value (used same as optparse) - help -- help string (used same as optparse) - dest -- option database attribute name (used same as optparse) - check -- check function - keys -- name of keys to obtain option from - choices -- list of choices (used same as optparse) - metavar -- FUTURE - """ - if dest is None: - dest = name - - kwargs = { - 'parser' : self.parser, - 'name' : name, - 'type' : type, - 'help' : help, - 'dest' : dest, - 'check' : check, - 'keys' : split_keys(keys), - 'choices' : choices, - 'metavar' : metavar, - 'default' : default} - - option = self.OptionClass(**kwargs) - self.parser.master_option_list.append(option) - self.parser.master_option_dict[dest] = option - self.option_list.append(option) - return option - - # <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> - - def format_option_help(self, formatter): - if not self.option_list: - return "" - result = [] - for option in self.option_list: - if not option.help == SUPPRESS_HELP: - result.append(formatter.format_option(option)) - return "".join(result) - - def format_description(self, formatter): - return formatter.format_description(self.get_description()) - - def format_help(self, formatter): - result = [] - if self.description: - result.append(self.format_description(formatter)) - if self.option_list: - result.append(self.format_option_help(formatter)) - return "\n".join(result) - # </borrowed> - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# O P T I O N G R O U P -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class OptionGroup(OptionContainer): - def __init__(self,parser,title,description): - OptionContainer.__init__(self,description) - self.parser = parser - self.title = title - - # <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> - def set_title (self, title): - self.title = title - - # -- Help-formatting methods --------------------------------------- - - def format_help (self, formatter): - result = formatter.format_heading(self.title) - formatter.indent() - result += OptionContainer.format_help(self, formatter) - formatter.dedent() - return result - # </borrowed> - - -class ConfigFile(object): - def __init__(self,cfgfile,content,keys,parent,parser): - - p,n = os.path.split(cfgfile) - try: - p = os.path.join(parent.path,p) - except AttributeError: - pass - p = os.path.abspath(p) - - self.path = p - self.filename = n - self.content = content - self.underkeys = keys - self.parent = parent - self.parser = parser - - self.parsed = False - - def get_filename(self): - return os.path.join(self.path,self.filename) - - def __str__(self): - return os.path.join(self.path,self.filename) - - __repr__ = _repr - - def get_as_str(self): - content = self.content - if content is None: - cfgfile = os.path.join(self.path,self.filename) - f = open(cfgfile) - content = f.read() - f.close() - else: - try: - content = self.content.read() - except AttributeError: - pass - return content - - def parse_if_unparsed(self): - if not self.parsed: - - # The parent is important in that it is used error messages but more - # importantly when getting ready to read a configuration script we - # must set the current directory of the parent so relative path - # specification of any configuration file works out. - try: - parent_path = self.parent.path - except AttributeError: - parent_path = os.getcwd() - - # Save so we can restore after we are done - cwd = os.getcwd() - - # Make sure file is present - cfgfile = os.path.join(self.path,self.filename) - if not self.content and not os.path.exists(cfgfile): - lines = ["File not found: '%s'" % cfgfile] - # remember, top level configuration file parent is just the - # current working directory when ConfigParser was instantiated - # FUTURE parent info in here too - FILE_NOT_FOUND = '\n'.join(lines) - raise ConfigParserUserError(FILE_NOT_FOUND) - - # Change the current working directory to where the configuration - # script is (to accomodate Python configuraton scripts so that it - # can use os.getcwd() to determine its location). - os.chdir(self.path) - - self.parse() - - # Restore - os.chdir(cwd) - - # Mark it as done so it isn't parsed twice - self.parsed = True - - -class ConfigFilePy(ConfigFile): - - def parse(self): - - underkeys = self.underkeys - parser = self.parser - - # Parsing can be easy! - options = {} - if self.content is None: - cfgfile = os.path.join(self.path,self.filename) - if sys.version_info < (3,0): - execfile(cfgfile,options) - else: - exec(compile(open(cfgfile).read(), cfgfile, 'exec'),options) - else: - exec(self.get_as_str(), options) - #if sys.version_info < (3,0): - # execfile(self.,options) - #else: - # exec(self.get_as_str(), options) - - # Update the keys. "KEYS_VARIABLE" option used to specify the - # environment variable that holds additional default keys, if - # present get keys from the environment using it. If reading - # configuration file that is being included under a key, don't - # bother with its keys because it would get too confusing. - if not underkeys: - try: - parser.keys.add_env_keys(options['KEYS_VARIABLE']) - del options['KEYS_VARIABLE'] - except KeyError: - pass - try: - parser.keys.add_cfg_keys(options['KEYS']) - del options['KEYS'] - except KeyError: - pass - - try: - CONFIG_FILES = options['CONFIG_FILES'] - del options['CONFIG_FILES'] - except KeyError: - CONFIG_FILES = None - - def merge_option(value,section_keys): - if value.__class__ is dict: - for key in value: - merge_option(value[key],section_keys+[key]) - else: - valueobj = Value(value,self,section_keys) - parser.merge_option(name,valueobj,underkeys+section_keys) - - # Merge all options that don't start with "_" into the options - for name,value in list(options.items()): - if not name.startswith('_'): - merge_option(value,[]) - - # Process the "CONFIG_FILES" option used to merge in configuration - # from other files. Users specify a comma separated (string) - # listing of configuration file names or a dictionary of (and - # optionally - of dictionaries of) file name strings. - def add_files(value,under): - if isinstance(value,dict): - for k,v in value.items(): - add_files(v,under+[k]) - else: - for cf in split_paths(value): - parser.add_file(cf,keys=under,parent=self) - - if CONFIG_FILES: - add_files(CONFIG_FILES,underkeys) - -class ConfigFileIni(ConfigFile): - - def _read(self): - - try: - self._already_read - return - except AttributeError: - self._already_read = True - - underkeys = self.underkeys - option_parser = self.parser - marker_fmt = '{{{%s-(?P<id>\d+)}}}' - _self = self - - class BaseClass(object): - def __init__(self,matchobj): - self.text = matchobj.group('text') - self.num = len(self.objects) - self.objects.append(self) - def restore(self): - return self.text - - class Line(BaseClass): - pass - - class Block(BaseClass): - objects = [] - find_re = re.compile('<b>(?P<text>.*?)</b>',re.DOTALL) - - class Verbatim(BaseClass): - objects = [] - find_re = re.compile('<v>(?P<text>.*?)</v>',re.DOTALL) - - class Comment(BaseClass): - objects = [] - find_re = re.compile('(?P<text>[ \t]*[#;].*)') - - class OptionPair(Value): - # OptionPair must subclass Value() because it is being submitted into the - # parser options dictionary (all options in the dictionary must be a - # subclass of Value). All things equal this would subclass BaseClass() - # but it implements all that functionality anyways. - objects = [] - find_re = re.compile('(?P<indent>[ \t]*)(?P<name>.+?)(?P<sep>\s*=\s*)(?P<value>.*)') - section = None - def __init__(self,matchobj): - section_keys = OptionPair.section.keys - # get the name right - name = matchobj.group('name') - self.extended_name = name - if '[' in name: - name,keys = name.strip(']').split('[') - section_keys = section_keys + split_keys(keys) - self.name = name - self.indent = matchobj.group('indent') - self.sep = matchobj.group('sep') - value = matchobj.group('value') - self.valueplus = value - try: - value = restore(Comment,value) - value = restore(Block,value) - value = restore(Verbatim,value) - self.linenum = re.findall(marker_fmt % 'Line',value)[0] - except IndexError: - self.linenum = 'new' - self.num = len(self.objects) - self.objects.append(self) - self.section = OptionPair.section - self.section.options[self.extended_name] = self - # The following are needed for Value() functionality - self.parent = _self - self.section_keys = section_keys - # self.value will be set later (can't now because value may contain - # interpolations which cannot be expanded until all options are read - # for this section - def get_roots(self): - return Value.get_roots(self) + ["Line: %s" % self.linenum] - def set(self,value): - Value.set(self,value) - value = [value] - def hit(matchobj): - value.append(matchobj.group(0)) - regexp = re.compile(marker_fmt % 'Comment') - regexp.sub(hit,self.valueplus) - self.valueplus = ''.join(value) - def restore(self): - return ''.join([self.extended_name,self.sep,self.valueplus]) - def expand(self,levellist=[]): - levellist = levellist + [self.name] - if len(levellist) > 10: - lines = self.get_roots() - lines.append("Interpolation: %s" % ' << '.join(levellist)) - lines.append("Maximum nesting level exceeded.") - MAX_NESTING_LEVEL_EXCEEDED = '\n' + '\n'.join(lines) - raise ConfigParserUserError(MAX_NESTING_LEVEL_EXCEEDED) - try: - return self.value - except AttributeError: - pass - value = remove(Comment,self.valueplus) - value = remove(Line,value) - value = restore(Block,value.strip(' \t')) - value = remove(Line,value) - # @future [ABSPATH] value = value.replace('%(ABSPATH(','%_(ABSPATH(') - regexp = re.compile('%\((?P<name>.*?)\)s') - def hit(matchobj): - name = matchobj.group('name') - try: - return self.section.options[name].expand(levellist) - except KeyError: - try: - return Section.default.options[name].expand(levellist) - except KeyError: - lines = self.get_roots() - lines.append("Interpolation: %s" % ' << '.join(levellist+[name])) - lines.append("'%s' not found in section or in [DEFAULT]." % name) - INTERPOLATION_ERROR = '\n' + '\n'.join(lines) - raise ConfigParserUserError(INTERPOLATION_ERROR) - value = regexp.sub(hit,value) - # @future [ABSPATH] regexp = re.compile(r'%_\(ABSPATH\((.*?)\)\)s') - # def hit(matchobj): - # return os.path.abspath(matchobj.group(1)) - # value = regexp.sub(hit,value) - self.value = remove(Line,restore(Verbatim,value)) - return self.value - - class Section(BaseClass): - objects = [] - find_re = re.compile('(?P<text>\n\[(?P<name>.*?)\].*?(?=\n\[))',re.DOTALL) - default = None - def __init__(self,matchobj): - self.options = {} - name = matchobj.group('name') - self.name = name - keys = split_keys(name) - if keys == ['DEFAULT']: - keys = [] - if not keys: - Section.default = self - self.keys = keys - OptionPair.section = self - self.text = collapse(OptionPair,matchobj.group(0)) - self.num = len(self.objects) - self.objects.append(self) - def add_option(self,name,value,help): - if help: - helplines = textwrap.fill(help,77).split('\n') - lines = ['# %s' % line for line in helplines] - else: - lines = [] - lines.append('%s = %s' % (name,value)) - OptionPair.section = self - block = collapse(Comment,'\n'+'\n'.join(lines)) - self.text += collapse(OptionPair,block) - option = OptionPair.objects[-1] - underkeys = _self.underkeys + option.section_keys - if not underkeys: - underkeys = ['DEFAULT'] - # submit new value to parser - _self.parser.merge_option(name,option,underkeys) - return option - - def collapse(SubClass,text): - marker_fmt = '{{{%s-%%d}}}' % (SubClass.__name__) - def hit(matchobj): - return marker_fmt % SubClass(matchobj).num - return SubClass.find_re.sub(hit,text) - - def remove(SubClass,text): - return re.compile(marker_fmt % SubClass.__name__).sub('',text) - - def restore(SubClass,text): - def hit(matchobj): - return SubClass.objects[int(matchobj.group('id'))].restore() - return re.compile(marker_fmt % SubClass.__name__).sub(hit,text) - - text = self.get_as_str() - lines = [] - i = 1 - for line in text.split('\n'): - lines.append(line + ('{{{Line-%d}}}' % i)) - i += 1 - text = '\n'.join(lines) - text = '\n[DEFAULT]\n#_START_MARKER_\n%s\n[' % text - text = collapse(Block,text) - text = collapse(Verbatim,text) - text = collapse(Comment,text) - text = collapse(Section,text) - - self._OptionPair = OptionPair - self._Line = Line - self._Comment = Comment - self._Block = Block - self._Verbatim = Verbatim - self._Section = Section - self._collapse = collapse - self._restore = restore - self._remove = remove - self.text = text - - def parse(self): - self._read() - for option in self._OptionPair.objects: - name = option.name - underkeys = self.underkeys + option.section_keys - value = option.expand() - if name == '<include>': - for cf in split_paths(value): - self.parser.add_file(cf,keys=underkeys,parent=self) - continue - if not underkeys: - if name == '<keys>': - self.parser.keys.add_cfg_keys(split_keys(value)) - continue - if name == '<keys_variable>': - self.parser.keys.add_env_keys(value) - continue - underkeys = ['DEFAULT'] - # call expand method to get .value attribute set - self.parser.merge_option(name,option,underkeys) - - def set_option(self,name,value,keys=None,help=None): - self._read() - value = str(value) - keys = split_keys(keys) - found = False - for option in self._OptionPair.objects: - if name==option.name and keys == option.section_keys: - option.set(value) - found = True - if not found: - Section = self._Section - for section in Section.objects: - if keys == section.keys: - found = True - section.add_option(name,value,help) - if not found: - block = '\n\n[%s]\n\n[\n' % join_keys(keys,'.') - self.text = '%s%s' % (self.text[:-2],self._collapse(Section,block)) - section = self._Section.objects[-1] - section.add_option(name,value,help) - - def write(self,file): - self._read() - - restore = self._restore - content = self.text - content = restore(self._Section,content) - content = restore(self._OptionPair,content) + '\n' - content = restore(self._Comment,content) - content = restore(self._Block,content) - content = restore(self._Verbatim,content) - content = self._remove(self._Line,content) - content = content.split('\n#_START_MARKER_\n')[1][:-3] - - try: - file.write(content) - except AttributeError: - f = open(file,'w') - f.write(content) - f.close() - - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# C O N F I G U R A T I O N P A R S E R -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -class ConfigParser(OptionContainer): - - KeysClass = Keys - OptionGroupClass = OptionGroup - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Initializer - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def __init__(self,description=None,allow_py=False,formatter=None,exception=False): - """ - description -- Introductory text placed above configuration option - help text. - allow_py -- Set to True to allow Python based configuraton files - to be executed. Defaults to False. Enabling this feature - poses a potential security hole for your application. - formatter -- Controls configuration option help text style. Set - to either the IndentedHelpFormatter or TitledHelpFormatter - classes found in cfgparse module (or a subclass of either). - exception -- set to True to raise ConfigParserUserError exception - when configuration error occurs (due to user error). Omitting - or setting to False will write error message to sys.stderr. - Set to an exception class to raise that exception when a user - configuration file error occurs. - """ - OptionContainer.__init__(self,description) - - self.exception = exception - - # Needed because shares same base class as an option group - # (option group constructor initializes parser using an arg). - self.parser = self - - self.option_dicts = {} - - self.option_groups = [] - self.optpar_option_partners = {} - - self.master_option_list = [] - self.master_option_dict = {} - - if formatter is None: - formatter = IndentedHelpFormatter() - self.formatter = formatter - self.formatter.set_parser(self) - - self.notes = [] - - # Set up database of option selection keys - self.keys = self.KeysClass() - - # Create database to store information on those configuration files - # to be read later (configuration files under keys are not read until - # all of the keys in which it is under are active. - self._pending = [] - - # Since it introduces a security risk, only allow Python based - # configuration files if application explicitly sets this True. - self.allow_python_cfg = allow_py - - self.optparse_dests = {} - - def add_optparse_keys_option(self,option_group,switches=('-k','--keys'),dest='cfgparse_keys',help="Comma separated list of configuration keys."): - """Adds configuration file keys list option to optparse object.""" - self.optparse_dests['keys'] = dest - option_group.add_option(dest=dest,metavar='LIST',help=help,*switches) - - def add_optparse_files_option(self,option_group,switches=('--cfgfiles',),dest='cfgparse_files',help="Comma separated list of configuration files."): - """Adds configuration file list option to optparse object.""" - self.optparse_dests['files'] = dest - option_group.add_option(dest=dest,metavar='LIST',help=help,*switches) - - def add_optparse_help_option(self,option_group,switches=('--cfghelp',),dest='cfgparse_help',help="Show configuration file help and exit."): - """Adds configuration file help option to optparse object.""" - self.optparse_dests['help'] = dest - option_group.add_option(dest=dest,action='store_true',help=help,*switches) - - def add_env_file(self,var,keys=[]): - """Adds configuration file specified by environment variable setting. - Arguments: - var -- name of environment variable holding configuration file name - keys -- section key list to place configuration file options settings under - """ - # Add configuration files specified by an environment variable - # if application script specified it. (Don't read right away, - # rather hold them in pending database until they are needed - # because adding options causes option_dicts to be set with a - # default for the option destination. - f = os.getenv(var,None) - if f: - f = self.add_file(cfgfile=f,keys=keys) - else: - f = None - return f - - def get_option(self,dest): - """Returns option object previously added. - Arguments: - dest -- destination attribute name of option - """ - opt = self.master_option_dict.get(dest,None) - if opt is None: - OPTION_NOT_FOUND = '\n'.join([ - 'ERROR: No option found', - 'option dest: %s' % dest]) - raise ConfigParserAppError(OPTION_NOT_FOUND) - return opt - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Adding Option Groups (adding options handled by base class) - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def add_option_group(self,title,description=None): - option_group = self.OptionGroupClass(self,title,description) - self.option_groups.append(option_group) - return option_group - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Pending Configuration - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def get_option_dict(self,name,keys): - self.parse_pending_cfg(keys) - return self.option_dicts.get(name,NOTHING_FOUND) - - def parse_pending_cfg(self,keys=[],read_all=False): - """ - read_all -- Set to True to read all configuration files up front. - Defaults to reading "on the fly" as the configuration files are - needed.""" - # Read any pending configuration files that could possibly effect the - # setting about to be retrieved. Note, it was decided that if the - # pending configuration file's under keys are all in the key list - # computed above it will be installed right away. The other - # alternative is to try retrieving the setting with the highest - # priority key alone (first reading any pending configuration files - # that are under that key), then if that fails try retrieving the - # setting with the top two highest priority keys (again first reading - # any pending configuration files that are under either or both of the - # keys), and repeating this process for each key in the list until a - # setting is found. This would have the benefit of only reading - # pending configuration files when it is absolutely necessary but at - # cost of performance and more difficult to explain how it works. - keys = split_keys(keys) - d = [] - while self._pending: - underkeys,cfgfileobj = self._pending.pop(0) - underkeys = split_keys(underkeys) - parse_it = read_all - if not parse_it: - for key in underkeys: - if key not in keys: - d.append((underkeys,cfgfileobj)) - break - else: - parse_it = True - if parse_it: - cfgfileobj.parse_if_unparsed() - self._pending = d - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - FileClasses = {'py' : ConfigFilePy, 'ini': ConfigFileIni, 'default' : ConfigFileIni} - - def add_file(self,cfgfile=None,content=None,type=None,keys=[],parent=None): - """ - Adds configuration file to parser. Note, file is not read until parse - or get_option method is called (and even then it may not be read if any - keys in the keys list are not in the keys being used to parse). - - cfgfile -- either the filename or a file stream, defaults to None (see table below) - content -- either file contents string or file stream, defaults to None (see table below) - type -- set to either 'py', 'ini', or None (default) to control file parser used. - When set to None, filename extension is used to determine parser to use. 'py' - interprets the file as Python code. Otherwise the 'ini' style parser is used. - keys -- key list that all options in the configuration file will - be placed under when the file is read. - parent -- Not intended to be used by the public - - The following table summarizes the legal combinations of the cfgfile and - arguments and resulting file name and contents utilized. - - cfgfile content result (when configuration is parsed) - -------- ------- ----------------------------------------------------------- - filename None file is opened and contents read - stream None stream contents are read, filename is stream name attribute - filename stream stream contents are read, filename is cfgfile argument - filename string both filename and content are used as is - """ - - if isinstance(cfgfile,str): - n = cfgfile - c = content - elif hasattr(cfgfile,'name'): - n = cfgfile.name - c = cfgfile - elif isinstance(content,str): - n = 'heredoc' - c = content - elif hasattr(content,'read'): - n = 'stream' - c = content - else: - ARGUMENT_CONFICT = "Illegal combination of 'cfgfile' and 'content' arguments" - raise ConfigParserAppError(ARGUMENT_CONFICT) - - uk = split_keys(keys) - - if type is None: - # calculate type (lower case extension) - type = os.path.splitext(n)[1][1:].lower() - - if type == 'py' and not self.allow_python_cfg: - return None - - FileClass = self.FileClasses.get(type) - if FileClass is None: - FileClass = self.FileClasses['default'] - - fileobj = FileClass(cfgfile=n,content=c,keys=uk,parent=parent,parser=self) - self._pending.append((uk,fileobj)) - return fileobj - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Read Configuration File Utilities - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def merge_option(self,name,value,keys): - """ Merge value (under keys) into options dictionary. - - name -- option name - value -- value to assign option (may be a dictionary or a option) - keys -- list of keys to place value in options dict under - """ - - # Add the option name to the key list so we can start walking at - # the top level of the options dictionary. - keys = [name] + keys - option_dicts = self.option_dicts - # Move through the options dictionary using the keys we are - # supposed to place the value under creating dictionaries and keys - # that aren't already present. - for key in keys[:-1]: - if key in option_dicts: - if option_dicts[key].__class__ is not dict: - option_dicts[key] = dict(DEFAULT=option_dicts[key]) - else: - option_dicts[key]={} - option_dicts = option_dicts[key] - option_dicts[keys[-1]] = value - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Option Parsing (not configuration file parsing) - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def write_errors(self,errors): - CFGPARSE_USER_ERROR = '\n' + '\n'.join(errors) - if not self.exception: - sys.stderr.write("ERROR: Configuration File Parser\n") - sys.stderr.write(CFGPARSE_USER_ERROR) - self.system_exit() - else: - if self.exception is True: - UserError = ConfigParserUserError - else: - UserError = self.exception - raise UserError(CFGPARSE_USER_ERROR) - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Option Parsing - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def parse(self,optparser=None,args=None,read_all=False): - """Partners the option parser and the configuration parser together - read_all -- Set to True to read all configuration files up front. - Defaults to reading "on the fly" as the configuration files are - needed. - """ - - if optparser: - # Marry up type, help, choices attributes between option parser and - # configuration parser options. - self.marry_options(optparser) - - # Parse command line arguments - options, args = optparser.parse_args(args) - self.optparser_options = options - - # generate help if requested from the command line - help_dest = self.optparse_dests.get('help') - if help_dest and getattr(options,help_dest): - self.print_help() - self.system_exit() - - # add command line keys - keys_dest = self.optparse_dests.get('keys') - if keys_dest: - self.keys.add_cmd_keys(getattr(options,keys_dest)) - - # add command line configuration files (must hold it as other configuration - # files may be pending that should be read first) - files_dest = self.optparse_dests.get('files') - if files_dest: - cfgfiles = getattr(options,files_dest) - for cf in split_paths(cfgfiles): - self.add_file(cfgfile=cf,keys=[]) - else: - class ConfigOptions(object): - pass - options = ConfigOptions() - - self.parse_pending_cfg([],read_all) - - # Go through each option in the configuration options and add them - # to options object. - errors = [] - for option in self.master_option_list: - setattr(options,option.dest,option.get(errors=errors)) - if errors: - self.write_errors(errors) - - if optparser: - return options, args - else: - return options - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def marry_options(self,optparser): - # create mapping: dest -> [optpar options] - optpar_lookup = {} - for option in optparser.option_list: - if option.dest: - optpar_lookup.setdefault(option.dest,[]).append(option) - for group in optparser.option_groups: - for option in group.option_list: - if option.dest: - optpar_lookup.setdefault(option.dest,[]).append(option) - self.optpar_option_partners = optpar_lookup - - # we are guarenteed no duplicate destinations with a configuration parser - for cfgpar_option in self.master_option_list: - optpar_options = optpar_lookup.get(cfgpar_option.dest,[]) - for optpar_option in optpar_options: - for attrname in ['metavar','type','choices','help']: - cfgpar_attr = getattr(cfgpar_option,attrname) - optpar_attr = getattr(optpar_option,attrname) - if cfgpar_attr is None: - cfgpar_attr = optpar_attr - setattr(cfgpar_option,attrname,cfgpar_attr) - if cfgpar_option.help == SUPPRESS_HELP: - continue - try: - # remove anything we've added previously - optpar_option.help = optpar_option.help.replace(optpar_option._cfgparse_help,'') - except AttributeError: - # must not have modified it previously - pass - help = " See also '%s' option in configuration file help." % (cfgpar_option.name) - if not optpar_option.help: - help = help.strip() - optpar_option.help = '' - optpar_option.help = optpar_option.help + help - optpar_option._cfgparse_help = help - if cfgpar_option.help == SUPPRESS_HELP: - continue - if cfgpar_option.help is None: - cfgpar_option.help = '' - try: - # remove anything we've added previously - cfgpar_option.help = cfgpar_option.help.replace(cfgpar_option._help_xref,'') - except AttributeError: - # must not have modified it previously - pass - switches = '/'.join([str(o) for o in optpar_options]) - if switches: - help = " See also '%s' command line switch." % (switches) - if not cfgpar_option.help: - help = help.strip() - cfgpar_option.help = cfgpar_option.help + help - cfgpar_option._help_xref = help - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - def marry_attribute(self,attrname,cfgpar_option,optpar_option): - cfgpar_attr = getattr(cfgpar_option,attrname) - optpar_attr = getattr(optpar_option,attrname) - if cfgpar_attr is None: - cfgpar_attr = optpar_attr - setattr(cfgpar_option,attrname,cfgpar_attr) - - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Help - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - # <borrowed file="Lib/optparse.py" version="python2.4" modified="yes"> - def format_option_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - formatter.store_option_strings(self) - result = [] - result.append(formatter.format_heading(_("Configuration file options"))) - formatter.indent() - if self.option_list: - result.append(OptionContainer.format_option_help(self, formatter)) - result.append("\n") - for group in self.option_groups: - result.append(group.format_help(formatter)) - result.append("\n") - formatter.dedent() - # Drop the last "\n", or the header if no options or option groups: - return "".join(result[:-1]) - - def format_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - result = [] - if self.description: - result.append(self.format_description(formatter) + "\n") - result.append(self.format_option_help(formatter)) - return "".join(result) - - def print_help(self, file=None): - """print_help(file : file = stdout) - - Print an extended help message, listing all options and any - help text provided with them, to 'file' (default stdout). - """ - if file is None: - file = sys.stdout - - file.write(self.format_help()) - - if self.notes: - file.write('\nNotes:\n%s\n'%'\n'.join(self.notes)) - - # </borrowed> - - def add_note(self,note): - self.notes.append(note) - - def system_exit(self): - sys.exit() - - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() - diff --git a/synthesis/configparser.py b/synthesis/configparser.py index 0ddbb22f..784b4022 100644 --- a/synthesis/configparser.py +++ b/synthesis/configparser.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import msg as p class ConfigParser(object): """Class for parsing python configuration files @@ -14,7 +15,7 @@ class ConfigParser(object): >>> p.add_config_file("test.py") >>> p.parse() {'modules': {'svn': 'path/to/svn', 'local': '/path/to/local'}, 'fetchto': '..'} - + Case2: Default value and lack of a variable >>> f = open("test.py", "w") >>> f.write('a="123"') @@ -25,7 +26,7 @@ class ConfigParser(object): >>> p.add_config_file("test.py") >>> p.parse() {'a': '123', 'b': 'borsuk'} - + Case3: Multiple types for a variable >>> f = open("test.py", "w") >>> f.write('a=[1,2,3]') @@ -36,7 +37,7 @@ class ConfigParser(object): >>> p.add_config_file("test.py") >>> p.parse() {'a': [1, 2, 3]} - + Case4: Unrecognized options >>> f = open("test.py", "w") >>> f.write('a = 123') @@ -50,7 +51,7 @@ class ConfigParser(object): File "configparser.py", line 107, in parse raise NameError("Unrecognized option: " + key) NameError: Unrecognized option: a - + Case5: Invalid parameter type >>> f = open("test.py","w") >>> f.write('a="123"') @@ -64,17 +65,44 @@ class ConfigParser(object): File "configparser.py", line 110, in parse raise RuntimeError("Given option: "+str(type(val))+" doesn't match specified types:"+str(opt.types)) RuntimeError: Given option: <type 'str'> doesn't match specified types:[<type 'int'>] - + + Case6: + >>> f = open("test.py","w") + >>> f.write('a={"zupa":1}') + >>> f.close() + >>> p = ConfigParser() + >>> p.add_option("a", type={}) + >>> p.add_allowed_key("a", "zupa") + >>> p.add_config_file("test.py") + >>> p.parse() + {'a': {'zupa': 1}} + + Case7 + >>> f = open("test.py","w") + >>> f.write('a={"kot":1}') + >>> f.close() + >>> p = ConfigParser() + >>> p.add_option("a", type={}) + >>> p.add_allowed_key("a", "kniaz") + >>> p.add_config_file("test.py") + >>> p.parse() + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + File "configparser.py", line 184, in parse + raise RuntimeError("Encountered unallowed key: " +key+ " for options '"+opt_name+"'") + RuntimeError: Encountered unallowed key: kot for options 'a' + Cleanup: >>> import os >>> os.remove("test.py") """ - + class Option: def __init__(self, name, **others): self.name = name self.keys = [] self.types = [] + self.help = "" for key in others: if key == "help": @@ -82,15 +110,10 @@ class ConfigParser(object): elif key == "default": self.default = others["default"] elif key == "type": - self.add_type(others["type"]) + self.add_type(type_obj=others["type"]) else: raise ValueError("Option not recognized: " + key) - if "default" in others: - self.hasdefault = True - else: - self.hasdefault = False - def add_type(self, type_obj): self.types.append(type(type_obj)) @@ -102,6 +125,20 @@ class ConfigParser(object): self.description = description self.options = {} + def help(self): + p.rawprint("Variables available in a manifest:") + for key in self.options: + opt = self.options[key] + line = ' {0:10}; {1:15}; {2:40}{3}{4:10}' + try: + tmp_def = opt.default + if tmp_def == "": + tmp_def = '""' + line = line.format(key, str(opt.types), opt.help,', default=', tmp_def) + except AttributeError: #no default value + line = line.format(key, str(opt.types), opt.help, "","") + p.rawprint(line) + def add_option(self, name, **others): if name in self.options: raise ValueError("Option already added: " + name) @@ -112,6 +149,18 @@ class ConfigParser(object): raise RuntimeError("Can't add type to a non-existing option") self.options[name].add_type(type_obj=type) + def add_allowed_key(self, name, key): + if not isinstance(key, basestring): + raise ValueError("Allowed key must be a string") + try: + self.options[name].allowed_keys.append(key) + except AttributeError: + if type(dict()) not in self.options[name].types: + raise RuntimeError("Allowing a key makes sense for dictionaries only") + self.options[name].allowed_keys = [key] + + self.options[name].allowed_keys.append(key) + def add_config_file(self, config_file): try: self.file #check if there is such attribute @@ -123,7 +172,7 @@ class ConfigParser(object): return raise RuntimeError("Config file should be added only once") - + def parse(self): options = {} ret = {} @@ -134,26 +183,35 @@ class ConfigParser(object): self.file = "/dev/null" execfile(self.file, options) - for key, val in list(options.items()): - if key.startswith('__'): + for opt_name, val in list(options.items()): #check delivered options + if opt_name.startswith('__'): continue - if key not in self.options: - raise NameError("Unrecognized option: " + key) - opt = self.options[key] + if opt_name not in self.options: + raise NameError("Unrecognized option: " + opt_name) + opt = self.options[opt_name] if type(val) not in opt.types: raise RuntimeError("Given option: "+str(type(val))+" doesn't match specified types:"+str(opt.types)) - ret[key] = val - for name, opt in self.options.items(): - if opt.hasdefault == True: + ret[opt_name] = val + if type(val) == type(dict()): + try: + for key in val: + if key not in self.options[opt_name].allowed_keys: + raise RuntimeError("Encountered unallowed key: " +key+ " for options '"+opt_name+"'") + except AttributeError: #no allowed_keys member - don't perform any check + pass + + for name, opt in self.options.items(): #set values for not listed items with defaults + try: if opt.name not in ret: ret[opt.name] = opt.default + except AttributeError: #no default value in the option + pass return ret - - + def _test(): import doctest doctest.testmod() - + if __name__ == "__main__": _test() \ No newline at end of file diff --git a/synthesis/connection.py b/synthesis/connection.py index 3b507d03..3c83d72d 100644 --- a/synthesis/connection.py +++ b/synthesis/connection.py @@ -2,11 +2,23 @@ import os import random import string +import msg class Connection: def __init__(self, ssh_user, ssh_server): self.ssh_user = ssh_user self.ssh_server = ssh_server + + def __str__(self): + return self.ssh_user + '@' + self.ssh_server + + def __data_given(self): + return self.ssh_user != None and self.ssh_server != None + + def __check(self): + if not self.__data_given(): + p.echo("Error: no data for connection given") + quit() def system(self, cmd): return os.system("ssh " + self.ssh_user + "@" + self.ssh_server + ' "' + cmd + '"') @@ -19,25 +31,25 @@ class Connection: Takes list of files and sends them to remote machine. Name of a directory, where files are put is returned """ + self.__check() if not isinstance(files, list): return None; - ssh_cmd = "ssh " + self.ssh_user + "@" + self.ssh_server - #create a new catalogue on remote machine if dest_folder == None: dest_folder = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(8)) mkdir_cmd = 'mkdir ' + dest_folder import msg as p - p.vprint("Connecting to " + ssh_cmd + " and creating directory " + dest_folder + ": " + mkdir_cmd) + p.vprint("Connecting to " + str(self) + " and creating directory " + dest_folder + ": " + mkdir_cmd) self.system(mkdir_cmd) #create a string with filenames from pipes import quote - local_files_str = ' '.join(quote(os.path.abspath(x)) for x in files) + local_files_str = ' '.join(quote(file.path) for file in files) - rsync_cmd = "rsync -Rav " + local_files_str + " " + self.ssh_user + "@" + self.ssh_server + ":" + dest_folder + rsync_cmd = "rsync -Rav " + local_files_str + " " + self.ssh_user + "@" + self.ssh_server + ":" + dest_folder + rsync_cmd += " > /dev/null" p.vprint("Coping files to remote machine: "+rsync_cmd) import subprocess p = subprocess.Popen(rsync_cmd, shell=True) @@ -45,6 +57,7 @@ class Connection: return dest_folder def transfer_files_back(self, dest_folder): + self.__check() rsync_cmd = "rsync -av " + self.ssh_user + "@" + self.ssh_server + ":" + dest_folder + ' /' p.vprint(rsync_cmd) os.system(rsync_cmd) @@ -54,16 +67,16 @@ class Connection: return 0 else: return 1 - -def check_address_length(module): - p = module.popen("uname -a") - p = p.readlines() - if not len(p): - p.echo("Checking address length failed") - return None - elif "i686" in p[0]: - return 32 - elif "x86_64" in p[0]: - return 64 - else: - return None \ No newline at end of file + + def check_address_length(self): + p = self.popen("uname -a") + p = p.readlines() + if not len(p): + p.echo("Checking address length failed") + return None + elif "i686" in p[0]: + return 32 + elif "x86_64" in p[0]: + return 64 + else: + return None \ No newline at end of file diff --git a/synthesis/depend.py b/synthesis/depend.py index 1e97c886..f1e64545 100644 --- a/synthesis/depend.py +++ b/synthesis/depend.py @@ -111,34 +111,42 @@ def generate_fetch_makefile(top_module): f = open("Makefile.fetch", "w") + modules = [top_module] f.write("fetch: ") - for m in top_module.svn: - basename = path.url_basename(m.url) - f.write(basename+"__fetch \n") - for m in top_module.git: - basename = path.url_basename(m.url) - f.write(basename+"__fetch \n") - f.write("\n") - for m in top_module.svn: - basename = path.url_basename(m.url) - dir = os.path.join(m.fetchto, basename) - f.write(basename+"__fetch:\n") - f.write("\t\t") - f.write("PWD=$(shell pwd) ; ") - f.write("cd " + rp(m.fetchto) + ' ; ') - f.write("svn checkout "+ m.url + ' ; ') - f.write("cd $(PWD) \n\n") - - for m in top_module.git: - basename = path.url_basename(m.url) - dir = os.path.join(m.fetchto, basename) - f.write(basename+"__fetch:\n") - f.write("\t\t") - f.write("PWD=$(shell pwd) ; ") - f.write("cd " + rp(m.fetchto) + ' ; ') - f.write("git clone "+ m.url + ' ; ') - f.write("cd $(PWD) \n\n") + while(len(modules) > 0): + module = modules.pop() + for m in module.svn: + modules.append(m) + basename = path.url_basename(m.url) + f.write(basename+"__fetch \\\n") + for m in module.git: + modules.append(m) + basename = path.url_basename(m.url) + f.write(basename+"__fetch \\\n") + + f.write("\n") + for m in module.svn: + basename = path.url_basename(m.url) + dir = os.path.join(m.fetchto, basename) + f.write(basename+"__fetch:\n") + f.write("\t\t") + f.write("PWD=$(shell pwd); ") + f.write("cd " + rp(m.fetchto) + '; ') + f.write("svn checkout "+ m.url + '; ') + f.write("cd $(PWD) \n\n") + + for m in module.git: + basename = path.url_basename(m.url) + dir = os.path.join(m.fetchto, basename) + f.write(basename+"__fetch:\n") + f.write("\t\t") + f.write("PWD=$(shell pwd); ") + f.write("cd " + rp(m.fetchto) + '; ') + f.write("if [ -d " + basename + " ]; then cd " + basename + '; ') + f.write("git pull; ") + f.write("else git clone "+ m.url + '; fi; ') + f.write("cd $(PWD) \n\n") f.close() def generate_pseudo_ipcore(file_deps_dict, filename="ipcore"): diff --git a/synthesis/global_mod.py b/synthesis/global_mod.py index b802c100..e911e395 100644 --- a/synthesis/global_mod.py +++ b/synthesis/global_mod.py @@ -4,8 +4,6 @@ options = None t0 = None ssh = None -synth_user = None -synth_server = None top_manifest = None cwd = None opt_map = None diff --git a/synthesis/hdlmake.py b/synthesis/hdlmake.py index 768b8419..1701b1ce 100755 --- a/synthesis/hdlmake.py +++ b/synthesis/hdlmake.py @@ -8,7 +8,6 @@ import path import path import time import os -from connection import check_address_length from connection import Connection import random import string @@ -16,14 +15,14 @@ import global_mod import msg as p import optparse from module import Module -from helper_classes import Manifest, SourceFile +from helper_classes import Manifest, SourceFile, ManifestParser def main(): global_mod.t0 = time.time() parser = optparse.OptionParser() #disabled due to introducing a new parser class. Help msg printing is not ready yet. - #parser.add_option("--manifest-help", action="store_true", dest="manifest_help", - #help="print manifest file variables description") + parser.add_option("--manifest-help", action="store_true", dest="manifest_help", + help="print manifest file variables description") parser.add_option("-k", "--make", dest="make", action="store_true", default=None, help="prepare makefile for simulation") parser.add_option("-f", "--fetch", action="store_true", dest="fetch", help="fetch files from modules listed in MANIFEST") parser.add_option("--make-fetch", action="store_true", dest="make_fetch", help="generate makefile for fetching needed modules") @@ -39,9 +38,13 @@ def main(): parser.add_option("--ise-file", dest="ise", help="specify .xise file for other actions", metavar="ISE") parser.add_option("--synth-server", dest="synth_server", default=None, help="use given SERVER for remote synthesis", metavar="SERVER") parser.add_option("--synth-user", dest="synth_user", default=None, help="use given USER for remote synthesis", metavar="USER") - (global_mod.options, args) = parser.parse_args() + (options, args) = parser.parse_args() + global_mod.options = options - # check if manifest is given in the command line + if options.manifest_help == True: + ManifestParser().help() + quit() + # check if manifest is given in the command line # if yes, then use it # if no, the look for it in the current directory (python manifest has priority) file = None @@ -49,7 +52,7 @@ def main(): file = "manifest.py" elif os.path.exists("Manifest.py"): file = "Manifest.py" - + if file != None: top_manifest = Manifest(path=os.path.abspath(file)) global_mod.top_module = Module(manifest=top_manifest, parent=None, source="local", fetchto=".") @@ -58,10 +61,7 @@ def main(): p.echo("No manifest found. At least an empty one is needed") quit() - if global_mod.options.synth_server != None: - global_mod.synth_server = global_mod.options.synth_server - if global_mod.options.synth_user != None: - global_mod.synth_user = global_mod.options.synth_user + global_mod.ssh = Connection(options.synth_user, options.synth_server) #if global_mod.options.tcl == None: # if global_mod.opt_map.tcl == None: #option taken, but no tcl given -> find it @@ -73,8 +73,7 @@ def main(): #else: # global_mod.opt_map.tcl = global_mod.options.tcl - #if global_mod.options.manifest_help == True: - # ManifestParser().print_help() + if global_mod.options.fetch == True: fetch() elif global_mod.options.local == True: @@ -133,78 +132,62 @@ def inject_into_ise(): quit() tm = global_mod.top_module - module_files_dict = make_list_of_files(file_type="vhd") - p.vprint("List of used files") - p.vpprint(module_files_dict) - depend.inject_files_into_ise(global_mod.options.ise_project, files) def generate_makefile(): import depend tm = global_mod.top_module vhdl_deps = tm.generate_deps_for_vhdl_in_modules() - sv_files = tm.make_lsit_of depend.generate_makefile(vhdl_deps) #NOT YET TRANSFORMED INTO CLASSES def remote_synthesis(): - if global_mod.opt_map.tcl == None: #option not taken but mandatory - p.echo("For Xilinx synthesis a .tcl file in the top module is required") - quit() - if not os.path.exists(global_mod.opt_map.tcl): - p.echo("Given .tcl doesn't exist: " + global_mod.opt_map.tcl) - quit() - p.vprint("The program will be using ssh connection: "+global_mod.synth_user+"@"+global_mod.synth_server) - global_mod.ssh = Connection(global_mod.synth_user, global_mod.synth_server) + ssh = global_mod.ssh + tm = global_mod.top_module - if not global_mod.ssh.is_good(): - p.echo("SSH connection failure.") + p.vprint("The program will be using ssh connection: "+str(ssh)) + if not ssh.is_good(): + p.echo("SSH connection failure. Remote host doesn't response.") quit() - if not os.path.exists(global_mod.fetchto): + if not os.path.exists(tm.fetchto): p.echo("There are no modules fetched. Are you sure it's correct?") - module_manifest_dict = path.make_list_of_modules(global_mod.top_manifest, global_mod.opt_map) - p.vprint ("Modules: ") - p.vpprint(module_manifest_dict) - - module_files_dict = path.make_list_of_files(module_manifest_dict) - files = [] - for module in module_files_dict: - files.extend(module_files_dict[module]) + modules = global_mod.top_module.make_list_of_modules() - p.vprint("Files that will be transfered") - p.vpprint(files) - dest_folder = global_mod.ssh.transfer_files_forth(files, global_mod.opt_map.name) + files = [file for mod in modules for file in mod.files] + dest_folder = ssh.transfer_files_forth(files, tm.name) - ret = global_mod.ssh.system("[ -e /opt/Xilinx/"+global_mod.opt_map.ise+" ]") + ret = ssh.system("[ -e /opt/Xilinx/" + tm.ise + " ]") if ret == 1: - p.echo("There is no "+global_mod.opt_map.ise+" ISE version installed on the remote machine") + p.echo("There is no " + tm.ise + " ISE version installed on the remote machine") quit() p.vprint("Checking address length at synthesis server") - address_length = check_address_length(global_mod.ssh) + address_length = ssh.check_address_length() if address_length == 32 or address_length == None: - path_ext = global_mod.ise_path_32[global_mod.opt_map.ise] + path_ext = global_mod.ise_path_32[tm.ise] else: - path_ext = global_mod.ise_path_64[global_mod.opt_map.ise] - - syn_cmd ="PATH=$PATH:"+path_ext+"&& cd "+dest_folder+global_mod.cwd+"/"+os.path.dirname(global_mod.opt_map.tcl) + path_ext = global_mod.ise_path_64[tm.ise] + cwd = os.getcwd() + quit() +#### tu zmienic (jak Tomek rozczai) + syn_cmd ="PATH=$PATH:"+path_ext+"&& cd "+dest_folder+cwd+"/"+os.path.dirname(global_mod.opt_map.tcl) syn_cmd += "&& xtclsh "+os.path.basename(global_mod.opt_map.tcl)+" run_process" - - p.vprint("Launching synthesis on " + global_mod.synth_server + ": " + syn_cmd) - ret = global_mod.ssh.system(syn_cmd) +### + p.vprint("Launching synthesis on " + str(ssh) + ": " + syn_cmd) + ret = ssh.system(syn_cmd) if ret == 1: p.echo("Synthesis failed. Nothing will be transfered back") quit() cur_dir = os.path.basename(global_mod.cwd) os.chdir("..") - global_mod.ssh.transfer_files_back(dest_folder+global_mod.cwd) + ssh.transfer_files_back(dest_folder+global_mod.cwd) os.chdir(cur_dir) if global_mod.options.no_del != True: p.echo("Deleting synthesis folder") - global_mod.ssh.system('rm -rf ' + dest_folder) + ssh.system('rm -rf ' + dest_folder) def local_synthesis(): if global_mod.options.tcl == None: @@ -238,8 +221,5 @@ def generate_list_makefile(): if __name__ == "__main__": #global options' map for use in the entire script t0 = None - global_mod.synth_user = "htsynth" - global_mod.synth_server = "htsynth" global_mod.cwd = os.getcwd() - #globa_mod.ssh = myssh.MySSH(global_mod.synth_user, global_mod.synth_server) main() diff --git a/synthesis/helper_classes.py b/synthesis/helper_classes.py index 584e5bc1..295de713 100644 --- a/synthesis/helper_classes.py +++ b/synthesis/helper_classes.py @@ -31,11 +31,16 @@ class ManifestParser(ConfigParser): self.add_option('tcl', default=None, help="Path to .tcl file used in synthesis", type='') self.add_option('ise', default=None, help="Version of ISE to be used in synthesis", type='') self.add_type('ise', type=1) + self.add_option('vsim_opt', default="", help="Additional options for vsim", type='') self.add_option('vcom_opt', default="", help="Additional options for vcom", type='') self.add_option('vlog_opt', default="", help="Additional options for vlog", type='') self.add_option('vmap_opt', default="", help="Additional options for vmap", type='') self.add_option('modules', default={}, help="List of local modules", type={}) + self.add_allowed_key('modules', key="svn") + self.add_allowed_key('modules', key="git") + self.add_allowed_key('modules', key="local") + self.add_option('library', default="work", help="Destination library for module's VHDL files", type="") self.add_option('files', default=[], help="List of files from the current module", type='') @@ -71,6 +76,9 @@ class SourceFile: ext = tmp[len(tmp)-1] return ext + def isdir(self): + return os.path.isdir(self.path) + def search_for_use(self): """ Reads a file and looks for 'use' clause. For every 'use' with diff --git a/synthesis/module.py b/synthesis/module.py index fab18102..b37ba3bf 100644 --- a/synthesis/module.py +++ b/synthesis/module.py @@ -64,8 +64,7 @@ class Module(object): elif os.path.exists(os.path.join(self.options["fetchto"], basename)): self.options["isfetched"] = True self.path = os.path.join(self.options["fetchto"], basename) - else: - print os.path.join(self.options["fetchto"], basename) + self.options["library"] = "work" self.parse_manifest() @@ -143,27 +142,30 @@ class Module(object): else: self.local = [] - if opt_map["files"] == None: - directory = os.path.dirname(self.path) - print "listing" + directory + self.library = opt_map["library"] + if opt_map["files"] == []: files = [] - for file in os.listdir(directory): - path = os.path.join(directory, file) - files.append(path) + for filename in os.listdir(self.path): + path = os.path.join(self.path, filename) + if not os.path.isdir(path): + file = SourceFile(path=path) + file.library = self.library + files.append(file) self.files = files else: - files = [] + paths = [] for path in opt_map["files"]: if not path_mod.is_abs_path(path): - files.append(path_mod.rel2abs(path, self.path)) + path = path_mod.rel2abs(path, self.path) + paths.append(path) else: p.echo(path + " is an absolute path. Omitting.") - self.files = files + self.__make_list_of_files(paths=paths) if "svn" in opt_map["modules"]: opt_map["modules"]["svn"] = self.make_list_(opt_map["modules"]["svn"]) svn = [] - for url in opt_map.svn: + for url in opt_map["modules"]["svn"]: svn.append(Module(url=url, source="svn", fetchto=fetchto, parent=self)) self.svn = svn else: @@ -172,7 +174,7 @@ class Module(object): if "git" in opt_map["modules"]: opt_map["modules"]["git"] = self.make_list_(opt_map["modules"]["git"]) git = [] - for url in opt_map.git: + for url in opt_map["modules"]["git"]: git.append(Module(url=url, source="git", fetchto=fetchto, parent=self)) self.git = git else: @@ -183,7 +185,9 @@ class Module(object): self.vlog_opt = opt_map["vlog_opt"] self.isparsed = True - self.library = opt_map["library"] + + + self.name = opt_map["name"] #if self.isfetched == True: <- avoid getting all files # self.make_list_of_files() @@ -318,7 +322,7 @@ class Module(object): p.vprint("No modules were found in " + self.fetchto) return modules - def make_list_of_files(self, file_type = None, ret_class = SourceFile): + def __make_list_of_files(self, paths, file_type = None, ret_class = SourceFile): def get_files_(path, file_type = None, ret_class = SourceFile): """ Get lists of normal files and list folders recursively @@ -340,18 +344,17 @@ class Module(object): return ret files = [] - - if self.manifest != None: - if self.files != []: - for file in self.files: - if os.path.isdir(file): - files.extend(get_files_(path=file, file_type=file_type, ret_class=ret_class)) - else: - files.append(ret_class(file)) + for path in paths: + if os.path.isdir(path): + files.extend(get_files_(path, file_type=file_type, ret_class=ret_class)) else: - files.extend(get_files_(path=self.path, file_type=file_type, ret_class=ret_class)) - else: - files = get_files_(path=self.path, file_type=file_type, ret_class=ret_class) + if file_type == None: + files.append(ret_class(path=path)) + else: + tmp = filename.rsplit('.') + ext = tmp[len(tmp)-1] + if ext == file_type: + files.append( ret_class(path=path) ) for file in files: file.library = self.library self.files = files diff --git a/synthesis/msg.py b/synthesis/msg.py index b1188294..6a7f0d85 100644 --- a/synthesis/msg.py +++ b/synthesis/msg.py @@ -7,6 +7,9 @@ import os import sys import pprint as prettyprinter +def rawprint(msg): + print msg + def echo(msg): print(("["+os.path.basename(sys.argv[0]) + " " + "%.5f" % (time.time()-global_mod.t0) + "]: " + str(msg))) -- GitLab