DBC_Converter/venv/Lib/site-packages/ttkwidgets/validated_entries/validators.py
2025-01-03 23:49:59 +09:00

216 lines
6.2 KiB
Python

"""
Author: Dogeek
License: GNU GPLv3
Source: This repository
Validators to validate entry input.
"""
import re
import sys
from ttkwidgets.utilities import isint, isfloat
class Validator:
"""
Base validator class
Specify the VALIDATE_ON class attribute (defaults to 'all') to change
on which tkinter condition the validation will be done.
Possibilities are :
* 'all'
* 'none'
* 'focus'
* 'focusin'
* 'focusout'
* 'key'
"""
VALIDATE_ON = 'all'
def __init__(self, validate_on=None):
if validate_on is not None:
self.VALIDATE_ON = validate_on
self.widget = None
def validate(self, widget):
self.widget = widget
validatecmd = (widget.register(self._validate), '%P')
return {'validate': self.VALIDATE_ON, 'validatecommand': validatecmd}
def _validate(self, value):
return not value
@property
def is_valid(self):
if self.widget is not None:
return self._validate(self.widget.get())
raise ValueError('Widget is not attached to this validator')
class MultiValidator:
"""
Base class to handle multiple validators attachment.
"""
def __init__(self, *validators, validate_on='all'):
"""
:param *validators: Validator instances or classes to attach
:param validate_on: tkinter condition on which the validation will be done
default : 'all', see `help(Validator)` for a list of
possible values
"""
if validate_on not in ('all', 'none', 'focusin', 'focusout', 'focus', 'key'):
raise ValueError(
"validate_on is not in ('all', 'none', "
"'focusin', 'focusout', 'focus', 'key')"
)
self.validators = []
for v in validators:
if isinstance(v, type):
v = v()
if isinstance(v, (MultiValidator, Validator)):
self.validators.append(v)
else:
raise TypeError("One of the provided validators is not a Validator or MultiValidator instance")
self.validate_on = validate_on
self.widget = None
def validate(self, widget):
self.widget = widget
validatecmd = (widget.register(self._validate), '%P')
return {'validate': self.validate_on, 'validatecommand': validatecmd}
def _validate(self, value):
raise NotImplementedError()
@property
def is_valid(self):
if self.widget is not None:
return self._validate(self.widget.get())
raise ValueError('Widget is not attached to this validator')
class AnyValidator(MultiValidator):
"""
Validates any attached validators. The input will be deemed valid if and only if
one of the attached validators deem the value valid.
"""
def _validate(self, value):
return any(validator._validate(value) for validator in self.validators)
class AllValidator(MultiValidator):
"""
Validates all attached validators. The input will be deemed valid if and only if
all attached validators deem the value valid.
"""
def _validate(self, value):
return all(validator._validate(value) for validator in self.validators)
class RegexValidator(Validator):
"""
A validator that will check against a regular expression stored in the REGEX
class attribute
REGEX can either be a re.Pattern or a python string.
"""
REGEX = None
def __init__(self, regex=None, **kwargs):
if regex is not None:
self.REGEX = regex
super().__init__(**kwargs)
def _validate(self, value):
if not isinstance(value, str):
raise TypeError('{} is not a string.'.format(value))
if super()._validate(value):
return True
# isinstance(self.REGEX, re.Pattern) only works on 3.7+
if re.search(r'(Pattern|SRE_Pattern)', self.REGEX.__class__.__name__):
return self.REGEX.search(value) is not None
if isinstance(self.REGEX, str):
return re.search(self.REGEX, value) is not None
raise TypeError('{} is not a pattern or a string'.format(self.regex))
class IntValidator(Validator):
def _validate(self, value):
if super()._validate(value):
return True
return isint(value)
class FloatValidator(RegexValidator):
REGEX = r'[0-9\.]'
class PercentValidator(Validator):
def _validate(self, value):
# percentages can be 0-100 integer
# float 0-1
if super()._validate(value):
return True
return ((isfloat(value) or isint(value)) and 0 <= float(value) <= 100)
class StringValidator(Validator):
"""
A validator you have to instanciate with a string containing the
characters you want in.
"""
def __init__(self, string):
"""
:param string: String of allowed characters
:type string: str
"""
self.string = string
def _validate(self, value):
if super()._validate(value):
return True
return all([c in self.string for c in value])
class CapitalizedStringValidator(RegexValidator):
REGEX = '[{}].*'.format("".join([chr(i) for i in range(sys.maxunicode) if chr(i).isupper()]))
class EmailValidator(RegexValidator):
VALIDATE_ON = 'focusout'
REGEX = (
r'^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|'
r'(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$'
)
class PasswordValidator(RegexValidator):
"""
Password validator. The requirements are as follows :
* At least 1 lowercase character
* At least 1 uppercase character
* At least 1 digit
* At least 1 special character (!@#$%^&*)
* Be at least 8 characters long
"""
VALIDATE_ON = 'focusout'
REGEX = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})'
class IPv4Validator(RegexValidator):
"""
Validates IPv4 addresses. The following are valid:
* localhost
* 192.168.0.1
* localhost:3158
"""
VALIDATE_ON = 'focusout'
REGEX = r'^((?:[0-9]{1,3}\.){3}[0-9]{1,3}|localhost)(:\d{2,6})?$'