# -*- coding: utf-8 -*- # # 2017 Darko Poljak (darko.poljak at gmail.com) # # This file is part of cdist. # # cdist 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. # # cdist 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 cdist. If not, see . # # import configparser import os import cdist import cdist.argparse import re import multiprocessing import logging import sys class Singleton(type): instance = None def __call__(cls, *args, **kwargs): if 'singleton' in kwargs and not kwargs['singleton']: return super(Singleton, cls).__call__(*args, **kwargs) else: if not cls.instance: cls.instance = super(Singleton, cls).__call__(*args, **kwargs) return cls.instance _VERBOSITY_VALUES = ( 'ERROR', 'WARNING', 'INFO', 'VERBOSE', 'DEBUG', 'TRACE', 'OFF', ) _ARCHIVING_VALUES = ( 'tar', 'tgz', 'tbz2', 'txz', 'none', ) class OptionBase: def __init__(self, name): self.name = name def get_converter(self, *args, **kwargs): raise NotImplementedError('Subclass should implement this method') def translate(self, val): return val def update_value(self, currval, newval, update_appends=False): '''Update current option value currval with new option value newval. If update_appends is True and if currval and newval are lists then resulting list contains all values in currval plus all values in newval. Otherwise, newval is returned. ''' if (isinstance(currval, list) and isinstance(newval, list) and update_appends): rv = [] if currval: rv.extend(currval) if newval: rv.extend(newval) if not rv: rv = None return rv else: return newval def should_override(self, currval, newval): return True class StringOption(OptionBase): def __init__(self, name): super().__init__(name) def get_converter(self): def string_converter(val): return self.translate(str(val)) return string_converter def translate(self, val): if val: return val else: return None class BooleanOption(OptionBase): BOOLEAN_STATES = configparser.ConfigParser.BOOLEAN_STATES # If default_overrides is False then previous config value will not be # overriden with default_value. def __init__(self, name, default_overrides=True, default_value=True): super().__init__(name) self.default_overrides = default_overrides self.default_value = default_value def get_converter(self): def boolean_converter(val): v = val.lower() if v not in self.BOOLEAN_STATES: raise ValueError('Invalid {} boolean value: {}'.format( self.name, val)) return self.translate(v) return boolean_converter def translate(self, val): return self.BOOLEAN_STATES[val] def should_override(self, currval, newval): if not self.default_overrides: return newval != self.default_value return True class IntOption(OptionBase): def __init__(self, name): super().__init__(name) def get_converter(self): def int_converter(val): return self.translate(int(val)) return int_converter class LowerBoundIntOption(IntOption): def __init__(self, name, lower_bound): super().__init__(name) self.lower_bound = lower_bound def get_converter(self): def lower_bound_converter(val): converted = super(LowerBoundIntOption, self).get_converter()(val) if converted < self.lower_bound: raise ValueError("Invalid {} value: {} < {}".format( self.name, val, self.lower_bound)) return converted return lower_bound_converter class SpecialCasesLowerBoundIntOption(LowerBoundIntOption): def __init__(self, name, lower_bound, special_cases_mapping): super().__init__(name, lower_bound) self.special_cases_mapping = special_cases_mapping def translate(self, val): if val in self.special_cases_mapping: return self.special_cases_mapping[val] else: return val class JobsOption(SpecialCasesLowerBoundIntOption): def __init__(self, name): super().__init__(name, -1, {-1: multiprocessing.cpu_count()}) class SelectOption(OptionBase): def __init__(self, name, valid_values): super().__init__(name) self.valid_values = valid_values def get_converter(self): def select_converter(val): if val in self.valid_values: return self.translate(val) else: raise ValueError("Invalid {} value: {}.".format( self.name, val)) return select_converter class VerbosityOption(SelectOption): def __init__(self): super().__init__('verbosity', _VERBOSITY_VALUES) def translate(self, val): name = 'VERBOSE_' + val verbose = getattr(cdist.argparse, name) return verbose class DelimitedValuesOption(OptionBase): def __init__(self, name, delimiter): super().__init__(name) self.delimiter = delimiter def get_converter(self): def delimited_values_converter(val): vals = re.split(r'(?