8000 gh-128398: improve error message when incorrectly `with` and `async with` by picnixz · Pull Request #132218 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-128398: improve error message when incorrectly with and async with #132218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 19, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move tests
  • Loading branch information
picnixz committed Apr 13, 2025
commit 69dc81ff64d830618f949124281ae436217886a9
28 changes: 0 additions & 28 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,6 @@
from test.support.os_helper import FakePath


class DummyEnter:
def __enter__(self, *args, **kwargs):
pass


class DummyExit:
def __exit__(self, *args, **kwargs):
pass


class SyncDummy(DummyEnter, DummyExit):
pass


class AsyncDummyEnter:
async def __aenter__(self, *args, **kwargs):
pass


class AsyncDummyExit:
async def __aexit__(self, *args, **kwargs):
pass


class AsyncDummy(AsyncDummyEnter, AsyncDummyExit):
pass


class TestSpecifics(unittest.TestCase):

def compile_single(self, source):
Expand Down
107 changes: 81 additions & 26 deletions Lib/test/test_with.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
"""Unit tests for the with statement specified in PEP 343."""
"""Unit tests for the 'with/async with' statements specified in PEP 343/492."""


__author__ = "Mike Bland"
__email__ = "mbland at acm dot org"

import re
import sys
import traceback
import unittest
from collections import deque
from contextlib import _GeneratorContextManager, contextmanager, nullcontext


def do_with(obj):
with obj:
pass


async def do_async_with(obj):
async with obj:
pass


class MockContextManager(_GeneratorContextManager):
def __init__(self, *args):
super().__init__(*args)
Expand Down Expand Up @@ -110,34 +121,77 @@ def fooNotDeclared():
with foo: pass
self.assertRaises(NameError, fooNotDeclared)

def testEnterAttributeError1(self):
class LacksEnter(object):
def __exit__(self, type, value, traceback):
pass

def fooLacksEnter():
foo = LacksEnter()
with foo: pass
self.assertRaisesRegex(TypeError, 'the context manager', fooLacksEnter)

def testEnterAttributeError2(self):
class LacksEnterAndExit(object):
pass
def testEnterAttributeError(self):
class LacksEnter:
def __exit__(self, type, value, traceback): ...

def fooLacksEnterAndExit():
foo = LacksEnterAndExit()
with foo: pass
self.assertRaisesRegex(TypeError, 'the context manager', fooLacksEnterAndExit)
with self.assertRaisesRegex(TypeError, re.escape((
"object does not support the context manager protocol "
"(missed __enter__ method)"
))):
do_with(LacksEnter())

def testExitAttributeError(self):
class LacksExit(object):
def __enter__(self):
pass

def fooLacksExit():
foo = LacksExit()
with foo: pass
self.assertRaisesRegex(TypeError, 'the context manager.*__exit__', fooLacksExit)
class LacksExit:
def __enter__(self): ...

msg = re.escape((
"object does not support the context manager protocol "
"(missed __exit__ method)"
))
# a missing __exit__ is reported missing before a missing __enter__
with self.assertRaisesRegex(TypeError, msg):
do_with(object())
with self.assertRaisesRegex(TypeError, msg):
do_with(LacksExit())

def testWithForAsyncManager(self):
class AsyncManager:
async def __aenter__(self): ...
async def __aexit__(self, type, value, traceback): ...

with self.assertRaisesRegex(TypeError, re.escape((
"object does not support the context manager protocol "
"(missed __exit__ method) but it supports the asynchronous "
"context manager protocol. Did you mean to use 'async with'?"
))):
do_with(AsyncManager())

def testAsyncEnterAttributeError(self):
class LacksAsyncEnter:
async def __aexit__(self, type, value, traceback): ...

with self.assertRaisesRegex(TypeError, re.escape((
"object does not support the asynchronous context manager protocol "
"(missed __aenter__ method)"
))):
do_async_with(LacksAsyncEnter()).send(None)

def testAsyncExitAttributeError(self):
class LacksAsyncExit:
async def __aenter__(self): ...

msg = re.escape((
"object does not support the asynchronous context manager protocol "
"(missed __aexit__ method)"
))
# a missing __aexit__ is reported missing before a missing __aenter__
with self.assertRaisesRegex(TypeError, msg):
do_async_with(object()).send(None)
with self.assertRaisesRegex(TypeError, msg):
do_async_with(LacksAsyncExit()).send(None)

def testAsyncWithForSyncManager(self):
class SyncManager:
def __enter__(self): ...
def __exit__(self, type, value, traceback): ...

with self.assertRaisesRegex(TypeError, re.escape((
"object does not support the asynchronous context manager protocol "
"(missed __aexit__ method) but it supports the context manager "
"protocol. Did you mean to use 'with'?"
))):
do_async_with(SyncManager()).send(None)

def assertRaisesSyntaxError(self, codestr):
def shouldRaiseSyntaxError(s):
Expand Down Expand Up @@ -190,6 +244,7 @@ def shouldThrow():
pass
self.assertRaises(RuntimeError, shouldThrow)


class ContextmanagerAssertionMixin(object):

def setUp(self):
Expand Down
Loading
0