Skip to content

Fix False Positive on tests with a no-member Lint Error on DecimalField #455

@pablolucas890

Description

@pablolucas890

Pylint incorrectly raises a no-member warning when accessing DecimalField attributes in Django models. Specifically, calling methods like .compare() or .adjusted() on self.decimalfield triggers this false positive, even though self.decimalfield is correctly assigned and used within the model.

1. Steps to Reproduce:

Define a Django model with a DecimalField.
Implement a method that calls .compare() or .adjusted() on self.decimalfield.
Run Pylint and observe the no-member warning.

2. Expected Behavior:

Pylint should correctly recognize DecimalField as having the expected methods when retrieved from a model instance.

3. Proposed Solution:

Ensure proper type conversion of self.decimalfield to Decimal before calling its methods.
Update Pylint rules or Django stubs to correctly handle DecimalField attributes.

4. Additional Context:

It's happened when I was running tests with pytest in pylint_django/tests/input/func_noerror_form_fields.py and pylint_django/tests/input/func_noerror_model_fields.py:

/usr/lib/python3/dist-packages/pylint/testutils/lint_module_test.py:142: AssertionError
______________________________________ test_everything[/pkgs/pylint-django/pylint-django/pylint_django/tests/input/func_noerror_model_fields] ______________________________________

test_file = FunctionalTest:/pkgs/pylint-django/pylint-django/pylint_django/tests/input/func_noerror_model_fields

    @pytest.mark.parametrize("test_file", TESTS, ids=TESTS_NAMES)
    def test_everything(test_file):
        # copied from pylint.tests.test_functional.test_functional
        lint_test = PylintDjangoLintModuleTest(test_file)
        lint_test.setUp()
>       lint_test.runTest()

pylint_django/tests/test_func.py:104: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pylint_django.tests.test_func.PylintDjangoLintModuleTest object at 0x7fae93561630>

    def runTest(self) -> None:
>       self._runTest()
E       AssertionError: Wrong message(s) raised for "func_noerror_model_fields.py":
E         
E         Unexpected in testdata:
E           75: no-member
E       assert Counter() == Counter({(75,...-member'): 1})
E         
E         Right contains 1 more item:
E         {(75, 'no-member'): 1}
E         
E         Full diff:
E         - Counter({
E         ?         ^...
E         
E         ...Full output truncated (4 lines hidden), use '-vv' to show

/usr/lib/python3/dist-packages/pylint/testutils/lint_module_test.py:142: AssertionError

/usr/lib/python3/dist-packages/pylint/testutils/lint_module_test.py:142: AssertionError                                                                                             
______________________________________ test_everything[/pkgs/pylint-django/pylint-django/pylint_django/tests/input/func_noerror_form_fields] _______________________________________
                                                                                                                                                                                    
test_file = FunctionalTest:/pkgs/pylint-django/pylint-django/pylint_django/tests/input/func_noerror_form_fields                                                                     
                                                                                                                                                                                    
    @pytest.mark.parametrize("test_file", TESTS, ids=TESTS_NAMES)                                                                                                                   
    def test_everything(test_file):                                                                                                                                                 
        # copied from pylint.tests.test_functional.test_functional                                                                                                                  
        lint_test = PylintDjangoLintModuleTest(test_file)                                                                                                                           
        lint_test.setUp()                                                                                                                                                           
>       lint_test.runTest()                                                                                                                                                         
                                                                                                                                                                                    
pylint_django/tests/test_func.py:104:                                                                                                                                               
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pylint_django.tests.test_func.PylintDjangoLintModuleTest object at 0x7fae91a1dc50> 

    def runTest(self) -> None:
>       self._runTest()
E       AssertionError: Wrong message(s) raised for "func_noerror_form_fields.py":
E         
E         Unexpected in testdata:
E           61: no-member
E       assert Counter() == Counter({(61,...-member'): 1})
E         
E         Right contains 1 more item:
E         {(61, 'no-member'): 1}
E         
E         Full diff:
E         - Counter({
E         ?         ^...
E         
E         ...Full output truncated (4 lines hidden), use '-vv' to show

5. Patch:

This is a patch idea to fix, maybe adding just a # pylint: disable=no-member its more appropriate:

---                                                                                                                                                                                 
 pylint_django/tests/input/func_noerror_form_fields.py  | 3 ++-                                                                                                                     
 pylint_django/tests/input/func_noerror_model_fields.py | 2 +-                                                                                                                      
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/pylint_django/tests/input/func_noerror_form_fields.py b/pylint_django/tests/input/func_noerror_form_fields.py                                                          
index dada077..65f1e95 100644                                                                                                                                                       
--- a/pylint_django/tests/input/func_noerror_form_fields.py                                                                                                                         
+++ b/pylint_django/tests/input/func_noerror_form_fields.py                                                                                                                         
@@ -6,6 +6,7 @@ methods on Django form forms.                                                                                                                                       
 from __future__ import print_function

 from datetime import date, datetime
+from decimal import Decimal                                                                                                                                                        

 from django import forms
 from django.contrib.auth.forms import UserCreationForm
@@ -58,7 +59,7 @@ class ManyFieldsForm(forms.Form):                                                                                                                                 
         print(self.datefield.isoformat())

     def decimalfield_tests(self):
-        print(self.decimalfield.adjusted())                                                                                                                                        
+        print(Decimal(str(self.decimalfield)).adjusted())                                                                                                                          

     def durationfield_tests(self):
         now = datetime.now()
diff --git a/pylint_django/tests/input/func_noerror_model_fields.py b/pylint_django/tests/input/func_noerror_model_fields.py                                                        
index c0389ff..9111f23 100644                                                                                                                                                       
--- a/pylint_django/tests/input/func_noerror_model_fields.py                                                                                                                        
+++ b/pylint_django/tests/input/func_noerror_model_fields.py                                                                                                                        
@@ -72,7 +72,7 @@ class LotsOfFieldsModel(models.Model):                                                                                                                            
         print(self.datefield.isoformat())

     def decimalfield_tests(self):
-        print(self.decimalfield.compare(Decimal("1.4")))                                                                                                                           
+        print(Decimal(str(self.decimalfield)).compare(Decimal("1.4")))                                                                                                             

     def durationfield_tests(self):
         now = datetime.now()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions