Issue #27100: With statement reports missing __enter__ before __exit__. (Contributed by Jonathan Ellington.)

This commit is contained in:
Raymond Hettinger 2016-11-21 17:24:23 -08:00
parent 4e17e04237
commit a3fec1543d
3 changed files with 20 additions and 7 deletions

View File

@ -109,7 +109,7 @@ class FailureTestCase(unittest.TestCase):
with foo: pass
self.assertRaises(NameError, fooNotDeclared)
def testEnterAttributeError(self):
def testEnterAttributeError1(self):
class LacksEnter(object):
def __exit__(self, type, value, traceback):
pass
@ -117,7 +117,16 @@ class FailureTestCase(unittest.TestCase):
def fooLacksEnter():
foo = LacksEnter()
with foo: pass
self.assertRaises(AttributeError, fooLacksEnter)
self.assertRaisesRegexp(AttributeError, '__enter__', fooLacksEnter)
def testEnterAttributeError2(self):
class LacksEnterAndExit(object):
pass
def fooLacksEnterAndExit():
foo = LacksEnterAndExit()
with foo: pass
self.assertRaisesRegexp(AttributeError, '__enter__', fooLacksEnterAndExit)
def testExitAttributeError(self):
class LacksExit(object):
@ -127,7 +136,7 @@ class FailureTestCase(unittest.TestCase):
def fooLacksExit():
foo = LacksExit()
with foo: pass
self.assertRaises(AttributeError, fooLacksExit)
self.assertRaisesRegexp(AttributeError, '__exit__', fooLacksExit)
def assertRaisesSyntaxError(self, codestr):
def shouldRaiseSyntaxError(s):

View File

@ -12,6 +12,10 @@ Core and Builtins
- Issue #28532: Show sys.version when -V option is supplied twice.
- Issue #27100: The with-statement now checks for __enter__ before it
checks for __exit__. This gives less confusing error messages when
both methods are missing. Patch by Jonathan Ellington.
- Issue #28746: Fix the set_inheritable() file descriptor method on platforms
that do not have the ioctl FIOCLEX and FIONCLEX commands.

View File

@ -3133,15 +3133,15 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
_Py_IDENTIFIER(__exit__);
_Py_IDENTIFIER(__enter__);
PyObject *mgr = TOP();
PyObject *exit = special_lookup(mgr, &PyId___exit__), *enter;
PyObject *enter = special_lookup(mgr, &PyId___enter__), *exit;
PyObject *res;
if (enter == NULL)
goto error;
exit = special_lookup(mgr, &PyId___exit__);
if (exit == NULL)
goto error;
SET_TOP(exit);
enter = special_lookup(mgr, &PyId___enter__);
Py_DECREF(mgr);
if (enter == NULL)
goto error;
res = PyObject_CallFunctionObjArgs(enter, NULL);
Py_DECREF(enter);
if (res == NULL)