]> granicus.if.org Git - python/commitdiff
Get rid of __context__, per the latest changes to PEP 343 and python-dev
authorGuido van Rossum <guido@python.org>
Tue, 2 May 2006 19:47:52 +0000 (19:47 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 2 May 2006 19:47:52 +0000 (19:47 +0000)
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.

15 files changed:
Doc/lib/libcontextlib.tex
Doc/ref/ref3.tex
Doc/ref/ref7.tex
Lib/calendar.py
Lib/compiler/pycodegen.py
Lib/contextlib.py
Lib/decimal.py
Lib/dummy_thread.py
Lib/test/test_contextlib.py
Lib/test/test_with.py
Lib/threading.py
Misc/Vim/syntax_test.py
Modules/threadmodule.c
Objects/fileobject.c
Python/compile.c

index 9ff85249d911f2e58ea835fcfa401e62490e3362..6c80a7162315606bc12fa70f593b774c2127e1c9 100644 (file)
@@ -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 "</%s>" % self.name
-        
-h1 = Tag("h1")
-
->>> with h1 as me:
-...     print "hello from", me
-<h1>
-hello from <__main__.Tag instance at 0x402ce8ec>
-</h1>
-\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}
index d22448c15eac1d610fc2b674868033a40c2a307c..ba0594f0df87ceacd223a7df60691033ce0be912 100644 (file)
@@ -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)
index 4453e87c84a4f6f51bf62989095f1c5fe78afb63..4a231332e58b9e80bb20f5cd0c386dbbbcd68851 100644 (file)
@@ -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.
index 7800aaeaf96a19547feb27144d2c54f6d7af13d9..00948efe5c084ea059158567377d6000ffc21b10 100644 (file)
@@ -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]
index f25b3fbf5f830671d2b90d0cedafa514321d3b8f..d5d68aa29ef0ed338034f921e670f56402f7eb74 100644 (file)
@@ -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)
index 4e3b9c2d389ed3bf102c8fdc27abc832e384189d..9d2c6a3a144fa7e6e93107463b91d6018f5ef32b 100644 (file)
@@ -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):
index 875e38a78ff6160d14999e6ea1826ebb2c77ce8f..2f989a8524e18d011875a3247db6f609c61b5656 100644 (file)
@@ -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):
index d69d84053d841b5d9885c4e8b47c40f716ab125b..21fd03f27f80bd1746ec8b3d1f01fd025af9027b 100644 (file)
@@ -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
index 53f23b27118801e7c57aba7c9da0d0ba1a044699..8d3dd1a72e8b89310f67337192498e91ad5a284e 100644 (file)
@@ -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
index 7adb05eeb18510af35b08ab730e02cd2b7ee192a..765bfec397933a335e551779caeef7a2e5b64736 100644 (file)
@@ -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:
index 27ec6b402d6fa967ce63843fc1bb17a15816403c..c27140d76edc55478bcad14a1179efaf41cae16a 100644 (file)
@@ -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 "<Condition(%s, %d)>" % (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()
index a530a25364f12f89f73dbaa9f6ab72e9a440af60..ccc7f309c611121e31dccaa66915a26e944f44f1 100644 (file)
@@ -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):
index 83313df787e3703ceb2a088b85e9312fb2673db0..9ac9881d9c19afdd3d4848014dc4fa2691fa72f6 100644 (file)
@@ -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,
index 25b336176685c15e340cd83dcc5b05de95c3d753..0f166cdb6c432f4a9817009743c9aa1d6cb65b34 100644 (file)
@@ -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
-
index 8b6f2f1c08e7742f8da704141c3223f282c7dbff..15e7e158118f9c19d3ec8f793bc622f898e98161 100644 (file)
@@ -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);