Skip to content

Commit

Permalink
Don't consider 'object' always truthy (#14510)
Browse files Browse the repository at this point in the history
There are two reasons I'm proposing this change. First, we know that
many subclasses of 'object' can be falsy. Second, mypy sometimes
simplifies `object | Any` into just `object`. The latter was considered
always truthy, while the prior one wasn't. Now both of them are treated
consistently. An alternative fix would be to not simplify unions like
`object | Any`, but this seems a bit ad hoc.

This only has an effect when the `truthy-bool` error code is explicitly
enabled.

Fixes #14480. This doesn't just fix the regression but fixes a more
general issue.
  • Loading branch information
JukkaL authored Jan 23, 2023
1 parent 77f8725 commit db440ab
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 4 deletions.
1 change: 1 addition & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5151,6 +5151,7 @@ def _is_truthy_type(self, t: ProperType) -> bool:
and bool(t.type)
and not t.type.has_readable_member("__bool__")
and not t.type.has_readable_member("__len__")
and t.type.fullname != "builtins.object"
)
or isinstance(t, FunctionLike)
or (
Expand Down
26 changes: 22 additions & 4 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -803,12 +803,15 @@ from typing_extensions import TypedDict

Foo = TypedDict("Bar", {}) # E: First argument "Bar" to TypedDict() does not match variable name "Foo" [name-match]
[builtins fixtures/dict.pyi]

[case testTruthyBool]
# flags: --enable-error-code truthy-bool
from typing import List, Union
from typing import List, Union, Any

class Foo:
pass
class Bar:
pass

foo = Foo()
if foo: # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
Expand Down Expand Up @@ -836,15 +839,30 @@ if good_union:
if not good_union:
pass

bad_union: Union[Foo, object] = Foo()
if bad_union: # E: "__main__.bad_union" has type "Union[Foo, object]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
bad_union: Union[Foo, Bar] = Foo()
if bad_union: # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
pass
if not bad_union: # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
pass

# 'object' is special and is treated as potentially falsy
obj: object = Foo()
if obj:
pass
if not bad_union: # E: "__main__.bad_union" has type "object" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
if not obj:
pass

lst: List[int] = []
if lst:
pass

a: Any
if a:
pass

any_or_object: Union[object, Any]
if any_or_object:
pass
[builtins fixtures/list.pyi]

[case testTruthyFunctions]
Expand Down

0 comments on commit db440ab

Please sign in to comment.