Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/validators/hostname.py: 96%
28 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:08 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:08 +0000
1"""Hostname."""
2# -*- coding: utf-8 -*-
4# standard
5from functools import lru_cache
6import re
8# local
9from .ip_address import ipv6, ipv4
10from .utils import validator
11from .domain import domain
14@lru_cache
15def _port_regex():
16 """Port validation regex."""
17 return re.compile(
18 r"^\:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|"
19 + r"6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3})$",
20 )
23@lru_cache
24def _simple_hostname_regex():
25 """Simple hostname validation regex."""
26 return re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$")
29def _port_validator(value: str):
30 """Returns host segment if port is valid."""
31 if value.count("]:") == 1:
32 # with ipv6
33 host_seg, port_seg = value.rsplit(":", 1)
34 if _port_regex().match(f":{port_seg}"):
35 return host_seg.lstrip("[").rstrip("]")
37 if value.count(":") == 1:
38 # with ipv4 or simple hostname
39 host_seg, port_seg = value.rsplit(":", 1)
40 if _port_regex().match(f":{port_seg}"):
41 return host_seg
43 return None
46@validator
47def hostname(
48 value: str,
49 /,
50 *,
51 skip_ipv6_addr: bool = False,
52 skip_ipv4_addr: bool = False,
53 may_have_port: bool = True,
54 maybe_simple: bool = True,
55 rfc_1034: bool = False,
56 rfc_2782: bool = False,
57):
58 """Return whether or not given value is a valid hostname.
60 Examples:
61 >>> hostname("ubuntu-pc:443")
62 # Output: True
63 >>> hostname("this-pc")
64 # Output: True
65 >>> hostname("xn----gtbspbbmkef.xn--p1ai:65535")
66 # Output: True
67 >>> hostname("_example.com")
68 # Output: True
69 >>> hostname("123.5.77.88:31000")
70 # Output: True
71 >>> hostname("12.12.12.12")
72 # Output: True
73 >>> hostname("[::1]:22")
74 # Output: True
75 >>> hostname("dead:beef:0:0:0:0000:42:1")
76 # Output: True
77 >>> hostname("[0:0:0:0:0:ffff:1.2.3.4]:-65538")
78 # Output: ValidationFailure(func=hostname, ...)
79 >>> hostname("[0:&:b:c:@:e:f::]:9999")
80 # Output: ValidationFailure(func=hostname, ...)
82 Args:
83 value:
84 Hostname string to validate.
85 skip_ipv6_addr:
86 When hostname string cannot be an IPv6 address.
87 skip_ipv4_addr:
88 When hostname string cannot be an IPv4 address.
89 may_have_port:
90 Hostname string may contain port number.
91 maybe_simple:
92 Hostname string maybe only hyphens and alpha-numerals.
93 rfc_1034:
94 Allow trailing dot in domain/host name.
95 Ref: [RFC 1034](https://www.rfc-editor.org/rfc/rfc1034).
96 rfc_2782:
97 Domain/Host name is of type service record.
98 Ref: [RFC 2782](https://www.rfc-editor.org/rfc/rfc2782).
100 Returns:
101 (Literal[True]):
102 If `value` is a valid hostname.
103 (ValidationFailure):
104 If `value` is an invalid hostname.
106 > *New in version 0.21.0*.
107 """
108 if not value:
109 return False
111 if may_have_port and (host_seg := _port_validator(value)):
112 return (
113 (_simple_hostname_regex().match(host_seg) if maybe_simple else False)
114 or domain(host_seg, rfc_1034=rfc_1034, rfc_2782=rfc_2782)
115 or (False if skip_ipv4_addr else ipv4(host_seg, cidr=False))
116 or (False if skip_ipv6_addr else ipv6(host_seg, cidr=False))
117 )
119 return (
120 (_simple_hostname_regex().match(value) if maybe_simple else False)
121 or domain(value, rfc_1034=rfc_1034, rfc_2782=rfc_2782)
122 or (False if skip_ipv4_addr else ipv4(value, cidr=False))
123 or (False if skip_ipv6_addr else ipv6(value, cidr=False))
124 )