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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

69 statements  

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 

14 from ..wrappers.request import Request 

15 from ..wrappers.response import Response 

16 from .map import MapAdapter 

17 from .rules import Rule 

18 

19 

20class RoutingException(Exception): 

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

22 about missing urls, etc. 

23 

24 :internal: 

25 """ 

26 

27 

28class RequestRedirect(HTTPException, RoutingException): 

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

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

31 

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

33 """ 

34 

35 code = 308 

36 

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

38 super().__init__(new_url) 

39 self.new_url = new_url 

40 

41 def get_response( 

42 self, 

43 environ: WSGIEnvironment | Request | None = None, 

44 scope: dict[str, t.Any] | None = None, 

45 ) -> Response: 

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

47 

48 

49class RequestPath(RoutingException): 

50 """Internal exception.""" 

51 

52 __slots__ = ("path_info",) 

53 

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

55 super().__init__() 

56 self.path_info = path_info 

57 

58 

59class RequestAliasRedirect(RoutingException): # noqa: B903 

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

61 

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

63 super().__init__() 

64 self.matched_values = matched_values 

65 self.endpoint = endpoint 

66 

67 

68class BuildError(RoutingException, LookupError): 

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

70 values provided. 

71 """ 

72 

73 def __init__( 

74 self, 

75 endpoint: t.Any, 

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

77 method: str | None, 

78 adapter: MapAdapter | None = None, 

79 ) -> None: 

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

81 self.endpoint = endpoint 

82 self.values = values 

83 self.method = method 

84 self.adapter = adapter 

85 

86 @cached_property 

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

88 return self.closest_rule(self.adapter) 

89 

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

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

92 return sum( 

93 [ 

94 0.98 

95 * difflib.SequenceMatcher( 

96 # endpoints can be any type, compare as strings 

97 None, 

98 str(rule.endpoint), 

99 str(self.endpoint), 

100 ).ratio(), 

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

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

103 ] 

104 ) 

105 

106 if adapter and adapter.map._rules: 

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

108 

109 return None 

110 

111 def __str__(self) -> str: 

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

113 if self.method: 

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

115 if self.values: 

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

117 message.append(".") 

118 if self.suggested: 

119 if self.endpoint == self.suggested.endpoint: 

120 if ( 

121 self.method 

122 and self.suggested.methods is not None 

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

124 ): 

125 message.append( 

126 " Did you mean to use methods" 

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

128 ) 

129 missing_values = self.suggested.arguments.union( 

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

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

132 if missing_values: 

133 message.append( 

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

135 ) 

136 else: 

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

138 return "".join(message) 

139 

140 

141class WebsocketMismatch(BadRequest): 

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

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

144 """ 

145 

146 

147class NoMatch(Exception): 

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

149 

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

151 self.have_match_for = have_match_for 

152 self.websocket_mismatch = websocket_mismatch