-
Notifications
You must be signed in to change notification settings - Fork 180
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
Detect unused functions and classes in closures #485
base: main
Are you sure you want to change the base?
Detect unused functions and classes in closures #485
Conversation
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. It looks good to me too. Apparently the redefinition warnings already work with functions and classes, so this adds consistency there. |
hmmm I'm not sure about the latest commit -- if someone is doing global decorator magic I think that should be flagged as suspicious |
Decorators don't always count as global or magic. For example in some tests on a project I work on we have a function that returns a flask app, something like: def make_app():
app = Flask('test app')
@app.route('/')
def home():
return 'Home'
return app I feel like this is pretty standard Pythonic behaviour. The suspiciousness of whether the variable is used depends vastly on the decorators used. Also one can always course rewrite |
that decorator is purely side-effects -- in my book, magic |
I disagree, it would be a block in Ruby or an anonymous function in JavaScript. Python just requires us to name our >1 line functions so you end up with side-effect decorators like this. Anyway, can we merge this as-is and make a separate issue for detecting such cases? I feel like it would want to be a separate check so it can be enabled/disabled separately. |
if anything we should merge this without that commit and then discuss separately that controversial portion |
I'd be ok with merging this without 8d3dbb8 if all side effecting functions are also forbidden.
|
8d3dbb8
to
a0e4eea
Compare
Whilst developing PyCQA/pyflakes#485 I ran it against the CPython code base and found these examples of unused functions that I think need fixing.
@asottile can you merge this one way or another? I still believe ignoring decorated functions/classes makes sense. I ran against Django master, with and without the "ignore decorated items" commit. Including decorated items, there are 138 errors:
Without decorated items, there are 118:
Of these 118, only 1 is an unused local function, that is placed into a Django view's locals to check that its stack-inspecting error logic doesn't accidentally call it:
The other 117 seem to all be checking for exceptions during class definition (Django models, forms, etc.). Due to meta classes, defining a class is not side-effect-free - just like a decorator - and its these side effects those tests are checking for. The 20 extra errors seen when including decorated functions/classes seem to all be based on various "registry patterns," similar to my original Flask example, for example with a Django signal:
Such code can always be rewritten like:
Which pyflakes would count as a legitimate variable use. I also ran against CPython master. There were also many unused classes, all of which seem to be in tests defined for their side effects, but a number of accidentally unused functions which I fixed in python/cpython#17189 . All unused functions with decorators were using a similar "registry" pattern again. |
there's a bit of a weird sign off model here where we need two approvals to merge (and I'd prefer without the decorator patch myself) -- but other than that the change looks good to me 👍 |
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.
I think the register pattern is extremely useful
Note that the decorator discussion is happening in #488 where the answer from maintainers is pretty unanimously "no" |
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 a possible way to approach to break the deadlock here is to create a new message for "UnusedDecorated", which can then be explicitly ignored when a codebase is intentionally using that type of magic.
Any codebase currently using pyflakes will likely already have an explicit ignore for the much broader unused message currently emitted for this. If they are using inline ignores, they will need to swap in the new more precise and self-explanatory ignore code. If they are using a global ignore, they will need to add the new one, and may not notice to remove the more generic code, but over time projects will remove any explicit ignores for the generic unused.
This more precise message type will surface broader user feedback.
pass | ||
''', m.UnusedClass) | ||
|
||
def test_unusedUnderscoreClass(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.
This test can be moved to a new PR and will pass as-is.
Tests for warning about unused classes. | ||
""" | ||
|
||
def test_unusedClass(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.
This test could be moved to a new PR and UnusedClass
introduced in a non-controversial manner.
Fixes #392.