Skip to content

Commit

Permalink
maint: improves domain module
Browse files Browse the repository at this point in the history
- Uses type hints, improve docs
- Regards [RFC 1035](https://www.rfc-editor.org/rfc/rfc1035)
- Updates corresponding test functions
- Closes python-validators#74, python-validators#81, python-validators#89, python-validators#124, python-validators#143, & python-validators#204
  • Loading branch information
yozachar committed Mar 2, 2023
1 parent 27eed7b commit 5c0fbe8
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 71 deletions.
80 changes: 46 additions & 34 deletions tests/test_domain.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,54 @@
"""Test Domain."""
# -*- coding: utf-8 -*-

# standard
import pytest

# local
from validators import domain, ValidationFailure


@pytest.mark.parametrize('value', [
'example.com',
'xn----gtbspbbmkef.xn--p1ai',
'underscore_subdomain.example.com',
'something.versicherung',
'someThing.versicherung',
'11.com',
'3.cn',
'a.cn',
'sub1.sub2.sample.co.uk',
'somerandomexample.xn--fiqs8s',
'kräuter.com',
'über.com'
])
def test_returns_true_on_valid_domain(value):
assert domain(value)
@pytest.mark.parametrize(
("value", "trailing_dot"),
[
("example.com", False),
("xn----gtbspbbmkef.xn--p1ai", False),
("underscore_subdomain.example.com", False),
("something.versicherung", False),
("someThing.versicherung.", True),
("11.com", False),
("3.cn.", True),
("a.cn", False),
("sub1.sub2.sample.co.uk", False),
("somerandomexample.xn--fiqs8s", False),
("kräuter.com.", True),
("über.com", False),
],
)
def test_returns_true_on_valid_domain(value: str, trailing_dot: bool):
"""Test returns true on valid domain."""
assert domain(value, trailing_dot=trailing_dot)


@pytest.mark.parametrize('value', [
'example.com/',
'example.com:4444',
'example.-com',
'example.',
'-example.com',
'example-.com',
'_example.com',
'example_.com',
'example',
'a......b.com',
'a.123',
'123.123',
'123.123.123',
'123.123.123.123'
])
def test_returns_failed_validation_on_invalid_domain(value):
assert isinstance(domain(value), ValidationFailure)
@pytest.mark.parametrize(
("value", "trailing_dot"),
[
("example.com/.", True),
("example.com:4444", False),
("example.-com", False),
("example.", False),
("-example.com", False),
("example-.com.", True),
("_example.com", False),
("example_.com", False),
("example", False),
("a......b.com", False),
("a.123", False),
("123.123", False),
("123.123.123.", True),
("123.123.123.123", False),
],
)
def test_returns_failed_validation_on_invalid_domain(value: str, trailing_dot: bool):
"""Test returns failed validation on invalid domain."""
assert isinstance(domain(value, trailing_dot=trailing_dot), ValidationFailure)
71 changes: 34 additions & 37 deletions validators/domain.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,51 @@
"""Domain."""

# standard
import re

# local
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::
def domain(value: str, /, *, trailing_dot: bool = False):
"""Return whether or not given value is a valid domain.
Examples:
>>> domain('example.com')
True
# Output: True
>>> domain('example.com/')
ValidationFailure(func=domain, ...)
Supports IDN domains as well::
# Output: ValidationFailure(func=domain, ...)
>>> # Supports IDN domains as well::
>>> domain('xn----gtbspbbmkef.xn--p1ai')
True
# Output: True
Args:
value:
Domain string to validate.
trailing_dots:
Optional parameter for RFC 1035
.. versionadded:: 0.9
.. versionchanged:: 0.10
Returns:
(Literal[True]):
If `value` is a valid domain name.
(ValidationFailure):
If `value` is an invalid domain name.
Added support for internationalized domain name (IDN) validation.
Note:
- *In version 0.10.0*:
- Added support for internationalized domain name (IDN) validation.
:param value: domain string to validate
> *New in version 0.9.0*.
"""
try:
return pattern.match(to_unicode(value).encode('idna').decode('ascii'))
except (UnicodeError, AttributeError):
return not re.search(r"\s", value) and 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
+ (r".$" if trailing_dot else r"$")
).match(value.encode("idna").decode("ascii"))
except UnicodeError:
return False

0 comments on commit 5c0fbe8

Please sign in to comment.