mirror of
https://github.com/ejwa/gitinspector.git
synced 2024-12-22 13:42:14 +01:00
Changed the argument parsing back to using getopt!
The support for optional boolean arguments is the same; but uses getopt instead of optparse. The whole adventure with optparse was a giant waste of time and just forced us to monkey-patch optparse with some very ugly solutions in order to make it do what we wanted; thus it was better to switch back to the more low-level getopt module. To accomplish this; a optval.gnu_getopt() function was added that is a duplicate of the original getopt.gnu_getopt function but with support for optional arguments. A long option which accepts an optional argument is denoted with arg:default_value in the long_options string. In the end, this solution feels much better than the one with optparse.
This commit is contained in:
parent
cae99cb3f7
commit
7f0e2b6fe8
5 changed files with 122 additions and 188 deletions
|
@ -1,34 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2013 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
def unicode(string):
|
||||
return str(string)
|
||||
|
||||
def convert_command_line():
|
||||
try:
|
||||
for num, arg in enumerate(sys.argv):
|
||||
sys.argv[num] = unicode(arg.decode("utf-8", "replace"))
|
||||
except AttributeError:
|
||||
pass
|
|
@ -26,7 +26,7 @@ import textwrap
|
|||
|
||||
DEFAULT_EXTENSIONS = ["java", "c", "cpp", "h", "hpp", "py", "glsl", "rb", "js", "sql"]
|
||||
|
||||
__extensions__ = None
|
||||
__extensions__ = DEFAULT_EXTENSIONS
|
||||
__located_extensions__ = set()
|
||||
|
||||
def get():
|
||||
|
|
|
@ -26,10 +26,11 @@ import os
|
|||
import zipfile
|
||||
|
||||
__available_formats__ = ["html", "htmlembedded", "text", "xml"]
|
||||
__selected_format__ = None
|
||||
|
||||
DEFAULT_FORMAT = __available_formats__[2]
|
||||
|
||||
__selected_format__ = DEFAULT_FORMAT
|
||||
|
||||
class InvalidFormatError(Exception):
|
||||
pass
|
||||
|
||||
|
|
|
@ -24,20 +24,15 @@ from __future__ import unicode_literals
|
|||
import localization
|
||||
localization.init()
|
||||
|
||||
try:
|
||||
from compatibility import unicode
|
||||
except:
|
||||
pass
|
||||
|
||||
import blame
|
||||
import changes
|
||||
import compatibility
|
||||
import config
|
||||
import extensions
|
||||
import filtering
|
||||
import format
|
||||
import help
|
||||
import interval
|
||||
import getopt
|
||||
import metrics
|
||||
import missing
|
||||
import os
|
||||
|
@ -51,11 +46,21 @@ import timeline
|
|||
import version
|
||||
|
||||
class Runner:
|
||||
def __init__(self, opts):
|
||||
self.opts = opts
|
||||
def __init__(self):
|
||||
self.hard = False
|
||||
self.include_metrics = False
|
||||
self.list_file_types = False
|
||||
self.localize_output = False
|
||||
self.repo = "."
|
||||
self.responsibilities = False
|
||||
self.grading = False
|
||||
self.timeline = False
|
||||
self.useweeks = False
|
||||
|
||||
def output(self):
|
||||
if not self.localize_output:
|
||||
localization.disable()
|
||||
|
||||
terminal.skip_escapes(not sys.stdout.isatty())
|
||||
terminal.set_stdout_encoding()
|
||||
previous_directory = os.getcwd()
|
||||
|
@ -69,43 +74,25 @@ class Runner:
|
|||
|
||||
os.chdir(absolute_path[0].decode("utf-8", "replace").strip())
|
||||
|
||||
if not format.select(self.opts.format):
|
||||
raise format.InvalidFormatError(_("specified output format not supported."))
|
||||
|
||||
if not self.opts.localize_output:
|
||||
localization.disable()
|
||||
|
||||
missing.set_checkout_missing(self.opts.checkout_missing)
|
||||
extensions.define(self.opts.file_types)
|
||||
|
||||
if self.opts.since != None:
|
||||
interval.set_since(self.opts.since)
|
||||
|
||||
if self.opts.until != None:
|
||||
interval.set_until(self.opts.until)
|
||||
|
||||
for ex in self.opts.exclude:
|
||||
filtering.add(ex)
|
||||
|
||||
format.output_header()
|
||||
outputable.output(changes.ChangesOutput(self.opts.hard))
|
||||
outputable.output(changes.ChangesOutput(self.hard))
|
||||
|
||||
if changes.get(self.opts.hard).get_commits():
|
||||
outputable.output(blame.BlameOutput(self.opts.hard))
|
||||
if changes.get(self.hard).get_commits():
|
||||
outputable.output(blame.BlameOutput(self.hard))
|
||||
|
||||
if self.opts.timeline:
|
||||
outputable.output(timeline.Timeline(changes.get(self.opts.hard), self.opts.useweeks))
|
||||
if self.timeline:
|
||||
outputable.output(timeline.Timeline(changes.get(self.hard), self.useweeks))
|
||||
|
||||
if self.opts.metrics:
|
||||
if self.include_metrics:
|
||||
outputable.output(metrics.Metrics())
|
||||
|
||||
if self.opts.responsibilities:
|
||||
outputable.output(responsibilities.ResponsibilitiesOutput(self.opts.hard))
|
||||
if self.responsibilities:
|
||||
outputable.output(responsibilities.ResponsibilitiesOutput(self.hard))
|
||||
|
||||
outputable.output(missing.Missing())
|
||||
outputable.output(filtering.Filtering())
|
||||
|
||||
if self.opts.list_file_types:
|
||||
if self.list_file_types:
|
||||
outputable.output(extensions.Extensions())
|
||||
|
||||
format.output_footer()
|
||||
|
@ -116,62 +103,82 @@ def __check_python_version__():
|
|||
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
|
||||
sys.exit(_("gitinspector requires at leat Python 2.6 to run (version {0} was found).").format(python_version))
|
||||
|
||||
def __handle_help__(__option__, __opt_str__, __value__, __parser__):
|
||||
help.output()
|
||||
sys.exit(0)
|
||||
|
||||
def __handle_version__(__option__, __opt_str__, __value__, __parser__):
|
||||
version.output()
|
||||
sys.exit(0)
|
||||
|
||||
def main():
|
||||
compatibility.convert_command_line()
|
||||
parser = optval.OptionParser(add_help_option=False)
|
||||
__run__ = Runner()
|
||||
|
||||
try:
|
||||
parser.add_option("-c", action="store_true", dest="checkout_missing")
|
||||
parser.add_option("-H", action="store_true", dest="hard")
|
||||
parser.add_option("-l", action="store_true", dest="list_file_types")
|
||||
parser.add_option("-L", action="store_true", dest="localize_output")
|
||||
parser.add_option("-m", action="store_true", dest="metrics")
|
||||
parser.add_option("-r", action="store_true", dest="responsibilities")
|
||||
parser.add_option("-T", action="store_true", dest="timeline")
|
||||
parser.add_option("-w", action="store_true", dest="useweeks")
|
||||
|
||||
optval.add_option(parser, "--checkout-missing", boolean=True)
|
||||
parser.add_option( "-f", "--file-types", type="string", default=",".join(extensions.DEFAULT_EXTENSIONS))
|
||||
parser.add_option( "-F", "--format", type="string", default=format.DEFAULT_FORMAT)
|
||||
optval.add_option(parser, "--grading", boolean=True, multidest=["hard", "metrics", "list_file_types",
|
||||
"responsibilities", "timeline", "useweeks"])
|
||||
parser.add_option( "-h", "--help", action="callback", callback=__handle_help__)
|
||||
optval.add_option(parser, "--hard", boolean=True)
|
||||
optval.add_option(parser, "--list-file-types", boolean=True)
|
||||
optval.add_option(parser, "--localize-output", boolean=True)
|
||||
optval.add_option(parser, "--metrics", boolean=True)
|
||||
optval.add_option(parser, "--responsibilities", boolean=True)
|
||||
parser.add_option( "--since", type="string")
|
||||
optval.add_option(parser, "--timeline", boolean=True)
|
||||
parser.add_option( "--until", type="string")
|
||||
parser.add_option( "--version", action="callback", callback=__handle_version__)
|
||||
optval.add_option(parser, "--weeks", boolean=True, dest="useweeks")
|
||||
parser.add_option( "-x", "--exclude", action="append", type="string", default=[])
|
||||
|
||||
(opts, args) = parser.parse_args()
|
||||
__run__ = Runner(opts)
|
||||
|
||||
for arg in args:
|
||||
__opts__, __args__ = optval.gnu_getopt(sys.argv[1:], "cf:F:hHlLmrTwx:", ["checkout-missing:true", "exclude=",
|
||||
"file-types=", "format=", "hard:true", "help",
|
||||
"list-file-types:true", "localize-output:true",
|
||||
"metrics:true", "responsibilities:true", "since=",
|
||||
"grading:true", "timeline:true", "until=", "version",
|
||||
"weeks:true"])
|
||||
for arg in __args__:
|
||||
__run__.repo = arg
|
||||
|
||||
#We need the repo above to be set before we read the git config.
|
||||
config.init(__run__)
|
||||
|
||||
parser.parse_args(values=opts)
|
||||
for o, a in __opts__:
|
||||
if o in("-c"):
|
||||
missing.set_checkout_missing(True)
|
||||
elif o in("-h", "--help"):
|
||||
help.output()
|
||||
sys.exit(0)
|
||||
elif o in("-f", "--file-types"):
|
||||
extensions.define(a)
|
||||
elif o in("-F", "--format"):
|
||||
if not format.select(a):
|
||||
raise format.InvalidFormatError(_("specified output format not supported."))
|
||||
elif o in("-H"):
|
||||
__run__.hard = True
|
||||
elif o in("--hard"):
|
||||
__run__.hard = optval.get_boolean_argument(a)
|
||||
elif o in("-l"):
|
||||
__run__.list_file_types = True
|
||||
elif o in("--list-file-types"):
|
||||
__run__.list_file_types = optval.get_boolean_argument(a)
|
||||
elif o in("-L"):
|
||||
__run__.localize_output = True
|
||||
elif o in("--localize-output"):
|
||||
__run__.localize_output = optval.get_boolean_argument(a)
|
||||
elif o in("-m"):
|
||||
__run__.include_metrics = True
|
||||
elif o in ("--metrics"):
|
||||
__run__.include_metrics = optval.get_boolean_argument(a)
|
||||
elif o in("-r"):
|
||||
__run__.responsibilities = True
|
||||
elif o in("--responsibilities"):
|
||||
__run__.responsibilities = optval.get_boolean_argument(a)
|
||||
elif o in("--since"):
|
||||
interval.set_since(a)
|
||||
elif o in("--version"):
|
||||
version.output()
|
||||
sys.exit(0)
|
||||
elif o in("--grading"):
|
||||
grading = optval.get_boolean_argument(a)
|
||||
__run__.include_metrics = grading
|
||||
__run__.list_file_types = grading
|
||||
__run__.responsibilities = grading
|
||||
__run__.grading = grading
|
||||
__run__.hard = grading
|
||||
__run__.timeline = grading
|
||||
__run__.useweeks = grading
|
||||
elif o in("-T"):
|
||||
__run__.timeline = True
|
||||
elif o in("--timeline"):
|
||||
__run__.timeline = optval.get_boolean_argument(a)
|
||||
elif o in("--until"):
|
||||
interval.set_until(a)
|
||||
elif o in("-w"):
|
||||
__run__.useweeks = True
|
||||
elif o in("--weeks"):
|
||||
__run__.useweeks = optval.get_boolean_argument(a)
|
||||
elif o in("-x", "--exclude"):
|
||||
filtering.add(a)
|
||||
|
||||
except (format.InvalidFormatError, optval.InvalidOptionArgument, optval.OptionParsingError) as msg:
|
||||
localization.enable()
|
||||
|
||||
print(sys.argv[0], "\b:", end=" ")
|
||||
print(unicode(msg))
|
||||
except (format.InvalidFormatError, optval.InvalidOptionArgument, getopt.error) as msg:
|
||||
print(sys.argv[0], "\b:", msg)
|
||||
print(_("Try `{0} --help' for more information.").format(sys.argv[0]))
|
||||
sys.exit(2)
|
||||
|
||||
|
|
|
@ -17,87 +17,47 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
try:
|
||||
from compatibility import unicode
|
||||
except:
|
||||
pass
|
||||
|
||||
import optparse
|
||||
import sys
|
||||
import getopt
|
||||
|
||||
class InvalidOptionArgument(Exception):
|
||||
pass
|
||||
|
||||
class OptionParsingError(RuntimeError):
|
||||
pass
|
||||
def __find_arg_in_options__(arg, options):
|
||||
for opt in options:
|
||||
if opt[0].find(arg) == 0:
|
||||
return opt
|
||||
|
||||
def __handle_boolean_argument__(option, __opt_str__, value, parser, *__args__, **kwargs):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
elif value == None or value.lower() == "false" or value.lower() == "f" or value == "0":
|
||||
value = False
|
||||
elif value.lower() == "true" or value.lower() == "t" or value == "1":
|
||||
value = True
|
||||
else:
|
||||
raise InvalidOptionArgument(_("The given option argument is not a valid boolean."))
|
||||
return None
|
||||
|
||||
if "multidest" in kwargs:
|
||||
for dest in kwargs["multidest"]:
|
||||
setattr(parser.values, dest, value)
|
||||
def __find_options_to_extend__(long_options):
|
||||
options_to_extend = []
|
||||
|
||||
setattr(parser.values, option.dest, value)
|
||||
for n, arg in enumerate(long_options):
|
||||
arg = arg.split(":")
|
||||
if len(arg) == 2:
|
||||
long_options[n] = arg[0] + "="
|
||||
options_to_extend.append(("--" + arg[0], arg[1]))
|
||||
|
||||
# Originaly taken from here (and modified):
|
||||
# http://stackoverflow.com/questions/1229146/parsing-empty-options-in-python
|
||||
return options_to_extend
|
||||
|
||||
def add_option(parser, *args, **kwargs):
|
||||
if "multidest" in kwargs:
|
||||
multidest = kwargs.pop("multidest")
|
||||
kwargs["callback_kwargs"] = {"multidest": multidest}
|
||||
if "boolean" in kwargs and kwargs["boolean"] == True:
|
||||
boolean = kwargs.pop("boolean")
|
||||
kwargs["type"] = "string"
|
||||
kwargs["action"] = "callback"
|
||||
kwargs["callback"] = __handle_boolean_argument__
|
||||
kwargs["default"] = not boolean
|
||||
# This is a duplicate of gnu_getopt, but with support for optional arguments in long options, in the form; "arg:default_value".
|
||||
|
||||
for i in range(len(sys.argv) - 1, 0, -1):
|
||||
arg = sys.argv[i]
|
||||
if arg in args:
|
||||
sys.argv.insert(i + 1, "true")
|
||||
def gnu_getopt(args, options, long_options):
|
||||
options_to_extend = __find_options_to_extend__(long_options)
|
||||
|
||||
parser.add_option(*args, **kwargs)
|
||||
for n, arg in enumerate(args):
|
||||
opt = __find_arg_in_options__(arg, options_to_extend)
|
||||
if opt:
|
||||
args[n] = arg + "=" + opt[1]
|
||||
|
||||
class OptionParser(optparse.OptionParser):
|
||||
def error(self, msg):
|
||||
if msg.find("requires") != -1:
|
||||
variable = msg.split()[0]
|
||||
raise OptionParsingError(_("option '{0}' requires an argument").format(variable))
|
||||
else:
|
||||
variable = msg.split()[-1]
|
||||
if variable[1] == "-":
|
||||
raise OptionParsingError(_("unrecognized option '{0}'").format(variable))
|
||||
else:
|
||||
raise OptionParsingError(_("invalid option -- '{0}'").format(variable[1:]))
|
||||
return getopt.gnu_getopt(args, options, long_options)
|
||||
|
||||
raise OptionParsingError(_("invalid command-line options"))
|
||||
def get_boolean_argument(arg):
|
||||
if isinstance(arg, bool):
|
||||
return arg
|
||||
elif arg == None or arg.lower() == "false" or arg.lower() == "f" or arg == "0":
|
||||
return False
|
||||
elif arg.lower() == "true" or arg.lower() == "t" or arg == "1":
|
||||
return True
|
||||
|
||||
#Originaly taken from the optparse module (and modified).
|
||||
def parse_args(self, args=None, values=None):
|
||||
rargs = self._get_args(args)
|
||||
if values is None:
|
||||
values = self.get_default_values()
|
||||
|
||||
#Pylint screams about these. However, this was how it was done in the original code.
|
||||
self.rargs = rargs
|
||||
self.largs = largs = []
|
||||
self.values = values
|
||||
|
||||
try:
|
||||
self._process_args(largs, rargs, values)
|
||||
except (optparse.BadOptionError, optparse.OptionValueError) as msg:
|
||||
self.error(unicode(msg))
|
||||
|
||||
args = largs + rargs
|
||||
return self.check_values(values, args)
|
||||
raise InvalidOptionArgument(_("The given option argument is not a valid boolean."))
|
||||
|
|
Loading…
Reference in a new issue