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

68 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1import difflib 

2import typing as t 

3 

4from ..exceptions import BadRequest 

5from ..exceptions import HTTPException 

6from ..utils import cached_property 

7from ..utils import redirect 

8 

9if t.TYPE_CHECKING: 

10 from _typeshed.wsgi import WSGIEnvironment 

11 from .map import MapAdapter 

12 from .rules import Rule # noqa: F401 

13 from ..wrappers.request import Request 

14 from ..wrappers.response import Response 

15 

16 

17class RoutingException(Exception): 

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

19 about missing urls, etc. 

20 

21 :internal: 

22 """ 

23 

24 

25class RequestRedirect(HTTPException, RoutingException): 

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

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

28 

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

30 """ 

31 

32 code = 308 

33 

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

35 super().__init__(new_url) 

36 self.new_url = new_url 

37 

38 def get_response( 

39 self, 

40 environ: t.Optional[t.Union["WSGIEnvironment", "Request"]] = None, 

41 scope: t.Optional[dict] = None, 

42 ) -> "Response": 

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

44 

45 

46class RequestPath(RoutingException): 

47 """Internal exception.""" 

48 

49 __slots__ = ("path_info",) 

50 

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

52 super().__init__() 

53 self.path_info = path_info 

54 

55 

56class RequestAliasRedirect(RoutingException): # noqa: B903 

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

58 

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

60 super().__init__() 

61 self.matched_values = matched_values 

62 self.endpoint = endpoint 

63 

64 

65class BuildError(RoutingException, LookupError): 

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

67 values provided. 

68 """ 

69 

70 def __init__( 

71 self, 

72 endpoint: str, 

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

74 method: t.Optional[str], 

75 adapter: t.Optional["MapAdapter"] = None, 

76 ) -> None: 

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

78 self.endpoint = endpoint 

79 self.values = values 

80 self.method = method 

81 self.adapter = adapter 

82 

83 @cached_property 

84 def suggested(self) -> t.Optional["Rule"]: 

85 return self.closest_rule(self.adapter) 

86 

87 def closest_rule(self, adapter: t.Optional["MapAdapter"]) -> t.Optional["Rule"]: 

88 def _score_rule(rule: "Rule") -> float: 

89 return sum( 

90 [ 

91 0.98 

92 * difflib.SequenceMatcher( 

93 None, rule.endpoint, self.endpoint 

94 ).ratio(), 

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

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

97 ] 

98 ) 

99 

100 if adapter and adapter.map._rules: 

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

102 

103 return None 

104 

105 def __str__(self) -> str: 

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

107 if self.method: 

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

109 if self.values: 

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

111 message.append(".") 

112 if self.suggested: 

113 if self.endpoint == self.suggested.endpoint: 

114 if ( 

115 self.method 

116 and self.suggested.methods is not None 

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

118 ): 

119 message.append( 

120 " Did you mean to use methods" 

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

122 ) 

123 missing_values = self.suggested.arguments.union( 

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

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

126 if missing_values: 

127 message.append( 

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

129 ) 

130 else: 

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

132 return "".join(message) 

133 

134 

135class WebsocketMismatch(BadRequest): 

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

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

138 """ 

139 

140 

141class NoMatch(Exception): 

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

143 

144 def __init__(self, have_match_for: t.Set[str], websocket_mismatch: bool) -> None: 

145 self.have_match_for = have_match_for 

146 self.websocket_mismatch = websocket_mismatch