From 44b631b80b9ed9a43c25a897b9c919c0d65b88a1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 29 Aug 2026 10:43:26 -0700 Subject: [PATCH] Support in/not in tests on sys.platform This is proposed for the spec in python/typing#2173. It is trivial to support in mypy and seems useful, so let's add it. --- mypy/reachability.py | 25 +++++-- test-data/unit/check-unreachable-code.test | 76 ++++++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/mypy/reachability.py b/mypy/reachability.py index 37c7a9715600a..1d95c49d86ecc 100644 --- a/mypy/reachability.py +++ b/mypy/reachability.py @@ -23,6 +23,7 @@ MemberExpr, NameExpr, OpExpr, + SetExpr, SliceExpr, StrExpr, TupleExpr, @@ -232,19 +233,27 @@ def consider_sys_platform(expr: Expression, platform: str) -> int: # - sys.platform == 'linux' # - sys.platform != 'win32' # - sys.platform.startswith('win') + # - sys.platform in {'linux', 'darwin'} + # - sys.platform not in ('win32', 'cygwin') if isinstance(expr, ComparisonExpr): # Let's not yet support chained comparisons. if len(expr.operators) > 1: return TRUTH_VALUE_UNKNOWN op = expr.operators[0] - if op not in ("==", "!="): - return TRUTH_VALUE_UNKNOWN if not is_sys_attr(expr.operands[0], "platform"): return TRUTH_VALUE_UNKNOWN right = expr.operands[1] - if not isinstance(right, StrExpr): + if op in ("==", "!=") and isinstance(right, StrExpr): + return fixed_comparison(platform, op, right.value) + if op not in ("in", "not in"): + return TRUTH_VALUE_UNKNOWN + items = contains_tuple_or_set_of_strings(right) + if items is None: return TRUTH_VALUE_UNKNOWN - return fixed_comparison(platform, op, right.value) + result = platform in items + if op == "not in": + result = not result + return ALWAYS_TRUE if result else ALWAYS_FALSE elif isinstance(expr, CallExpr): if not isinstance(expr.callee, MemberExpr): return TRUTH_VALUE_UNKNOWN @@ -296,6 +305,14 @@ def contains_int_or_tuple_of_ints(expr: Expression) -> None | int | tuple[int, . return None +def contains_tuple_or_set_of_strings(expr: Expression) -> tuple[str, ...] | None: + if isinstance(expr, (TupleExpr, SetExpr)) and all( + isinstance(item, StrExpr) for item in expr.items + ): + return tuple(item.value for item in expr.items if isinstance(item, StrExpr)) + return None + + def contains_sys_version_info(expr: Expression) -> None | int | tuple[int | None, int | None]: if is_sys_attr(expr, "version_info"): return (None, None) # Same as sys.version_info[:] diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 42aa76b4ba951..b0fbdd1057040 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -474,6 +474,82 @@ reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/ops.pyi] [out] +[case testCustomSysPlatformMembershipTuple] +# flags: --platform linux +import sys +if sys.platform in ('linux', 'darwin'): + x = "foo" +else: + x = 3 +reveal_type(x) # N: Revealed type is "builtins.str" + +if sys.platform not in ('win32', 'cygwin'): + y = "foo" +else: + y = 3 +reveal_type(y) # N: Revealed type is "builtins.str" + +if sys.platform in ('win32', 'cygwin'): + z = "foo" +else: + z = 3 +reveal_type(z) # N: Revealed type is "builtins.int" + +if sys.platform not in ('linux', 'darwin'): + w = "foo" +else: + w = 3 +reveal_type(w) # N: Revealed type is "builtins.int" +[builtins fixtures/ops.pyi] +[out] + +[case testCustomSysPlatformMembershipSet] +# flags: --platform linux +import sys +if sys.platform in {'linux', 'darwin'}: + x = "foo" +else: + x = 3 +reveal_type(x) # N: Revealed type is "builtins.str" + +if sys.platform not in {'linux', 'darwin'}: + y = "foo" +else: + y = 3 +reveal_type(y) # N: Revealed type is "builtins.int" +[builtins fixtures/set.pyi] +[out] + +[case testSysPlatformMembershipUnknown] +import sys + +platform = sys.platform +if sys.platform in ('linux', platform): + def f() -> int: return 0 +else: + def f() -> str: return '' # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def f() -> int \ + # N: Redefinition: \ + # N: def f() -> str +[builtins fixtures/ops.pyi] +[out] + +[case testSysPlatformMembershipListIsUnknown] +# flags: --platform linux +import sys + +if sys.platform in ['linux', 'darwin']: + def f() -> int: return 0 +else: + def f() -> str: return '' # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def f() -> int \ + # N: Redefinition: \ + # N: def f() -> str +[builtins fixtures/list.pyi] +[out] + [case testShortCircuitInExpression] import typing def make() -> bool: pass