From 03d7128183789fe3d1e393615a42411a80eb6abd Mon Sep 17 00:00:00 2001 From: sanyamk23 Date: Fri, 31 Jul 2026 22:37:36 +0530 Subject: [PATCH] Fix type narrowing with not isinstance(x, cls) in classmethods --- .github/workflows/build_wheels.yml | 4 ++-- .github/workflows/docs.yml | 4 ++-- .github/workflows/mypy_primer.yml | 10 +++++----- .github/workflows/mypy_primer_comment.yml | 4 ++-- .github/workflows/sync_typeshed.yml | 4 ++-- .github/workflows/test.yml | 6 +++--- .github/workflows/test_stubgenc.yml | 4 ++-- mypy/checker.py | 3 +++ test-data/unit/check-narrowing.test | 15 +++++++++++++++ 9 files changed, 36 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index dae4937d5081d..bf46ce914377d 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -13,10 +13,10 @@ jobs: if: github.repository == 'python/mypy' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.11' - name: Trigger script diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b8303342a254c..40c3657d7616e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -37,10 +37,10 @@ jobs: TOX_SKIP_MISSING_INTERPRETERS: False VERIFY_MYPY_ERROR_CODES: 1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.12' - name: Install tox diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 478dc09c3d464..2d419ac5b6d75 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -32,12 +32,12 @@ jobs: fail-fast: false timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: path: mypy_to_test fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.14" - name: Install dependencies @@ -75,7 +75,7 @@ jobs: run: | echo ${{ github.event.pull_request.number }} | tee pr_number.txt - name: Upload mypy_primer diff + PR number - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: ${{ matrix.shard-index == 0 }} with: name: mypy_primer_diffs-${{ matrix.shard-index }} @@ -83,7 +83,7 @@ jobs: diff_${{ matrix.shard-index }}.txt pr_number.txt - name: Upload mypy_primer diff - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: ${{ matrix.shard-index != 0 }} with: name: mypy_primer_diffs-${{ matrix.shard-index }} @@ -95,7 +95,7 @@ jobs: needs: [mypy_primer] steps: - name: Merge artifacts - uses: actions/upload-artifact/merge@v4 + uses: actions/upload-artifact/merge@v6 with: name: mypy_primer_diffs pattern: mypy_primer_diffs-* diff --git a/.github/workflows/mypy_primer_comment.yml b/.github/workflows/mypy_primer_comment.yml index 21f1222a5b893..64b00e9e6214f 100644 --- a/.github/workflows/mypy_primer_comment.yml +++ b/.github/workflows/mypy_primer_comment.yml @@ -19,7 +19,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download diffs - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require('fs'); @@ -45,7 +45,7 @@ jobs: - name: Post comment id: post-comment - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/sync_typeshed.yml b/.github/workflows/sync_typeshed.yml index 2d5361a5919ce..d3af572d56222 100644 --- a/.github/workflows/sync_typeshed.yml +++ b/.github/workflows/sync_typeshed.yml @@ -17,13 +17,13 @@ jobs: pull-requests: write timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 persist-credentials: true # needed to `git push` the PR branch # TODO: use whatever solution ends up working for # https://github.com/python/typeshed/issues/8434 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.10" - name: git config diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b06f181746c35..bc00e7f0481b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -160,7 +160,7 @@ jobs: PYTEST_ADDOPTS: --color=yes steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false @@ -198,7 +198,7 @@ jobs: sudo ln -s /opt/pythondev/bin/python3 /opt/pythondev/bin/python sudo ln -s /opt/pythondev/bin/pip3 /opt/pythondev/bin/pip echo "/opt/pythondev/bin" >> $GITHUB_PATH - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 if: ${{ !(matrix.debug_build || endsWith(matrix.python, '-dev')) }} with: python-version: ${{ matrix.python }} @@ -253,7 +253,7 @@ jobs: CXX: i686-linux-gnu-g++ CC: i686-linux-gnu-gcc steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false - name: Install 32-bit build dependencies diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 7102b6513ddc2..ffbdcdf614dfb 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -28,12 +28,12 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false - name: Setup 🐍 3.10 - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.10' diff --git a/mypy/checker.py b/mypy/checker.py index 813939ca49646..697a9c39727be 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8361,6 +8361,9 @@ def get_type_range_of_type(self, typ: Type) -> TypeRange | None: is_upper_bound = False if isinstance(typ.item, Instance) and typ.item.type.is_final: is_upper_bound = False + if isinstance(typ.item, TypeVarType) and typ.item.id.is_self(): + # except for Self types, because isinstance checks for exact type + is_upper_bound = False return TypeRange(typ.item, is_upper_bound=is_upper_bound) if isinstance(typ, AnyType): return TypeRange(typ, is_upper_bound=False) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 29f4cd47929d9..02d696a4e4608 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -3052,6 +3052,21 @@ def check_d(arg: D[T]) -> None: [builtins fixtures/tuple.pyi] +[case testIsinstanceNarrowingWithSelfTypeInClassmethod] +from typing_extensions import Self + +class Foo: + @classmethod + def foo_1(cls, x: Self | float) -> None: + assert isinstance(x, cls) + reveal_type(x) # N: Revealed type is "Self`0" + + @classmethod + def foo_2(cls, x: Self | float) -> None: + assert not isinstance(x, cls) + reveal_type(x) # N: Revealed type is "builtins.float" +[builtins fixtures/isinstancelist.pyi] + [case testNarrowingUnionMixins] # flags: --strict-equality --warn-unreachable class Base: ...