Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --config option to use different config file. #79

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 38 additions & 1 deletion khard/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,52 @@ class Actions:

@classmethod
def get_action_for_alias(cls, alias):
"""Find the name of the action for the supplied alias. If no action s
asociated with the given alias, None is returned.

:param alias: the alias to look up
:type alias: str
:rturns: the name of the corresponding action or None
:rtype: str or NoneType

"""
for action, alias_list in cls.action_map.items():
if alias in alias_list:
return action
return None

@classmethod
def get_alias_list_for_action(cls, action):
"""Find all aliases for the given action. If there is no such action,
None is returned.

:param action: the action name to look up
:type action: str
:returns: the list of aliases or None
:rtype: list(str) or NoneType

"""
return cls.action_map.get(action)

@classmethod
def get_list_of_all_actions(cls):
return list(cls.action_map.keys())
"""Find the names of all defined actions.

:returns: all action names
:rtype: iterable(str)
"""
return cls.action_map.keys()

@classmethod
def get_all_actions_and_aliases(cls):
"""Find the names of all defined actions and their aliases.

:returns: the names of all actions and aliases
:rtype: list(str)

"""
all = []
for key, value in cls.action_map.items():
all.append(key)
all.extend(value)
return all
11 changes: 6 additions & 5 deletions khard/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

class Config:

def __init__(self):
def __init__(self, config_file=""):
self.config = None
self.address_book_list = []
self.original_uid_dict = {}
Expand All @@ -30,10 +30,11 @@ def __init__(self):
locale.setlocale(locale.LC_ALL, '')

# load config file
xdg_config_home = os.environ.get("XDG_CONFIG_HOME") or \
os.path.expanduser("~/.config")
config_file = os.environ.get("KHARD_CONFIG") or os.path.join(
xdg_config_home, "khard", "khard.conf")
if config_file == "":
xdg_config_home = os.getenv("XDG_CONFIG_HOME",
os.path.expanduser("~/.config"))
config_file = os.getenv("KHARD_CONFIG", os.path.join(
xdg_config_home, "khard", "khard.conf"))
if not os.path.exists(config_file):
print("Config file %s not available" % config_file)
sys.exit(2)
Expand Down
118 changes: 68 additions & 50 deletions khard/khard.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,57 +1169,39 @@ def copy_or_move_subcommand(action, vcard_list, target_address_book_list):
break


# Patch argparse.ArgumentParser, taken from http://stackoverflow.com/a/26379693
def set_default_subparser(self, name):
"""Default subparser selection. Call after setup, just before parse_args().
def parse_args():
"""Parse the command line arguments and return the namespace that was
creates by argparse.ArgumentParser.parse_args().

:param name: the name of the subparser to call by default
:type name: str
:returns: None
:rtype: None
:returns: the namespace parsed from the command line
:rtype: argparse.Namespace

"""
for arg in sys.argv[1:]:
if arg in ['-h', '--help']: # global help if no subparser
break
else:
for x in self._subparsers._actions:
if not isinstance(x, argparse._SubParsersAction):
continue
for sp_name in x._name_parser_map.keys():
if sp_name in sys.argv[1:]:
return # found a subcommand
else:
# Find position to insert default command.
options = self._option_string_actions.keys()
for index, arg in enumerate(sys.argv[1:], 1):
if arg in options:
continue
else:
# Insert command before first non option string (possibly
# an argument for the subcommand).
sys.argv.insert(index, name)
break
else:
# Otherwise append default command.
sys.argv.append(name)


argparse.ArgumentParser.set_default_subparser = set_default_subparser


def main():
# create the args parser
parser = argparse.ArgumentParser(
# Create the base argument parser. It will be reused for the first and
# second round of argument parsing.
base = argparse.ArgumentParser(
description="Khard is a carddav address book for the console",
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("--debug", action="store_true",
formatter_class=argparse.RawTextHelpFormatter, add_help=False)
base.add_argument("-c", "--config", default="", help="config file to use")
base.add_argument("--debug", action="store_true",
help="enable debug output")
parser.add_argument("--skip-unparsable", action="store_true",
base.add_argument("--skip-unparsable", action="store_true",
help="skip unparsable vcard files")
parser.add_argument("-v", "--version", action="version",
base.add_argument("-v", "--version", action="version",
version="Khard version %s" % khard_version)

# Create the first argument parser. Its main job is to set the correct
# config file. The config file is needed to get the default command if no
# subcommand is given on the command line. This parser will ignore most
# arguments, as they will be parsed by the second parser.
first_parser = argparse.ArgumentParser(parents=[base])
first_parser.add_argument('remainder', nargs=argparse.REMAINDER)

# Create the main argument parser. It will handle the complete command
# line only ignoring the config and debug options as these have already
# been set.
parser = argparse.ArgumentParser(parents=[base])

# create address book subparsers with different help texts
default_addressbook_parser = argparse.ArgumentParser(add_help=False)
default_addressbook_parser.add_argument(
Expand Down Expand Up @@ -1454,20 +1436,56 @@ def main():
description="list addressbooks",
help="list addressbooks")

# Replace the print_help method of the first parser with the print_help
# method of the main parser. This makes it possible to have the first
# parser handle the help option so that command line help can be printed
# without parsing the config file first (which is a problem if there are
# errors in the config file). The config file will still be parsed before
# the full command line is parsed so errors in the config file might be
# reported before command line syntax errors.
first_parser.print_help = parser.print_help

# Parese the command line with the first argument parser. It will handle
# the config option (its main job) and also the help, version and debug
# options as these do not depend on anything else.
args = first_parser.parse_args()
remainder = args.remainder

# Set the loglevel to debug if given on the command line. This is done
# before parsing the config file to make it possible to debug the parsing
# of the config file.
if "debug" in args and args.debug:
logging.basicConfig(level=logging.DEBUG)

# Create the global config instance.
global config
config = Config()
config = Config(args.config)

# Set the default command from the config file and parse the command line.
parser.set_default_subparser(config.get_default_action())
args = parser.parse_args()

# debug
# Check the log level again and merge the value from the command line with
# the config file.
if "debug" in args and args.debug:
config.set_debug(True)
if config.debug():
logging.basicConfig(level=logging.DEBUG)
logging.debug("args={}".format(args))
logging.debug("first args={}".format(args))
logging.debug("remainder={}".format(remainder))

# Set the default command from the config file if none was given on the
# command line.
if len(remainder) == 0 or \
remainder[0] not in Actions.get_all_actions_and_aliases():
remainder.insert(0, config.get_default_action())
logging.debug("updated remainder={}".format(remainder))

# Parse the remainder of the command line. All options from the previous
# run have already been processed and are not needed any more.
args = parser.parse_args(remainder)
logging.debug("second args={}".format(args))
return args


def main():
args = parse_args()

# if args.action isn't one of the defined actions, it must be an alias
if args.action not in Actions.get_list_of_all_actions():
Expand Down