Repository: python/mypy

Issue:
Mypy fails to typecheck itself following the recent black upgrade
_Note: make sure you run mypy with a cold cache each time if you're trying to repro this bug locally. It seems to not always reproduce if you have a warm cache._

**Bug Report**

Following https://github.com/python/mypy/commit/8107e53158d83d30bb04d290ac10d8d3ccd344f8 (bumping the version of black we use in CI to 24.1.1), mypy fails to typecheck its own codebase if you're using Python 3.10.6 or newer. This is a result of a CPython bug where the `ast` stdlib module on newer versions of Python thinks certain syntax around context managers is illegal on Python 3.8, even though that syntax works fine if you're actually _using_ Python 3.8: https://github.com/python/cpython/issues/115881.

Hopefully the CPython bug will be fixed, but it won't be backported to Python 3.10, since Python 3.10 is old enough that it now only accepts patches if they relate to security issues (and this bug isn't a security issue). If we want mypy to be able to typecheck itself using Python 3.10, we'll need to find a workaround.

**To Reproduce**

Inside a local clone of mypy, using Python 3.10.6 or newer, with all test requirements installed (including an editable install of mypy):

```
% rm -rf .mypy_cache
% python runtests.py self
run self: ['/Users/alexw/dev/mypy/310-6-venv/bin/python3', '-m', 'mypy', '--config-file', 'mypy_self_check.ini', '-p', 'mypy', '-p', 'mypyc']
mypy/checker.py:540: error: Parenthesized context managers are only supported in Python 3.9 and greater  [syntax]
                return True
                ^
mypy/checker.py:540: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-syntax for more info
Found 1 error in 1 file (errors prevented further checking)

FAILED: self
```

**Expected Behavior**

Mypy should be able to type-check its own codebase on all versions of Python

**Actual Behavior**

Mypy cannot typecheck its own codebase on newer versions of Python

**Your Environment**

<!-- Include as many relevant details about the environment you experienced the bug in -->

- Mypy version used: the mypy master branch
- Python version used: I tested using Python 3.8, 3.9, 3.10.5, 3.10.6, 3.11.0 and 3.12.0



Discussion:

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -4,7 +4,7 @@
 
 import itertools
 from collections import defaultdict
-from contextlib import contextmanager, nullcontext
+from contextlib import ExitStack, contextmanager
 from typing import (
     AbstractSet,
     Callable,
@@ -526,17 +526,11 @@ def check_second_pass(
                     # print("XXX in pass %d, class %s, function %s" %
                     #       (self.pass_num, type_name, node.fullname or node.name))
                     done.add(node)
-                    with (
-                        self.tscope.class_scope(active_typeinfo)
-                        if active_typeinfo
-                        else nullcontext()
-                    ):
-                        with (
-                            self.scope.push_class(active_typeinfo)
-                            if active_typeinfo
-                            else nullcontext()
-                        ):
-                            self.check_partial(node)
+                    with ExitStack() as stack:
+                        if active_typeinfo:
+                            stack.enter_context(self.tscope.class_scope(active_typeinfo))
+                            stack.enter_context(self.scope.push_class(active_typeinfo))
+                        self.check_partial(node)
             return True
 
     def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> None:


Test patch: