-
Notifications
You must be signed in to change notification settings - Fork 118
Description
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()