@@ -78,6 +78,16 @@ def wrap_attributeerrors():
78
78
raise exc .with_traceback (info [2 ])
79
79
80
80
81
+ def safe_property (func ):
82
+ """Property decorator to ensure AttributeErrors raised in properties are properly handled"""
83
+
84
+ @property
85
+ def new_func (self ):
86
+ with wrap_attributeerrors ():
87
+ return func (self )
88
+
89
+ return new_func
90
+
81
91
class Empty :
82
92
"""
83
93
Placeholder for unset attributes.
@@ -193,12 +203,12 @@ def __class_getitem__(cls, *args, **kwargs):
193
203
def _default_negotiator (self ):
194
204
return api_settings .DEFAULT_CONTENT_NEGOTIATION_CLASS ()
195
205
196
- @property
206
+ @safe_property
197
207
def content_type (self ):
198
208
meta = self ._request .META
199
209
return meta .get ('CONTENT_TYPE' , meta .get ('HTTP_CONTENT_TYPE' , '' ))
200
210
201
- @property
211
+ @safe_property
202
212
def stream (self ):
203
213
"""
204
214
Returns an object that may be used to stream the request content.
@@ -207,28 +217,27 @@ def stream(self):
207
217
self ._load_stream ()
208
218
return self ._stream
209
219
210
- @property
220
+ @safe_property
211
221
def query_params (self ):
212
222
"""
213
223
More semantically correct name for request.GET.
214
224
"""
215
225
return self ._request .GET
216
226
217
- @property
227
+ @safe_property
218
228
def data (self ):
219
229
if not _hasattr (self , '_full_data' ):
220
230
self ._load_data_and_files ()
221
231
return self ._full_data
222
232
223
- @property
233
+ @safe_property
224
234
def user (self ):
225
235
"""
226
236
Returns the user associated with the current request, as authenticated
227
237
by the authentication classes provided to the request.
228
238
"""
229
239
if not hasattr (self , '_user' ):
230
- with wrap_attributeerrors ():
231
- self ._authenticate ()
240
+ self ._authenticate ()
232
241
return self ._user
233
242
234
243
@user .setter
@@ -244,15 +253,14 @@ def user(self, value):
244
253
self ._user = value
245
254
self ._request .user = value
246
255
247
- @property
256
+ @safe_property
248
257
def auth (self ):
249
258
"""
250
259
Returns any non-user authentication information associated with the
251
260
request, such as an authentication token.
252
261
"""
253
262
if not hasattr (self , '_auth' ):
254
- with wrap_attributeerrors ():
255
- self ._authenticate ()
263
+ self ._authenticate ()
256
264
return self ._auth
257
265
258
266
@auth .setter
@@ -264,15 +272,14 @@ def auth(self, value):
264
272
self ._auth = value
265
273
self ._request .auth = value
266
274
267
- @property
275
+ @safe_property
268
276
def successful_authenticator (self ):
269
277
"""
270
278
Return the instance of the authentication instance class that was used
271
279
to authenticate the request, or `None`.
272
280
"""
273
281
if not hasattr (self , '_authenticator' ):
274
- with wrap_attributeerrors ():
275
- self ._authenticate ()
282
+ self ._authenticate ()
276
283
return self ._authenticator
277
284
278
285
def _load_data_and_files (self ):
@@ -420,9 +427,9 @@ def __getattr__(self, attr):
420
427
_request = self .__getattribute__ ("_request" )
421
428
return getattr (_request , attr )
422
429
except AttributeError :
423
- return self .__getattribute__ ( attr )
430
+ raise AttributeError ( f"' { self .__class__ . __name__ } ' object has no attribute ' { attr } '" )
424
431
425
- @property
432
+ @safe_property
426
433
def POST (self ):
427
434
# Ensure that request.POST uses our request parsing.
428
435
if not _hasattr (self , '_data' ):
@@ -431,7 +438,7 @@ def POST(self):
431
438
return self ._data
432
439
return QueryDict ('' , encoding = self ._request ._encoding )
433
440
434
- @property
441
+ @safe_property
435
442
def FILES (self ):
436
443
# Leave this one alone for backwards compat with Django's request.FILES
437
444
# Different from the other two cases, which are not valid property
0 commit comments