Source code for kvalikirstu2.argument_parser

"""A module for parsing command line arguments and logging.
"""
import argparse
import logging
import logging.config
import os
import tempfile
import time
from kvalikirstu2 import arg_parser
from kvalikirstu2 import localization
from kvalikirstu2.localization import _

_LOG_DIR = tempfile.gettempdir()
_LOG_LEVELS = [
    'ERROR',
    'WARNING',
    'INFO',
    'DEBUG'
]
_DEFAULT_CONF_SAVE_DIR = os.path.join(os.path.expanduser('~'), '.kvalikirstu2')
_DATA = {'settings': None,
         'parser': None,
         'running_gui': False,
         'langcodes': None,
         'archive_interface': None}
logger = logging.getLogger(__name__)


[docs]def register_archive_interface(interface): """ Called when the package is loaded to allow for data archive specific arguments to be loaded. :param DataArchiveInterface interface: The interface. """ if not register_archive_interface.registered: _DATA['archive_interface'] = interface register_archive_interface.registered = True else: raise AssertionError('Tried to register archive interface twice.')
register_archive_interface.registered = False
[docs]class ArgumentsNotSavedException(Exception): """Exception occurred while saving args.""" def __init__(self, inner_exception, *args, **kwargs): self.inner_exception = inner_exception super().__init__(*args, **kwargs)
[docs]class ArgumentsNotParsedException(Exception): """get_args called before parsing arguments"""
[docs]class ConfigFileParsingException(Exception): """Raised when parsing args fails (during gui run)"""
[docs]class Settings(): """Class for command line settings.""" def __init__(self): self.parser = arg_parser.Kvalikirstu2ConfigArgParser(config_files=arg_parser.DEFAULT_CONF_FILES, description=_("Kvalikirstu 2, a tool for analyzing" " qualitative studies.")) self.settings = None self.log_config = None
[docs] def setup_logging(self): """Setup :mod: `logging` module.""" logging.config.dictConfig(self.log_config) logger.info("Started logging, level: %s", self.settings.loglevel) logger.debug("Read settings: %s", self.settings)
[docs] def get_shared_args(self): """Gets arguments that are shared between the GUI and the cmd version""" _DATA['archive_interface'].add_shared_arguments(self.parser) self.parser.add_argument('-c', '--config-file', is_config_file=True, help='config file path') self.parser.add_argument('--header-regex', type=str, help=argparse.SUPPRESS) self.parser.add_argument('--begindata', type=str, help=argparse.SUPPRESS) self.parser.add_argument('--enddata', type=str, help=argparse.SUPPRESS) self.parser.add_argument('--header_line_format', type=str, help=argparse.SUPPRESS) self.parser.add_argument('--min-headers-paragraph', type=int, help=argparse.SUPPRESS) self.parser.add_argument('--logconfig', type=str, help=argparse.SUPPRESS) self.parser.add_argument('--loglevel', action='store', choices=_LOG_LEVELS, type=str, help=argparse.SUPPRESS) self.parser.add_argument('--overwrite-temp', action='store_true', help=_('Should temporary files be overwritten?')) self.parser.add_argument('--separate_daf_symbol', type=str, help=_('The symbol used for declaring a daF path in a header.')) self.parser.add_argument('--index-data-folder', type=str, default="html", help=_('The name of the folder where the index dependencies(subject files, etc.)' ' should be stored in.')) self.parser.add_argument('--study-data-folder', type=str, default='data', help=_('The name of the folder where the data in the study is.')) self.parser.add_argument('--index-lang', type=str, default="fi_FI", help=_('Index language.')) self.parser.add_argument('--encoding', type=str, default='utf-8', help=_('The default encoding to be used in the application.')) self.parser.add_argument('--csv-encoding', type=str, default='utf-8', help=_('The encoding the .csv file is saved into.')) self.parser.add_argument('--max-header-length', type=int, default=256, help=_('The maximum length of a header in characters.')) self.parser.add_argument('--subjects-per-page', type=int, default=50, help=_('How many subjects should be shown per page by default in the index.')) self.parser.add_argument('--max-backups', type=int, default=15, help=_('The maximum number of folder backups.')) self.parser.add_argument('--max-backup-folder-size', type=int, default=20e6, help=_('The maximum size of a backed up folder.')) self.parser.add_argument('--max-chars-in-table', type=int, default=64, help=_('Maximum number of characters to display in the cell of the table')) self.parser.add_argument('--max-chars-in-table-header', type=int, default=48, help=_('Maximum number of characters to display in the cell of the table')) self.parser.add_argument('--html-file-name', type=str, default='daF{study_number}_{name}.html', help=_('The format string used for the html filename.')) self.parser.add_argument('--lang', type=str, default=None, help=_('Language of the program. If not specified' ' selects it based on user locale.')) self.parser.add_argument('--langcode_file', default=os.path.join(os.path.dirname(__file__), 'languages.txt'), type=str, nargs='*', help=_('File that contains language codes.')) self.parser.add_argument('--default-table-width', default=10, type=int, help=_('Default width for table.')) self.parser.add_argument('--table-widths', default=[6, 7, 8, 9, 10, 11, 12], type=int, nargs='*', help=_('Table width options.')) self.parser.add_argument('--timeout', default=30, type=float, help=_('Timeout for running subprocesses.')) self.parser.add_argument('--detection-encodings', default=['utf-8', 'utf-8-sig'], nargs='+', help=_('Set of encodings accepted by encoding detector. Set to empty' ' for no encodings.')) self.parser.add_argument('--default-detection-encoding', default='windows-1252', help=_('Default encoding for' ' encoding detection '))
[docs] def parse_command_line(self, args): """Parse command line arguments and assign parser options to settings. :param argv: arguments passed to ``configargparse.ArgumentParser`` :returns: parsed arguments. :rtype: :obj:`argparse.Namespace` """ self.get_shared_args() _DATA['archive_interface'].add_cli_arguments(self.parser) self.parser.add_argument('--path', type=str, help=_("The folder from which the data files in the study are searched from.")) self.parser.add_argument('--generate', action='store_true', help=argparse.SUPPRESS) self.parser.add_argument('--parse', action='store_true', help=_('Parses files from the data folder')) self.parser.add_argument('--citreq', action='store_true', help=_('Generate a citation requirement for all the data files in the folder.')) self.parser.add_argument('--add-text', nargs='?', const=None, type=str, help=_('Text to be added to every data file.')) self.parser.add_argument('--save-settings', nargs='?', const=None, type=str, help=_('Save current settings.')) self.parser.add_argument('--headers', nargs='*', default=[], type=str, help=_('A list of headers used in the chosen task, such as header selection for' ' parsing or setting up external file studies.')) self.parser.add_argument('--convert', default='', type=str, help=_('Convert files in data folder based on the parameter given.' ' Options: encoding, txt, odt. Specify txt encoding with --encoding')) self.parser.add_argument('--rename-files', action='store_true', help=_('Rename data files in data folder.')) self.parser.add_argument('--rename-headers', action='store_true', help=_('Rename headers in data files')) self.parser.add_argument('--setup_external_file_study', action='store_true', help=_('Sets up a study containing external files and a single file that contains' ' the metadata')) self.parser.add_argument('--print-dafs', action='store_true', help=_('Prints the data files in the data folder.')) self.parser.add_argument('--files', nargs='*', default=[], type=str, help=_('A list of files to be marked with a certain language code.')) self.parser.add_argument('--langcode', default=None, type=str, help=_('A language code to mark files with. Specify files via the --files parameter.')) self.parser.add_argument('--replace', nargs='*', type=str, default=[], help=_('Replaces text in .txt and .odt files.' ' Usage: --replace old_value new_value\n')) self.parser.add_argument('--restore', type=int, default=None, help=_('Restores a backup with the given index.')) self.settings = self.parser.parse_known_args(args)[0] store_settings(self.settings)
[docs] def parse_gui(self, args): """Parse GUI arguments and return parser's namespace. :param argv: arguments passed to ``configargparse.ArgumentParser`` :returns: parsed arguments. :rtype: :obj:`argparse.Namespace` """ self.get_shared_args() _DATA['archive_interface'].add_gui_arguments(self.parser) self.parser.add_argument("--padding", type=int, help=_('The amount of padding in the controls.')) self.settings = self.parser.parse_known_args(args)[0] store_settings(self.settings)
[docs] def set_up_cli_settings(self, argv): """Sets up settings with command line arguments and starts logging""" self.parse_command_line(argv) _set_language() self._set_log_config(True) self.setup_logging() store_parser(self.parser)
[docs] def set_up_gui_settings(self, argv): """Sets up settings and starts logging""" self.parse_gui(argv) _set_language() self._set_log_config(False) self.setup_logging() store_parser(self.parser) _DATA['running_gui'] = True
def _set_log_config(self, is_cli): """Sets up the log config. :param is_cli: Is the program being run from the command line? """ self.log_config = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' }, 'gui': { 'format': '%(message)s' } }, 'handlers': { 'default': { 'level': self.settings.loglevel, 'formatter': 'standard', 'class': 'logging.handlers.RotatingFileHandler', 'filename': os.path.join(_LOG_DIR, 'kvalilog.log'), 'mode': 'a', 'maxBytes': 200000, 'backupCount': 5 } }, 'loggers': { 'kvalikirstu2': { 'level': 'DEBUG', 'handlers': ['default'], 'propagate': False } } } if is_cli: self.log_config['handlers']['console'] = { 'level': 'INFO', 'formatter': 'standard', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout' } self.log_config['loggers']['kvalikirstu2']['handlers'].append('console') else: self.log_config['handlers']['gui'] = { 'level': 'WARN', 'formatter': 'gui', 'class': 'kvalikirstu2.warning_handler.WarningHandler' } self.log_config['loggers']['kvalikirstu2']['handlers'].append('gui')
[docs]def save_config(filepath=None): """Save current config to file :param str filepath: The destination filepath of the config. """ timestamp = time.strftime("%Y%m%d_%H%M%S") conf_dir = _DEFAULT_CONF_SAVE_DIR if not filepath: filepath = "kvali%s.conf" % timestamp else: filepath += '.conf' if not os.path.exists(conf_dir): logger.debug("making dir %s", conf_dir) os.mkdir(conf_dir) file_path = os.path.join(conf_dir, filepath) logger.info("filepath=%s", file_path) try: _DATA['parser'].write_config_file(_DATA['settings'], [file_path]) # configargparse method except Exception as exception: logger.error(exception) raise ArgumentsNotSavedException(exception)
[docs]def store_parser(parser): """Stores parser to global list _DATA""" _DATA['parser'] = parser
[docs]def store_settings(settings): """Stores settings (parsed values) to global list _DATA""" _DATA['settings'] = settings
[docs]def change_setting(setting_title, new_value): """Change a single setting :param setting_title: The name of the setting. :param new_value: The new value for the setting. """ vars(_DATA['settings']).update({setting_title: new_value})
[docs]def get_args(): """Gets command line/config file arguments""" if _DATA['settings'] is None: logger.warning('get_args called before parsing args! (Args not parsed)') return _DATA['settings']
[docs]def get_languages(): """Get language codes from the configuration.""" if _DATA['langcodes'] is None: args = get_args() codes = [] with open(args.langcode_file, encoding=args.encoding, mode='r') as file: for line in file: codes.append(line.strip()) _DATA['langcodes'] = codes return _DATA['langcodes']
def _set_language(): args = get_args() if args.lang: localization.set_language(args.lang)