Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/views.py: 54%

48 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:03 +0000

1import typing as t 

2 

3from . import typing as ft 

4from .globals import current_app 

5from .globals import request 

6 

7 

8http_method_funcs = frozenset( 

9 ["get", "post", "head", "options", "delete", "put", "trace", "patch"] 

10) 

11 

12 

13class View: 

14 """Alternative way to use view functions. A subclass has to implement 

15 :meth:`dispatch_request` which is called with the view arguments from 

16 the URL routing system. If :attr:`methods` is provided the methods 

17 do not have to be passed to the :meth:`~flask.Flask.add_url_rule` 

18 method explicitly:: 

19 

20 class MyView(View): 

21 methods = ['GET'] 

22 

23 def dispatch_request(self, name): 

24 return f"Hello {name}!" 

25 

26 app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview')) 

27 

28 When you want to decorate a pluggable view you will have to either do that 

29 when the view function is created (by wrapping the return value of 

30 :meth:`as_view`) or you can use the :attr:`decorators` attribute:: 

31 

32 class SecretView(View): 

33 methods = ['GET'] 

34 decorators = [superuser_required] 

35 

36 def dispatch_request(self): 

37 ... 

38 

39 The decorators stored in the decorators list are applied one after another 

40 when the view function is created. Note that you can *not* use the class 

41 based decorators since those would decorate the view class and not the 

42 generated view function! 

43 """ 

44 

45 #: A list of methods this view can handle. 

46 methods: t.Optional[t.List[str]] = None 

47 

48 #: Setting this disables or force-enables the automatic options handling. 

49 provide_automatic_options: t.Optional[bool] = None 

50 

51 #: The canonical way to decorate class-based views is to decorate the 

52 #: return value of as_view(). However since this moves parts of the 

53 #: logic from the class declaration to the place where it's hooked 

54 #: into the routing system. 

55 #: 

56 #: You can place one or more decorators in this list and whenever the 

57 #: view function is created the result is automatically decorated. 

58 #: 

59 #: .. versionadded:: 0.8 

60 decorators: t.List[t.Callable] = [] 

61 

62 def dispatch_request(self) -> ft.ResponseReturnValue: 

63 """Subclasses have to override this method to implement the 

64 actual view function code. This method is called with all 

65 the arguments from the URL rule. 

66 """ 

67 raise NotImplementedError() 

68 

69 @classmethod 

70 def as_view( 

71 cls, name: str, *class_args: t.Any, **class_kwargs: t.Any 

72 ) -> t.Callable: 

73 """Converts the class into an actual view function that can be used 

74 with the routing system. Internally this generates a function on the 

75 fly which will instantiate the :class:`View` on each request and call 

76 the :meth:`dispatch_request` method on it. 

77 

78 The arguments passed to :meth:`as_view` are forwarded to the 

79 constructor of the class. 

80 """ 

81 

82 def view(*args: t.Any, **kwargs: t.Any) -> ft.ResponseReturnValue: 

83 self = view.view_class(*class_args, **class_kwargs) # type: ignore 

84 return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs) 

85 

86 if cls.decorators: 

87 view.__name__ = name 

88 view.__module__ = cls.__module__ 

89 for decorator in cls.decorators: 

90 view = decorator(view) 

91 

92 # We attach the view class to the view function for two reasons: 

93 # first of all it allows us to easily figure out what class-based 

94 # view this thing came from, secondly it's also used for instantiating 

95 # the view class so you can actually replace it with something else 

96 # for testing purposes and debugging. 

97 view.view_class = cls # type: ignore 

98 view.__name__ = name 

99 view.__doc__ = cls.__doc__ 

100 view.__module__ = cls.__module__ 

101 view.methods = cls.methods # type: ignore 

102 view.provide_automatic_options = cls.provide_automatic_options # type: ignore 

103 return view 

104 

105 

106class MethodViewType(type): 

107 """Metaclass for :class:`MethodView` that determines what methods the view 

108 defines. 

109 """ 

110 

111 def __init__(cls, name, bases, d): 

112 super().__init__(name, bases, d) 

113 

114 if "methods" not in d: 

115 methods = set() 

116 

117 for base in bases: 

118 if getattr(base, "methods", None): 

119 methods.update(base.methods) 

120 

121 for key in http_method_funcs: 

122 if hasattr(cls, key): 

123 methods.add(key.upper()) 

124 

125 # If we have no method at all in there we don't want to add a 

126 # method list. This is for instance the case for the base class 

127 # or another subclass of a base method view that does not introduce 

128 # new methods. 

129 if methods: 

130 cls.methods = methods 

131 

132 

133class MethodView(View, metaclass=MethodViewType): 

134 """A class-based view that dispatches request methods to the corresponding 

135 class methods. For example, if you implement a ``get`` method, it will be 

136 used to handle ``GET`` requests. :: 

137 

138 class CounterAPI(MethodView): 

139 def get(self): 

140 return session.get('counter', 0) 

141 

142 def post(self): 

143 session['counter'] = session.get('counter', 0) + 1 

144 return 'OK' 

145 

146 app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter')) 

147 """ 

148 

149 def dispatch_request(self, *args: t.Any, **kwargs: t.Any) -> ft.ResponseReturnValue: 

150 meth = getattr(self, request.method.lower(), None) 

151 

152 # If the request method is HEAD and we don't have a handler for it 

153 # retry with GET. 

154 if meth is None and request.method == "HEAD": 

155 meth = getattr(self, "get", None) 

156 

157 assert meth is not None, f"Unimplemented method {request.method!r}" 

158 return current_app.ensure_sync(meth)(*args, **kwargs)