3 # Copyright 2008 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Custom optparse OptionParser and functions for reading Python settings files.
19 Default values for trunk/scripts flags can be specified in valid Python syntax
20 in the ~/.soc_scripts_settings file. For example, a default value for the
21 --user flag can be specified with a variable assignment in the settings file
26 Defaults in the ~/.soc_scripts_settings file can be explicitly overridden by
27 supplied the actual flag. For example, supplying:
31 would override the default value present in the settings file.
33 Option: class derived from optparse.Option that adds a 'required' parameter
34 OptionParser: class derived from optparse.OptionParser for use with Option
36 readPythonSettings(): interprets a valid Python file as a settings file
40 # alphabetical order by last name, please
41 '"Todd Larsen" <tlarsen@google.com>',
50 DEF_SETTINGS_FILE_DIR
= "~"
51 DEF_SETTINGS_FILE_NAME
= '.soc_scripts_settings'
54 class Error(Exception):
55 """Base exception class for all exceptions in the settings module."""
59 class Option(optparse
.Option
):
60 """Class derived from optparse.Option that adds a 'required' parameter."""
62 ATTRS
= optparse
.Option
.ATTRS
+ ['required']
64 def _check_required(self
):
65 """Insures that 'required' option can accept a value."""
66 if self
.required
and (not self
.takes_value()):
67 raise optparse
.OptionError(
68 "'required' parameter set for option that does not take a value",
71 # Make sure _check_required() is called from the constructor!
72 CHECK_METHODS
= optparse
.Option
.CHECK_METHODS
+ [_check_required
]
74 def process(self
, opt
, value
, values
, parser
):
75 optparse
.Option
.process(self
, opt
, value
, values
, parser
)
76 parser
.option_seen
[self
] = 1
79 class OptionParser(optparse
.OptionParser
):
80 """Class derived from optparse.OptionParser for use with Option."""
82 def _init_parsing_state(self
):
83 """Sets up dict to track options seen so far."""
84 optparse
.OptionParser
._init
_parsing
_state
(self
)
87 def error(self
, *args
):
88 """Convert errors reported by optparse.OptionParser to Error exceptions.
91 *args: passed through to the Error exception __init__() constructor,
92 usually a list of strings
95 Error with the supplied *args
99 def check_values(self
, values
, args
):
100 """Checks to make sure all required=True options were supplied.
103 values, args: passed through unchanged (see Returns:)
106 (values, args) unchanged.
109 Error if an option was not supplied that had required=True; exception
110 positional arguments are the error message strings.
114 for option
in self
.option_list
:
115 if (isinstance(option
, Option
)
117 and (not self
.option_seen
.has_key(option
))):
119 'required %s option not supplied'
120 ' (and default settings not allowed)' % option
)
128 def printErrors(errors
, exit_code
=1):
129 """Prints error message strings to sys.stderr and returns an exit code.
132 errors: error message string or list of error message strings to be printed
134 exit_code: exit code to return (so that this function can be used as an
135 expression in sys.exit() for example); default is 1
140 sys
.stderr
.write('\nERRORS:\n')
142 if (not isinstance(errors
, tuple)) and (not isinstance(errors
, list)):
146 sys
.stderr
.write(' %s\n' % msg
)
148 sys
.stderr
.write('\n')
153 def printErrorsAndUsage(errors
, parser
, exit_code
=1):
154 """Prints error messages and usage text to sys.stderr and returns exit code.
157 errors: error message string or list of error message strings to be printed
159 parser: OptionParser with a print_help() method
160 exit_code: exit code to return (so that this function can be used as an
161 expression in sys.exit() for example); default is 1
166 exit_code
= printErrors(errors
, exit_code
=exit_code
)
167 parser
.print_help(file=sys
.stderr
)
172 def getExpandedPath(path
):
173 """Returns an expanded, normalized, absolute path.
176 path: path (possibly relative, possibly containing environment variables,
177 etc.) to be expanded, normalized and made absolute
180 absolute path, after expanding any environment variables and "~", then
181 removing excess . and .. path elements
183 return os
.path
.abspath(
186 os
.path
.expandvars(path
))))
189 def readPythonSettings(defaults
={}, # {} OK since defaults is always copied
190 settings_dir
=DEF_SETTINGS_FILE_DIR
,
191 settings_file
=DEF_SETTINGS_FILE_NAME
):
192 """Executes a Python-syntax settings file and returns the local variables.
195 defaults: dict of default values to use when settings are not present
196 in the settings file (or if no settings file is present at all); this
197 dict is *copied* and is not altered at all
198 settings_dir: optional directory containing settings_file
199 settings_file: optional settings file name found in settings_dir
202 dict of setting name/value pairs (possibly including some values from the
203 defaults parameter). Since the settings file is full-fledged Python
204 source, the values could be any valid Python object.
207 Error if some error occurred parsing the present settings file; exception
208 positional arguments are the error message strings.
210 # do not let the original defaults be altered
211 defaults
= defaults
.copy()
213 # form absolute path to the settings file, expanding any environment
214 # variables and "~", then removing excess . and .. path elements
215 path
= getExpandedPath(os
.path
.join(settings_dir
, settings_file
))
217 # empty dict to capture the local variables in the settings file
221 # execute the Python source file and recover the local variables as settings
222 execfile(path
, {}, settings_locals
)
224 # If the settings file is not present, there are no defaults.
226 except Exception, error
:
227 # Other exceptions usually mean a faulty settings file.
229 'faulty settings file:',
230 (' %s: %s' % (error
.__class
__.__name
__, str(error
))),
233 # overwrite defaults copy with values from the (possibly empty) settings file
234 defaults
.update(settings_locals
)
239 def readPythonSettingsOrDie(parser
=None, **kwargs
):
240 """Calls readPythonSettings(), calling sys.exit() on any errors.
243 parser: if supplied, an OptionParser instance used to call print_help()
244 to print usage information if errors occur
245 **kwargs: see readPythonSettings()
248 On success, returns results of readPythonSettings().
251 On any error from readPythonSettings(), prints error messages to stderr,
252 possibly prints usage information, and calls sys.exit(1).
255 return readPythonSettings(**kwargs
)
258 sys
.exit(printErrorsAndUsage(error
.args
, parser
))
260 sys
.exit(printErrors(error
.args
))
263 def makeOptionParserOrDie(*args
, **kwargs
):
264 """Creates and returns an OptionParser, calling sys.exit() on any errors.
267 *args, **kwargs: supplied directly to OptionParser constructor
270 On success, returns an OptionParser instance.
273 On any error, prints error messages to stderr and calls sys.exit(1).
276 return OptionParser(*args
, **kwargs
)
278 sys
.exit(printErrors(error
.args
))
281 def parseOptionsOrDie(parser
, args
):
282 """Parses command-line options, calling sys.exit() on any errors.
285 parser: an OptionParser instance
286 args: list of command-line arguments to supply to parser
289 On success, returns (options, args) returned by parser.parse_args(args).
292 On any error, prints error messages and usage information to stderr and
296 return parser
.parse_args(args
)
298 sys
.exit(printErrorsAndUsage(error
.args
, parser
))
301 def checkCommonSvnOptions(options
):
302 """Checks a common subset of command-line options.
304 Multiple scripts accept a subset of common command-line options. This
305 function does some sanity checks on these flags. These checks are collected
306 here because they were being duplicated in multiple scripts.
309 options: OptionParser.parse_args() options instance to check
312 list of error message strings, or an empty list if no errors
318 ['--repo must be supplied or have a settings file default'])
322 ['--wc must be supplied or have a settings file default'])
324 if not options
.branch
:
327 ['at least one of --branch or --user must be supplied'])
332 def checkCommonSvnOptionsOrDie(options
, parser
):
333 """Checks subset of command-line options, calling sys.exit() on any errors.
336 options: see checkCommonSvnOptions()
337 parser: an OptionParser instance used to call print_help() to print
338 usage information if errors occur
341 On any error messages returned by checkCommonSvnOptions(), prints error
342 messages and usage information to stderr and calls sys.exit(1).
344 errors
= checkCommonSvnOptions(options
)
347 sys
.exit(printErrorsAndUsage(errors
, parser
))