Skip to content

Commit 006305e

Browse files
committed
Allow dangerous identity comparisons to Any typed variables
Fixes #21134
1 parent ef7e8a6 commit 006305e

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

mypy/checkexpr.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,8 +3800,20 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
38003800
elif operator == "is" or operator == "is not":
38013801
right_type = self.accept(right) # validate the right operand
38023802
sub_result = self.bool_type()
3803-
if not self.chk.can_skip_diagnostics and self.dangerous_comparison(
3804-
left_type, right_type, identity_check=True
3803+
if (
3804+
not self.chk.can_skip_diagnostics
3805+
and self.dangerous_comparison(left_type, right_type, identity_check=True)
3806+
# Allow dangerous identity comparisons with objects explicitly typed as Any
3807+
and not (
3808+
isinstance(left, NameExpr)
3809+
and isinstance(left.node, Var)
3810+
and isinstance(left.node.type, AnyType)
3811+
)
3812+
and not (
3813+
isinstance(right, NameExpr)
3814+
and isinstance(right.node, Var)
3815+
and isinstance(right.node.type, AnyType)
3816+
)
38053817
):
38063818
# Show the most specific literal types possible
38073819
left_type = try_getting_literal(left_type)

test-data/unit/check-expressions.test

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,6 +2380,40 @@ if 1 in ('x', 'y'): # E: Non-overlapping container check (element type: "int",
23802380
[builtins fixtures/tuple.pyi]
23812381
[typing fixtures/typing-full.pyi]
23822382

2383+
[case testStrictEqualityWithAnySentinel]
2384+
# flags: --strict-equality
2385+
from __future__ import annotations
2386+
from typing import Any
2387+
2388+
class A: ...
2389+
class B: ...
2390+
2391+
sentinel: Any = object()
2392+
2393+
def f1(a: A = sentinel, b: B = sentinel):
2394+
if a is sentinel and b is sentinel:
2395+
raise
2396+
2397+
2398+
def f2(a: A | None = sentinel, b: B | None = sentinel):
2399+
if a is sentinel and b is sentinel:
2400+
raise
2401+
2402+
2403+
sentinel_strict = object()
2404+
2405+
def f3(a: A = sentinel_strict, b: B = sentinel_strict): # E: Incompatible default for parameter "a" (default has type "object", parameter has type "A") \
2406+
# E: Incompatible default for parameter "b" (default has type "object", parameter has type "B")
2407+
if a is sentinel_strict and b is sentinel_strict: # E: Non-overlapping identity check (left operand type: "B", right operand type: "A")
2408+
raise
2409+
2410+
2411+
def f4(a: A | None = sentinel_strict, b: B | None = sentinel_strict): # E: Incompatible default for parameter "a" (default has type "object", parameter has type "A | None") \
2412+
# E: Incompatible default for parameter "b" (default has type "object", parameter has type "B | None")
2413+
if a is sentinel_strict and b is sentinel_strict: # E: Non-overlapping identity check (left operand type: "B | None", right operand type: "A | None")
2414+
raise
2415+
[builtins fixtures/bool.pyi]
2416+
23832417
[case testOverlappingAnyTypeWithoutStrictOptional]
23842418
# flags: --no-strict-optional --strict-equality
23852419
from typing import Any, Optional

0 commit comments

Comments
 (0)