From da5b701aeef755f2317a41e36cc950cfdc0c95cb Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 2 May 2006 19:47:52 +0000 Subject: [PATCH] Get rid of __context__, per the latest changes to PEP 343 and python-dev discussion. There are two places of documentation that still mention __context__: Doc/lib/libstdtypes.tex -- I wasn't quite sure how to rewrite that without spending a whole lot of time thinking about it; and whatsnew, which Andrew usually likes to change himself. --- Doc/lib/libcontextlib.tex | 47 --------------------- Doc/ref/ref3.tex | 16 -------- Doc/ref/ref7.tex | 13 +++--- Lib/calendar.py | 3 -- Lib/compiler/pycodegen.py | 2 - Lib/contextlib.py | 10 +---- Lib/decimal.py | 2 +- Lib/dummy_thread.py | 3 -- Lib/test/test_contextlib.py | 10 ++--- Lib/test/test_with.py | 81 ++++--------------------------------- Lib/threading.py | 13 +++--- Misc/Vim/syntax_test.py | 2 - Modules/threadmodule.c | 9 ----- Objects/fileobject.c | 5 --- Python/compile.c | 13 ++---- 15 files changed, 27 insertions(+), 202 deletions(-) diff --git a/Doc/lib/libcontextlib.tex b/Doc/lib/libcontextlib.tex index 9ff85249d91..6c80a716231 100644 --- a/Doc/lib/libcontextlib.tex +++ b/Doc/lib/libcontextlib.tex @@ -54,34 +54,6 @@ action (rather than to suppress it entirely), the generator must reraise that exception. Otherwise the \keyword{with} statement will treat the exception as having been handled, and resume execution with the statement immediately following the \keyword{with} statement. - -Note that you can use \code{@contextfactory} to define a context -manager's \method{__context__} method. This is usually more -convenient than creating another class just to serve as a context -object. For example: - -\begin{verbatim} -from __future__ import with_statement -from contextlib import contextfactory - -class Tag: - def __init__(self, name): - self.name = name - - @contextfactory - def __context__(self): - print "<%s>" % self.name - yield self - print "" % self.name - -h1 = Tag("h1") - ->>> with h1 as me: -... print "hello from", me -

-hello from <__main__.Tag instance at 0x402ce8ec> -

