mirror of
https://github.com/ejwa/gitinspector.git
synced 2024-11-16 00:28:25 +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"]
|
DEFAULT_EXTENSIONS = ["java", "c", "cpp", "h", "hpp", "py", "glsl", "rb", "js", "sql"]
|
||||||
|
|
||||||
__extensions__ = None
|
__extensions__ = DEFAULT_EXTENSIONS
|
||||||
__located_extensions__ = set()
|
__located_extensions__ = set()
|
||||||
|
|
||||||
def get():
|
def get():
|
||||||
|
|
|
@ -26,10 +26,11 @@ import os
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
__available_formats__ = ["html", "htmlembedded", "text", "xml"]
|
__available_formats__ = ["html", "htmlembedded", "text", "xml"]
|
||||||
__selected_format__ = None
|
|
||||||
|
|
||||||
DEFAULT_FORMAT = __available_formats__[2]
|
DEFAULT_FORMAT = __available_formats__[2]
|
||||||
|
|
||||||
|
__selected_format__ = DEFAULT_FORMAT
|
||||||
|
|
||||||
class InvalidFormatError(Exception):
|
class InvalidFormatError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -24,20 +24,15 @@ from __future__ import unicode_literals
|
||||||
import localization
|
import localization
|
||||||
localization.init()
|
localization.init()
|
||||||
|
|
||||||
try:
|
|
||||||
from compatibility import unicode
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
import blame
|
import blame
|
||||||
import changes
|
import changes
|
||||||
import compatibility
|
|
||||||
import config
|
import config
|
||||||
import extensions
|
import extensions
|
||||||
import filtering
|
import filtering
|
||||||
import format
|
import format
|
||||||
import help
|
import help
|
||||||
import interval
|
import interval
|
||||||
|
import getopt
|
||||||
import metrics
|
import metrics
|
||||||
import missing
|
import missing
|
||||||
import os
|
import os
|
||||||
|
@ -51,11 +46,21 @@ import timeline
|
||||||
import version
|
import version
|
||||||
|
|
||||||
class Runner:
|
class Runner:
|
||||||
def __init__(self, opts):
|
def __init__(self):
|
||||||
self.opts = opts
|
self.hard = False
|
||||||
|
self.include_metrics = False
|
||||||
|
self.list_file_types = False
|
||||||
|
self.localize_output = False
|
||||||
self.repo = "."
|
self.repo = "."
|
||||||
|
self.responsibilities = False
|
||||||
|
self.grading = False
|
||||||
|
self.timeline = False
|
||||||
|
self.useweeks = False
|
||||||
|
|
||||||
def output(self):
|
def output(self):
|
||||||
|
if not self.localize_output:
|
||||||
|
localization.disable()
|
||||||
|
|
||||||
terminal.skip_escapes(not sys.stdout.isatty())
|
terminal.skip_escapes(not sys.stdout.isatty())
|
||||||
terminal.set_stdout_encoding()
|
terminal.set_stdout_encoding()
|
||||||
previous_directory = os.getcwd()
|
previous_directory = os.getcwd()
|
||||||
|
@ -69,43 +74,25 @@ class Runner:
|
||||||
|
|
||||||
os.chdir(absolute_path[0].decode("utf-8", "replace").strip())
|
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()
|
format.output_header()
|
||||||
outputable.output(changes.ChangesOutput(self.opts.hard))
|
outputable.output(changes.ChangesOutput(self.hard))
|
||||||
|
|
||||||
if changes.get(self.opts.hard).get_commits():
|
if changes.get(self.hard).get_commits():
|
||||||
outputable.output(blame.BlameOutput(self.opts.hard))
|
outputable.output(blame.BlameOutput(self.hard))
|
||||||
|
|
||||||
if self.opts.timeline:
|
if self.timeline:
|
||||||
outputable.output(timeline.Timeline(changes.get(self.opts.hard), self.opts.useweeks))
|
outputable.output(timeline.Timeline(changes.get(self.hard), self.useweeks))
|
||||||
|
|
||||||
if self.opts.metrics:
|
if self.include_metrics:
|
||||||
outputable.output(metrics.Metrics())
|
outputable.output(metrics.Metrics())
|
||||||
|
|
||||||
if self.opts.responsibilities:
|
if self.responsibilities:
|
||||||
outputable.output(responsibilities.ResponsibilitiesOutput(self.opts.hard))
|
outputable.output(responsibilities.ResponsibilitiesOutput(self.hard))
|
||||||
|
|
||||||
outputable.output(missing.Missing())
|
outputable.output(missing.Missing())
|
||||||
outputable.output(filtering.Filtering())
|
outputable.output(filtering.Filtering())
|
||||||
|
|
||||||
if self.opts.list_file_types:
|
if self.list_file_types:
|
||||||
outputable.output(extensions.Extensions())
|
outputable.output(extensions.Extensions())
|
||||||
|
|
||||||
format.output_footer()
|
format.output_footer()
|
||||||
|
@ -116,62 +103,82 @@ def __check_python_version__():
|
||||||
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
|
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))
|
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():
|
def main():
|
||||||
compatibility.convert_command_line()
|
__run__ = Runner()
|
||||||
parser = optval.OptionParser(add_help_option=False)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
parser.add_option("-c", action="store_true", dest="checkout_missing")
|
__opts__, __args__ = optval.gnu_getopt(sys.argv[1:], "cf:F:hHlLmrTwx:", ["checkout-missing:true", "exclude=",
|
||||||
parser.add_option("-H", action="store_true", dest="hard")
|
"file-types=", "format=", "hard:true", "help",
|
||||||
parser.add_option("-l", action="store_true", dest="list_file_types")
|
"list-file-types:true", "localize-output:true",
|
||||||
parser.add_option("-L", action="store_true", dest="localize_output")
|
"metrics:true", "responsibilities:true", "since=",
|
||||||
parser.add_option("-m", action="store_true", dest="metrics")
|
"grading:true", "timeline:true", "until=", "version",
|
||||||
parser.add_option("-r", action="store_true", dest="responsibilities")
|
"weeks:true"])
|
||||||
parser.add_option("-T", action="store_true", dest="timeline")
|
for arg in __args__:
|
||||||
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:
|
|
||||||
__run__.repo = arg
|
__run__.repo = arg
|
||||||
|
|
||||||
#We need the repo above to be set before we read the git config.
|
#We need the repo above to be set before we read the git config.
|
||||||
config.init(__run__)
|
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:
|
except (format.InvalidFormatError, optval.InvalidOptionArgument, getopt.error) as msg:
|
||||||
localization.enable()
|
print(sys.argv[0], "\b:", msg)
|
||||||
|
|
||||||
print(sys.argv[0], "\b:", end=" ")
|
|
||||||
print(unicode(msg))
|
|
||||||
print(_("Try `{0} --help' for more information.").format(sys.argv[0]))
|
print(_("Try `{0} --help' for more information.").format(sys.argv[0]))
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
|
@ -17,87 +17,47 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import getopt
|
||||||
try:
|
|
||||||
from compatibility import unicode
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
import optparse
|
|
||||||
import sys
|
|
||||||
|
|
||||||
class InvalidOptionArgument(Exception):
|
class InvalidOptionArgument(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class OptionParsingError(RuntimeError):
|
def __find_arg_in_options__(arg, options):
|
||||||
pass
|
for opt in options:
|
||||||
|
if opt[0].find(arg) == 0:
|
||||||
|
return opt
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __find_options_to_extend__(long_options):
|
||||||
|
options_to_extend = []
|
||||||
|
|
||||||
|
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]))
|
||||||
|
|
||||||
|
return options_to_extend
|
||||||
|
|
||||||
|
# This is a duplicate of gnu_getopt, but with support for optional arguments in long options, in the form; "arg:default_value".
|
||||||
|
|
||||||
|
def gnu_getopt(args, options, long_options):
|
||||||
|
options_to_extend = __find_options_to_extend__(long_options)
|
||||||
|
|
||||||
|
for n, arg in enumerate(args):
|
||||||
|
opt = __find_arg_in_options__(arg, options_to_extend)
|
||||||
|
if opt:
|
||||||
|
args[n] = arg + "=" + opt[1]
|
||||||
|
|
||||||
|
return getopt.gnu_getopt(args, options, long_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
|
||||||
|
|
||||||
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."))
|
raise InvalidOptionArgument(_("The given option argument is not a valid boolean."))
|
||||||
|
|
||||||
if "multidest" in kwargs:
|
|
||||||
for dest in kwargs["multidest"]:
|
|
||||||
setattr(parser.values, dest, value)
|
|
||||||
|
|
||||||
setattr(parser.values, option.dest, value)
|
|
||||||
|
|
||||||
# Originaly taken from here (and modified):
|
|
||||||
# http://stackoverflow.com/questions/1229146/parsing-empty-options-in-python
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
for i in range(len(sys.argv) - 1, 0, -1):
|
|
||||||
arg = sys.argv[i]
|
|
||||||
if arg in args:
|
|
||||||
sys.argv.insert(i + 1, "true")
|
|
||||||
|
|
||||||
parser.add_option(*args, **kwargs)
|
|
||||||
|
|
||||||
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:]))
|
|
||||||
|
|
||||||
raise OptionParsingError(_("invalid command-line options"))
|
|
||||||
|
|
||||||
#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)
|
|
||||||
|
|
Loading…
Reference in a new issue