Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/werkzeug/routing/exceptions.py: 43%

69 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-09 07:17 +0000

1from __future__ import annotations 

2 

3import difflib 

4import typing as t 

5 

6from ..exceptions import BadRequest 

7from ..exceptions import HTTPException 

8from ..utils import cached_property 

9from ..utils import redirect 

10 

11if t.TYPE_CHECKING: 

12 from _typeshed.wsgi import WSGIEnvironment 

13 from .map import MapAdapter 

14 from .rules import Rule 

15 from ..wrappers.request import Request 

16 from ..wrappers.response import Response 

17 

18 

19class RoutingException(Exception): 

20 """Special exceptions that require the application to redirect, notifying 

21 about missing urls, etc. 

22 

23 :internal: 

24 """ 

25 

26 

27class RequestRedirect(HTTPException, RoutingException): 

28 """Raise if the map requests a redirect. This is for example the case if 

29 `strict_slashes` are activated and an url that requires a trailing slash. 

30 

31 The attribute `new_url` contains the absolute destination url. 

32 """ 

33 

34 code = 308 

35 

36 def __init__(self, new_url: str) -> None: 

37 super().__init__(new_url) 

38 self.new_url = new_url 

39 

40 def get_response( 

41 self, 

42 environ: WSGIEnvironment | Request | None = None, 

43 scope: dict | None = None, 

44 ) -> Response: 

45 return redirect(self.new_url, self.code) 

46 

47 

48class RequestPath(RoutingException): 

49 """Internal exception.""" 

50 

51 __slots__ = ("path_info",) 

52 

53 def __init__(self, path_info: str) -> None: 

54 super().__init__() 

55 self.path_info = path_info 

56 

57 

58class RequestAliasRedirect(RoutingException): # noqa: B903 

59 """This rule is an alias and wants to redirect to the canonical URL.""" 

60 

61 def __init__(self, matched_values: t.Mapping[str, t.Any], endpoint: str) -> None: 

62 super().__init__() 

63 self.matched_values = matched_values 

64 self.endpoint = endpoint 

65 

66 

67class BuildError(RoutingException, LookupError): 

68 """Raised if the build system cannot find a URL for an endpoint with the 

69 values provided. 

70 """ 

71 

72 def __init__( 

73 self, 

74 endpoint: str, 

75 values: t.Mapping[str, t.Any], 

76 method: str | None, 

77 adapter: MapAdapter | None = None, 

78 ) -> None: 

79 super().__init__(endpoint, values, method) 

80 self.endpoint = endpoint 

81 self.values = values 

82 self.method = method 

83 self.adapter = adapter 

84 

85 @cached_property 

86 def suggested(self) -> Rule | None: 

87 return self.closest_rule(self.adapter) 

88 

89 def closest_rule(self, adapter: MapAdapter | None) -> Rule | None: 

90 def _score_rule(rule: Rule) -> float: 

91 return sum( 

92 [ 

93 0.98 

94 * difflib.SequenceMatcher( 

95 None, rule.endpoint, self.endpoint 

96 ).ratio(), 

97 0.01 * bool(set(self.values or ()).issubset(rule.arguments)), 

98 0.01 * bool(rule.methods and self.method in rule.methods), 

99 ] 

100 ) 

101 

102 if adapter and adapter.map._rules: 

103 return max(adapter.map._rules, key=_score_rule) 

104 

105 return None 

106 

107 def __str__(self) -> str: 

108 message = [f"Could not build url for endpoint {self.endpoint!r}"] 

109 if self.method: 

110 message.append(f" ({self.method!r})") 

111 if self.values: 

112 message.append(f" with values {sorted(self.values)!r}") 

113 message.append(".") 

114 if self.suggested: 

115 if self.endpoint == self.suggested.endpoint: 

116 if ( 

117 self.method 

118 and self.suggested.methods is not None 

119 and self.method not in self.suggested.methods 

120 ): 

121 message.append( 

122 " Did you mean to use methods" 

123 f" {sorted(self.suggested.methods)!r}?" 

124 ) 

125 missing_values = self.suggested.arguments.union( 

126 set(self.suggested.defaults or ()) 

127 ) - set(self.values.keys()) 

128 if missing_values: 

129 message.append( 

130 f" Did you forget to specify values {sorted(missing_values)!r}?" 

131 ) 

132 else: 

133 message.append(f" Did you mean {self.suggested.endpoint!r} instead?") 

134 return "".join(message) 

135 

136 

137class WebsocketMismatch(BadRequest): 

138 """The only matched rule is either a WebSocket and the request is 

139 HTTP, or the rule is HTTP and the request is a WebSocket. 

140 """ 

141 

142 

143class NoMatch(Exception): 

144 __slots__ = ("have_match_for", "websocket_mismatch") 

145 

146 def __init__(self, have_match_for: set[str], websocket_mismatch: bool) -> None: 

147 self.have_match_for = have_match_for 

148 self.websocket_mismatch = websocket_mismatch