1
1
import difflib
2
2
from functools import wraps , partial
3
3
import re
4
- from flask import request , Response , url_for
4
+ from flask import request , Response , url_for , current_app
5
5
from flask import abort as original_flask_abort
6
6
from flask .views import MethodView
7
7
from flask .signals import got_request_exception
@@ -69,11 +69,14 @@ def __init__(self, app=None, prefix='',
69
69
self .default_mediatype = default_mediatype
70
70
self .decorators = decorators if decorators else []
71
71
self .catch_all_404s = catch_all_404s
72
+ self .endpoints = set ()
73
+ self .resources = []
74
+ self .app = None
72
75
73
76
if app is not None :
77
+ self .app = app
74
78
self .init_app (app )
75
- else :
76
- self .app = None
79
+
77
80
78
81
def init_app (self , app ):
79
82
"""Initialize this class with the given :class:`flask.Flask`
@@ -89,11 +92,13 @@ def init_app(self, app):
89
92
api.add_resource(...)
90
93
91
94
"""
92
- self .app = app
93
- self .endpoints = set ()
94
95
app .handle_exception = partial (self .error_router , app .handle_exception )
95
96
app .handle_user_exception = partial (self .error_router , app .handle_user_exception )
96
97
98
+ if len (self .resources ) > 0 :
99
+ for resource , urls , kwargs in self .resources :
100
+ self ._register_view (app , resource , * urls , ** kwargs )
101
+
97
102
98
103
def _should_use_fr_error_handler (self ):
99
104
""" Determine if error should be handled with FR or default Flask
@@ -104,7 +109,7 @@ def _should_use_fr_error_handler(self):
104
109
105
110
:return: bool
106
111
"""
107
- adapter = self . app .create_url_adapter (request )
112
+ adapter = current_app .create_url_adapter (request )
108
113
109
114
try :
110
115
adapter .match ()
@@ -152,9 +157,9 @@ def handle_error(self, e):
152
157
:type e: Exception
153
158
154
159
"""
155
- got_request_exception .send (self . app , exception = e )
160
+ got_request_exception .send (current_app . _get_current_object () , exception = e )
156
161
157
- if not hasattr (e , 'code' ) and self . app .propagate_exceptions :
162
+ if not hasattr (e , 'code' ) and current_app .propagate_exceptions :
158
163
exc_type , exc_value , tb = sys .exc_info ()
159
164
if exc_value is e :
160
165
exc = exc_type (exc_value )
@@ -171,14 +176,14 @@ def handle_error(self, e):
171
176
# There's currently a bug in Python3 that disallows calling
172
177
# logging.exception() when an exception hasn't actually be raised
173
178
if sys .exc_info () == (None , None , None ):
174
- self . app .logger .error ("Internal Error" )
179
+ current_app .logger .error ("Internal Error" )
175
180
else :
176
- self . app .logger .exception ("Internal Error" )
181
+ current_app .logger .exception ("Internal Error" )
177
182
178
183
if code == 404 and ('message' not in data or
179
184
data ['message' ] == HTTP_STATUS_CODES [404 ]):
180
185
rules = dict ([(re .sub ('(<.*>)' , '' , rule .rule ), rule .rule )
181
- for rule in self . app .url_map .iter_rules ()])
186
+ for rule in current_app .url_map .iter_rules ()])
182
187
close_matches = difflib .get_close_matches (request .path , rules .keys ())
183
188
if close_matches :
184
189
# If we already have a message, add punctuation and continue it.
@@ -196,7 +201,7 @@ def handle_error(self, e):
196
201
197
202
if code == 401 :
198
203
resp = unauthorized (resp ,
199
- self . app .config .get ("HTTP_BASIC_AUTH_REALM" , "flask-restful" ))
204
+ current_app .config .get ("HTTP_BASIC_AUTH_REALM" , "flask-restful" ))
200
205
201
206
return resp
202
207
@@ -229,11 +234,18 @@ def add_resource(self, resource, *urls, **kwargs):
229
234
api.add_resource(FooSpecial, '/special/foo', endpoint="foo")
230
235
231
236
"""
237
+
238
+ if self .app is not None :
239
+ self ._register_view (self .app , resource , * urls , ** kwargs )
240
+ else :
241
+ self .resources .append ((resource , urls , kwargs ))
242
+
243
+ def _register_view (self , app , resource , * urls , ** kwargs ):
232
244
endpoint = kwargs .pop ('endpoint' , None ) or resource .__name__ .lower ()
233
245
self .endpoints .add (endpoint )
234
246
235
- if endpoint in self . app .view_functions .keys ():
236
- previous_view_class = self . app .view_functions [endpoint ].__dict__ ['view_class' ]
247
+ if endpoint in app .view_functions .keys ():
248
+ previous_view_class = app .view_functions [endpoint ].__dict__ ['view_class' ]
237
249
238
250
# if you override the endpoint with a different class, avoid the collision by raising an exception
239
251
if previous_view_class != resource :
@@ -246,9 +258,8 @@ def add_resource(self, resource, *urls, **kwargs):
246
258
for decorator in self .decorators :
247
259
resource_func = decorator (resource_func )
248
260
249
-
250
261
for url in urls :
251
- self . app .add_url_rule (self .prefix + url , view_func = resource_func , ** kwargs )
262
+ app .add_url_rule (self .prefix + url , view_func = resource_func , ** kwargs )
252
263
253
264
def output (self , resource ):
254
265
"""Wraps a resource (as a flask view function), for cases where the
@@ -268,7 +279,7 @@ def wrapper(*args, **kwargs):
268
279
def url_for (self , resource , ** values ):
269
280
"""Generates a URL to the given resource."""
270
281
return url_for (resource .endpoint , ** values )
271
-
282
+
272
283
def make_response (self , data , * args , ** kwargs ):
273
284
"""Looks up the representation transformer for the requested media
274
285
type, invoking the transformer to create a response object. This
0 commit comments