|
| 1 | +# Copyright (c) 2018 Alexander Todorov <atodorov@MrSenko.com> |
| 2 | + |
| 3 | +# Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
| 4 | +# For details: https://github.com/PyCQA/pylint-django/blob/master/LICENSE |
| 5 | +""" |
| 6 | +Various suggestions about JSON http responses |
| 7 | +""" |
| 8 | + |
| 9 | +import astroid |
| 10 | + |
| 11 | +from pylint import interfaces |
| 12 | +from pylint import checkers |
| 13 | +from pylint.checkers import utils |
| 14 | +from pylint_django.__pkginfo__ import BASE_ID |
| 15 | + |
| 16 | + |
| 17 | +class JsonResponseChecker(checkers.BaseChecker): |
| 18 | + """ |
| 19 | + Looks for some common patterns when returning http responses containing |
| 20 | + JSON data! |
| 21 | + """ |
| 22 | + |
| 23 | + __implements__ = (interfaces.IAstroidChecker,) |
| 24 | + |
| 25 | + # configuration section name |
| 26 | + name = 'json-response-checker' |
| 27 | + msgs = {'R%s01' % BASE_ID: ("Instead of HttpResponse(json.dumps(data)) use JsonResponse(data)", |
| 28 | + 'http-response-with-json-dumps', |
| 29 | + 'Used when json.dumps() is used as an argument to HttpResponse().'), |
| 30 | + 'R%s02' % BASE_ID: ("Instead of HttpResponse(content_type='application/json') use JsonResponse()", |
| 31 | + 'http-response-with-content-type-json', |
| 32 | + 'Used when HttpResponse() is returning application/json.'), |
| 33 | + 'R%s03' % BASE_ID: ("Redundant content_type parameter for JsonResponse()", |
| 34 | + 'redundant-content-type-for-json-response', |
| 35 | + 'Used when JsonResponse() contains content_type parameter. ' |
| 36 | + 'This is either redundant or the content_type is not JSON ' |
| 37 | + 'which is probably an error.')} |
| 38 | + |
| 39 | + @utils.check_messages('http-response-with-json-dumps', |
| 40 | + 'http-response-with-content-type-json', |
| 41 | + 'redundant-content-type-for-json-response') |
| 42 | + def visit_call(self, node): |
| 43 | + if (node.func.as_string().endswith('HttpResponse') and node.args |
| 44 | + and isinstance(node.args[0], astroid.Call) |
| 45 | + and node.args[0].func.as_string() == 'json.dumps'): |
| 46 | + self.add_message('http-response-with-json-dumps', node=node) |
| 47 | + |
| 48 | + if node.func.as_string().endswith('HttpResponse') and node.keywords: |
| 49 | + for keyword in node.keywords: |
| 50 | + if (keyword.arg == 'content_type' |
| 51 | + and keyword.value.as_string().lower().find('application/json') > -1): |
| 52 | + self.add_message('http-response-with-content-type-json', node=node) |
| 53 | + break |
| 54 | + |
| 55 | + if node.func.as_string().endswith('JsonResponse') and node.keywords: |
| 56 | + for keyword in node.keywords: |
| 57 | + if keyword.arg == 'content_type': |
| 58 | + self.add_message('redundant-content-type-for-json-response', node=node) |
| 59 | + break |
0 commit comments