-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #5371: Correctly count arguments to static methods missing @staticmethod decorator #5412
Fix #5371: Correctly count arguments to static methods missing @staticmethod decorator #5412
Conversation
@staticmethod | ||
def should_have_been_decorated_as_static(arg1, arg2): | ||
return arg1 + arg2 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what was reported as generating a false positive.
def func(self, data): | ||
def func(self, data): # [arguments-differ] | ||
super().func(data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here was a false negative!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice fix !
Pull Request Test Coverage Report for Build 1536924678
π - Coveralls |
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a functional test using the problematic Enum directly ? I think it might be a case where a specific handling of it to not have false positive elsewhere would be warranted.
def func(self, data): | ||
def func(self, data): # [arguments-differ] | ||
super().func(data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice fix !
""" | ||
Acceptable use of vararg in subclass because it does not violate LSP. | ||
""" | ||
super().impl(*args, **kwargs) | ||
|
||
@staticmethod | ||
def should_have_been_decorated_as_static(arg1, arg2): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imo it should emit argument-differ, there are two errors here, the first one lack a self argument but this one's argument differ as there are 3 instead of 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, let me see if I can follow. How are you counting three arguments? I think that's a code-style suggestion to say that there should be no static methods inside classes without a decorator, so if someone has done that, we get here and they just have 2 == 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I meant 2 instead of 3. The first function would need self, arg1, arg2 and this one too but as it's static having 2 is equivalent to 3 (like the false positive you found higher up)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the first function arg1
should be called self
but as long as it's not fixed we don't know if it has 2 or 3 args (will the user rename arg1 to self or add self on top of arg1 arg2 ?). Assuming arg1 will be renamed to self and as the second function is static it means it should only have one arg equivalent to arg2
as the self is implicit. If we assume self will be added before arg1 after the user fix it then no error makes more sense. But we have to detect two problems correctly instead of one for that to work, not making assumption is safer imo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the first function arg1 should be called self but as long as it's not fixed we don't know if it has 2 or 3 args (will the user rename arg1 to self or add self on top of arg1 arg2 ?).
I guess this is the part I'm having trouble with -- let's say a user has disabled no-self-use
because they don't care whether their static methods are decorated as such. Now following your idea pylint will be coupling the behavior of arguments-differ
to the choice of no-self-use
which will be frustrating for that user. There's nothing strictly wrong with the Enum class in the report, it just makes typing harder. Some users don't type.
will the user rename arg1 to self or add self on top of arg1 arg2 ?
This is the assumption I would be reluctant to make -- why are we assuming self
needs to be added at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the assumption I would be reluctant to make -- why are we assuming self needs to be added at all?
I agree we should not couple no-self-argument
to argument-differ
.
But argument differ here:
class Mother:
def should_have_been_decorated_as_static(arg1, arg2): # pylint: disable=no-self-argument
# Enum._generate_next_value in stdlib should be static
return arg1 + arg2
class Daugther(Mother):
@staticmethod
def should_have_been_decorated_as_static(arg1, arg2): #[argument-differ]
return arg1 + arg2
Mother need 1 explicit args (self is implicit + arg2):
>>> m = Mother()
>>> m.should_have_been_decorated_as_static(1, 2 )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: should_have_been_decorated_as_static() takes 2 positional arguments but 3 were given
>>> m.should_have_been_decorated_as_static(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in should_have_been_decorated_as_static
TypeError: unsupported operand type(s) for +: 'Mother' and 'int'
While Daugter needs 2 explicit args:
>>> d = Daugther()
>>> d.should_have_been_decorated_as_static(1, 2)
3
>>> d.should_have_been_decorated_as_static(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: should_have_been_decorated_as_static() missing 1 required positional argument: 'arg2'
Thus argument-differ should be triggered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, but only if you call it on the instance!
>>> Mother.should_have_been_decorated_as_static(1, 2)
3
>>> Daugther.should_have_been_decorated_as_static(1, 2)
3
I think this was the scenario in the stdlib, which is why I didn't raise a bug against CPython. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK it makes sense now. I think @staticmethod
is a stronger signal than the arg not being called self but there is already a message for the argument not being called self.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for clarifying the misunderstanding from my previous comment :) Would it be possible to add an example using the enum from the original issue ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice, thank you !
Type of Changes
Description
Before
Static methods lacking a
@staticmethod
decorator, such asEnum._generate_next_value()
in the stdlib would have their first argument stripped when counting their arguments, even though it wasn'tself
orcls
. This caused comparison issues inarguments-differ
, including both false negatives and positives.Now
Check if the method is bound before stripping the first argument.
Closes #5371