1067 lines
90 KiB
HTML
1067 lines
90 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 343 – The “with” Statement | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0343/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 343 – The “with” Statement | peps.python.org'>
|
||
<meta property="og:description" content="This PEP adds a new statement “with” to the Python language to make it possible to factor out standard uses of try/finally statements.">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0343/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="This PEP adds a new statement “with” to the Python language to make it possible to factor out standard uses of try/finally statements.">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 343</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 343 – The “with” Statement</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Guido van Rossum, Alyssa Coghlan</dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Created<span class="colon">:</span></dt>
|
||
<dd class="field-even">13-May-2005</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">2.5</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">02-Jun-2005, 16-Oct-2005, 29-Oct-2005, 23-Apr-2006, 01-May-2006,
|
||
30-Jul-2006</dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#author-s-note">Author’s Note</a></li>
|
||
<li><a class="reference internal" href="#introduction">Introduction</a></li>
|
||
<li><a class="reference internal" href="#motivation-and-summary">Motivation and Summary</a></li>
|
||
<li><a class="reference internal" href="#use-cases">Use Cases</a></li>
|
||
<li><a class="reference internal" href="#specification-the-with-statement">Specification: The ‘with’ Statement</a></li>
|
||
<li><a class="reference internal" href="#transition-plan">Transition Plan</a></li>
|
||
<li><a class="reference internal" href="#generator-decorator">Generator Decorator</a></li>
|
||
<li><a class="reference internal" href="#context-managers-in-the-standard-library">Context Managers in the Standard Library</a></li>
|
||
<li><a class="reference internal" href="#standard-terminology">Standard Terminology</a></li>
|
||
<li><a class="reference internal" href="#caching-context-managers">Caching Context Managers</a></li>
|
||
<li><a class="reference internal" href="#resolved-issues">Resolved Issues</a></li>
|
||
<li><a class="reference internal" href="#rejected-options">Rejected Options</a></li>
|
||
<li><a class="reference internal" href="#examples">Examples</a></li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP adds a new statement “with” to the Python language to make
|
||
it possible to factor out standard uses of <code class="docutils literal notranslate"><span class="pre">try/finally</span></code> statements.</p>
|
||
<p>In this PEP, context managers provide <code class="docutils literal notranslate"><span class="pre">__enter__()</span></code> and <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code>
|
||
methods that are invoked on entry to and exit from the body of the
|
||
with statement.</p>
|
||
</section>
|
||
<section id="author-s-note">
|
||
<h2><a class="toc-backref" href="#author-s-note" role="doc-backlink">Author’s Note</a></h2>
|
||
<p>This PEP was originally written in first person by Guido, and
|
||
subsequently updated by Alyssa (Nick) Coghlan to reflect later discussion
|
||
on python-dev. Any first person references are from Guido’s
|
||
original.</p>
|
||
<p>Python’s alpha release cycle revealed terminology problems in this
|
||
PEP and in the associated documentation and implementation <a class="footnote-reference brackets" href="#id29" id="id1">[13]</a>.
|
||
The PEP stabilised around the time of the first Python 2.5 beta
|
||
release.</p>
|
||
<p>Yes, the verb tense is messed up in a few places. We’ve been
|
||
working on this PEP for over a year now, so things that were
|
||
originally in the future are now in the past :)</p>
|
||
</section>
|
||
<section id="introduction">
|
||
<h2><a class="toc-backref" href="#introduction" role="doc-backlink">Introduction</a></h2>
|
||
<p>After a lot of discussion about <a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a> and alternatives, I
|
||
decided to withdraw <a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a> and proposed a slight variant on PEP
|
||
310. After more discussion, I have added back a mechanism for
|
||
raising an exception in a suspended generator using a <code class="docutils literal notranslate"><span class="pre">throw()</span></code>
|
||
method, and a <code class="docutils literal notranslate"><span class="pre">close()</span></code> method which throws a new GeneratorExit
|
||
exception; these additions were first proposed on python-dev in
|
||
<a class="footnote-reference brackets" href="#id18" id="id2">[2]</a> and universally approved of. I’m also changing the keyword to
|
||
‘with’.</p>
|
||
<p>After acceptance of this PEP, the following PEPs were rejected due
|
||
to overlap:</p>
|
||
<ul class="simple">
|
||
<li><a class="pep reference internal" href="../pep-0310/" title="PEP 310 – Reliable Acquisition/Release Pairs">PEP 310</a>, Reliable Acquisition/Release Pairs. This is the
|
||
original with-statement proposal.</li>
|
||
<li><a class="pep reference internal" href="../pep-0319/" title="PEP 319 – Python Synchronize/Asynchronize Block">PEP 319</a>, Python Synchronize/Asynchronize Block. Its use cases
|
||
can be covered by the current PEP by providing suitable
|
||
with-statement controllers: for ‘synchronize’ we can use the
|
||
“locking” template from example 1; for ‘asynchronize’ we can use
|
||
a similar “unlocking” template. I don’t think having an
|
||
“anonymous” lock associated with a code block is all that
|
||
important; in fact it may be better to always be explicit about
|
||
the mutex being used.</li>
|
||
</ul>
|
||
<p><a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a> and <a class="pep reference internal" href="../pep-0346/" title="PEP 346 – User Defined (”with”) Statements">PEP 346</a> also overlapped with this PEP, but were
|
||
voluntarily withdrawn when this PEP was submitted.</p>
|
||
<p>Some discussion of earlier incarnations of this PEP took place on
|
||
the Python Wiki <a class="footnote-reference brackets" href="#id19" id="id3">[3]</a>.</p>
|
||
</section>
|
||
<section id="motivation-and-summary">
|
||
<h2><a class="toc-backref" href="#motivation-and-summary" role="doc-backlink">Motivation and Summary</a></h2>
|
||
<p><a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a>, Anonymous Block Statements, combined many powerful ideas:
|
||
using generators as block templates, adding exception handling and
|
||
finalization to generators, and more. Besides praise it received
|
||
a lot of opposition from people who didn’t like the fact that it
|
||
was, under the covers, a (potential) looping construct. This
|
||
meant that break and continue in a block-statement would break or
|
||
continue the block-statement, even if it was used as a non-looping
|
||
resource management tool.</p>
|
||
<p>But the final blow came when I read Raymond Chen’s rant about
|
||
flow-control macros <a class="footnote-reference brackets" href="#id17" id="id4">[1]</a>. Raymond argues convincingly that hiding
|
||
flow control in macros makes your code inscrutable, and I find
|
||
that his argument applies to Python as well as to C. I realized
|
||
that <a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a> templates can hide all sorts of control flow; for
|
||
example, its example 4 (<code class="docutils literal notranslate"><span class="pre">auto_retry()</span></code>) catches exceptions and
|
||
repeats the block up to three times.</p>
|
||
<p>However, the with-statement of <a class="pep reference internal" href="../pep-0310/" title="PEP 310 – Reliable Acquisition/Release Pairs">PEP 310</a> does <strong>not</strong> hide control
|
||
flow, in my view: while a finally-suite temporarily suspends the
|
||
control flow, in the end, the control flow resumes as if the
|
||
finally-suite wasn’t there at all.</p>
|
||
<p>Remember, <a class="pep reference internal" href="../pep-0310/" title="PEP 310 – Reliable Acquisition/Release Pairs">PEP 310</a> proposes roughly this syntax (the “VAR =” part is
|
||
optional):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">VAR</span> <span class="o">=</span> <span class="n">EXPR</span><span class="p">:</span>
|
||
<span class="n">BLOCK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>which roughly translates into this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">VAR</span> <span class="o">=</span> <span class="n">EXPR</span>
|
||
<span class="n">VAR</span><span class="o">.</span><span class="fm">__enter__</span><span class="p">()</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">BLOCK</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">VAR</span><span class="o">.</span><span class="fm">__exit__</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Now consider this example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"/etc/passwd"</span><span class="p">):</span>
|
||
<span class="n">BLOCK1</span>
|
||
<span class="n">BLOCK2</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here, just as if the first line was “if True” instead, we know
|
||
that if <code class="docutils literal notranslate"><span class="pre">BLOCK1</span></code> completes without an exception, BLOCK2 will be
|
||
reached; and if <code class="docutils literal notranslate"><span class="pre">BLOCK1</span></code> raises an exception or executes a non-local
|
||
goto (a break, continue or return), <code class="docutils literal notranslate"><span class="pre">BLOCK2</span></code> is <strong>not</strong> reached. The
|
||
magic added by the with-statement at the end doesn’t affect this.</p>
|
||
<p>(You may ask, what if a bug in the <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> method causes an
|
||
exception? Then all is lost – but this is no worse than with
|
||
other exceptions; the nature of exceptions is that they can happen
|
||
<strong>anywhere</strong>, and you just have to live with that. Even if you
|
||
write bug-free code, a KeyboardInterrupt exception can still cause
|
||
it to exit between any two virtual machine opcodes.)</p>
|
||
<p>This argument almost led me to endorse <a class="pep reference internal" href="../pep-0310/" title="PEP 310 – Reliable Acquisition/Release Pairs">PEP 310</a>, but I had one idea
|
||
left from the <a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a> euphoria that I wasn’t ready to drop: using
|
||
generators as “templates” for abstractions like acquiring and
|
||
releasing a lock or opening and closing a file is a powerful idea,
|
||
as can be seen by looking at the examples in that PEP.</p>
|
||
<p>Inspired by a counter-proposal to <a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a> by Phillip Eby I tried
|
||
to create a decorator that would turn a suitable generator into an
|
||
object with the necessary <code class="docutils literal notranslate"><span class="pre">__enter__()</span></code> and <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> methods.
|
||
Here I ran into a snag: while it wasn’t too hard for the locking
|
||
example, it was impossible to do this for the opening example.
|
||
The idea was to define the template like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">opening</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">f</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>and used it like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">f</span> <span class="o">=</span> <span class="n">opening</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
|
||
<span class="o">...</span><span class="n">read</span> <span class="n">data</span> <span class="kn">from</span> <span class="nn">f...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The problem is that in <a class="pep reference internal" href="../pep-0310/" title="PEP 310 – Reliable Acquisition/Release Pairs">PEP 310</a>, the result of calling <code class="docutils literal notranslate"><span class="pre">EXPR</span></code> is
|
||
assigned directly to <code class="docutils literal notranslate"><span class="pre">VAR</span></code>, and then <code class="docutils literal notranslate"><span class="pre">VAR</span></code>’s <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> method is
|
||
called upon exit from <code class="docutils literal notranslate"><span class="pre">BLOCK1</span></code>. But here, <code class="docutils literal notranslate"><span class="pre">VAR</span></code> clearly needs to
|
||
receive the opened file, and that would mean that <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> would
|
||
have to be a method on the file.</p>
|
||
<p>While this can be solved using a proxy class, this is awkward and
|
||
made me realize that a slightly different translation would make
|
||
writing the desired decorator a piece of cake: let <code class="docutils literal notranslate"><span class="pre">VAR</span></code> receive the
|
||
result from calling the <code class="docutils literal notranslate"><span class="pre">__enter__()</span></code> method, and save the value of
|
||
<code class="docutils literal notranslate"><span class="pre">EXPR</span></code> to call its <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> method later. Then the decorator can
|
||
return an instance of a wrapper class whose <code class="docutils literal notranslate"><span class="pre">__enter__()</span></code> method
|
||
calls the generator’s <code class="docutils literal notranslate"><span class="pre">next()</span></code> method and returns whatever <code class="docutils literal notranslate"><span class="pre">next()</span></code>
|
||
returns; the wrapper instance’s <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> method calls <code class="docutils literal notranslate"><span class="pre">next()</span></code>
|
||
again but expects it to raise StopIteration. (Details below in
|
||
the section Optional Generator Decorator.)</p>
|
||
<p>So now the final hurdle was that the <a class="pep reference internal" href="../pep-0310/" title="PEP 310 – Reliable Acquisition/Release Pairs">PEP 310</a> syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">VAR</span> <span class="o">=</span> <span class="n">EXPR</span><span class="p">:</span>
|
||
<span class="n">BLOCK1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>would be deceptive, since <code class="docutils literal notranslate"><span class="pre">VAR</span></code> does <strong>not</strong> receive the value of
|
||
<code class="docutils literal notranslate"><span class="pre">EXPR</span></code>. Borrowing from <a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a>, it was an easy step to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">EXPR</span> <span class="k">as</span> <span class="n">VAR</span><span class="p">:</span>
|
||
<span class="n">BLOCK1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Additional discussion showed that people really liked being able
|
||
to “see” the exception in the generator, even if it was only to
|
||
log it; the generator is not allowed to yield another value, since
|
||
the with-statement should not be usable as a loop (raising a
|
||
different exception is marginally acceptable). To enable this, a
|
||
new <code class="docutils literal notranslate"><span class="pre">throw()</span></code> method for generators is proposed, which takes one to
|
||
three arguments representing an exception in the usual fashion
|
||
(type, value, traceback) and raises it at the point where the
|
||
generator is suspended.</p>
|
||
<p>Once we have this, it is a small step to proposing another
|
||
generator method, <code class="docutils literal notranslate"><span class="pre">close()</span></code>, which calls <code class="docutils literal notranslate"><span class="pre">throw()</span></code> with a special
|
||
exception, <code class="docutils literal notranslate"><span class="pre">GeneratorExit</span></code>. This tells the generator to exit, and
|
||
from there it’s another small step to proposing that <code class="docutils literal notranslate"><span class="pre">close()</span></code> be
|
||
called automatically when the generator is garbage-collected.</p>
|
||
<p>Then, finally, we can allow a yield-statement inside a try-finally
|
||
statement, since we can now guarantee that the finally-clause will
|
||
(eventually) be executed. The usual cautions about finalization
|
||
apply – the process may be terminated abruptly without finalizing
|
||
any objects, and objects may be kept alive forever by cycles or
|
||
memory leaks in the application (as opposed to cycles or leaks in
|
||
the Python implementation, which are taken care of by GC).</p>
|
||
<p>Note that we’re not guaranteeing that the finally-clause is
|
||
executed immediately after the generator object becomes unused,
|
||
even though this is how it will work in CPython. This is similar
|
||
to auto-closing files: while a reference-counting implementation
|
||
like CPython deallocates an object as soon as the last reference
|
||
to it goes away, implementations that use other GC algorithms do
|
||
not make the same guarantee. This applies to Jython, IronPython,
|
||
and probably to Python running on Parrot.</p>
|
||
<p>(The details of the changes made to generators can now be found in
|
||
<a class="pep reference internal" href="../pep-0342/" title="PEP 342 – Coroutines via Enhanced Generators">PEP 342</a> rather than in the current PEP)</p>
|
||
</section>
|
||
<section id="use-cases">
|
||
<h2><a class="toc-backref" href="#use-cases" role="doc-backlink">Use Cases</a></h2>
|
||
<p>See the Examples section near the end.</p>
|
||
</section>
|
||
<section id="specification-the-with-statement">
|
||
<h2><a class="toc-backref" href="#specification-the-with-statement" role="doc-backlink">Specification: The ‘with’ Statement</a></h2>
|
||
<p>A new statement is proposed with the syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">EXPR</span> <span class="k">as</span> <span class="n">VAR</span><span class="p">:</span>
|
||
<span class="n">BLOCK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here, ‘with’ and ‘as’ are new keywords; <code class="docutils literal notranslate"><span class="pre">EXPR</span></code> is an arbitrary
|
||
expression (but not an expression-list) and <code class="docutils literal notranslate"><span class="pre">VAR</span></code> is a single
|
||
assignment target. It can <strong>not</strong> be a comma-separated sequence of
|
||
variables, but it <strong>can</strong> be a <strong>parenthesized</strong> comma-separated
|
||
sequence of variables. (This restriction makes a future extension
|
||
possible of the syntax to have multiple comma-separated resources,
|
||
each with its own optional as-clause.)</p>
|
||
<p>The “as VAR” part is optional.</p>
|
||
<p>The translation of the above statement is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mgr</span> <span class="o">=</span> <span class="p">(</span><span class="n">EXPR</span><span class="p">)</span>
|
||
<span class="n">exit</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">mgr</span><span class="p">)</span><span class="o">.</span><span class="fm">__exit__</span> <span class="c1"># Not calling it yet</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">mgr</span><span class="p">)</span><span class="o">.</span><span class="fm">__enter__</span><span class="p">(</span><span class="n">mgr</span><span class="p">)</span>
|
||
<span class="n">exc</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">VAR</span> <span class="o">=</span> <span class="n">value</span> <span class="c1"># Only if "as VAR" is present</span>
|
||
<span class="n">BLOCK</span>
|
||
<span class="k">except</span><span class="p">:</span>
|
||
<span class="c1"># The exceptional case is handled here</span>
|
||
<span class="n">exc</span> <span class="o">=</span> <span class="kc">False</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">exit</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="o">*</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()):</span>
|
||
<span class="k">raise</span>
|
||
<span class="c1"># The exception is swallowed if exit() returns true</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="c1"># The normal and non-local-goto cases are handled here</span>
|
||
<span class="k">if</span> <span class="n">exc</span><span class="p">:</span>
|
||
<span class="n">exit</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here, the lowercase variables (mgr, exit, value, exc) are internal
|
||
variables and not accessible to the user; they will most likely be
|
||
implemented as special registers or stack positions.</p>
|
||
<p>The details of the above translation are intended to prescribe the
|
||
exact semantics. If either of the relevant methods are not found
|
||
as expected, the interpreter will raise <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code>, in the
|
||
order that they are tried (<code class="docutils literal notranslate"><span class="pre">__exit__</span></code>, <code class="docutils literal notranslate"><span class="pre">__enter__</span></code>).
|
||
Similarly, if any of the calls raises an exception, the effect is
|
||
exactly as it would be in the above code. Finally, if <code class="docutils literal notranslate"><span class="pre">BLOCK</span></code>
|
||
contains a break, continue or return statement, the <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code>
|
||
method is called with three None arguments just as if <code class="docutils literal notranslate"><span class="pre">BLOCK</span></code>
|
||
completed normally. (I.e. these “pseudo-exceptions” are not seen
|
||
as exceptions by <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code>.)</p>
|
||
<p>If the “as VAR” part of the syntax is omitted, the “VAR =” part of
|
||
the translation is omitted (but <code class="docutils literal notranslate"><span class="pre">mgr.__enter__()</span></code> is still called).</p>
|
||
<p>The calling convention for <code class="docutils literal notranslate"><span class="pre">mgr.__exit__()</span></code> is as follows. If the
|
||
finally-suite was reached through normal completion of <code class="docutils literal notranslate"><span class="pre">BLOCK</span></code> or
|
||
through a non-local goto (a break, continue or return statement in
|
||
<code class="docutils literal notranslate"><span class="pre">BLOCK</span></code>), <code class="docutils literal notranslate"><span class="pre">mgr.__exit__()</span></code> is called with three <code class="docutils literal notranslate"><span class="pre">None</span></code> arguments. If
|
||
the finally-suite was reached through an exception raised in
|
||
<code class="docutils literal notranslate"><span class="pre">BLOCK</span></code>, <code class="docutils literal notranslate"><span class="pre">mgr.__exit__()</span></code> is called with three arguments representing
|
||
the exception type, value, and traceback.</p>
|
||
<p>IMPORTANT: if <code class="docutils literal notranslate"><span class="pre">mgr.__exit__()</span></code> returns a “true” value, the exception
|
||
is “swallowed”. That is, if it returns “true”, execution
|
||
continues at the next statement after the with-statement, even if
|
||
an exception happened inside the with-statement. However, if the
|
||
with-statement was left via a non-local goto (break, continue or
|
||
return), this non-local return is resumed when <code class="docutils literal notranslate"><span class="pre">mgr.__exit__()</span></code>
|
||
returns regardless of the return value. The motivation for this
|
||
detail is to make it possible for <code class="docutils literal notranslate"><span class="pre">mgr.__exit__()</span></code> to swallow
|
||
exceptions, without making it too easy (since the default return
|
||
value, <code class="docutils literal notranslate"><span class="pre">None</span></code>, is false and this causes the exception to be
|
||
re-raised). The main use case for swallowing exceptions is to
|
||
make it possible to write the <code class="docutils literal notranslate"><span class="pre">@contextmanager</span></code> decorator so
|
||
that a try/except block in a decorated generator behaves exactly
|
||
as if the body of the generator were expanded in-line at the place
|
||
of the with-statement.</p>
|
||
<p>The motivation for passing the exception details to <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code>, as
|
||
opposed to the argument-less <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> from <a class="pep reference internal" href="../pep-0310/" title="PEP 310 – Reliable Acquisition/Release Pairs">PEP 310</a>, was given by
|
||
the <code class="docutils literal notranslate"><span class="pre">transactional()</span></code> use case, example 3 below. The template in
|
||
that example must commit or roll back the transaction depending on
|
||
whether an exception occurred or not. Rather than just having a
|
||
boolean flag indicating whether an exception occurred, we pass the
|
||
complete exception information, for the benefit of an
|
||
exception-logging facility for example. Relying on <code class="docutils literal notranslate"><span class="pre">sys.exc_info()</span></code>
|
||
to get at the exception information was rejected; <code class="docutils literal notranslate"><span class="pre">sys.exc_info()</span></code>
|
||
has very complex semantics and it is perfectly possible that it
|
||
returns the exception information for an exception that was caught
|
||
ages ago. It was also proposed to add an additional boolean to
|
||
distinguish between reaching the end of <code class="docutils literal notranslate"><span class="pre">BLOCK</span></code> and a non-local
|
||
goto. This was rejected as too complex and unnecessary; a
|
||
non-local goto should be considered unexceptional for the purposes
|
||
of a database transaction roll-back decision.</p>
|
||
<p>To facilitate chaining of contexts in Python code that directly
|
||
manipulates context managers, <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> methods should <strong>not</strong>
|
||
re-raise the error that is passed in to them. It is always the
|
||
responsibility of the <strong>caller</strong> of the <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> method to do any
|
||
reraising in that case.</p>
|
||
<p>That way, if the caller needs to tell whether the <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code>
|
||
invocation <strong>failed</strong> (as opposed to successfully cleaning up before
|
||
propagating the original error), it can do so.</p>
|
||
<p>If <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> returns without an error, this can then be
|
||
interpreted as success of the <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> method itself (regardless
|
||
of whether or not the original error is to be propagated or
|
||
suppressed).</p>
|
||
<p>However, if <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> propagates an exception to its caller, this
|
||
means that <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> <strong>itself</strong> has failed. Thus, <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code>
|
||
methods should avoid raising errors unless they have actually
|
||
failed. (And allowing the original error to proceed isn’t a
|
||
failure.)</p>
|
||
</section>
|
||
<section id="transition-plan">
|
||
<h2><a class="toc-backref" href="#transition-plan" role="doc-backlink">Transition Plan</a></h2>
|
||
<p>In Python 2.5, the new syntax will only be recognized if a future
|
||
statement is present:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">with_statement</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This will make both ‘with’ and ‘as’ keywords. Without the future
|
||
statement, using ‘with’ or ‘as’ as an identifier will cause a
|
||
Warning to be issued to stderr.</p>
|
||
<p>In Python 2.6, the new syntax will always be recognized; ‘with’
|
||
and ‘as’ are always keywords.</p>
|
||
</section>
|
||
<section id="generator-decorator">
|
||
<h2><a class="toc-backref" href="#generator-decorator" role="doc-backlink">Generator Decorator</a></h2>
|
||
<p>With <a class="pep reference internal" href="../pep-0342/" title="PEP 342 – Coroutines via Enhanced Generators">PEP 342</a> accepted, it is possible to write a decorator
|
||
that makes it possible to use a generator that yields exactly once
|
||
to control a with-statement. Here’s a sketch of such a decorator:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">GeneratorContextManager</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">gen</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">gen</span> <span class="o">=</span> <span class="n">gen</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">gen</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">"generator didn't yield"</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">type</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">traceback</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="nb">type</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">gen</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">"generator didn't stop"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">gen</span><span class="o">.</span><span class="n">throw</span><span class="p">(</span><span class="nb">type</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">traceback</span><span class="p">)</span>
|
||
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">"generator didn't stop after throw()"</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">True</span>
|
||
<span class="k">except</span><span class="p">:</span>
|
||
<span class="c1"># only re-raise if it's *not* the exception that was</span>
|
||
<span class="c1"># passed to throw(), because __exit__() must not raise</span>
|
||
<span class="c1"># an exception unless __exit__() itself failed. But</span>
|
||
<span class="c1"># throw() has to raise the exception to signal</span>
|
||
<span class="c1"># propagation, so this fixes the impedance mismatch</span>
|
||
<span class="c1"># between the throw() protocol and the __exit__()</span>
|
||
<span class="c1"># protocol.</span>
|
||
<span class="c1">#</span>
|
||
<span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="k">raise</span>
|
||
|
||
<span class="k">def</span> <span class="nf">contextmanager</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">helper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">GeneratorContextManager</span><span class="p">(</span><span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">))</span>
|
||
<span class="k">return</span> <span class="n">helper</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This decorator could be used as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">opening</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="c1"># IOError is untouched by GeneratorContext</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">f</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># Ditto for errors here (however unlikely)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A robust implementation of this decorator will be made
|
||
part of the standard library.</p>
|
||
</section>
|
||
<section id="context-managers-in-the-standard-library">
|
||
<h2><a class="toc-backref" href="#context-managers-in-the-standard-library" role="doc-backlink">Context Managers in the Standard Library</a></h2>
|
||
<p>It would be possible to endow certain objects, like files,
|
||
sockets, and locks, with <code class="docutils literal notranslate"><span class="pre">__enter__()</span></code> and <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> methods so
|
||
that instead of writing:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">locking</span><span class="p">(</span><span class="n">myLock</span><span class="p">):</span>
|
||
<span class="n">BLOCK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>one could write simply:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">myLock</span><span class="p">:</span>
|
||
<span class="n">BLOCK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>I think we should be careful with this; it could lead to mistakes
|
||
like:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
|
||
<span class="k">with</span> <span class="n">f</span><span class="p">:</span>
|
||
<span class="n">BLOCK1</span>
|
||
<span class="k">with</span> <span class="n">f</span><span class="p">:</span>
|
||
<span class="n">BLOCK2</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>which does not do what one might think (f is closed before <code class="docutils literal notranslate"><span class="pre">BLOCK2</span></code>
|
||
is entered).</p>
|
||
<p>OTOH such mistakes are easily diagnosed; for example, the
|
||
generator context decorator above raises <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code> when a
|
||
second with-statement calls <code class="docutils literal notranslate"><span class="pre">f.__enter__()</span></code> again. A similar error
|
||
can be raised if <code class="docutils literal notranslate"><span class="pre">__enter__</span></code> is invoked on a closed file object.</p>
|
||
<p>For Python 2.5, the following types have been identified as
|
||
context managers:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">-</span> <span class="n">file</span>
|
||
<span class="o">-</span> <span class="n">thread</span><span class="o">.</span><span class="n">LockType</span>
|
||
<span class="o">-</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span>
|
||
<span class="o">-</span> <span class="n">threading</span><span class="o">.</span><span class="n">RLock</span>
|
||
<span class="o">-</span> <span class="n">threading</span><span class="o">.</span><span class="n">Condition</span>
|
||
<span class="o">-</span> <span class="n">threading</span><span class="o">.</span><span class="n">Semaphore</span>
|
||
<span class="o">-</span> <span class="n">threading</span><span class="o">.</span><span class="n">BoundedSemaphore</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A context manager will also be added to the decimal module to
|
||
support using a local decimal arithmetic context within the body
|
||
of a with statement, automatically restoring the original context
|
||
when the with statement is exited.</p>
|
||
</section>
|
||
<section id="standard-terminology">
|
||
<h2><a class="toc-backref" href="#standard-terminology" role="doc-backlink">Standard Terminology</a></h2>
|
||
<p>This PEP proposes that the protocol consisting of the <code class="docutils literal notranslate"><span class="pre">__enter__()</span></code>
|
||
and <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> methods be known as the “context management protocol”,
|
||
and that objects that implement that protocol be known as “context
|
||
managers”. <a class="footnote-reference brackets" href="#id20" id="id5">[4]</a></p>
|
||
<p>The expression immediately following the with keyword in the
|
||
statement is a “context expression” as that expression provides the
|
||
main clue as to the runtime environment the context manager
|
||
establishes for the duration of the statement body.</p>
|
||
<p>The code in the body of the with statement and the variable name
|
||
(or names) after the as keyword don’t really have special terms at
|
||
this point in time. The general terms “statement body” and “target
|
||
list” can be used, prefixing with “with” or “with statement” if the
|
||
terms would otherwise be unclear.</p>
|
||
<p>Given the existence of objects such as the decimal module’s
|
||
arithmetic context, the term “context” is unfortunately ambiguous.
|
||
If necessary, it can be made more specific by using the terms
|
||
“context manager” for the concrete object created by the context
|
||
expression and “runtime context” or (preferably) “runtime
|
||
environment” for the actual state modifications made by the context
|
||
manager. When simply discussing use of the with statement, the
|
||
ambiguity shouldn’t matter too much as the context expression fully
|
||
defines the changes made to the runtime environment.
|
||
The distinction is more important when discussing the mechanics of
|
||
the with statement itself and how to go about actually implementing
|
||
context managers.</p>
|
||
</section>
|
||
<section id="caching-context-managers">
|
||
<h2><a class="toc-backref" href="#caching-context-managers" role="doc-backlink">Caching Context Managers</a></h2>
|
||
<p>Many context managers (such as files and generator-based contexts)
|
||
will be single-use objects. Once the <code class="docutils literal notranslate"><span class="pre">__exit__()</span></code> method has been
|
||
called, the context manager will no longer be in a usable state
|
||
(e.g. the file has been closed, or the underlying generator has
|
||
finished execution).</p>
|
||
<p>Requiring a fresh manager object for each with statement is the
|
||
easiest way to avoid problems with multi-threaded code and nested
|
||
with statements trying to use the same context manager. It isn’t
|
||
coincidental that all of the standard library context managers
|
||
that support reuse come from the threading module - they’re all
|
||
already designed to deal with the problems created by threaded
|
||
and nested usage.</p>
|
||
<p>This means that in order to save a context manager with particular
|
||
initialisation arguments to be used in multiple with statements, it
|
||
will typically be necessary to store it in a zero-argument callable
|
||
that is then called in the context expression of each statement
|
||
rather than caching the context manager directly.</p>
|
||
<p>When this restriction does not apply, the documentation of the
|
||
affected context manager should make that clear.</p>
|
||
</section>
|
||
<section id="resolved-issues">
|
||
<h2><a class="toc-backref" href="#resolved-issues" role="doc-backlink">Resolved Issues</a></h2>
|
||
<p>The following issues were resolved by BDFL approval (and a lack
|
||
of any major objections on python-dev).</p>
|
||
<ol class="arabic">
|
||
<li>What exception should <code class="docutils literal notranslate"><span class="pre">GeneratorContextManager</span></code> raise when the
|
||
underlying generator-iterator misbehaves? The following quote is
|
||
the reason behind Guido’s choice of <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code> for both this
|
||
and for the generator <code class="docutils literal notranslate"><span class="pre">close()</span></code> method in <a class="pep reference internal" href="../pep-0342/" title="PEP 342 – Coroutines via Enhanced Generators">PEP 342</a> (from <a class="footnote-reference brackets" href="#id24" id="id6">[8]</a>):<p>“I’d rather not introduce a new exception class just for this
|
||
purpose, since it’s not an exception that I want people to catch:
|
||
I want it to turn into a traceback which is seen by the
|
||
programmer who then fixes the code. So now I believe they
|
||
should both raise <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code>.
|
||
There are some precedents for that: it’s raised by the core
|
||
Python code in situations where endless recursion is detected,
|
||
and for uninitialized objects (and for a variety of
|
||
miscellaneous conditions).”</p>
|
||
</li>
|
||
<li>It is fine to raise <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> instead of <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> if the
|
||
relevant methods aren’t present on a class involved in a with
|
||
statement. The fact that the abstract object C API raises
|
||
<code class="docutils literal notranslate"><span class="pre">TypeError</span></code> rather than <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> is an accident of history,
|
||
rather than a deliberate design decision <a class="footnote-reference brackets" href="#id27" id="id7">[11]</a>.</li>
|
||
<li>Objects with <code class="docutils literal notranslate"><span class="pre">__enter__/__exit__</span></code> methods are called “context
|
||
managers” and the decorator to convert a generator function
|
||
into a context manager factory is <code class="docutils literal notranslate"><span class="pre">contextlib.contextmanager</span></code>.
|
||
There were some other suggestions <a class="footnote-reference brackets" href="#id31" id="id8">[15]</a> during the 2.5 release
|
||
cycle but no compelling arguments for switching away from the
|
||
terms that had been used in the PEP implementation were made.</li>
|
||
</ol>
|
||
</section>
|
||
<section id="rejected-options">
|
||
<h2><a class="toc-backref" href="#rejected-options" role="doc-backlink">Rejected Options</a></h2>
|
||
<p>For several months, the PEP prohibited suppression of exceptions
|
||
in order to avoid hidden flow control. Implementation
|
||
revealed this to be a right royal pain, so Guido restored the
|
||
ability <a class="footnote-reference brackets" href="#id28" id="id9">[12]</a>.</p>
|
||
<p>Another aspect of the PEP that caused no end of questions and
|
||
terminology debates was providing a <code class="docutils literal notranslate"><span class="pre">__context__()</span></code> method that
|
||
was analogous to an iterable’s <code class="docutils literal notranslate"><span class="pre">__iter__()</span></code> method <a class="footnote-reference brackets" href="#id21" id="id10">[5]</a> <a class="footnote-reference brackets" href="#id23" id="id11">[7]</a> <a class="footnote-reference brackets" href="#id25" id="id12">[9]</a>.
|
||
The ongoing problems <a class="footnote-reference brackets" href="#id26" id="id13">[10]</a> <a class="footnote-reference brackets" href="#id28" id="id14">[12]</a> with explaining what it was and why
|
||
it was and how it was meant to work eventually lead to Guido
|
||
killing the concept outright <a class="footnote-reference brackets" href="#id30" id="id15">[14]</a> (and there was much rejoicing!).</p>
|
||
<p>The notion of using the <a class="pep reference internal" href="../pep-0342/" title="PEP 342 – Coroutines via Enhanced Generators">PEP 342</a> generator API directly to define
|
||
the with statement was also briefly entertained <a class="footnote-reference brackets" href="#id22" id="id16">[6]</a>, but quickly
|
||
dismissed as making it too difficult to write non-generator
|
||
based context managers.</p>
|
||
</section>
|
||
<section id="examples">
|
||
<h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2>
|
||
<p>The generator based examples rely on <a class="pep reference internal" href="../pep-0342/" title="PEP 342 – Coroutines via Enhanced Generators">PEP 342</a>. Also, some of the
|
||
examples are unnecessary in practice, as the appropriate objects,
|
||
such as <code class="docutils literal notranslate"><span class="pre">threading.RLock</span></code>, are able to be used directly in with
|
||
statements.</p>
|
||
<p>The tense used in the names of the example contexts is not
|
||
arbitrary. Past tense (“-ed”) is used when the name refers to an
|
||
action which is done in the <code class="docutils literal notranslate"><span class="pre">__enter__</span></code> method and undone in the
|
||
<code class="docutils literal notranslate"><span class="pre">__exit__</span></code> method. Progressive tense (“-ing”) is used when the name
|
||
refers to an action which is to be done in the <code class="docutils literal notranslate"><span class="pre">__exit__</span></code> method.</p>
|
||
<ol class="arabic">
|
||
<li>A template for ensuring that a lock, acquired at the start of a
|
||
block, is released when the block is left:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">locked</span><span class="p">(</span><span class="n">lock</span><span class="p">):</span>
|
||
<span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">lock</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Used as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">locked</span><span class="p">(</span><span class="n">myLock</span><span class="p">):</span>
|
||
<span class="c1"># Code here executes with myLock held. The lock is</span>
|
||
<span class="c1"># guaranteed to be released when the block is left (even</span>
|
||
<span class="c1"># if via return or by an uncaught exception).</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>A template for opening a file that ensures the file is closed
|
||
when the block is left:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">opened</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s2">"r"</span><span class="p">):</span>
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">f</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Used as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">opened</span><span class="p">(</span><span class="s2">"/etc/passwd"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
|
||
<span class="nb">print</span> <span class="n">line</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>A template for committing or rolling back a database
|
||
transaction:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">transaction</span><span class="p">(</span><span class="n">db</span><span class="p">):</span>
|
||
<span class="n">db</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="kc">None</span>
|
||
<span class="k">except</span><span class="p">:</span>
|
||
<span class="n">db</span><span class="o">.</span><span class="n">rollback</span><span class="p">()</span>
|
||
<span class="k">raise</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">db</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Example 1 rewritten without a generator:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">locked</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lock</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lock</span> <span class="o">=</span> <span class="n">lock</span>
|
||
<span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
|
||
<span class="k">def</span> <span class="fm">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">type</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">tb</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lock</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>(This example is easily modified to implement the other
|
||
relatively stateless examples; it shows that it is easy to avoid
|
||
the need for a generator if no special state needs to be
|
||
preserved.)</p>
|
||
</li>
|
||
<li>Redirect stdout temporarily:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">stdout_redirected</span><span class="p">(</span><span class="n">new_stdout</span><span class="p">):</span>
|
||
<span class="n">save_stdout</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span> <span class="o">=</span> <span class="n">new_stdout</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="kc">None</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span> <span class="o">=</span> <span class="n">save_stdout</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Used as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">opened</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
|
||
<span class="k">with</span> <span class="n">stdout_redirected</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
|
||
<span class="nb">print</span> <span class="s2">"Hello world"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This isn’t thread-safe, of course, but neither is doing this
|
||
same dance manually. In single-threaded programs (for example,
|
||
in scripts) it is a popular way of doing things.</p>
|
||
</li>
|
||
<li>A variant on <code class="docutils literal notranslate"><span class="pre">opened()</span></code> that also returns an error condition:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">opened_w_error</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s2">"r"</span><span class="p">):</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">IOError</span><span class="p">,</span> <span class="n">err</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="kc">None</span><span class="p">,</span> <span class="n">err</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">f</span><span class="p">,</span> <span class="kc">None</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Used as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">opened_w_error</span><span class="p">(</span><span class="s2">"/etc/passwd"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">)</span> <span class="k">as</span> <span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">err</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">err</span><span class="p">:</span>
|
||
<span class="nb">print</span> <span class="s2">"IOError:"</span><span class="p">,</span> <span class="n">err</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"guido::0:0::/:/bin/sh</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Another useful example would be an operation that blocks
|
||
signals. The use could be like this:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">signal</span>
|
||
|
||
<span class="k">with</span> <span class="n">signal</span><span class="o">.</span><span class="n">blocked</span><span class="p">():</span>
|
||
<span class="c1"># code executed without worrying about signals</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>An optional argument might be a list of signals to be blocked;
|
||
by default all signals are blocked. The implementation is left
|
||
as an exercise to the reader.</p>
|
||
</li>
|
||
<li>Another use for this feature is the Decimal context. Here’s a
|
||
simple example, after one posted by Michael Chermside:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">decimal</span>
|
||
|
||
<span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">extra_precision</span><span class="p">(</span><span class="n">places</span><span class="o">=</span><span class="mi">2</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">decimal</span><span class="o">.</span><span class="n">getcontext</span><span class="p">()</span>
|
||
<span class="n">saved_prec</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">prec</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">prec</span> <span class="o">+=</span> <span class="n">places</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="kc">None</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">prec</span> <span class="o">=</span> <span class="n">saved_prec</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Sample usage (adapted from the Python Library Reference):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">sin</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
|
||
<span class="s2">"Return the sine of x as measured in radians."</span>
|
||
<span class="k">with</span> <span class="n">extra_precision</span><span class="p">():</span>
|
||
<span class="n">i</span><span class="p">,</span> <span class="n">lasts</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">fact</span><span class="p">,</span> <span class="n">num</span><span class="p">,</span> <span class="n">sign</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="mi">1</span>
|
||
<span class="k">while</span> <span class="n">s</span> <span class="o">!=</span> <span class="n">lasts</span><span class="p">:</span>
|
||
<span class="n">lasts</span> <span class="o">=</span> <span class="n">s</span>
|
||
<span class="n">i</span> <span class="o">+=</span> <span class="mi">2</span>
|
||
<span class="n">fact</span> <span class="o">*=</span> <span class="n">i</span> <span class="o">*</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="n">num</span> <span class="o">*=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span>
|
||
<span class="n">sign</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span>
|
||
<span class="n">s</span> <span class="o">+=</span> <span class="n">num</span> <span class="o">/</span> <span class="n">fact</span> <span class="o">*</span> <span class="n">sign</span>
|
||
<span class="c1"># The "+s" rounds back to the original precision,</span>
|
||
<span class="c1"># so this must be outside the with-statement:</span>
|
||
<span class="k">return</span> <span class="o">+</span><span class="n">s</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Here’s a simple context manager for the decimal module:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">localcontext</span><span class="p">(</span><span class="n">ctx</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Set a new local decimal context for the block"""</span>
|
||
<span class="c1"># Default to using the current context</span>
|
||
<span class="k">if</span> <span class="n">ctx</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">ctx</span> <span class="o">=</span> <span class="n">getcontext</span><span class="p">()</span>
|
||
<span class="c1"># We set the thread context to a copy of this context</span>
|
||
<span class="c1"># to ensure that changes within the block are kept</span>
|
||
<span class="c1"># local to the block.</span>
|
||
<span class="n">newctx</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||
<span class="n">oldctx</span> <span class="o">=</span> <span class="n">decimal</span><span class="o">.</span><span class="n">getcontext</span><span class="p">()</span>
|
||
<span class="n">decimal</span><span class="o">.</span><span class="n">setcontext</span><span class="p">(</span><span class="n">newctx</span><span class="p">)</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">yield</span> <span class="n">newctx</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="c1"># Always restore the original context</span>
|
||
<span class="n">decimal</span><span class="o">.</span><span class="n">setcontext</span><span class="p">(</span><span class="n">oldctx</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Sample usage:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">decimal</span> <span class="kn">import</span> <span class="n">localcontext</span><span class="p">,</span> <span class="n">ExtendedContext</span>
|
||
|
||
<span class="k">def</span> <span class="nf">sin</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
|
||
<span class="k">with</span> <span class="n">localcontext</span><span class="p">()</span> <span class="k">as</span> <span class="n">ctx</span><span class="p">:</span>
|
||
<span class="n">ctx</span><span class="o">.</span><span class="n">prec</span> <span class="o">+=</span> <span class="mi">2</span>
|
||
<span class="c1"># Rest of sin calculation algorithm</span>
|
||
<span class="c1"># uses a precision 2 greater than normal</span>
|
||
<span class="k">return</span> <span class="o">+</span><span class="n">s</span> <span class="c1"># Convert result to normal precision</span>
|
||
|
||
<span class="k">def</span> <span class="nf">sin</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
|
||
<span class="k">with</span> <span class="n">localcontext</span><span class="p">(</span><span class="n">ExtendedContext</span><span class="p">):</span>
|
||
<span class="c1"># Rest of sin calculation algorithm</span>
|
||
<span class="c1"># uses the Extended Context from the</span>
|
||
<span class="c1"># General Decimal Arithmetic Specification</span>
|
||
<span class="k">return</span> <span class="o">+</span><span class="n">s</span> <span class="c1"># Convert result to normal context</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>A generic “object-closing” context manager:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">closing</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">obj</span>
|
||
<span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span>
|
||
<span class="k">def</span> <span class="fm">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">exc_info</span><span class="p">):</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">close_it</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">close</span>
|
||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">close_it</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This can be used to deterministically close anything with a
|
||
close method, be it file, generator, or something else. It
|
||
can even be used when the object isn’t guaranteed to require
|
||
closing (e.g., a function that accepts an arbitrary
|
||
iterable):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># emulate opening():</span>
|
||
<span class="k">with</span> <span class="n">closing</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s2">"argument.txt"</span><span class="p">))</span> <span class="k">as</span> <span class="n">contradiction</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">contradiction</span><span class="p">:</span>
|
||
<span class="nb">print</span> <span class="n">line</span>
|
||
|
||
<span class="c1"># deterministically finalize an iterator:</span>
|
||
<span class="k">with</span> <span class="n">closing</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">data_source</span><span class="p">))</span> <span class="k">as</span> <span class="n">data</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">datum</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
|
||
<span class="n">process</span><span class="p">(</span><span class="n">datum</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>(Python 2.5’s contextlib module contains a version
|
||
of this context manager)</p>
|
||
</li>
|
||
<li><a class="pep reference internal" href="../pep-0319/" title="PEP 319 – Python Synchronize/Asynchronize Block">PEP 319</a> gives a use case for also having a <code class="docutils literal notranslate"><span class="pre">released()</span></code>
|
||
context to temporarily release a previously acquired lock;
|
||
this can be written very similarly to the locked context
|
||
manager above by swapping the <code class="docutils literal notranslate"><span class="pre">acquire()</span></code> and <code class="docutils literal notranslate"><span class="pre">release()</span></code> calls:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">released</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lock</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lock</span> <span class="o">=</span> <span class="n">lock</span>
|
||
<span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lock</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
|
||
<span class="k">def</span> <span class="fm">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">type</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">tb</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Sample usage:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">my_lock</span><span class="p">:</span>
|
||
<span class="c1"># Operations with the lock held</span>
|
||
<span class="k">with</span> <span class="n">released</span><span class="p">(</span><span class="n">my_lock</span><span class="p">):</span>
|
||
<span class="c1"># Operations without the lock</span>
|
||
<span class="c1"># e.g. blocking I/O</span>
|
||
<span class="c1"># Lock is held again here</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>A “nested” context manager that automatically nests the
|
||
supplied contexts from left-to-right to avoid excessive
|
||
indentation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@contextmanager</span>
|
||
<span class="k">def</span> <span class="nf">nested</span><span class="p">(</span><span class="o">*</span><span class="n">contexts</span><span class="p">):</span>
|
||
<span class="n">exits</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="nb">vars</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">context</span> <span class="ow">in</span> <span class="n">contexts</span><span class="p">:</span>
|
||
<span class="n">exit</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="fm">__exit__</span>
|
||
<span class="n">enter</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="fm">__enter__</span>
|
||
<span class="nb">vars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">enter</span><span class="p">())</span>
|
||
<span class="n">exits</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">exit</span><span class="p">)</span>
|
||
<span class="k">yield</span> <span class="nb">vars</span>
|
||
<span class="k">except</span><span class="p">:</span>
|
||
<span class="n">exc</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">exc</span> <span class="o">=</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="k">while</span> <span class="n">exits</span><span class="p">:</span>
|
||
<span class="n">exit</span> <span class="o">=</span> <span class="n">exits</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">exit</span><span class="p">(</span><span class="o">*</span><span class="n">exc</span><span class="p">)</span>
|
||
<span class="k">except</span><span class="p">:</span>
|
||
<span class="n">exc</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">exc</span> <span class="o">=</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">exc</span> <span class="o">!=</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">):</span>
|
||
<span class="c1"># sys.exc_info() may have been</span>
|
||
<span class="c1"># changed by one of the exit methods</span>
|
||
<span class="c1"># so provide explicit exception info</span>
|
||
<span class="k">raise</span> <span class="n">exc</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">exc</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">exc</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Sample usage:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">nested</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> <span class="k">as</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">):</span>
|
||
<span class="c1"># Perform operation</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Is equivalent to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">a</span> <span class="k">as</span> <span class="n">x</span><span class="p">:</span>
|
||
<span class="k">with</span> <span class="n">b</span> <span class="k">as</span> <span class="n">y</span><span class="p">:</span>
|
||
<span class="k">with</span> <span class="n">c</span> <span class="k">as</span> <span class="n">z</span><span class="p">:</span>
|
||
<span class="c1"># Perform operation</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>(Python 2.5’s contextlib module contains a version
|
||
of this context manager)</p>
|
||
</li>
|
||
</ol>
|
||
</section>
|
||
<section id="reference-implementation">
|
||
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
|
||
<p>This PEP was first accepted by Guido at his EuroPython
|
||
keynote, 27 June 2005.
|
||
It was accepted again later, with <code class="docutils literal notranslate"><span class="pre">the</span> <span class="pre">__context__</span></code> method added.
|
||
The PEP was implemented in Subversion for Python 2.5a1
|
||
The <code class="docutils literal notranslate"><span class="pre">__context__()</span></code> method was removed in Python 2.5b1</p>
|
||
</section>
|
||
<section id="acknowledgements">
|
||
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
|
||
<p>Many people contributed to the ideas and concepts in this PEP,
|
||
including all those mentioned in the acknowledgements for <a class="pep reference internal" href="../pep-0340/" title="PEP 340 – Anonymous Block Statements">PEP 340</a>
|
||
and <a class="pep reference internal" href="../pep-0346/" title="PEP 346 – User Defined (”with”) Statements">PEP 346</a>.</p>
|
||
<p>Additional thanks goes to (in no meaningful order): Paul Moore,
|
||
Phillip J. Eby, Greg Ewing, Jason Orendorff, Michael Hudson,
|
||
Raymond Hettinger, Walter Dörwald, Aahz, Georg Brandl, Terry Reedy,
|
||
A.M. Kuchling, Brett Cannon, and all those that participated in the
|
||
discussions on python-dev.</p>
|
||
</section>
|
||
<section id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id17" role="doc-footnote">
|
||
<dt class="label" id="id17">[<a href="#id4">1</a>]</dt>
|
||
<dd>Raymond Chen’s article on hidden flow control
|
||
<a class="reference external" href="https://devblogs.microsoft.com/oldnewthing/20050106-00/?p=36783">https://devblogs.microsoft.com/oldnewthing/20050106-00/?p=36783</a></aside>
|
||
<aside class="footnote brackets" id="id18" role="doc-footnote">
|
||
<dt class="label" id="id18">[<a href="#id2">2</a>]</dt>
|
||
<dd>Guido suggests some generator changes that ended up in PEP 342
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-May/053885.html">https://mail.python.org/pipermail/python-dev/2005-May/053885.html</a></aside>
|
||
<aside class="footnote brackets" id="id19" role="doc-footnote">
|
||
<dt class="label" id="id19">[<a href="#id3">3</a>]</dt>
|
||
<dd>Wiki discussion of PEP 343
|
||
<a class="reference external" href="http://wiki.python.org/moin/WithStatement">http://wiki.python.org/moin/WithStatement</a></aside>
|
||
<aside class="footnote brackets" id="id20" role="doc-footnote">
|
||
<dt class="label" id="id20">[<a href="#id5">4</a>]</dt>
|
||
<dd>Early draft of some documentation for the with statement
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-July/054658.html">https://mail.python.org/pipermail/python-dev/2005-July/054658.html</a></aside>
|
||
<aside class="footnote brackets" id="id21" role="doc-footnote">
|
||
<dt class="label" id="id21">[<a href="#id10">5</a>]</dt>
|
||
<dd>Proposal to add the __with__ method
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-October/056947.html">https://mail.python.org/pipermail/python-dev/2005-October/056947.html</a></aside>
|
||
<aside class="footnote brackets" id="id22" role="doc-footnote">
|
||
<dt class="label" id="id22">[<a href="#id16">6</a>]</dt>
|
||
<dd>Proposal to use the PEP 342 enhanced generator API directly
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-October/056969.html">https://mail.python.org/pipermail/python-dev/2005-October/056969.html</a></aside>
|
||
<aside class="footnote brackets" id="id23" role="doc-footnote">
|
||
<dt class="label" id="id23">[<a href="#id11">7</a>]</dt>
|
||
<dd>Guido lets me (Alyssa Coghlan) talk him into a bad idea ;)
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-October/057018.html">https://mail.python.org/pipermail/python-dev/2005-October/057018.html</a></aside>
|
||
<aside class="footnote brackets" id="id24" role="doc-footnote">
|
||
<dt class="label" id="id24">[<a href="#id6">8</a>]</dt>
|
||
<dd>Guido raises some exception handling questions
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-June/054064.html">https://mail.python.org/pipermail/python-dev/2005-June/054064.html</a></aside>
|
||
<aside class="footnote brackets" id="id25" role="doc-footnote">
|
||
<dt class="label" id="id25">[<a href="#id12">9</a>]</dt>
|
||
<dd>Guido answers some questions about the __context__ method
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-October/057520.html">https://mail.python.org/pipermail/python-dev/2005-October/057520.html</a></aside>
|
||
<aside class="footnote brackets" id="id26" role="doc-footnote">
|
||
<dt class="label" id="id26">[<a href="#id13">10</a>]</dt>
|
||
<dd>Guido answers more questions about the __context__ method
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-October/057535.html">https://mail.python.org/pipermail/python-dev/2005-October/057535.html</a></aside>
|
||
<aside class="footnote brackets" id="id27" role="doc-footnote">
|
||
<dt class="label" id="id27">[<a href="#id7">11</a>]</dt>
|
||
<dd>Guido says AttributeError is fine for missing special methods
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2005-October/057625.html">https://mail.python.org/pipermail/python-dev/2005-October/057625.html</a></aside>
|
||
<aside class="footnote brackets" id="id28" role="doc-footnote">
|
||
<dt class="label" id="id28">[12]<em> (<a href='#id9'>1</a>, <a href='#id14'>2</a>) </em></dt>
|
||
<dd>Guido restores the ability to suppress exceptions
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2006-February/061909.html">https://mail.python.org/pipermail/python-dev/2006-February/061909.html</a></aside>
|
||
<aside class="footnote brackets" id="id29" role="doc-footnote">
|
||
<dt class="label" id="id29">[<a href="#id1">13</a>]</dt>
|
||
<dd>A simple question kickstarts a thorough review of PEP 343
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2006-April/063859.html">https://mail.python.org/pipermail/python-dev/2006-April/063859.html</a></aside>
|
||
<aside class="footnote brackets" id="id30" role="doc-footnote">
|
||
<dt class="label" id="id30">[<a href="#id15">14</a>]</dt>
|
||
<dd>Guido kills the __context__() method
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2006-April/064632.html">https://mail.python.org/pipermail/python-dev/2006-April/064632.html</a></aside>
|
||
<aside class="footnote brackets" id="id31" role="doc-footnote">
|
||
<dt class="label" id="id31">[<a href="#id8">15</a>]</dt>
|
||
<dd>Proposal to use ‘context guard’ instead of ‘context manager’
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2006-May/064676.html">https://mail.python.org/pipermail/python-dev/2006-May/064676.html</a></aside>
|
||
</aside>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document has been placed in the public domain.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0343.rst">https://github.com/python/peps/blob/main/peps/pep-0343.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0343.rst">2023-10-11 12:05:51 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#author-s-note">Author’s Note</a></li>
|
||
<li><a class="reference internal" href="#introduction">Introduction</a></li>
|
||
<li><a class="reference internal" href="#motivation-and-summary">Motivation and Summary</a></li>
|
||
<li><a class="reference internal" href="#use-cases">Use Cases</a></li>
|
||
<li><a class="reference internal" href="#specification-the-with-statement">Specification: The ‘with’ Statement</a></li>
|
||
<li><a class="reference internal" href="#transition-plan">Transition Plan</a></li>
|
||
<li><a class="reference internal" href="#generator-decorator">Generator Decorator</a></li>
|
||
<li><a class="reference internal" href="#context-managers-in-the-standard-library">Context Managers in the Standard Library</a></li>
|
||
<li><a class="reference internal" href="#standard-terminology">Standard Terminology</a></li>
|
||
<li><a class="reference internal" href="#caching-context-managers">Caching Context Managers</a></li>
|
||
<li><a class="reference internal" href="#resolved-issues">Resolved Issues</a></li>
|
||
<li><a class="reference internal" href="#rejected-options">Rejected Options</a></li>
|
||
<li><a class="reference internal" href="#examples">Examples</a></li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0343.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |