diff --git a/tests/test_hostname.py b/tests/test_hostname.py new file mode 100644 index 00000000..5e9573b7 --- /dev/null +++ b/tests/test_hostname.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +import pytest + +from validators import hostname, ValidationFailure + + +@pytest.mark.parametrize(('address',), [ + ('127.0.0.1',), + ('123.5.77.88',), + ('12.12.12.12',), + ('google.com',), + ('www.duck.com',), + ('ashdjkha.asdjaks.to',), +]) +def test_returns_true_on_valid_hostname(address): + assert hostname(address) + + +@pytest.mark.parametrize(('address',), [ + ('abc.0.0.1',), + ('1278.0.0.1',), + ('900.200.100.75',), + ('0127.0.0.1',), + ('!@#google.com',), + (' www.duck.com',), + ('ashdj_>kha.asdjaks.to',), +]) +def test_returns_failed_validation_on_invalid_hostname(address): + assert isinstance(hostname(address), ValidationFailure) diff --git a/validators/__init__.py b/validators/__init__.py index f623e12f..0670d609 100644 --- a/validators/__init__.py +++ b/validators/__init__.py @@ -14,6 +14,7 @@ from .email import email from .extremes import Max, Min from .hashes import md5, sha1, sha224, sha256, sha512 +from .hostname import hostname from .i18n import fi_business_id, fi_ssn from .iban import iban from .ip_address import ipv4, ipv4_cidr, ipv6, ipv6_cidr @@ -26,7 +27,7 @@ from .uuid import uuid __all__ = ('between', 'domain', 'email', 'Max', 'Min', 'md5', 'sha1', 'sha224', - 'sha256', 'sha512', 'fi_business_id', 'fi_ssn', 'iban', 'ipv4', + 'sha256', 'sha512', 'fi_business_id', 'fi_ssn', 'hostname', 'iban', 'ipv4', 'ipv4_cidr', 'ipv6', 'ipv6_cidr', 'length', 'mac_address', 'slug', 'truthy', 'url', 'ValidationFailure', 'validator', 'uuid', 'card_number', 'visa', 'mastercard', 'amex', 'unionpay', 'diners', diff --git a/validators/hostname.py b/validators/hostname.py new file mode 100644 index 00000000..5dd86629 --- /dev/null +++ b/validators/hostname.py @@ -0,0 +1,38 @@ +import re + +from .utils import validator + +""" +sources: +https://regexr.com/3dt4r +""" +hostname_re = re.compile( + r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[" + r"a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", + re.IGNORECASE) + + +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 hostname(value: str): + """ + Return whether a given value is a valid hostname. + If the value is valid hostname this function returns ``True``, otherwise + :class:`~validators.utils.ValidationFailure`. + Examples:: + >>> + True + :param value: hostname string to validate + to the end if the procided value and validation retried) + """ + try: + return hostname_re.match(to_unicode(value).encode('idna').decode('ascii')) + except (UnicodeError, AttributeError): + return False