Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework/negotiation.py: 69%

40 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2Content negotiation deals with selecting an appropriate renderer given the 

3incoming request. Typically this will be based on the request's Accept header. 

4""" 

5from django.http import Http404 

6 

7from rest_framework import HTTP_HEADER_ENCODING, exceptions 

8from rest_framework.settings import api_settings 

9from rest_framework.utils.mediatypes import ( 

10 _MediaType, media_type_matches, order_by_precedence 

11) 

12 

13 

14class BaseContentNegotiation: 

15 def select_parser(self, request, parsers): 

16 raise NotImplementedError('.select_parser() must be implemented') 

17 

18 def select_renderer(self, request, renderers, format_suffix=None): 

19 raise NotImplementedError('.select_renderer() must be implemented') 

20 

21 

22class DefaultContentNegotiation(BaseContentNegotiation): 

23 settings = api_settings 

24 

25 def select_parser(self, request, parsers): 

26 """ 

27 Given a list of parsers and a media type, return the appropriate 

28 parser to handle the incoming request. 

29 """ 

30 for parser in parsers: 30 ↛ 33line 30 didn't jump to line 33, because the loop on line 30 didn't complete

31 if media_type_matches(parser.media_type, request.content_type): 

32 return parser 

33 return None 

34 

35 def select_renderer(self, request, renderers, format_suffix=None): 

36 """ 

37 Given a request and a list of renderers, return a two-tuple of: 

38 (renderer, media type). 

39 """ 

40 # Allow URL style format override. eg. "?format=json 

41 format_query_param = self.settings.URL_FORMAT_OVERRIDE 

42 format = format_suffix or request.query_params.get(format_query_param) 

43 

44 if format: 44 ↛ 45line 44 didn't jump to line 45, because the condition on line 44 was never true

45 renderers = self.filter_renderers(renderers, format) 

46 

47 accepts = self.get_accept_list(request) 

48 

49 # Check the acceptable media types against each renderer, 

50 # attempting more specific media types first 

51 # NB. The inner loop here isn't as bad as it first looks :) 

52 # Worst case is we're looping over len(accept_list) * len(self.renderers) 

53 for media_type_set in order_by_precedence(accepts): 53 ↛ 76line 53 didn't jump to line 76, because the loop on line 53 didn't complete

54 for renderer in renderers: 54 ↛ 53line 54 didn't jump to line 53, because the loop on line 54 didn't complete

55 for media_type in media_type_set: 55 ↛ 54line 55 didn't jump to line 54, because the loop on line 55 didn't complete

56 if media_type_matches(renderer.media_type, media_type): 56 ↛ 55line 56 didn't jump to line 55, because the condition on line 56 was never false

57 # Return the most specific media type as accepted. 

58 media_type_wrapper = _MediaType(media_type) 

59 if ( 59 ↛ 74line 59 didn't jump to line 74

60 _MediaType(renderer.media_type).precedence > 

61 media_type_wrapper.precedence 

62 ): 

63 # Eg client requests '*/*' 

64 # Accepted media type is 'application/json' 

65 full_media_type = ';'.join( 

66 (renderer.media_type,) + 

67 tuple('{}={}'.format( 

68 key, value.decode(HTTP_HEADER_ENCODING)) 

69 for key, value in media_type_wrapper.params.items())) 

70 return renderer, full_media_type 

71 else: 

72 # Eg client requests 'application/json; indent=8' 

73 # Accepted media type is 'application/json; indent=8' 

74 return renderer, media_type 

75 

76 raise exceptions.NotAcceptable(available_renderers=renderers) 

77 

78 def filter_renderers(self, renderers, format): 

79 """ 

80 If there is a '.json' style format suffix, filter the renderers 

81 so that we only negotiation against those that accept that format. 

82 """ 

83 renderers = [renderer for renderer in renderers 

84 if renderer.format == format] 

85 if not renderers: 

86 raise Http404 

87 return renderers 

88 

89 def get_accept_list(self, request): 

90 """ 

91 Given the incoming request, return a tokenized list of media 

92 type strings. 

93 """ 

94 header = request.META.get('HTTP_ACCEPT', '*/*') 

95 return [token.strip() for token in header.split(',')]