Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: sky/tools/webkitpy/style/optparser.py

Issue 675343003: Prune a bunch of webkitpy. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1. Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
13 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
16 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23 """Supports the parsing of command-line options for check-webkit-style."""
24
25 import logging
26 from optparse import OptionParser
27 import os.path
28 import sys
29
30 from filter import validate_filter_rules
31 # This module should not import anything from checker.py.
32
33 _log = logging.getLogger(__name__)
34
35 _USAGE = """usage: %prog [--help] [options] [path1] [path2] ...
36
37 Overview:
38 Check coding style according to WebKit style guidelines:
39
40 http://webkit.org/coding/coding-style.html
41
42 Path arguments can be files and directories. If neither a git commit nor
43 paths are passed, then all changes in your source control working directory
44 are checked.
45
46 Style errors:
47 This script assigns to every style error a confidence score from 1-5 and
48 a category name. A confidence score of 5 means the error is certainly
49 a problem, and 1 means it could be fine.
50
51 Category names appear in error messages in brackets, for example
52 [whitespace/indent]. See the options section below for an option that
53 displays all available categories and which are reported by default.
54
55 Filters:
56 Use filters to configure what errors to report. Filters are specified using
57 a comma-separated list of boolean filter rules. The script reports errors
58 in a category if the category passes the filter, as described below.
59
60 All categories start out passing. Boolean filter rules are then evaluated
61 from left to right, with later rules taking precedence. For example, the
62 rule "+foo" passes any category that starts with "foo", and "-foo" fails
63 any such category. The filter input "-whitespace,+whitespace/braces" fails
64 the category "whitespace/tab" and passes "whitespace/braces".
65
66 Examples: --filter=-whitespace,+whitespace/braces
67 --filter=-whitespace,-runtime/printf,+runtime/printf_format
68 --filter=-,+build/include_what_you_use
69
70 Paths:
71 Certain style-checking behavior depends on the paths relative to
72 the WebKit source root of the files being checked. For example,
73 certain types of errors may be handled differently for files in
74 WebKit/gtk/webkit/ (e.g. by suppressing "readability/naming" errors
75 for files in this directory).
76
77 Consequently, if the path relative to the source root cannot be
78 determined for a file being checked, then style checking may not
79 work correctly for that file. This can occur, for example, if no
80 WebKit checkout can be found, or if the source root can be detected,
81 but one of the files being checked lies outside the source tree.
82
83 If a WebKit checkout can be detected and all files being checked
84 are in the source tree, then all paths will automatically be
85 converted to paths relative to the source root prior to checking.
86 This is also useful for display purposes.
87
88 Currently, this command can detect the source root only if the
89 command is run from within a WebKit checkout (i.e. if the current
90 working directory is below the root of a checkout). In particular,
91 it is not recommended to run this script from a directory outside
92 a checkout.
93
94 Running this script from a top-level WebKit source directory and
95 checking only files in the source tree will ensure that all style
96 checking behaves correctly -- whether or not a checkout can be
97 detected. This is because all file paths will already be relative
98 to the source root and so will not need to be converted."""
99
100 _EPILOG = ("This script can miss errors and does not substitute for "
101 "code review.")
102
103
104 # This class should not have knowledge of the flag key names.
105 class DefaultCommandOptionValues(object):
106
107 """Stores the default check-webkit-style command-line options.
108
109 Attributes:
110 output_format: A string that is the default output format.
111 min_confidence: An integer that is the default minimum confidence level.
112
113 """
114
115 def __init__(self, min_confidence, output_format):
116 self.min_confidence = min_confidence
117 self.output_format = output_format
118
119
120 # This class should not have knowledge of the flag key names.
121 class CommandOptionValues(object):
122
123 """Stores the option values passed by the user via the command line.
124
125 Attributes:
126 is_verbose: A boolean value of whether verbose logging is enabled.
127
128 filter_rules: The list of filter rules provided by the user.
129 These rules are appended to the base rules and
130 path-specific rules and so take precedence over
131 the base filter rules, etc.
132
133 git_commit: A string representing the git commit to check.
134 The default is None.
135
136 min_confidence: An integer between 1 and 5 inclusive that is the
137 minimum confidence level of style errors to report.
138 The default is 1, which reports all errors.
139
140 output_format: A string that is the output format. The supported
141 output formats are "emacs" which emacs can parse
142 and "vs7" which Microsoft Visual Studio 7 can parse.
143
144 """
145 def __init__(self,
146 filter_rules=None,
147 git_commit=None,
148 diff_files=None,
149 is_verbose=False,
150 min_confidence=1,
151 output_format="emacs"):
152 if filter_rules is None:
153 filter_rules = []
154
155 if (min_confidence < 1) or (min_confidence > 5):
156 raise ValueError('Invalid "min_confidence" parameter: value '
157 "must be an integer between 1 and 5 inclusive. "
158 'Value given: "%s".' % min_confidence)
159
160 if output_format not in ("emacs", "vs7"):
161 raise ValueError('Invalid "output_format" parameter: '
162 'value must be "emacs" or "vs7". '
163 'Value given: "%s".' % output_format)
164
165 self.filter_rules = filter_rules
166 self.git_commit = git_commit
167 self.diff_files = diff_files
168 self.is_verbose = is_verbose
169 self.min_confidence = min_confidence
170 self.output_format = output_format
171
172 # Useful for unit testing.
173 def __eq__(self, other):
174 """Return whether this instance is equal to another."""
175 if self.filter_rules != other.filter_rules:
176 return False
177 if self.git_commit != other.git_commit:
178 return False
179 if self.diff_files != other.diff_files:
180 return False
181 if self.is_verbose != other.is_verbose:
182 return False
183 if self.min_confidence != other.min_confidence:
184 return False
185 if self.output_format != other.output_format:
186 return False
187
188 return True
189
190 # Useful for unit testing.
191 def __ne__(self, other):
192 # Python does not automatically deduce this from __eq__().
193 return not self.__eq__(other)
194
195
196 class ArgumentPrinter(object):
197
198 """Supports the printing of check-webkit-style command arguments."""
199
200 def _flag_pair_to_string(self, flag_key, flag_value):
201 return '--%(key)s=%(val)s' % {'key': flag_key, 'val': flag_value }
202
203 def to_flag_string(self, options):
204 """Return a flag string of the given CommandOptionValues instance.
205
206 This method orders the flag values alphabetically by the flag key.
207
208 Args:
209 options: A CommandOptionValues instance.
210
211 """
212 flags = {}
213 flags['min-confidence'] = options.min_confidence
214 flags['output'] = options.output_format
215 # Only include the filter flag if user-provided rules are present.
216 filter_rules = options.filter_rules
217 if filter_rules:
218 flags['filter'] = ",".join(filter_rules)
219 if options.git_commit:
220 flags['git-commit'] = options.git_commit
221 if options.diff_files:
222 flags['diff_files'] = options.diff_files
223
224 flag_string = ''
225 # Alphabetizing lets us unit test this method.
226 for key in sorted(flags.keys()):
227 flag_string += self._flag_pair_to_string(key, flags[key]) + ' '
228
229 return flag_string.strip()
230
231
232 class ArgumentParser(object):
233
234 # FIXME: Move the documentation of the attributes to the __init__
235 # docstring after making the attributes internal.
236 """Supports the parsing of check-webkit-style command arguments.
237
238 Attributes:
239 create_usage: A function that accepts a DefaultCommandOptionValues
240 instance and returns a string of usage instructions.
241 Defaults to the function that generates the usage
242 string for check-webkit-style.
243 default_options: A DefaultCommandOptionValues instance that provides
244 the default values for options not explicitly
245 provided by the user.
246 stderr_write: A function that takes a string as a parameter and
247 serves as stderr.write. Defaults to sys.stderr.write.
248 This parameter should be specified only for unit tests.
249
250 """
251
252 def __init__(self,
253 all_categories,
254 default_options,
255 base_filter_rules=None,
256 mock_stderr=None,
257 usage=None):
258 """Create an ArgumentParser instance.
259
260 Args:
261 all_categories: The set of all available style categories.
262 default_options: See the corresponding attribute in the class
263 docstring.
264 Keyword Args:
265 base_filter_rules: The list of filter rules at the beginning of
266 the list of rules used to check style. This
267 list has the least precedence when checking
268 style and precedes any user-provided rules.
269 The class uses this parameter only for display
270 purposes to the user. Defaults to the empty list.
271 create_usage: See the documentation of the corresponding
272 attribute in the class docstring.
273 stderr_write: See the documentation of the corresponding
274 attribute in the class docstring.
275
276 """
277 if base_filter_rules is None:
278 base_filter_rules = []
279 stderr = sys.stderr if mock_stderr is None else mock_stderr
280 if usage is None:
281 usage = _USAGE
282
283 self._all_categories = all_categories
284 self._base_filter_rules = base_filter_rules
285
286 # FIXME: Rename these to reflect that they are internal.
287 self.default_options = default_options
288 self.stderr_write = stderr.write
289
290 self._parser = self._create_option_parser(stderr=stderr,
291 usage=usage,
292 default_min_confidence=self.default_options.min_confidence,
293 default_output_format=self.default_options.output_format)
294
295 def _create_option_parser(self, stderr, usage,
296 default_min_confidence, default_output_format):
297 # Since the epilog string is short, it is not necessary to replace
298 # the epilog string with a mock epilog string when testing.
299 # For this reason, we use _EPILOG directly rather than passing it
300 # as an argument like we do for the usage string.
301 parser = OptionParser(usage=usage, epilog=_EPILOG)
302
303 filter_help = ('set a filter to control what categories of style '
304 'errors to report. Specify a filter using a comma-'
305 'delimited list of boolean filter rules, for example '
306 '"--filter -whitespace,+whitespace/braces". To display '
307 'all categories and which are enabled by default, pass '
308 """no value (e.g. '-f ""' or '--filter=').""")
309 parser.add_option("-f", "--filter-rules", metavar="RULES",
310 dest="filter_value", help=filter_help)
311
312 git_commit_help = ("check all changes in the given commit. "
313 "Use 'commit_id..' to check all changes after commmit _id")
314 parser.add_option("-g", "--git-diff", "--git-commit",
315 metavar="COMMIT", dest="git_commit", help=git_commit_h elp,)
316
317 diff_files_help = "diff the files passed on the command line rather than checking the style of every line"
318 parser.add_option("--diff-files", action="store_true", dest="diff_files" , default=False, help=diff_files_help)
319
320 min_confidence_help = ("set the minimum confidence of style errors "
321 "to report. Can be an integer 1-5, with 1 "
322 "displaying all errors. Defaults to %default.")
323 parser.add_option("-m", "--min-confidence", metavar="INT",
324 type="int", dest="min_confidence",
325 default=default_min_confidence,
326 help=min_confidence_help)
327
328 output_format_help = ('set the output format, which can be "emacs" '
329 'or "vs7" (for Visual Studio). '
330 'Defaults to "%default".')
331 parser.add_option("-o", "--output-format", metavar="FORMAT",
332 choices=["emacs", "vs7"],
333 dest="output_format", default=default_output_format,
334 help=output_format_help)
335
336 verbose_help = "enable verbose logging."
337 parser.add_option("-v", "--verbose", dest="is_verbose", default=False,
338 action="store_true", help=verbose_help)
339
340 # Override OptionParser's error() method so that option help will
341 # also display when an error occurs. Normally, just the usage
342 # string displays and not option help.
343 parser.error = self._parse_error
344
345 # Override OptionParser's print_help() method so that help output
346 # does not render to the screen while running unit tests.
347 print_help = parser.print_help
348 parser.print_help = lambda file=stderr: print_help(file=file)
349
350 return parser
351
352 def _parse_error(self, error_message):
353 """Print the help string and an error message, and exit."""
354 # The method format_help() includes both the usage string and
355 # the flag options.
356 help = self._parser.format_help()
357 # Separate help from the error message with a single blank line.
358 self.stderr_write(help + "\n")
359 if error_message:
360 _log.error(error_message)
361
362 # Since we are using this method to replace/override the Python
363 # module optparse's OptionParser.error() method, we match its
364 # behavior and exit with status code 2.
365 #
366 # As additional background, Python documentation says--
367 #
368 # "Unix programs generally use 2 for command line syntax errors
369 # and 1 for all other kind of errors."
370 #
371 # (from http://docs.python.org/library/sys.html#sys.exit )
372 sys.exit(2)
373
374 def _exit_with_categories(self):
375 """Exit and print the style categories and default filter rules."""
376 self.stderr_write('\nAll categories:\n')
377 for category in sorted(self._all_categories):
378 self.stderr_write(' ' + category + '\n')
379
380 self.stderr_write('\nDefault filter rules**:\n')
381 for filter_rule in sorted(self._base_filter_rules):
382 self.stderr_write(' ' + filter_rule + '\n')
383 self.stderr_write('\n**The command always evaluates the above rules, '
384 'and before any --filter flag.\n\n')
385
386 sys.exit(0)
387
388 def _parse_filter_flag(self, flag_value):
389 """Parse the --filter flag, and return a list of filter rules.
390
391 Args:
392 flag_value: A string of comma-separated filter rules, for
393 example "-whitespace,+whitespace/indent".
394
395 """
396 filters = []
397 for uncleaned_filter in flag_value.split(','):
398 filter = uncleaned_filter.strip()
399 if not filter:
400 continue
401 filters.append(filter)
402 return filters
403
404 def parse(self, args):
405 """Parse the command line arguments to check-webkit-style.
406
407 Args:
408 args: A list of command-line arguments as returned by sys.argv[1:].
409
410 Returns:
411 A tuple of (paths, options)
412
413 paths: The list of paths to check.
414 options: A CommandOptionValues instance.
415
416 """
417 (options, paths) = self._parser.parse_args(args=args)
418
419 filter_value = options.filter_value
420 git_commit = options.git_commit
421 diff_files = options.diff_files
422 is_verbose = options.is_verbose
423 min_confidence = options.min_confidence
424 output_format = options.output_format
425
426 if filter_value is not None and not filter_value:
427 # Then the user explicitly passed no filter, for
428 # example "-f ''" or "--filter=".
429 self._exit_with_categories()
430
431 # Validate user-provided values.
432
433 min_confidence = int(min_confidence)
434 if (min_confidence < 1) or (min_confidence > 5):
435 self._parse_error('option --min-confidence: invalid integer: '
436 '%s: value must be between 1 and 5'
437 % min_confidence)
438
439 if filter_value:
440 filter_rules = self._parse_filter_flag(filter_value)
441 else:
442 filter_rules = []
443
444 try:
445 validate_filter_rules(filter_rules, self._all_categories)
446 except ValueError, err:
447 self._parse_error(err)
448
449 options = CommandOptionValues(filter_rules=filter_rules,
450 git_commit=git_commit,
451 diff_files=diff_files,
452 is_verbose=is_verbose,
453 min_confidence=min_confidence,
454 output_format=output_format)
455
456 return (paths, options)
457
OLDNEW
« no previous file with comments | « sky/tools/webkitpy/style/main_unittest.py ('k') | sky/tools/webkitpy/style/optparser_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698