mirror of https://github.com/chriskiehl/Gooey.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
5.2 KiB
201 lines
5.2 KiB
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)
|
|
|
|
|