# -*- coding: utf-8 -*-
"""
:platform: Unix
:synopsis: Class and methods used to parse command-line arguments.
"""
import argparse
import os
import re
import sys
from argparse import RawTextHelpFormatter, ArgumentTypeError, Action, ArgumentParser
from datetime import datetime
from gettext import gettext
[docs]class DirectoryChecker(Action):
"""
Custom action class for argument parser to use with the Python
`argparse <https://docs.python.org/2/library/argparse.html>`_ module.
"""
def __call__(self, parser, namespace, values, option_string=None):
if isinstance(values, list):
checked_vals = [self.directory_checker(x) for x in values]
else:
checked_vals = self.directory_checker(values)
setattr(namespace, self.dest, checked_vals)
[docs] @staticmethod
def directory_checker(path):
"""
Checks if the supplied directory exists. The path is normalized without trailing slash.
:param str path: The path list to check
:returns: The same path list
:rtype: *str*
:raises Error: If one of the directory does not exist
"""
path = os.path.abspath(os.path.normpath(path))
if not os.path.isdir(path):
msg = 'No such directory: {}'.format(path)
raise ArgumentTypeError(msg)
return path
[docs]class VersionChecker(Action):
"""
Custom action class for argument parser to use with the Python
`argparse <https://docs.python.org/2/library/argparse.html>`_ module.
"""
def __call__(self, parser, namespace, value, option_string=None):
checked_val = self.version_checker(value)
setattr(namespace, self.dest, checked_val)
[docs] @staticmethod
def version_checker(version):
"""
Checks the version format from command-line.
:type version: The version string from command-line
:returns: The version if allowed
:rtype: *str*
:raises Error: If invalid version format
"""
if re.compile(r'^[\d]{1,8}$').search(str(version)):
if len(version) == 8:
try:
datetime.strptime(version, '%Y%m%d')
except ValueError:
msg = 'Invalid version date: {}.'.format(str(version))
raise ArgumentTypeError(msg)
return version
else:
msg = 'Invalid version type: {}.\nAvailable format is YYYYMMDD or an integer.'.format(str(version))
raise ArgumentTypeError(msg)
[docs]def keyval_converter(pair):
"""
Checks the key value syntax.
:param str pair: The key/value pair to check
:returns: The key/value pair
:rtype: *list*
:raises Error: If invalid pair syntax
"""
pattern = re.compile(r'([^=]+)=([^=]+)(?:,|$)')
if not pattern.search(pair):
msg = 'Bad argument syntax: {}'.format(pair)
raise ArgumentTypeError(msg)
return pattern.search(pair).groups()
[docs]def regex_validator(string):
"""
Validates a Python regular expression syntax.
:param str string: The string to check
:returns: The Python regex
:rtype: *re.compile*
:raises Error: If invalid regular expression
"""
try:
return re.compile(string)
except re.error:
msg = 'Bad regex syntax: {}'.format(string)
raise ArgumentTypeError(msg)
[docs]def processes_validator(value):
"""
Validates the max processes number.
:param str value: The max processes number submitted
:return:
"""
pnum = int(value)
if pnum < 1 and pnum != -1:
msg = 'Invalid processes number. Should be a positive integer or "-1".'
raise ArgumentTypeError(msg)
if pnum == -1:
# Max processes = None corresponds to cpu.count() in Pool creation
return None
else:
return pnum
class CustomArgumentParser(ArgumentParser):
def error(self, message):
"""
Overwrite the original method to change exist status.
"""
self.print_usage(sys.stderr)
self.exit(-1, gettext('%s: error: %s\n') % (self.prog, message))
def set_default_subparser(self, name, args=None):
"""
Set default sub-parser. Call after setup, just before parse_args().
"""
subparser_found = False
for arg in sys.argv[1:]:
# Breaks if global option without sub-parser
if arg in ['-h', '--help', '-v', '--version']:
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:]:
subparser_found = True
if not subparser_found:
# If no subparser found insert the default one in first position
# This implies no global options without a sub_parsers specified
if args is None:
sys.argv.insert(1, name)
else:
args.insert(0, name)