You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Reject method/function object used as a condition #2073
The example was overly simplistic. This is more realistic, and here we need type information:
class A:
def name(self) -> Optional[str]: ...
def f(a: A) -> None:
if a.name: # Probably meant a.name()?
do_something(a.name())
I've been bitten by this and I've heard similar bugs mentioned by other people. It's probably not a very big thing, but it should be pretty easy to catch these.
@JukkaL@gvanrossum It seems to be that this is related to 9a8a8c0 ... The boolean value of a.name in @JukkaL's example above is true_only
Where this will be super useful is with the new async/await support. In Hack-land, we'd always see bugs of the form:
asyncdefis_foo_active() ->bool:
returnawaitsome_database_call_about_foo()
asyncdefdo_a_thing() ->None:
ifis_foo_active(): # BUG! Engineer forgot to await so this is always truthy!!!!!do_foo()
else:
do_bar() # Dead code
Hack now throws an error if you branch on something always truth-y to solve this problem.
This doesn't necessarily result in dead code, but sometimes it does. An always-true condition in an if/while statement could be flagged as a potential error, but not always. while True: and if 1: should likely be okay, as it's quite clear that this is what the programmer intended.
I think that a reasonable goal would be to reject code that looks to the reader like it might not be truthy, when we actually know that it will always be truthy.
Reacted by Roy Williams, rvolgers and Finite State Machine
I recently typed if user.is_admin: instead of if user.is_admin(): and was pretty disappointed none of the linters was able to catch this. From where I'm standing this looks like a pretty important feature.
Btw before anyone says this should have been a property, there are other similar methods that take parameters so it's trying to be consistent.
Reacted by Elazar Gershuni and Finite State Machine
A possible more general approach would be to give an error if an object is used in a boolean context, but doesn't override object's __bool__ method, which means it will always appear as True. I don't know if that would cause a lot of false positives though.
I think that it would be okay to flag an error if a condition is a direct reference to a module-level function or method. Anything else we may want to allow. In particular, variables with an explicit Callable[...] type might have a custom __bool__ (they could be instances or user-defined classes), so perhaps we should always allow them to be used in conditions.
fromtypingimportOptional, Callabledefrequired(cb: Callable[[], None]) ->None:
ifcb: # error: Function "cb" could always be true in boolean context [truthy-function]passdefoptional(cb: Optional[Callable[[], None]]) ->None:
ifcb: # no errorpass
The missing-await issue above seems better covered by the (much newer, but more specific) #16069
Activity
gvanrossum commentedon Sep 1, 2016
Is that really common? It sounds like more of a linter feature.
JukkaL commentedon Sep 2, 2016
The example was overly simplistic. This is more realistic, and here we need type information:
I've been bitten by this and I've heard similar bugs mentioned by other people. It's probably not a very big thing, but it should be pretty easy to catch these.
rowillia commentedon Sep 2, 2016
@JukkaL @gvanrossum It seems to be that this is related to 9a8a8c0 ... The boolean value of
a.namein @JukkaL's example above istrue_onlyWhere this will be super useful is with the new
async/awaitsupport. In Hack-land, we'd always see bugs of the form:Hack now throws an error if you branch on something always truth-y to solve this problem.
ddfisher commentedon Sep 2, 2016
More generally, we could warn about dead code?
JukkaL commentedon Sep 5, 2016
This doesn't necessarily result in dead code, but sometimes it does. An always-true condition in an if/while statement could be flagged as a potential error, but not always.
while True:andif 1:should likely be okay, as it's quite clear that this is what the programmer intended.I think that a reasonable goal would be to reject code that looks to the reader like it might not be truthy, when we actually know that it will always be truthy.
elazarg commentedon Sep 11, 2016
I think that such errors are very common, especially when turning a method into a property, and it would be most useful.
rvolgers commentedon Aug 17, 2018
I recently typed
if user.is_admin:instead ofif user.is_admin():and was pretty disappointed none of the linters was able to catch this. From where I'm standing this looks like a pretty important feature.Btw before anyone says this should have been a property, there are other similar methods that take parameters so it's trying to be consistent.
ilevkivskyi commentedon Aug 17, 2018
OK, I am raising priority to normal.
JelleZijlstra commentedon Aug 17, 2018
A possible more general approach would be to give an error if an object is used in a boolean context, but doesn't override
object's__bool__method, which means it will always appear as True. I don't know if that would cause a lot of false positives though.elazarg commentedon Aug 17, 2018
You mean if the type does not have any possible instance that overrides object's
__bool__(). There aren't manyfinaltypes in Python.I think handling (unions of) functions, specifically, is the way to go.
ilevkivskyi commentedon Feb 10, 2019
An important corner case to consider is:
I think we should always allow such code.
JukkaL commentedon Feb 12, 2019
I think that it would be okay to flag an error if a condition is a direct reference to a module-level function or method. Anything else we may want to allow. In particular, variables with an explicit
Callable[...]type might have a custom__bool__(they could be instances or user-defined classes), so perhaps we should always allow them to be used in conditions.huonw commentedon Oct 7, 2024
It seems like this may be resolved now, via the
truthy-functionerror code: https://mypy-play.net/?mypy=latest&python=3.12&gist=067efa3ee1e7617950d94c90cc60116eThe missing-
awaitissue above seems better covered by the (much newer, but more specific) #16069Maybe this one can be closed?