Skip to content

Commit 5f752b6

Browse files
psrbatodorov
authored andcommitted
Ignore unused-argument warning for request arguments (Fixes #249)
The previous implementation (result of #155) of disabling the unused-argument warning for functions/methods having an argument named `request` was to broad. As soon as a function/method contains a `request` argument no unused-argument warning will be issued for this function anymore, even though some other arguments are unused! This commit monkey patches `VariablesChecker._is_ignored_named` to specifically suppress the generation of unused-argument warnings for arguments named `request`. This way unused-argument warnings will still be issued for every other unused argument of the function/method.
1 parent d2ad9ff commit 5f752b6

File tree

5 files changed

+79
-29
lines changed

5 files changed

+79
-29
lines changed

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
* [matusvalo](https://github.com/matusvalo)
1313
* [fadedDexofan](https://github.com/fadeddexofan)
1414
* [imomaliev](https://github.com/imomaliev)
15+
* [psrb](https://github.com/psrb)

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ It is possible to make tests with expected error output, for example, if
136136
adding a new message or simply accepting that pylint is supposed to warn.
137137
A ``test_file_name.txt`` file contains a list of expected error messages in the
138138
format
139-
``error-type:line number:class name or empty:1st line of detailed error text``.
139+
``error-type:line number:class name or empty:1st line of detailed error text:confidence or empty``.
140140

141141

142142
License

pylint_django/augmentations/__init__.py

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -644,23 +644,17 @@ def is_model_view_subclass_method_shouldnt_be_function(node):
644644
return parent is not None and node_is_subclass(parent, *subclass)
645645

646646

647-
def is_model_view_subclass_unused_argument(node):
647+
def ignore_unused_argument_warnings_for_request(orig_method, self, stmt, name):
648648
"""
649-
Checks that node is get or post method of the View class and it has valid arguments.
649+
Ignore unused-argument warnings for function arguments named "request".
650650
651-
TODO: Bad checkings, need to be more smart.
651+
The signature of Django view functions require the request argument but it is okay if the request is not used.
652+
This function should be used as a wrapper for the `VariablesChecker._is_name_ignored` method.
652653
"""
653-
if not is_model_view_subclass_method_shouldnt_be_function(node):
654-
return False
655-
656-
return is_argument_named_request(node)
654+
if name == 'request':
655+
return True
657656

658-
659-
def is_argument_named_request(node):
660-
"""
661-
If an unused-argument is named 'request' ignore that!
662-
"""
663-
return 'request' in node.argnames()
657+
return orig_method(self, stmt, name)
664658

665659

666660
def is_model_field_display_method(node):
@@ -742,7 +736,7 @@ def is_class(class_name):
742736
def wrap(orig_method, with_method):
743737
@functools.wraps(orig_method)
744738
def wrap_func(*args, **kwargs):
745-
with_method(orig_method, *args, **kwargs)
739+
return with_method(orig_method, *args, **kwargs)
746740
return wrap_func
747741

748742

@@ -759,6 +753,31 @@ def pylint_newstyle_classdef_compat(linter, warning_name, augment):
759753
suppress_message(linter, getattr(NewStyleConflictChecker, 'visit_classdef'), warning_name, augment)
760754

761755

756+
def apply_wrapped_augmentations():
757+
"""
758+
Apply augmentation and supression rules through monkey patching of pylint.
759+
"""
760+
# NOTE: The monkey patching is done with wrap and needs to be done in a thread safe manner to support the
761+
# parallel option of pylint (-j).
762+
# This is achieved by comparing __name__ of the monkey patched object to the original value and only patch it if
763+
# these are equal.
764+
765+
# Unused argument 'request' (get, post)
766+
current_is_name_ignored = VariablesChecker._is_name_ignored # pylint: disable=protected-access
767+
if current_is_name_ignored.__name__ == '_is_name_ignored':
768+
# pylint: disable=protected-access
769+
VariablesChecker._is_name_ignored = wrap(current_is_name_ignored, ignore_unused_argument_warnings_for_request)
770+
771+
# ForeignKey and OneToOneField
772+
current_leave_module = VariablesChecker.leave_module
773+
if current_leave_module.__name__ == 'leave_module':
774+
# current_leave_module is not wrapped
775+
# Two threads may hit the next assignment concurrently, but the result is the same
776+
VariablesChecker.leave_module = wrap(current_leave_module, ignore_import_warnings_for_related_fields)
777+
# VariablesChecker.leave_module is now wrapped
778+
# else VariablesChecker.leave_module is already wrapped
779+
780+
762781
# augment things
763782
def apply_augmentations(linter):
764783
"""Apply augmentation and suppression rules."""
@@ -826,10 +845,6 @@ def apply_augmentations(linter):
826845
# Method could be a function (get, post)
827846
suppress_message(linter, ClassChecker.leave_functiondef, 'no-self-use',
828847
is_model_view_subclass_method_shouldnt_be_function)
829-
# Unused argument 'request' (get, post)
830-
suppress_message(linter, VariablesChecker.leave_functiondef, 'unused-argument',
831-
is_model_view_subclass_unused_argument)
832-
suppress_message(linter, VariablesChecker.leave_functiondef, 'unused-argument', is_argument_named_request)
833848

834849
# django-mptt
835850
suppress_message(linter, DocStringChecker.visit_classdef, 'missing-docstring', is_model_mpttmeta_subclass)
@@ -841,15 +856,7 @@ def apply_augmentations(linter):
841856
suppress_message(linter, TypeChecker.visit_attribute, 'no-member', is_model_factory)
842857
suppress_message(linter, ClassChecker.visit_functiondef, 'no-self-argument', is_factory_post_generation_method)
843858

844-
# ForeignKey and OneToOneField
845-
# Must update this in a thread safe way to support the parallel option on pylint (-j)
846-
current_leave_module = VariablesChecker.leave_module
847-
if current_leave_module.__name__ == 'leave_module':
848-
# current_leave_module is not wrapped
849-
# Two threads may hit the next assignment concurrently, but the result is the same
850-
VariablesChecker.leave_module = wrap(current_leave_module, ignore_import_warnings_for_related_fields)
851-
# VariablesChecker.leave_module is now wrapped
852-
# else VariablesChecker.leave_module is already wrapped
853-
854859
# wsgi.py
855860
suppress_message(linter, NameChecker.visit_assignname, 'invalid-name', is_wsgi_application)
861+
862+
apply_wrapped_augmentations()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
Checks that Pylint still complains about unused-arguments for other
3+
arguments if a function/method contains an argument named `request`.
4+
"""
5+
# pylint: disable=missing-docstring
6+
7+
from django.http import JsonResponse
8+
from django.views import View
9+
10+
# Pylint generates the warning `redefined-outer-name` if an argument name shadows
11+
# a variable name from an outer scope. But if that argument name is ignored this
12+
# warning will not be generated.
13+
# Therefore define request here to cover this behaviour in this test case.
14+
15+
request = None # pylint: disable=invalid-name
16+
17+
18+
def user_detail(request, user_id): # [unused-argument]
19+
# nothing is done with user_id
20+
return JsonResponse({'username': 'steve'})
21+
22+
23+
class UserView(View):
24+
def get(self, request, user_id): # [unused-argument]
25+
# nothing is done with user_id
26+
return JsonResponse({'username': 'steve'})
27+
28+
29+
# The following views are already covered in other test cases.
30+
# They are included here for completeness sake.
31+
32+
def welcome_view(request):
33+
# just don't use `request' b/c we could have Django views
34+
# which never use it!
35+
return JsonResponse({'message': 'welcome'})
36+
37+
38+
class CBV(View):
39+
def get(self, request):
40+
return JsonResponse({'message': 'hello world'})
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
unused-argument:18:user_detail:Unused argument 'user_id':HIGH
2+
unused-argument:24:UserView.get:Unused argument 'user_id':INFERENCE

0 commit comments

Comments
 (0)