-\end{verbatim} \end{funcdesc} \begin{funcdesc}{nested}{ctx1\optional{, ctx2\optional{, ...}}} @@ -147,25 +119,6 @@ with closing(urllib.urlopen('http://www.python.org')) as page: without needing to explicitly close \code{page}. Even if an error occurs, \code{page.close()} will be called when the \keyword{with} block is exited. - -Context managers with a close method can use this context factory -to easily implement their own \method{__context__()} method. -\begin{verbatim} -from __future__ import with_statement -from contextlib import closing - -class MyClass: - def close(self): - print "Closing", self - def __context__(self): - return closing(self) - ->>> with MyClass() as x: -... print "Hello from", x -... -Hello from <__main__.MyClass instance at 0xb7df02ec> -Closing <__main__.MyClass instance at 0xb7df02ec> -\end{verbatim} \end{funcdesc} \begin{seealso} diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex index d22448c15ea..ba0594f0df8 100644 --- a/Doc/ref/ref3.tex +++ b/Doc/ref/ref3.tex @@ -2138,22 +2138,6 @@ For more information on context managers and context objects, see ``\ulink{Context Types}{../lib/typecontext.html}'' in the \citetitle[../lib/lib.html]{Python Library Reference}. -\begin{methoddesc}[context manager]{__context__}{self} -Invoked when the object is used as the context expression of a -\keyword{with} statement. The returned object must implement -\method{__enter__()} and \method{__exit__()} methods. - -Context managers written in Python can also implement this method -using a generator function decorated with the -\function{contextlib.contextfactory} decorator, as this can be simpler -than writing individual \method{__enter__()} and \method{__exit__()} -methods on a separate object when the state to be managed is complex. - -\keyword{with} statement context objects also need to implement this -method; they are required to return themselves (that is, this method -will simply return \var{self}). -\end{methoddesc} - \begin{methoddesc}[with statement context]{__enter__}{self} Enter the runtime context related to this object. The \keyword{with} statement will bind this method's return value to the target(s) diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex index 4453e87c84a..4a231332e58 100644 --- a/Doc/ref/ref7.tex +++ b/Doc/ref/ref7.tex @@ -322,21 +322,18 @@ be encapsulated for convenient reuse. \begin{productionlist} \production{with_stmt} - {"with" \token{expression} ["as" target_list] ":" \token{suite}} + {"with" \token{expression} ["as" target] ":" \token{suite}} \end{productionlist} The execution of the \keyword{with} statement proceeds as follows: \begin{enumerate} -\item The context expression is evaluated, to obtain a context manager. +\item The context expression is evaluated to obtain a context manager. -\item The context manger's \method{__context__()} method is -invoked to obtain a \keyword{with} statement context object. +\item The context manager's \method{__enter__()} method is invoked. -\item The context object's \method{__enter__()} method is invoked. - -\item If a target list was included in the \keyword{with} +\item If a target was included in the \keyword{with} statement, the return value from \method{__enter__()} is assigned to it. \note{The \keyword{with} statement guarantees that if the @@ -347,7 +344,7 @@ an error occurring within the suite would be. See step 6 below.} \item The suite is executed. -\item The context object's \method{__exit__()} method is invoked. If +\item The context manager's \method{__exit__()} method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to \method{__exit__()}. Otherwise, three \constant{None} arguments are supplied. diff --git a/Lib/calendar.py b/Lib/calendar.py index 7800aaeaf96..00948efe5c0 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -484,9 +484,6 @@ class TimeEncoding: def __init__(self, locale): self.locale = locale - def __context__(self): - return self - def __enter__(self): self.oldlocale = locale.setlocale(locale.LC_TIME, self.locale) return locale.getlocale(locale.LC_TIME)[1] diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index f25b3fbf5f8..d5d68aa29ef 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -833,8 +833,6 @@ class CodeGenerator: self.__with_count += 1 self.set_lineno(node) self.visit(node.expr) - self.emit('LOAD_ATTR', '__context__') - self.emit('CALL_FUNCTION', 0) self.emit('DUP_TOP') self.emit('LOAD_ATTR', '__exit__') self._implicitNameOp('STORE', exitvar) diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 4e3b9c2d389..9d2c6a3a144 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -10,9 +10,6 @@ class GeneratorContext(object): def __init__(self, gen): self.gen = gen - def __context__(self): - return self - def __enter__(self): try: return self.gen.next() @@ -88,7 +85,7 @@ def contextfactory(func): @contextfactory -def nested(*contexts): +def nested(*managers): """Support multiple context managers in a single with-statement. Code like this: @@ -109,8 +106,7 @@ def nested(*contexts): exc = (None, None, None) try: try: - for context in contexts: - mgr = context.__context__() + for mgr in managers: exit = mgr.__exit__ enter = mgr.__enter__ vars.append(enter()) @@ -152,8 +148,6 @@ class closing(object): """ def __init__(self, thing): self.thing = thing - def __context__(self): - return self def __enter__(self): return self.thing def __exit__(self, *exc_info): diff --git a/Lib/decimal.py b/Lib/decimal.py index 875e38a78ff..2f989a8524e 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -2248,7 +2248,7 @@ class Context(object): s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']') return ', '.join(s) + ')' - def __context__(self): + def context_manager(self): return WithStatementContext(self.copy()) def clear_flags(self): diff --git a/Lib/dummy_thread.py b/Lib/dummy_thread.py index d69d84053d8..21fd03f27f8 100644 --- a/Lib/dummy_thread.py +++ b/Lib/dummy_thread.py @@ -118,9 +118,6 @@ class LockType(object): def __exit__(self, typ, val, tb): self.release() - def __context__(self): - return self - def release(self): """Release the dummy lock.""" # XXX Perhaps shouldn't actually bother to test? Could lead diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 53f23b27118..8d3dd1a72e8 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -51,7 +51,7 @@ class ContextManagerTestCase(unittest.TestCase): @contextfactory def whee(): yield - ctx = whee().__context__() + ctx = whee() ctx.__enter__() # Calling __exit__ should not result in an exception self.failIf(ctx.__exit__(TypeError, TypeError("foo"), None)) @@ -63,7 +63,7 @@ class ContextManagerTestCase(unittest.TestCase): yield except: yield - ctx = whoo().__context__() + ctx = whoo() ctx.__enter__() self.assertRaises( RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None @@ -152,8 +152,6 @@ class NestedTestCase(unittest.TestCase): def a(): yield 1 class b(object): - def __context__(self): - return self def __enter__(self): return 2 def __exit__(self, *exc_info): @@ -341,12 +339,12 @@ class DecimalContextTestCase(unittest.TestCase): orig_context = ctx.copy() try: ctx.prec = save_prec = decimal.ExtendedContext.prec + 5 - with decimal.ExtendedContext: + with decimal.ExtendedContext.context_manager(): self.assertEqual(decimal.getcontext().prec, decimal.ExtendedContext.prec) self.assertEqual(decimal.getcontext().prec, save_prec) try: - with decimal.ExtendedContext: + with decimal.ExtendedContext.context_manager(): self.assertEqual(decimal.getcontext().prec, decimal.ExtendedContext.prec) 1/0 diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index 7adb05eeb18..765bfec3979 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -17,15 +17,10 @@ from test.test_support import run_unittest class MockContextManager(GeneratorContext): def __init__(self, gen): GeneratorContext.__init__(self, gen) - self.context_called = False self.enter_called = False self.exit_called = False self.exit_args = None - def __context__(self): - self.context_called = True - return GeneratorContext.__context__(self) - def __enter__(self): self.enter_called = True return GeneratorContext.__enter__(self) @@ -60,21 +55,17 @@ def mock_contextmanager_generator(): class Nested(object): - def __init__(self, *contexts): - self.contexts = contexts + def __init__(self, *managers): + self.managers = managers self.entered = None - def __context__(self): - return self - def __enter__(self): if self.entered is not None: raise RuntimeError("Context is not reentrant") self.entered = deque() vars = [] try: - for context in self.contexts: - mgr = context.__context__() + for mgr in self.managers: vars.append(mgr.__enter__()) self.entered.appendleft(mgr) except: @@ -99,17 +90,12 @@ class Nested(object): class MockNested(Nested): - def __init__(self, *contexts): - Nested.__init__(self, *contexts) - self.context_called = False + def __init__(self, *managers): + Nested.__init__(self, *managers) self.enter_called = False self.exit_called = False self.exit_args = None - def __context__(self): - self.context_called = True - return Nested.__context__(self) - def __enter__(self): self.enter_called = True return Nested.__enter__(self) @@ -126,24 +112,8 @@ class FailureTestCase(unittest.TestCase): with foo: pass self.assertRaises(NameError, fooNotDeclared) - def testContextAttributeError(self): - class LacksContext(object): - def __enter__(self): - pass - - def __exit__(self, type, value, traceback): - pass - - def fooLacksContext(): - foo = LacksContext() - with foo: pass - self.assertRaises(AttributeError, fooLacksContext) - def testEnterAttributeError(self): class LacksEnter(object): - def __context__(self): - pass - def __exit__(self, type, value, traceback): pass @@ -154,9 +124,6 @@ class FailureTestCase(unittest.TestCase): def testExitAttributeError(self): class LacksExit(object): - def __context__(self): - pass - def __enter__(self): pass @@ -192,27 +159,10 @@ class FailureTestCase(unittest.TestCase): 'with mock as (foo, None, bar):\n' ' pass') - def testContextThrows(self): - class ContextThrows(object): - def __context__(self): - raise RuntimeError("Context threw") - - def shouldThrow(): - ct = ContextThrows() - self.foo = None - with ct as self.foo: - pass - self.assertRaises(RuntimeError, shouldThrow) - self.assertEqual(self.foo, None) - def testEnterThrows(self): class EnterThrows(object): - def __context__(self): - return self - def __enter__(self): - raise RuntimeError("Context threw") - + raise RuntimeError("Enter threw") def __exit__(self, *args): pass @@ -226,8 +176,6 @@ class FailureTestCase(unittest.TestCase): def testExitThrows(self): class ExitThrows(object): - def __context__(self): - return self def __enter__(self): return def __exit__(self, *args): @@ -241,13 +189,11 @@ class ContextmanagerAssertionMixin(object): TEST_EXCEPTION = RuntimeError("test exception") def assertInWithManagerInvariants(self, mock_manager): - self.assertTrue(mock_manager.context_called) self.assertTrue(mock_manager.enter_called) self.assertFalse(mock_manager.exit_called) self.assertEqual(mock_manager.exit_args, None) def assertAfterWithManagerInvariants(self, mock_manager, exit_args): - self.assertTrue(mock_manager.context_called) self.assertTrue(mock_manager.enter_called) self.assertTrue(mock_manager.exit_called) self.assertEqual(mock_manager.exit_args, exit_args) @@ -268,7 +214,6 @@ class ContextmanagerAssertionMixin(object): raise self.TEST_EXCEPTION def assertAfterWithManagerInvariantsWithError(self, mock_manager): - self.assertTrue(mock_manager.context_called) self.assertTrue(mock_manager.enter_called) self.assertTrue(mock_manager.exit_called) self.assertEqual(mock_manager.exit_args[0], RuntimeError) @@ -472,7 +417,6 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin): # The inner statement stuff should never have been touched self.assertEqual(self.bar, None) - self.assertFalse(mock_b.context_called) self.assertFalse(mock_b.enter_called) self.assertFalse(mock_b.exit_called) self.assertEqual(mock_b.exit_args, None) @@ -506,13 +450,9 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin): self.assertRaises(StopIteration, shouldThrow) def testRaisedStopIteration2(self): - class cm (object): - def __context__(self): - return self - + class cm(object): def __enter__(self): pass - def __exit__(self, type, value, traceback): pass @@ -535,12 +475,8 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin): def testRaisedGeneratorExit2(self): class cm (object): - def __context__(self): - return self - def __enter__(self): pass - def __exit__(self, type, value, traceback): pass @@ -629,7 +565,6 @@ class AssignmentTargetTestCase(unittest.TestCase): def testMultipleComplexTargets(self): class C: - def __context__(self): return self def __enter__(self): return 1, 2, 3 def __exit__(self, t, v, tb): pass targets = {1: [0, 1, 2]} @@ -651,7 +586,6 @@ class ExitSwallowsExceptionTestCase(unittest.TestCase): def testExitTrueSwallowsException(self): class AfricanSwallow: - def __context__(self): return self def __enter__(self): pass def __exit__(self, t, v, tb): return True try: @@ -662,7 +596,6 @@ class ExitSwallowsExceptionTestCase(unittest.TestCase): def testExitFalseDoesntSwallowException(self): class EuropeanSwallow: - def __context__(self): return self def __enter__(self): pass def __exit__(self, t, v, tb): return False try: diff --git a/Lib/threading.py b/Lib/threading.py index 27ec6b402d6..c27140d76ed 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -90,9 +90,6 @@ class _RLock(_Verbose): self.__owner and self.__owner.getName(), self.__count) - def __context__(self): - return self - def acquire(self, blocking=1): me = currentThread() if self.__owner is me: @@ -182,8 +179,11 @@ class _Condition(_Verbose): pass self.__waiters = [] - def __context__(self): - return self.__lock.__context__() + def __enter__(self): + return self.__lock.__enter__() + + def __exit__(self, *args): + return self.__lock.__exit__(*args) def __repr__(self): return "" % (self.__lock, len(self.__waiters)) @@ -278,9 +278,6 @@ class _Semaphore(_Verbose): self.__cond = Condition(Lock()) self.__value = value - def __context__(self): - return self - def acquire(self, blocking=1): rc = False self.__cond.acquire() diff --git a/Misc/Vim/syntax_test.py b/Misc/Vim/syntax_test.py index a530a25364f..ccc7f309c61 100644 --- a/Misc/Vim/syntax_test.py +++ b/Misc/Vim/syntax_test.py @@ -19,8 +19,6 @@ assert True # keyword def foo(): # function definition return [] class Bar(object): # Class definition - def __context__(self): - return self def __enter__(self): pass def __exit__(self, *args): diff --git a/Modules/threadmodule.c b/Modules/threadmodule.c index 83313df787e..9ac9881d9c1 100644 --- a/Modules/threadmodule.c +++ b/Modules/threadmodule.c @@ -98,13 +98,6 @@ PyDoc_STRVAR(locked_doc, \n\ Return whether the lock is in the locked state."); -static PyObject * -lock_context(lockobject *self) -{ - Py_INCREF(self); - return (PyObject *)self; -} - static PyMethodDef lock_methods[] = { {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, METH_VARARGS, acquire_doc}, @@ -118,8 +111,6 @@ static PyMethodDef lock_methods[] = { METH_NOARGS, locked_doc}, {"locked", (PyCFunction)lock_locked_lock, METH_NOARGS, locked_doc}, - {"__context__", (PyCFunction)lock_context, - METH_NOARGS, PyDoc_STR("__context__() -> self.")}, {"__enter__", (PyCFunction)lock_PyThread_acquire_lock, METH_VARARGS, acquire_doc}, {"__exit__", (PyCFunction)lock_PyThread_release_lock, diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 25b33617668..0f166cdb6c4 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -1706,9 +1706,6 @@ PyDoc_STRVAR(close_doc, PyDoc_STRVAR(isatty_doc, "isatty() -> true or false. True if the file is connected to a tty device."); -PyDoc_STRVAR(context_doc, - "__context__() -> self."); - PyDoc_STRVAR(enter_doc, "__enter__() -> self."); @@ -1729,7 +1726,6 @@ static PyMethodDef file_methods[] = { {"flush", (PyCFunction)file_flush, METH_NOARGS, flush_doc}, {"close", (PyCFunction)file_close, METH_NOARGS, close_doc}, {"isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc}, - {"__context__", (PyCFunction)file_self, METH_NOARGS, context_doc}, {"__enter__", (PyCFunction)file_self, METH_NOARGS, enter_doc}, {"__exit__", (PyCFunction)file_close, METH_VARARGS, close_doc}, {NULL, NULL} /* sentinel */ @@ -2445,4 +2441,3 @@ Py_UniversalNewlineFread(char *buf, size_t n, #ifdef __cplusplus } #endif - diff --git a/Python/compile.c b/Python/compile.c index 8b6f2f1c08e..15e7e158118 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3371,7 +3371,7 @@ expr_constant(expr_ty e) It is implemented roughly as: - context = (EXPR).__context__() + context = EXPR exit = context.__exit__ # not calling it value = context.__enter__() try: @@ -3387,17 +3387,12 @@ expr_constant(expr_ty e) static int compiler_with(struct compiler *c, stmt_ty s) { - static identifier context_attr, enter_attr, exit_attr; + static identifier enter_attr, exit_attr; basicblock *block, *finally; identifier tmpexit, tmpvalue = NULL; assert(s->kind == With_kind); - if (!context_attr) { - context_attr = PyString_InternFromString("__context__"); - if (!context_attr) - return 0; - } if (!enter_attr) { enter_attr = PyString_InternFromString("__enter__"); if (!enter_attr) @@ -3436,10 +3431,8 @@ compiler_with(struct compiler *c, stmt_ty s) PyArena_AddPyObject(c->c_arena, tmpvalue); } - /* Evaluate (EXPR).__context__() */ + /* Evaluate EXPR */ VISIT(c, expr, s->v.With.context_expr); - ADDOP_O(c, LOAD_ATTR, context_attr, names); - ADDOP_I(c, CALL_FUNCTION, 0); /* Squirrel away context.__exit__ */ ADDOP(c, DUP_TOP);