Skip to content
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

B006 (unsafe fix) : does not fix correctly with @overload #10083

Closed
yoann9344 opened this issue Feb 22, 2024 · 4 comments
Closed

B006 (unsafe fix) : does not fix correctly with @overload #10083

yoann9344 opened this issue Feb 22, 2024 · 4 comments
Labels
fixes Related to suggested fixes for violations help wanted Contributions especially welcome

Comments

@yoann9344
Copy link

yoann9344 commented Feb 22, 2024

     @overload
     @classmethod
     def query(
         cls,
-        my_list: list[Whatever | int] = [],
+        my_list: list[Whatever | int] | None = None,
     ) -> Whatever:
-        ...
+        if my_list is None:
+            my_list = []

     @overload
     @classmethod
     def query(
         cls,
-        my_list: list[Whatever | int] = [],
+        my_list: list[Whatever | int] | None = None,
     ) -> Whatever:
-        ...
+        if my_list is None:
+            my_list = []

     @classmethod
-    def query(cls, session, my_list=[]):
+    def query(cls, session, my_list=None):
+        if my_list is None:
+            my_list = []

It should not touch overloaded functions except to change the right argument :

-        my_list: list[Whatever | int] = [],
+        my_list: list[Whatever | int] | None = None,

But must not add logic in it :

-        ...
+        if my_list is None:
+            my_list = []

So the result should look like this for overloaded methods :

     @overload
     @classmethod
     def query(
         cls,
-        my_list: list[Whatever | int] = [],
+        my_list: list[Whatever | int] | None = None,
     ) -> Whatever:
    ...

NB : this overloaded method has no sens, I removed the unnecessary.

@MichaReiser MichaReiser added the fixes Related to suggested fixes for violations label Feb 23, 2024
@MichaReiser
Copy link
Member

MichaReiser commented Feb 23, 2024

Thanks for reporting this improvement. I summarised the issue again in my own words after I have played with the example for a bit.

The B006 replaces the [] default value with None and inserts the

if <argument> is None:
	<argument> = []

as the first statement in the body which in itself is the semantically correct refactor.

However, inserting the statement is undesired if the function has a stub body. I don't think we necessarily have to test if the function has the @overload signature because changing the body is also undesired in typing stub files.

Playground

@MichaReiser MichaReiser added the help wanted Contributions especially welcome label Feb 23, 2024
@yoann9344
Copy link
Author

yoann9344 commented Feb 23, 2024

The B006 replaces the [] default value with None and inserts the

if <argument> is None:
	<argument> = []

as the first statement in the body which in itself is the semantically correct refactor.

Yes that's correct, it also replaces the typing by <initial type> | None

However, inserting the statement is undesired if the function has a stub body. I don't think we necessarily have to test if the function has the @overload signature because changing the body is also undesired in typing stub files.

Well see ! I think it is the best way to handle it ! (to be clear : no statement added in stubs but method signature modified)

Playground

Except at the end there's not a stub body.
class Test:
    @overload
    @classmethod
    def query(
        cls,
        my_list: list[Whatever | int] = [],
    ) -> Whatever:
        ...
    
    @overload
    @classmethod
    def query(
        cls,
        my_list: list[Whatever | int] = [],
    ) -> Whatever:
        ...
    
    @classmethod
    def query(cls, my_list: list[Whatever | int] = []) -> Whatever:
        raise NotImplementedError("")

charliermarsh pushed a commit that referenced this issue Feb 28, 2024
…`) (#10152)

## Summary

Adapts the fix for rule B006 to no longer modify the body of function
stubs, while retaining the change in method signature.

## Test Plan

The existing tests for B006 were adapted to reflect this change in
behavior.

## Relevant issue

#10083
@Philipp-Thiel
Copy link
Contributor

I added code that identifies all functions, where the body consists only out of a docstring, the ... literal and/or the pass keyword as a stub and doesn't modify the body then. But now that I think more about it, I also find the case where the function body only raises a NotImplementedError compelling. Should that maybe also be identified as a stub?

@charliermarsh
Copy link
Member

@Philipp-Thiel - Sure, that seems like a reasonable extension. You can use the SemanticModel to identify the exception type.

nkxxll pushed a commit to nkxxll/ruff that referenced this issue Mar 10, 2024
…`) (astral-sh#10152)

## Summary

Adapts the fix for rule B006 to no longer modify the body of function
stubs, while retaining the change in method signature.

## Test Plan

The existing tests for B006 were adapted to reflect this change in
behavior.

## Relevant issue

astral-sh#10083
charliermarsh pushed a commit that referenced this issue Apr 17, 2024
…unctions (#10990)

## Summary

As discussed in
#10083 (comment),
stubs detection now also covers the case where the function body raises
NotImplementedError and does nothing else.

## Test Plan

Tests for the relevant cases were added in B006_8.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fixes Related to suggested fixes for violations help wanted Contributions especially welcome
Projects
None yet
Development

No branches or pull requests

4 participants