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

1"""Hostname.""" 

2# -*- coding: utf-8 -*- 

3 

4# standard 

5from functools import lru_cache 

6import re 

7 

8# local 

9from .ip_address import ipv6, ipv4 

10from .utils import validator 

11from .domain import domain 

12 

13 

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 ) 

21 

22 

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]$") 

27 

28 

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("]") 

36 

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 

42 

43 return None 

44 

45 

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. 

59 

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, ...) 

81 

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). 

99 

100 Returns: 

101 (Literal[True]): 

102 If `value` is a valid hostname. 

103 (ValidationFailure): 

104 If `value` is an invalid hostname. 

105 

106 > *New in version 0.21.0*. 

107 """ 

108 if not value: 

109 return False 

110 

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 ) 

118 

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 )