summaryrefslogtreecommitdiffstats
path: root/venv/lib/python3.9/site-packages/validators
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.9/site-packages/validators')
-rw-r--r--venv/lib/python3.9/site-packages/validators/__init__.py35
-rw-r--r--venv/lib/python3.9/site-packages/validators/between.py61
-rw-r--r--venv/lib/python3.9/site-packages/validators/btc_address.py55
-rw-r--r--venv/lib/python3.9/site-packages/validators/card.py183
-rw-r--r--venv/lib/python3.9/site-packages/validators/domain.py54
-rw-r--r--venv/lib/python3.9/site-packages/validators/email.py75
-rw-r--r--venv/lib/python3.9/site-packages/validators/extremes.py61
-rw-r--r--venv/lib/python3.9/site-packages/validators/hashes.py121
-rw-r--r--venv/lib/python3.9/site-packages/validators/i18n/__init__.py4
-rw-r--r--venv/lib/python3.9/site-packages/validators/i18n/es.py200
-rw-r--r--venv/lib/python3.9/site-packages/validators/i18n/fi.py94
-rw-r--r--venv/lib/python3.9/site-packages/validators/iban.py52
-rw-r--r--venv/lib/python3.9/site-packages/validators/ip_address.py156
-rw-r--r--venv/lib/python3.9/site-packages/validators/length.py37
-rw-r--r--venv/lib/python3.9/site-packages/validators/mac_address.py33
-rw-r--r--venv/lib/python3.9/site-packages/validators/slug.py28
-rw-r--r--venv/lib/python3.9/site-packages/validators/truthy.py39
-rw-r--r--venv/lib/python3.9/site-packages/validators/url.py154
-rw-r--r--venv/lib/python3.9/site-packages/validators/utils.py85
-rw-r--r--venv/lib/python3.9/site-packages/validators/uuid.py41
20 files changed, 1568 insertions, 0 deletions
diff --git a/venv/lib/python3.9/site-packages/validators/__init__.py b/venv/lib/python3.9/site-packages/validators/__init__.py
new file mode 100644
index 00000000..f623e12f
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/__init__.py
@@ -0,0 +1,35 @@
+from .between import between
+from .btc_address import btc_address
+from .card import (
+ amex,
+ card_number,
+ diners,
+ discover,
+ jcb,
+ mastercard,
+ unionpay,
+ visa
+)
+from .domain import domain
+from .email import email
+from .extremes import Max, Min
+from .hashes import md5, sha1, sha224, sha256, sha512
+from .i18n import fi_business_id, fi_ssn
+from .iban import iban
+from .ip_address import ipv4, ipv4_cidr, ipv6, ipv6_cidr
+from .length import length
+from .mac_address import mac_address
+from .slug import slug
+from .truthy import truthy
+from .url import url
+from .utils import ValidationFailure, validator
+from .uuid import uuid
+
+__all__ = ('between', 'domain', 'email', 'Max', 'Min', 'md5', 'sha1', 'sha224',
+ 'sha256', 'sha512', 'fi_business_id', 'fi_ssn', 'iban', 'ipv4',
+ 'ipv4_cidr', 'ipv6', 'ipv6_cidr', 'length', 'mac_address', 'slug',
+ 'truthy', 'url', 'ValidationFailure', 'validator', 'uuid',
+ 'card_number', 'visa', 'mastercard', 'amex', 'unionpay', 'diners',
+ 'jcb', 'discover', 'btc_address')
+
+__version__ = '0.20.0'
diff --git a/venv/lib/python3.9/site-packages/validators/between.py b/venv/lib/python3.9/site-packages/validators/between.py
new file mode 100644
index 00000000..46f223c9
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/between.py
@@ -0,0 +1,61 @@
+from .extremes import Max, Min
+from .utils import validator
+
+
+@validator
+def between(value, min=None, max=None):
+ """
+ Validate that a number is between minimum and/or maximum value.
+
+ This will work with any comparable type, such as floats, decimals and dates
+ not just integers.
+
+ This validator is originally based on `WTForms NumberRange validator`_.
+
+ .. _WTForms NumberRange validator:
+ https://github.com/wtforms/wtforms/blob/master/wtforms/validators.py
+
+ Examples::
+
+ >>> from datetime import datetime
+
+ >>> between(5, min=2)
+ True
+
+ >>> between(13.2, min=13, max=14)
+ True
+
+ >>> between(500, max=400)
+ ValidationFailure(func=between, args=...)
+
+ >>> between(
+ ... datetime(2000, 11, 11),
+ ... min=datetime(1999, 11, 11)
+ ... )
+ True
+
+ :param min:
+ The minimum required value of the number. If not provided, minimum
+ value will not be checked.
+ :param max:
+ The maximum value of the number. If not provided, maximum value
+ will not be checked.
+
+ .. versionadded:: 0.2
+ """
+ if min is None and max is None:
+ raise AssertionError(
+ 'At least one of `min` or `max` must be specified.'
+ )
+ if min is None:
+ min = Min
+ if max is None:
+ max = Max
+ try:
+ min_gt_max = min > max
+ except TypeError:
+ min_gt_max = max < min
+ if min_gt_max:
+ raise AssertionError('`min` cannot be more than `max`.')
+
+ return min <= value and max >= value
diff --git a/venv/lib/python3.9/site-packages/validators/btc_address.py b/venv/lib/python3.9/site-packages/validators/btc_address.py
new file mode 100644
index 00000000..35ada853
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/btc_address.py
@@ -0,0 +1,55 @@
+import re
+from hashlib import sha256
+
+from .utils import validator
+
+segwit_pattern = re.compile(
+ r'^(bc|tc)[0-3][02-9ac-hj-np-z]{14,74}$')
+
+
+def validate_segwit_address(addr):
+ return segwit_pattern.match(addr)
+
+
+def decode_base58(addr):
+ alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+ return sum([
+ (58 ** e) * alphabet.index(i)
+ for e, i in enumerate(addr[::-1])
+ ])
+
+
+def validate_old_btc_address(addr):
+ "Validate P2PKH and P2SH type address"
+ if not len(addr) in range(25, 35):
+ return False
+ decoded_bytes = decode_base58(addr).to_bytes(25, "big")
+ header = decoded_bytes[:-4]
+ checksum = decoded_bytes[-4:]
+ return checksum == sha256(sha256(header).digest()).digest()[:4]
+
+
+@validator
+def btc_address(value):
+ """
+ Return whether or not given value is a valid bitcoin address.
+
+ If the value is valid bitcoin address this function returns ``True``,
+ otherwise :class:`~validators.utils.ValidationFailure`.
+
+ Full validation is implemented for P2PKH and P2SH addresses.
+ For segwit addresses a regexp is used to provide a reasonable estimate
+ on whether the address is valid.
+
+ Examples::
+
+ >>> btc_address('3Cwgr2g7vsi1bXDUkpEnVoRLA9w4FZfC69')
+ True
+
+ :param value: Bitcoin address string to validate
+ """
+ if not value or not isinstance(value, str):
+ return False
+ if value[:2] in ("bc", "tb"):
+ return validate_segwit_address(value)
+ return validate_old_btc_address(value)
diff --git a/venv/lib/python3.9/site-packages/validators/card.py b/venv/lib/python3.9/site-packages/validators/card.py
new file mode 100644
index 00000000..8b8cbeea
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/card.py
@@ -0,0 +1,183 @@
+import re
+
+from .utils import validator
+
+
+@validator
+def card_number(value):
+ """
+ Return whether or not given value is a valid card number.
+
+ This validator is based on Luhn algorithm.
+
+ .. luhn:
+ https://github.com/mmcloughlin/luhn
+
+ Examples::
+
+ >>> card_number('4242424242424242')
+ True
+
+ >>> card_number('4242424242424241')
+ ValidationFailure(func=card_number, args={'value': '4242424242424241'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: card number string to validate
+ """
+ try:
+ digits = list(map(int, value))
+ odd_sum = sum(digits[-1::-2])
+ even_sum = sum([sum(divmod(2 * d, 10)) for d in digits[-2::-2]])
+ return (odd_sum + even_sum) % 10 == 0
+ except ValueError:
+ return False
+
+
+@validator
+def visa(value):
+ """
+ Return whether or not given value is a valid Visa card number.
+
+ Examples::
+
+ >>> visa('4242424242424242')
+ True
+
+ >>> visa('2223003122003222')
+ ValidationFailure(func=visa, args={'value': '2223003122003222'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: Visa card number string to validate
+ """
+ pattern = re.compile(r'^4')
+ return card_number(value) and len(value) == 16 and pattern.match(value)
+
+
+@validator
+def mastercard(value):
+ """
+ Return whether or not given value is a valid Mastercard card number.
+
+ Examples::
+
+ >>> mastercard('5555555555554444')
+ True
+
+ >>> mastercard('4242424242424242')
+ ValidationFailure(func=mastercard, args={'value': '4242424242424242'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: Mastercard card number string to validate
+ """
+ pattern = re.compile(r'^(51|52|53|54|55|22|23|24|25|26|27)')
+ return card_number(value) and len(value) == 16 and pattern.match(value)
+
+
+@validator
+def amex(value):
+ """
+ Return whether or not given value is a valid American Express card number.
+
+ Examples::
+
+ >>> amex('378282246310005')
+ True
+
+ >>> amex('4242424242424242')
+ ValidationFailure(func=amex, args={'value': '4242424242424242'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: American Express card number string to validate
+ """
+ pattern = re.compile(r'^(34|37)')
+ return card_number(value) and len(value) == 15 and pattern.match(value)
+
+
+@validator
+def unionpay(value):
+ """
+ Return whether or not given value is a valid UnionPay card number.
+
+ Examples::
+
+ >>> unionpay('6200000000000005')
+ True
+
+ >>> unionpay('4242424242424242')
+ ValidationFailure(func=unionpay, args={'value': '4242424242424242'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: UnionPay card number string to validate
+ """
+ pattern = re.compile(r'^62')
+ return card_number(value) and len(value) == 16 and pattern.match(value)
+
+
+@validator
+def diners(value):
+ """
+ Return whether or not given value is a valid Diners Club card number.
+
+ Examples::
+
+ >>> diners('3056930009020004')
+ True
+
+ >>> diners('4242424242424242')
+ ValidationFailure(func=diners, args={'value': '4242424242424242'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: Diners Club card number string to validate
+ """
+ pattern = re.compile(r'^(30|36|38|39)')
+ return (
+ card_number(value) and len(value) in [14, 16] and pattern.match(value)
+ )
+
+
+@validator
+def jcb(value):
+ """
+ Return whether or not given value is a valid JCB card number.
+
+ Examples::
+
+ >>> jcb('3566002020360505')
+ True
+
+ >>> jcb('4242424242424242')
+ ValidationFailure(func=jcb, args={'value': '4242424242424242'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: JCB card number string to validate
+ """
+ pattern = re.compile(r'^35')
+ return card_number(value) and len(value) == 16 and pattern.match(value)
+
+
+@validator
+def discover(value):
+ """
+ Return whether or not given value is a valid Discover card number.
+
+ Examples::
+
+ >>> discover('6011111111111117')
+ True
+
+ >>> discover('4242424242424242')
+ ValidationFailure(func=discover, args={'value': '4242424242424242'})
+
+ .. versionadded:: 0.15.0
+
+ :param value: Discover card number string to validate
+ """
+ pattern = re.compile(r'^(60|64|65)')
+ return card_number(value) and len(value) == 16 and pattern.match(value)
diff --git a/venv/lib/python3.9/site-packages/validators/domain.py b/venv/lib/python3.9/site-packages/validators/domain.py
new file mode 100644
index 00000000..d9bf44f0
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/domain.py
@@ -0,0 +1,54 @@
+import re
+
+from .utils import validator
+
+pattern = re.compile(
+ r'^(?:[a-zA-Z0-9]' # First character of the domain
+ r'(?:[a-zA-Z0-9-_]{0,61}[A-Za-z0-9])?\.)' # Sub domain + hostname
+ r'+[A-Za-z0-9][A-Za-z0-9-_]{0,61}' # First 61 characters of the gTLD
+ r'[A-Za-z]$' # Last character of the gTLD
+)
+
+
+def to_unicode(obj, charset='utf-8', errors='strict'):
+ if obj is None:
+ return None
+ if not isinstance(obj, bytes):
+ return str(obj)
+ return obj.decode(charset, errors)
+
+
+@validator
+def domain(value):
+ """
+ Return whether or not given value is a valid domain.
+
+ If the value is valid domain name this function returns ``True``, otherwise
+ :class:`~validators.utils.ValidationFailure`.
+
+ Examples::
+
+ >>> domain('example.com')
+ True
+
+ >>> domain('example.com/')
+ ValidationFailure(func=domain, ...)
+
+
+ Supports IDN domains as well::
+
+ >>> domain('xn----gtbspbbmkef.xn--p1ai')
+ True
+
+ .. versionadded:: 0.9
+
+ .. versionchanged:: 0.10
+
+ Added support for internationalized domain name (IDN) validation.
+
+ :param value: domain string to validate
+ """
+ try:
+ return pattern.match(to_unicode(value).encode('idna').decode('ascii'))
+ except (UnicodeError, AttributeError):
+ return False
diff --git a/venv/lib/python3.9/site-packages/validators/email.py b/venv/lib/python3.9/site-packages/validators/email.py
new file mode 100644
index 00000000..229c8e46
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/email.py
@@ -0,0 +1,75 @@
+import re
+
+from .utils import validator
+
+user_regex = re.compile(
+ # dot-atom
+ r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+"
+ r"(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$"
+ # quoted-string
+ r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|'
+ r"""\\[\001-\011\013\014\016-\177])*"$)""",
+ re.IGNORECASE
+)
+domain_regex = re.compile(
+ # domain
+ r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
+ r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?$)'
+ # literal form, ipv4 address (SMTP 4.1.3)
+ r'|^\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)'
+ r'(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$',
+ re.IGNORECASE)
+domain_whitelist = ['localhost']
+
+
+@validator
+def email(value, whitelist=None):
+ """
+ Validate an email address.
+
+ This validator is based on `Django's email validator`_. Returns
+ ``True`` on success and :class:`~validators.utils.ValidationFailure`
+ when validation fails.
+
+ Examples::
+
+ >>> email('someone@example.com')
+ True
+
+ >>> email('bogus@@')
+ ValidationFailure(func=email, ...)
+
+ .. _Django's email validator:
+ https://github.com/django/django/blob/master/django/core/validators.py
+
+ .. versionadded:: 0.1
+
+ :param value: value to validate
+ :param whitelist: domain names to whitelist
+
+ :copyright: (c) Django Software Foundation and individual contributors.
+ :license: BSD
+ """
+
+ if whitelist is None:
+ whitelist = domain_whitelist
+
+ if not value or '@' not in value:
+ return False
+
+ user_part, domain_part = value.rsplit('@', 1)
+
+ if not user_regex.match(user_part):
+ return False
+
+ if len(user_part.encode("utf-8")) > 64:
+ return False
+
+ if domain_part not in whitelist and not domain_regex.match(domain_part):
+ # Try for possible IDN domain-part
+ try:
+ domain_part = domain_part.encode('idna').decode('ascii')
+ return domain_regex.match(domain_part)
+ except UnicodeError:
+ return False
+ return True
diff --git a/venv/lib/python3.9/site-packages/validators/extremes.py b/venv/lib/python3.9/site-packages/validators/extremes.py
new file mode 100644
index 00000000..43d168a7
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/extremes.py
@@ -0,0 +1,61 @@
+from functools import total_ordering
+
+
+@total_ordering
+class Min(object):
+ """
+ An object that is less than any other object (except itself).
+
+ Inspired by https://pypi.python.org/pypi/Extremes
+
+ Examples::
+
+ >>> import sys
+
+ >>> Min < -sys.maxint
+ True
+
+ >>> Min < None
+ True
+
+ >>> Min < ''
+ True
+
+ .. versionadded:: 0.2
+ """
+ def __lt__(self, other):
+ if other is Min:
+ return False
+ return True
+
+
+@total_ordering
+class Max(object):
+ """
+ An object that is greater than any other object (except itself).
+
+ Inspired by https://pypi.python.org/pypi/Extremes
+
+ Examples::
+
+ >>> import sys
+
+ >>> Max > Min
+ True
+
+ >>> Max > sys.maxint
+ True
+
+ >>> Max > 99999999999999999
+ True
+
+ .. versionadded:: 0.2
+ """
+ def __gt__(self, other):
+ if other is Max:
+ return False
+ return True
+
+
+Min = Min()
+Max = Max()
diff --git a/venv/lib/python3.9/site-packages/validators/hashes.py b/venv/lib/python3.9/site-packages/validators/hashes.py
new file mode 100644
index 00000000..4db7f78e
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/hashes.py
@@ -0,0 +1,121 @@
+import re
+
+from .utils import validator
+
+md5_regex = re.compile(
+ r"^[0-9a-f]{32}$",
+ re.IGNORECASE
+)
+sha1_regex = re.compile(
+ r"^[0-9a-f]{40}$",
+ re.IGNORECASE
+)
+sha224_regex = re.compile(
+ r"^[0-9a-f]{56}$",
+ re.IGNORECASE
+)
+sha256_regex = re.compile(
+ r"^[0-9a-f]{64}$",
+ re.IGNORECASE
+)
+sha512_regex = re.compile(
+ r"^[0-9a-f]{128}$",
+ re.IGNORECASE
+)
+
+
+@validator
+def md5(value):
+ """
+ Return whether or not given value is a valid MD5 hash.
+
+ Examples::
+
+ >>> md5('d41d8cd98f00b204e9800998ecf8427e')
+ True
+
+ >>> md5('900zz11')
+ ValidationFailure(func=md5, args={'value': '900zz11'})
+
+ :param value: MD5 string to validate
+ """
+ return md5_regex.match(value)
+
+
+@validator
+def sha1(value):
+ """
+ Return whether or not given value is a valid SHA1 hash.
+
+ Examples::
+
+ >>> sha1('da39a3ee5e6b4b0d3255bfef95601890afd80709')
+ True
+
+ >>> sha1('900zz11')
+ ValidationFailure(func=sha1, args={'value': '900zz11'})
+
+ :param value: SHA1 string to validate
+ """
+ return sha1_regex.match(value)
+
+
+@validator
+def sha224(value):
+ """
+ Return whether or not given value is a valid SHA224 hash.
+
+ Examples::
+
+ >>> sha224('d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f')
+ True
+
+ >>> sha224('900zz11')
+ ValidationFailure(func=sha224, args={'value': '900zz11'})
+
+ :param value: SHA224 string to validate
+ """
+ return sha224_regex.match(value)
+
+
+@validator
+def sha256(value):
+ """
+ Return whether or not given value is a valid SHA256 hash.
+
+ Examples::
+
+ >>> sha256(
+ ... 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b'
+ ... '855'
+ ... )
+ True
+
+ >>> sha256('900zz11')
+ ValidationFailure(func=sha256, args={'value': '900zz11'})
+
+ :param value: SHA256 string to validate
+ """
+ return sha256_regex.match(value)
+
+
+@validator
+def sha512(value):
+ """
+ Return whether or not given value is a valid SHA512 hash.
+
+ Examples::
+
+ >>> sha512(
+ ... 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce'
+ ... '9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af9'
+ ... '27da3e'
+ ... )
+ True
+
+ >>> sha512('900zz11')
+ ValidationFailure(func=sha512, args={'value': '900zz11'})
+
+ :param value: SHA512 string to validate
+ """
+ return sha512_regex.match(value)
diff --git a/venv/lib/python3.9/site-packages/validators/i18n/__init__.py b/venv/lib/python3.9/site-packages/validators/i18n/__init__.py
new file mode 100644
index 00000000..12775c6c
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/i18n/__init__.py
@@ -0,0 +1,4 @@
+# TODO: remove, let the user import it if they really want it
+from .fi import fi_business_id, fi_ssn # noqa
+
+__all__ = ('fi_business_id', 'fi_ssn')
diff --git a/venv/lib/python3.9/site-packages/validators/i18n/es.py b/venv/lib/python3.9/site-packages/validators/i18n/es.py
new file mode 100644
index 00000000..ed2e2a63
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/i18n/es.py
@@ -0,0 +1,200 @@
+# -*- coding: utf-8 -*-
+from validators.utils import validator
+
+__all__ = ('es_cif', 'es_nif', 'es_nie', 'es_doi',)
+
+
+def nif_nie_validation(doi, number_by_letter, special_cases):
+ """
+ Validate if the doi is a NIF or a NIE.
+ :param doi: DOI to validate.
+ :return: boolean if it's valid.
+ """
+ doi = doi.upper()
+ if doi in special_cases:
+ return False
+
+ table = 'TRWAGMYFPDXBNJZSQVHLCKE'
+
+ if len(doi) != 9:
+ return False
+
+ control = doi[8]
+
+ # If it is not a DNI, convert the first letter to the corresponding
+ # digit
+ numbers = number_by_letter.get(doi[0], doi[0]) + doi[1:8]
+
+ return numbers.isdigit() and control == table[int(numbers) % 23]
+
+
+@validator
+def es_cif(doi):
+ """
+ Validate a Spanish CIF.
+
+ Each company in Spain prior to 2008 had a distinct CIF and has been
+ discontinued. For more information see `wikipedia.org/cif`_.
+
+ The new replacement is to use NIF for absolutely everything. The issue is
+ that there are "types" of NIFs now: company, person[citizen vs recident]
+ all distinguished by the first character of the DOI. For this reason we
+ will continue to call CIF NIFs that are used for companies.
+
+ This validator is based on `generadordni.es`_.
+
+ .. _generadordni.es:
+ https://generadordni.es/
+
+ .. _wikipedia.org/cif:
+ https://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal
+
+ Examples::
+
+ >>> es_cif('B25162520')
+ True
+
+ >>> es_cif('B25162529')
+ ValidationFailure(func=es_cif, args=...)
+
+ .. versionadded:: 0.13.0
+
+ :param doi: DOI to validate
+ """
+ doi = doi.upper()
+
+ if len(doi) != 9:
+ return False
+
+ table = 'JABCDEFGHI'
+ first_chr = doi[0]
+ doi_body = doi[1:8]
+ control = doi[8]
+
+ if not doi_body.isdigit():
+ return False
+
+ odd_result = 0
+ even_result = 0
+ for index, char in enumerate(doi_body):
+ if index % 2 == 0:
+ # Multiply each each odd position doi digit by 2 and sum it all
+ # together
+ odd_result += sum(map(int, str(int(char) * 2)))
+ else:
+ even_result += int(char)
+
+ res = (10 - (even_result + odd_result) % 10) % 10
+
+ if first_chr in 'ABEH': # Number type
+ return str(res) == control
+ elif first_chr in 'PSQW': # Letter type
+ return table[res] == control
+ elif first_chr not in 'CDFGJNRUV':
+ return False
+
+ return control == str(res) or control == table[res]
+
+
+@validator
+def es_nif(doi):
+ """
+ Validate a Spanish NIF.
+
+ Each entity, be it person or company in Spain has a distinct NIF. Since
+ we've designated CIF to be a company NIF, this NIF is only for person.
+ For more information see `wikipedia.org/nif`_.
+
+ This validator is based on `generadordni.es`_.
+
+ .. _generadordni.es:
+ https://generadordni.es/
+
+ .. _wikipedia.org/nif:
+ https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal
+
+ Examples::
+
+ >>> es_nif('26643189N')
+ True
+
+ >>> es_nif('26643189X')
+ ValidationFailure(func=es_nif, args=...)
+
+ .. versionadded:: 0.13.0
+
+ :param doi: DOI to validate
+ """
+ number_by_letter = {'L': '0', 'M': '0', 'K': '0'}
+ special_cases = ['X0000000T', '00000000T', '00000001R']
+ return nif_nie_validation(doi, number_by_letter, special_cases)
+
+
+@validator
+def es_nie(doi):
+ """
+ Validate a Spanish NIE.
+
+ The NIE is a tax identification number in Spain, known in Spanish as the
+ NIE, or more formally the NĂºmero de identidad de extranjero. For more
+ information see `wikipedia.org/nie`_.
+
+ This validator is based on `generadordni.es`_.
+
+ .. _generadordni.es:
+ https://generadordni.es/
+
+ .. _wikipedia.org/nie:
+ https://es.wikipedia.org/wiki/N%C3%BAmero_de_identidad_de_extranjero
+
+ Examples::
+
+ >>> es_nie('X0095892M')
+ True
+
+ >>> es_nie('X0095892X')
+ ValidationFailure(func=es_nie, args=...)
+
+ .. versionadded:: 0.13.0
+
+ :param doi: DOI to validate
+ """
+ number_by_letter = {'X': '0', 'Y': '1', 'Z': '2'}
+ special_cases = ['X0000000T']
+
+ # NIE must must start with X Y or Z
+ if not doi or doi[0] not in number_by_letter.keys():
+ return False
+
+ return nif_nie_validation(doi, number_by_letter, special_cases)
+
+
+@validator
+def es_doi(doi):
+ """
+ Validate a Spanish DOI.
+
+ A DOI in spain is all NIF / CIF / NIE / DNI -- a digital ID. For more
+ information see `wikipedia.org/doi`_.
+
+ This validator is based on `generadordni.es`_.
+
+ .. _generadordni.es:
+ https://generadordni.es/
+
+ .. _wikipedia.org/doi:
+ https://es.wikipedia.org/wiki/Identificador_de_objeto_digital
+
+ Examples::
+
+ >>> es_doi('X0095892M')
+ True
+
+ >>> es_doi('X0095892X')
+ ValidationFailure(func=es_doi, args=...)
+
+ .. versionadded:: 0.13.0
+
+ :param doi: DOI to validate
+ """
+ return es_nie(doi) or es_nif(doi) or es_cif(doi)
diff --git a/venv/lib/python3.9/site-packages/validators/i18n/fi.py b/venv/lib/python3.9/site-packages/validators/i18n/fi.py
new file mode 100644
index 00000000..2e5eb578
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/i18n/fi.py
@@ -0,0 +1,94 @@
+import re
+
+from validators.utils import validator
+
+business_id_pattern = re.compile(r'^[0-9]{7}-[0-9]$')
+ssn_checkmarks = '0123456789ABCDEFHJKLMNPRSTUVWXY'
+ssn_pattern = re.compile(
+ r"""^
+ (?P<date>(0[1-9]|[1-2]\d|3[01])
+ (0[1-9]|1[012])
+ (\d{{2}}))
+ [A+-]
+ (?P<serial>(\d{{3}}))
+ (?P<checksum>[{checkmarks}])$""".format(checkmarks=ssn_checkmarks),
+ re.VERBOSE
+)
+
+
+@validator
+def fi_business_id(business_id):
+ """
+ Validate a Finnish Business ID.
+
+ Each company in Finland has a distinct business id. For more
+ information see `Finnish Trade Register`_
+
+ .. _Finnish Trade Register:
+ http://en.wikipedia.org/wiki/Finnish_Trade_Register
+
+ Examples::
+
+ >>> fi_business_id('0112038-9') # Fast Monkeys Ltd
+ True
+
+ >>> fi_business_id('1234567-8') # Bogus ID
+ ValidationFailure(func=fi_business_id, ...)
+
+ .. versionadded:: 0.4
+ .. versionchanged:: 0.5
+ Method renamed from ``finnish_business_id`` to ``fi_business_id``
+
+ :param business_id: business_id to validate
+ """
+ if not business_id or not re.match(business_id_pattern, business_id):
+ return False
+ factors = [7, 9, 10, 5, 8, 4, 2]
+ numbers = map(int, business_id[:7])
+ checksum = int(business_id[8])
+ sum_ = sum(f * n for f, n in zip(factors, numbers))
+ modulo = sum_ % 11
+ return (11 - modulo == checksum) or (modulo == 0 and checksum == 0)
+
+
+@validator
+def fi_ssn(ssn, allow_temporal_ssn=True):
+ """
+ Validate a Finnish Social Security Number.
+
+ This validator is based on `django-localflavor-fi`_.
+
+ .. _django-localflavor-fi:
+ https://github.com/django/django-localflavor-fi/
+
+ Examples::
+
+ >>> fi_ssn('010101-0101')
+ True
+
+ >>> fi_ssn('101010-0102')
+ ValidationFailure(func=fi_ssn, args=...)
+
+ .. versionadded:: 0.5
+
+ :param ssn: Social Security Number to validate
+ :param allow_temporal_ssn:
+ Whether to accept temporal SSN numbers. Temporal SSN numbers are the
+ ones where the serial is in the range [900-999]. By default temporal
+ SSN numbers are valid.
+
+ """
+ if not ssn:
+ return False
+
+ result = re.match(ssn_pattern, ssn)
+ if not result:
+ return False
+ gd = result.groupdict()
+ checksum = int(gd['date'] + gd['serial'])
+ return (
+ int(gd['serial']) >= 2 and
+ (allow_temporal_ssn or int(gd['serial']) <= 899) and
+ ssn_checkmarks[checksum % len(ssn_checkmarks)] ==
+ gd['checksum']
+ )
diff --git a/venv/lib/python3.9/site-packages/validators/iban.py b/venv/lib/python3.9/site-packages/validators/iban.py
new file mode 100644
index 00000000..7413d127
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/iban.py
@@ -0,0 +1,52 @@
+import re
+
+from .utils import validator
+
+regex = (
+ r'^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$'
+)
+pattern = re.compile(regex)
+
+
+def char_value(char):
+ """A=10, B=11, ..., Z=35
+ """
+ if char.isdigit():
+ return int(char)
+ else:
+ return 10 + ord(char) - ord('A')
+
+
+def modcheck(value):
+ """Check if the value string passes the mod97-test.
+ """
+ # move country code and check numbers to end
+ rearranged = value[4:] + value[:4]
+ # convert letters to numbers
+ converted = [char_value(char) for char in rearranged]
+ # interpret as integer
+ integerized = int(''.join([str(i) for i in converted]))
+ return (integerized % 97 == 1)
+
+
+@validator
+def iban(value):
+ """
+ Return whether or not given value is a valid IBAN code.
+
+ If the value is a valid IBAN this function returns ``True``, otherwise
+ :class:`~validators.utils.ValidationFailure`.
+
+ Examples::
+
+ >>> iban('DE29100500001061045672')
+ True
+
+ >>> iban('123456')
+ ValidationFailure(func=iban, ...)
+
+ .. versionadded:: 0.8
+
+ :param value: IBAN string to validate
+ """
+ return pattern.match(value) and modcheck(value)
diff --git a/venv/lib/python3.9/site-packages/validators/ip_address.py b/venv/lib/python3.9/site-packages/validators/ip_address.py
new file mode 100644
index 00000000..e0c061db
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/ip_address.py
@@ -0,0 +1,156 @@
+from .utils import validator
+
+
+@validator
+def ipv4(value):
+ """
+ Return whether a given value is a valid IP version 4 address.
+
+ This validator is based on `WTForms IPAddress validator`_
+
+ .. _WTForms IPAddress validator:
+ https://github.com/wtforms/wtforms/blob/master/wtforms/validators.py
+
+ Examples::
+
+ >>> ipv4('123.0.0.7')
+ True
+
+ >>> ipv4('900.80.70.11')
+ ValidationFailure(func=ipv4, args={'value': '900.80.70.11'})
+
+ .. versionadded:: 0.2
+
+ :param value: IP address string to validate
+ """
+ groups = value.split(".")
+ if (
+ len(groups) != 4
+ or any(not x.isdigit() for x in groups)
+ or any(len(x) > 3 for x in groups)
+ ):
+ return False
+ return all(0 <= int(part) < 256 for part in groups)
+
+
+@validator
+def ipv4_cidr(value):
+ """
+ Return whether a given value is a valid CIDR-notated IP version 4
+ address range.
+
+ This validator is based on RFC4632 3.1.
+
+ Examples::
+
+ >>> ipv4_cidr('1.1.1.1/8')
+ True
+
+ >>> ipv4_cidr('1.1.1.1')
+ ValidationFailure(func=ipv4_cidr, args={'value': '1.1.1.1'})
+ """
+ try:
+ prefix, suffix = value.split('/', 2)
+ except ValueError:
+ return False
+ if not ipv4(prefix) or not suffix.isdigit():
+ return False
+ return 0 <= int(suffix) <= 32
+
+
+@validator
+def ipv6(value):
+ """
+ Return whether a given value is a valid IP version 6 address
+ (including IPv4-mapped IPv6 addresses).
+
+ This validator is based on `WTForms IPAddress validator`_.
+
+ .. _WTForms IPAddress validator:
+ https://github.com/wtforms/wtforms/blob/master/wtforms/validators.py
+
+ Examples::
+
+ >>> ipv6('abcd:ef::42:1')
+ True
+
+ >>> ipv6('::ffff:192.0.2.128')
+ True
+
+ >>> ipv6('::192.0.2.128')
+ True
+
+ >>> ipv6('abc.0.0.1')
+ ValidationFailure(func=ipv6, args={'value': 'abc.0.0.1'})
+
+ .. versionadded:: 0.2
+
+ :param value: IP address string to validate
+ """
+ ipv6_groups = value.split(':')
+ if len(ipv6_groups) == 1:
+ return False
+ ipv4_groups = ipv6_groups[-1].split('.')
+
+ if len(ipv4_groups) > 1:
+ if not ipv4(ipv6_groups[-1]):
+ return False
+ ipv6_groups = ipv6_groups[:-1]
+ else:
+ ipv4_groups = []
+
+ count_blank = 0
+ for part in ipv6_groups:
+ if not part:
+ count_blank += 1
+ continue
+ try:
+ num = int(part, 16)
+ except ValueError:
+ return False
+ else:
+ if not 0 <= num <= 65536 or len(part) > 4:
+ return False
+
+ max_groups = 6 if ipv4_groups else 8
+ part_count = len(ipv6_groups) - count_blank
+ if count_blank == 0 and part_count == max_groups:
+ # no :: -> must have size of max_groups
+ return True
+ elif count_blank == 1 and ipv6_groups[-1] and ipv6_groups[0] and part_count < max_groups:
+ # one :: inside the address or prefix or suffix : -> filter least two cases
+ return True
+ elif count_blank == 2 and part_count < max_groups and (
+ ((ipv6_groups[0] and not ipv6_groups[-1]) or (not ipv6_groups[0] and ipv6_groups[-1])) or ipv4_groups):
+ # leading or trailing :: or : at end and begin -> filter last case
+ # Check if it has ipv4 groups because they get removed from the ipv6_groups
+ return True
+ elif count_blank == 3 and part_count == 0:
+ # :: is the address -> filter everything else
+ return True
+ return False
+
+
+@validator
+def ipv6_cidr(value):
+ """
+ Returns whether a given value is a valid CIDR-notated IP version 6
+ address range.
+
+ This validator is based on RFC4632 3.1.
+
+ Examples::
+
+ >>> ipv6_cidr('::1/128')
+ True
+
+ >>> ipv6_cidr('::1')
+ ValidationFailure(func=ipv6_cidr, args={'value': '::1'})
+ """
+ try:
+ prefix, suffix = value.split('/', 2)
+ except ValueError:
+ return False
+ if not ipv6(prefix) or not suffix.isdigit():
+ return False
+ return 0 <= int(suffix) <= 128
diff --git a/venv/lib/python3.9/site-packages/validators/length.py b/venv/lib/python3.9/site-packages/validators/length.py
new file mode 100644
index 00000000..d0f91fd3
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/length.py
@@ -0,0 +1,37 @@
+from .between import between
+from .utils import validator
+
+
+@validator
+def length(value, min=None, max=None):
+ """
+ Return whether or not the length of given string is within a specified
+ range.
+
+ Examples::
+
+ >>> length('something', min=2)
+ True
+
+ >>> length('something', min=9, max=9)
+ True
+
+ >>> length('something', max=5)
+ ValidationFailure(func=length, ...)
+
+ :param value:
+ The string to validate.
+ :param min:
+ The minimum required length of the string. If not provided, minimum
+ length will not be checked.
+ :param max:
+ The maximum length of the string. If not provided, maximum length
+ will not be checked.
+
+ .. versionadded:: 0.2
+ """
+ if (min is not None and min < 0) or (max is not None and max < 0):
+ raise AssertionError(
+ '`min` and `max` need to be greater than zero.'
+ )
+ return between(len(value), min=min, max=max)
diff --git a/venv/lib/python3.9/site-packages/validators/mac_address.py b/venv/lib/python3.9/site-packages/validators/mac_address.py
new file mode 100644
index 00000000..bdb19947
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/mac_address.py
@@ -0,0 +1,33 @@
+import re
+
+from .utils import validator
+
+pattern = re.compile(r'^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$')
+
+
+@validator
+def mac_address(value):
+ """
+ Return whether or not given value is a valid MAC address.
+
+ If the value is valid MAC address this function returns ``True``,
+ otherwise :class:`~validators.utils.ValidationFailure`.
+
+ This validator is based on `WTForms MacAddress validator`_.
+
+ .. _WTForms MacAddress validator:
+ https://github.com/wtforms/wtforms/blob/master/wtforms/validators.py
+
+ Examples::
+
+ >>> mac_address('01:23:45:67:ab:CD')
+ True
+
+ >>> mac_address('00:00:00:00:00')
+ ValidationFailure(func=mac_address, args={'value': '00:00:00:00:00'})
+
+ .. versionadded:: 0.2
+
+ :param value: Mac address string to validate
+ """
+ return pattern.match(value)
diff --git a/venv/lib/python3.9/site-packages/validators/slug.py b/venv/lib/python3.9/site-packages/validators/slug.py
new file mode 100644
index 00000000..83bfd4b1
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/slug.py
@@ -0,0 +1,28 @@
+import re
+
+from .utils import validator
+
+slug_regex = re.compile(r'^[-a-zA-Z0-9_]+$')
+
+
+@validator
+def slug(value):
+ """
+ Validate whether or not given value is valid slug.
+
+ Valid slug can contain only alphanumeric characters, hyphens and
+ underscores.
+
+ Examples::
+
+ >>> slug('my.slug')
+ ValidationFailure(func=slug, args={'value': 'my.slug'})
+
+ >>> slug('my-slug-2134')
+ True
+
+ .. versionadded:: 0.6
+
+ :param value: value to validate
+ """
+ return slug_regex.match(value)
diff --git a/venv/lib/python3.9/site-packages/validators/truthy.py b/venv/lib/python3.9/site-packages/validators/truthy.py
new file mode 100644
index 00000000..517149aa
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/truthy.py
@@ -0,0 +1,39 @@
+from .utils import validator
+
+
+@validator
+def truthy(value):
+ """
+ Validate that given value is not a falsey value.
+
+ This validator is based on `WTForms DataRequired validator`_.
+
+ .. _WTForms DataRequired validator:
+ https://github.com/wtforms/wtforms/blob/master/wtforms/validators.py
+
+ Examples::
+
+ >>> truthy(1)
+ True
+
+ >>> truthy('someone')
+ True
+
+ >>> truthy(0)
+ ValidationFailure(func=truthy, args={'value': 0})
+
+ >>> truthy(' ')
+ ValidationFailure(func=truthy, args={'value': ' '})
+
+ >>> truthy(False)
+ ValidationFailure(func=truthy, args={'value': False})
+
+ >>> truthy(None)
+ ValidationFailure(func=truthy, args={'value': None})
+
+ .. versionadded:: 0.2
+ """
+ return (
+ value and
+ (not isinstance(value, str) or value.strip())
+ )
diff --git a/venv/lib/python3.9/site-packages/validators/url.py b/venv/lib/python3.9/site-packages/validators/url.py
new file mode 100644
index 00000000..37d946cb
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/url.py
@@ -0,0 +1,154 @@
+import re
+
+from .utils import validator
+
+ip_middle_octet = r"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5]))"
+ip_last_octet = r"(?:\.(?:0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5]))"
+
+regex = re.compile( # noqa: W605
+ r"^"
+ # protocol identifier
+ r"(?:(?:https?|ftp)://)"
+ # user:pass authentication
+ r"(?:[-a-z\u00a1-\uffff0-9._~%!$&'()*+,;=:]+"
+ r"(?::[-a-z0-9._~%!$&'()*+,;=:]*)?@)?"
+ r"(?:"
+ r"(?P<private_ip>"
+ # IP address exclusion
+ # private & local networks
+ r"(?:(?:10|127)" + ip_middle_octet + r"{2}" + ip_last_octet + r")|"
+ r"(?:(?:169\.254|192\.168)" + ip_middle_octet + ip_last_octet + r")|"
+ r"(?:172\.(?:1[6-9]|2\d|3[0-1])" + ip_middle_octet + ip_last_octet + r"))"
+ r"|"
+ # private & local hosts
+ r"(?P<private_host>"
+ r"(?:localhost))"
+ r"|"
+ # IP address dotted notation octets
+ # excludes loopback network 0.0.0.0
+ # excludes reserved space >= 224.0.0.0
+ # excludes network & broadcast addresses
+ # (first & last IP address of each class)
+ r"(?P<public_ip>"
+ r"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])"
+ r"" + ip_middle_octet + r"{2}"
+ r"" + ip_last_octet + r")"
+ r"|"
+ # IPv6 RegEx from https://stackoverflow.com/a/17871737
+ r"\[("
+ # 1:2:3:4:5:6:7:8
+ r"([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|"
+ # 1:: 1:2:3:4:5:6:7::
+ r"([0-9a-fA-F]{1,4}:){1,7}:|"
+ # 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8
+ r"([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|"
+ # 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8
+ r"([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|"
+ # 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8
+ r"([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|"
+ # 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8
+ r"([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|"
+ # 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8
+ r"([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|"
+ # 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8
+ r"[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|"
+ # ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::
+ r":((:[0-9a-fA-F]{1,4}){1,7}|:)|"
+ # fe80::7:8%eth0 fe80::7:8%1
+ # (link-local IPv6 addresses with zone index)
+ r"fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
+ r"::(ffff(:0{1,4}){0,1}:){0,1}"
+ r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}"
+ # ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255
+ # (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
+ r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|"
+ r"([0-9a-fA-F]{1,4}:){1,4}:"
+ r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}"
+ # 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33
+ # (IPv4-Embedded IPv6 Address)
+ r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
+ r")\]|"
+ # host name
+ r"(?:(?:(?:xn--[-]{0,2})|[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]-?)*"
+ r"[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]+)"
+ # domain name
+ r"(?:\.(?:(?:xn--[-]{0,2})|[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]-?)*"
+ r"[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]+)*"
+ # TLD identifier
+ r"(?:\.(?:(?:xn--[-]{0,2}[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]{2,})|"
+ r"[a-z\u00a1-\uffff\U00010000-\U0010ffff]{2,}))"
+ r")"
+ # port number
+ r"(?::\d{2,5})?"
+ # resource path
+ r"(?:/[-a-z\u00a1-\uffff\U00010000-\U0010ffff0-9._~%!$&'()*+,;=:@/]*)?"
+ # query string
+ r"(?:\?\S*)?"
+ # fragment
+ r"(?:#\S*)?"
+ r"$",
+ re.UNICODE | re.IGNORECASE
+)
+
+pattern = re.compile(regex)
+
+
+@validator
+def url(value, public=False):
+ """
+ Return whether or not given value is a valid URL.
+
+ If the value is valid URL this function returns ``True``, otherwise
+ :class:`~validators.utils.ValidationFailure`.
+
+ This validator is based on the wonderful `URL validator of dperini`_.
+
+ .. _URL validator of dperini:
+ https://gist.github.com/dperini/729294
+
+ Examples::
+
+ >>> url('http://foobar.dk')
+ True
+
+ >>> url('ftp://foobar.dk')
+ True
+
+ >>> url('http://10.0.0.1')
+ True
+
+ >>> url('http://foobar.d')
+ ValidationFailure(func=url, ...)
+
+ >>> url('http://10.0.0.1', public=True)
+ ValidationFailure(func=url, ...)
+
+ .. versionadded:: 0.2
+
+ .. versionchanged:: 0.10.2
+
+ Added support for various exotic URLs and fixed various false
+ positives.
+
+ .. versionchanged:: 0.10.3
+
+ Added ``public`` parameter.
+
+ .. versionchanged:: 0.11.0
+
+ Made the regular expression this function uses case insensitive.
+
+ .. versionchanged:: 0.11.3
+
+ Added support for URLs containing localhost
+
+ :param value: URL address string to validate
+ :param public: (default=False) Set True to only allow a public IP address
+ """
+ result = pattern.match(value)
+ if not public:
+ return result
+
+ return result and not any(
+ (result.groupdict().get(key) for key in ('private_ip', 'private_host'))
+ )
diff --git a/venv/lib/python3.9/site-packages/validators/utils.py b/venv/lib/python3.9/site-packages/validators/utils.py
new file mode 100644
index 00000000..3044477b
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/utils.py
@@ -0,0 +1,85 @@
+import inspect
+import itertools
+from collections import OrderedDict
+
+from decorator import decorator
+
+
+class ValidationFailure(Exception):
+ def __init__(self, func, args):
+ self.func = func
+ self.__dict__.update(args)
+
+ def __repr__(self):
+ return u'ValidationFailure(func={func}, args={args})'.format(
+ func=self.func.__name__,
+ args=dict(
+ [(k, v) for (k, v) in self.__dict__.items() if k != 'func']
+ )
+ )
+
+ def __str__(self):
+ return repr(self)
+
+ def __unicode__(self):
+ return repr(self)
+
+ def __bool__(self):
+ return False
+
+ def __nonzero__(self):
+ return False
+
+
+def func_args_as_dict(func, args, kwargs):
+ """
+ Return given function's positional and key value arguments as an ordered
+ dictionary.
+ """
+ _getargspec = inspect.getfullargspec
+
+ arg_names = list(
+ OrderedDict.fromkeys(
+ itertools.chain(
+ _getargspec(func)[0],
+ kwargs.keys()
+ )
+ )
+ )
+ return OrderedDict(
+ list(zip(arg_names, args)) +
+ list(kwargs.items())
+ )
+
+
+def validator(func, *args, **kwargs):
+ """
+ A decorator that makes given function validator.
+
+ Whenever the given function is called and returns ``False`` value
+ this decorator returns :class:`ValidationFailure` object.
+
+ Example::
+
+ >>> @validator
+ ... def even(value):
+ ... return not (value % 2)
+
+ >>> even(4)
+ True
+
+ >>> even(5)
+ ValidationFailure(func=even, args={'value': 5})
+
+ :param func: function to decorate
+ :param args: positional function arguments
+ :param kwargs: key value function arguments
+ """
+ def wrapper(func, *args, **kwargs):
+ value = func(*args, **kwargs)
+ if not value:
+ return ValidationFailure(
+ func, func_args_as_dict(func, args, kwargs)
+ )
+ return True
+ return decorator(wrapper, func)
diff --git a/venv/lib/python3.9/site-packages/validators/uuid.py b/venv/lib/python3.9/site-packages/validators/uuid.py
new file mode 100644
index 00000000..20080088
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/validators/uuid.py
@@ -0,0 +1,41 @@
+from __future__ import absolute_import
+
+import re
+from uuid import UUID
+
+from .utils import validator
+
+pattern = re.compile(r'^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')
+
+
+@validator
+def uuid(value):
+ """
+ Return whether or not given value is a valid UUID.
+
+ If the value is valid UUID this function returns ``True``, otherwise
+ :class:`~validators.utils.ValidationFailure`.
+
+ This validator is based on `WTForms UUID validator`_.
+
+ .. _WTForms UUID validator:
+ https://github.com/wtforms/wtforms/blob/master/wtforms/validators.py
+
+ Examples::
+
+ >>> uuid('2bc1c94f-0deb-43e9-92a1-4775189ec9f8')
+ True
+
+ >>> uuid('2bc1c94f 0deb-43e9-92a1-4775189ec9f8')
+ ValidationFailure(func=uuid, ...)
+
+ .. versionadded:: 0.2
+
+ :param value: UUID value to validate
+ """
+ if isinstance(value, UUID):
+ return True
+ try:
+ return pattern.match(value)
+ except TypeError:
+ return False