import re from functools import wraps from gooey.gui.components.filtering.prefix_filter import OperatorType class SuperBool(object): """ A boolean which keeps with it the rationale for when it is false. """ def __init__(self, value, rationale): self.value = value self.rationale = rationale def __bool__(self): return self.value __nonzero__ = __bool__ def __str__(self): return str(self.value) def lift(f): """ Lifts a basic predicate to the SuperBool type stealing the docstring as the rationale message. This is largely just goofing around and experimenting since it's a private internal API. """ @wraps(f) def inner(value): result = f(value) return SuperBool(result, f.__doc__) if not isinstance(result, SuperBool) else result return inner @lift def is_tuple_or_list(value): """Must be either a list or tuple""" return isinstance(value, list) or isinstance(value, tuple) @lift def is_str(value): """Must be of type `str`""" return isinstance(value, str) @lift def is_str_or_coll(value): """ Colors must be either a hex string or collection of RGB values. e.g. Hex string: #fff0ce RGB Collection: [0, 255, 128] or (0, 255, 128) """ return bool(is_str(value)) or bool(is_tuple_or_list(value)) @lift def has_valid_channel_values(rgb_coll): """Colors in an RGB collection must all be in the range 0-255""" return all([is_0to255(c) and is_int(c) for c in rgb_coll]) @lift def is_three_channeled(value): """Missing channels! Colors in an RGB collection should be of the form [R,G,B] or (R,G,B)""" return len(value) == 3 @lift def is_hex_string(value: str): """Invalid hexadecimal format. Expected: "#FFFFFF" """ return isinstance(value, str) and bool(re.match('^#[\dABCDEF]{6}$', value, flags=2)) @lift def is_bool(value): """Must be of type Boolean""" return isinstance(value, bool) @lift def non_empty_string(value): """Must be a non-empty non-blank string""" return bool(value) and bool(value.strip()) @lift def is_tokenization_operator(value): """Operator must be a valid OperatorType i.e. one of: (AND, OR)""" return bool(value) in (OperatorType.AND, OperatorType.OR) @lift def is_tokenizer(value): """Tokenizers must be valid Regular expressions. see: options.PrefixTokenizers""" return bool(non_empty_string(value)) @lift def is_int(value): """Invalid type. Expected `int`""" return isinstance(value, int) @lift def is_0to255(value): """RGB values must be in the range 0 - 255 (inclusive)""" return 0 <= value <= 255 def is_0to20(value): """Precision values must be in the range 0 - 20 (inclusive)""" return 0 <= value <= 20 @lift def is_valid_color(value): """Must be either a valid hex string or RGB list""" if is_str(value): return is_hex_string(value) elif is_tuple_or_list(value): return (is_tuple_or_list(value) and is_three_channeled(value) and has_valid_channel_values(value)) else: return is_str_or_coll(value) validators = { 'label_color': is_valid_color, 'label_bg_color': is_valid_color, 'help_color': is_valid_color, 'help_bg_color': is_valid_color, 'error_color': is_valid_color, 'error_bg_color': is_valid_color, 'show_label': is_bool, 'show_help': is_bool, 'visible': is_bool, 'full_width': is_bool, 'height': is_int, 'readonly': is_bool, 'initial_selection': is_int, 'title': non_empty_string, 'checkbox_label': non_empty_string, 'placeholder': non_empty_string, 'empty_message': non_empty_string, 'max_size': is_int, 'choice_tokenizer': is_tokenizer, 'input_tokenizer': is_tokenizer, 'ignore_case': is_bool, 'operator': is_tokenization_operator, 'index_suffix': is_bool, 'wildcard': non_empty_string, 'default_dir': non_empty_string, 'default_file': non_empty_string, 'default_path': non_empty_string, 'message': non_empty_string, 'precision': is_0to20 } def collect_errors(predicates, m): return { k:predicates[k](v).rationale for k,v in m.items() if k in predicates and not predicates[k](v)} def validate(pred, value): result = pred(value) if not result: raise ValueError(result.rationale) if __name__ == '__main__': # TODO: there should be tests pass # print(validateColor((1, 'ergerg', 1234))) # print(validateColor(1234)) # print(validateColor(123.234)) # print(validateColor('123.234')) # print(validateColor('FFFAAA')) # print(validateColor('#FFFAAA')) # print(validateColor([])) # print(validateColor(())) # print(validateColor((1, 2))) # print(validateColor((1, 2, 1234))) # print(is_lifted(lift(is_int))) # print(is_lifted(is_int)) # print(OR(is_poop, is_int)('poop')) # print(AND(is_poop, is_lower, is_lower)('pooP')) # print(OR(is_poop, is_int)) # print(is_lifted(OR(is_poop, is_int))) # print(validate(is_valid_color, [255, 255, 256])) # print(is_valid_color('#fff000')) # print(is_valid_color([255, 244, 256])) # print(non_empty_string('asdf') and non_empty_string('asdf')) # validate(is_valid_color, 1234)