これは既にサポートされていない古いバージョンの Python のドキュメントです。 是非 Python をアップグレードし、このドキュメントを読みましょう: 現在の安定板リリースの Python ドキュメント.

# `contextlib` --- `with` 文コンテキスト用ユーティリティ

ソースコード: Lib/contextlib.py

このモジュールは `with` 文に関わる一般的なタスクのためのユーティリティを提供します。詳しい情報は、 コンテキストマネージャ型 と with文とコンテキストマネージャ を参照してください。

## ユーティリティ

以下の関数とクラスを提供しています:

class `contextlib.``AbstractContextManager`

  An abstract base class for classes that implement `object.__enter__()` and `object.__exit__()`. A default implementation for `object.__enter__()` is provided which returns `self` while `object.__exit__()` is an abstract method which by default returns `None`. See also the definition of コンテキストマネージャ型.

  バージョン 3.6 で追加.

class `contextlib.``AbstractAsyncContextManager`

  An abstract base class for classes that implement `object.__aenter__()` and `object.__aexit__()`. A default implementation for `object.__aenter__()` is provided which returns `self` while `object.__aexit__()` is an abstract method which by default returns `None`. See also the definition of 非同期コンテキストマネージャ (Asynchronous Context Manager).

  バージョン 3.7 で追加.

`@``contextlib.``contextmanager`

  この関数は `with` 文コンテキストマネージャのファクトリ関数を定義するために利用できる デコレータ です。新しいクラスや `__enter__()` と `__exit__()` メソッドを別々に定義しなくても、ファクトリ関数を定義することができます。

  While many objects natively support use in with statements, sometimes a resource needs to be managed that isn't a context manager in its own right, and doesn't implement a `close()` method for use with `contextlib.closing`

  An abstract example would be the following to ensure correct resource management:

      from contextlib import contextmanager

      @contextmanager
      def managed_resource(*args, **kwds):
          # Code to acquire resource, e.g.:
          resource = acquire_resource(*args, **kwds)
          try:
              yield resource
          finally:
              # Code to release resource, e.g.:
              release_resource(resource)

      >>> with managed_resource(timeout=3600) as resource:
      ...     # Resource is released at the end of this block,
      ...     # even if code in the block raises an exception

  デコレート対象の関数は呼び出されたときに ジェネレータ-イテレータを返す必要があります。このイテレータは必ず値を1つ yield しなければなりません。 `with` 文の `as` 節が存在するなら、その値は as 節のターゲットへ束縛されることになります。

  ジェネレータが yield を実行した箇所で `with` 文のネストされたブロックが実行されます。ブロックから抜けた後でジェネレータは再開されます。ブロック内で処理されない例外が発生した場合は、ジェネレータ内部の yield を実行した箇所で例外が再送出されます。なので、(もしあれば) エラーを捕捉したり、クリーンアップ処理を確実に実行したりするために、`try`...`except`...`finally` 構文を使用できます。例外を捕捉する目的が、(完全に例外を抑制してしまうのではなく) 単に例外のログをとるため、もしくはあるアクションを実行するためなら、ジェネレータはその例外を再送出しなければなりません。例外を再送出しない場合、ジェネレータのコンテキストマネージャは `with` 文に対して例外が処理されたことを示し、`with` 文の直後の文から実行を再開します。

  `contextmanager()` は `ContextDecorator` を使っているので、 `contextmanager()` で作ったコンテキストマネージャは `with` 文だけでなくデコレータとしても利用できます。デコレーターとして利用された場合、新しい generator インスタンスが関数呼び出しのたびに暗黙に生成されます (このことによって、 `contextmanager()` によって作られたなにがしか「単発」コンテキストマネージャを、コンテキストマネージャがデコレータとして使われるためには多重に呼び出されることをサポートする必要がある、という要件に合致させることが出来ます。)

  バージョン 3.2 で変更: `ContextDecorator` の使用。

`@``contextlib.``asynccontextmanager`

  Similar to `contextmanager()`, but creates an asynchronous context manager.

  This function is a decorator that can be used to define a factory function for `async with` statement asynchronous context managers, without needing to create a class or separate `__aenter__()` and `__aexit__()` methods. It must be applied to an asynchronous generator function.

  簡単な例:

      from contextlib import asynccontextmanager

      @asynccontextmanager
      async def get_connection():
          conn = await acquire_db_connection()
          try:
              yield conn
          finally:
              await release_db_connection(conn)

      async def get_all_users():
          async with get_connection() as conn:
              return conn.query('SELECT ...')

  バージョン 3.7 で追加.

`contextlib.``closing`(thing)

  ブロックの完了時に thing を close するコンテキストマネージャを返します。これは基本的に以下と等価です:

      from contextlib import contextmanager

      @contextmanager
      def closing(thing):
          try:
              yield thing
          finally:
              thing.close()

  そして、明示的に `page` を close する必要なしに、次のように書くことができます:

      from contextlib import closing
      from urllib.request import urlopen

      with closing(urlopen('http://www.python.org')) as page:
          for line in page:
              print(line)

  `page` を明示的に close する必要は無く、エラーが発生した場合でも、 `with` ブロックを出るときに `page.close()` が呼ばれます。

`contextlib.``nullcontext`(enter_result=None)

  Return a context manager that returns enter_result from `__enter__`, but otherwise does nothing. It is intended to be used as a stand-in for an optional context manager, for example:

      def myfunction(arg, ignore_exceptions=False):
          if ignore_exceptions:
              # Use suppress to ignore all exceptions.
              cm = contextlib.suppress(Exception)
          else:
              # Do not ignore any exceptions, cm has no effect.
              cm = contextlib.nullcontext()
          with cm:
              # Do something

  An example using enter_result:

      def process_file(file_or_path):
          if isinstance(file_or_path, str):
              # If string, open file
              cm = open(file_or_path)
          else:
              # Caller is responsible for closing file
              cm = nullcontext(file_or_path)

          with cm as file:
              # Perform processing on the file

  バージョン 3.7 で追加.

`contextlib.``suppress`(*exceptions)

  任意の例外リストを受け取り、with ブロック内でいずれかが起こると with ブロックの直後から黙って実行を再開するコンテキストマネージャを返します。

  ほかの完全に例外を抑制するメカニズム同様、このコンテキストマネージャは、黙ってプログラム実行を続けることが正しいことであるとわかっている、非常に限定的なエラーをカバーする以上の使い方はしてはいけません。

  例えば:

      from contextlib import suppress

      with suppress(FileNotFoundError):
          os.remove('somefile.tmp')

      with suppress(FileNotFoundError):
          os.remove('someotherfile.tmp')

  これは以下と等価です:

      try:
          os.remove('somefile.tmp')
      except FileNotFoundError:
          pass

      try:
          os.remove('someotherfile.tmp')
      except FileNotFoundError:
          pass

  このコンテキストマネージャは 再入可能(リエントラント) です。

  バージョン 3.4 で追加.

`contextlib.``redirect_stdout`(new_target)

  Context manager for temporarily redirecting `sys.stdout` to another file or file-like object.

  This tool adds flexibility to existing functions or classes whose output is hardwired to stdout.

  For example, the output of `help()` normally is sent to sys.stdout. You can capture that output in a string by redirecting the output to an `io.StringIO` object:

      f = io.StringIO()
      with redirect_stdout(f):
          help(pow)
      s = f.getvalue()

  To send the output of `help()` to a file on disk, redirect the output to a regular file:

      with open('help.txt', 'w') as f:
          with redirect_stdout(f):
              help(pow)

  To send the output of `help()` to sys.stderr:

      with redirect_stdout(sys.stderr):
          help(pow)

  Note that the global side effect on `sys.stdout` means that this context manager is not suitable for use in library code and most threaded applications. It also has no effect on the output of subprocesses. However, it is still a useful approach for many utility scripts.

  このコンテキストマネージャは 再入可能(リエントラント) です。

  バージョン 3.4 で追加.

`contextlib.``redirect_stderr`(new_target)

  Similar to `redirect_stdout()` but redirecting `sys.stderr` to another file or file-like object.

  このコンテキストマネージャは 再入可能(リエントラント) です。

  バージョン 3.5 で追加.

class `contextlib.``ContextDecorator`

  コンテキストマネージャをデコレータとしても使用できるようにする基底クラスです。

  `ContextDecorator` から継承したコンテキストマネージャは、通常のコンテキストマネージャーと同じく `__enter__` および `__exit__` を実装する必要があります。`__exit__` はデコレータとして使用された場合でも例外をオプションの引数として受け取ります。

  `contextmanager()` は `ContextDecorator` を利用しているので、自動的にデコレーターとしても利用できるようになります。

  `ContextDecorator` の例:

      from contextlib import ContextDecorator

      class mycontext(ContextDecorator):
          def __enter__(self):
              print('Starting')
              return self

          def __exit__(self, *exc):
              print('Finishing')
              return False

      >>> @mycontext()
      ... def function():
      ...     print('The bit in the middle')
      ...
      >>> function()
      Starting
      The bit in the middle
      Finishing

      >>> with mycontext():
      ...     print('The bit in the middle')
      ...
      Starting
      The bit in the middle
      Finishing

  これは次のような形のコードに対するシンタックスシュガーになります:

      def f():
          with cm():
              # Do stuff

  `ContextDecorator` を使うと代わりに次のように書けます:

      @cm()
      def f():
          # Do stuff

  デコレーターを使うと、`cm` が関数の一部ではなく全体に適用されていることが明確になります (インデントレベルを1つ節約できるのもメリットです)。

  すでに基底クラスを持っているコンテキストマネージャーも、`ContextDecorator` を mixin クラスとして利用することで拡張できます:

      from contextlib import ContextDecorator

      class mycontext(ContextBaseClass, ContextDecorator):
          def __enter__(self):
              return self

          def __exit__(self, *exc):
              return False

  注釈

  デコレートされた関数が複数回呼び出せるように、内部のコンテキストマネージャーは複数の `with` 文に対応する必要があります。そうでないなら、明示的な `with` 文を関数内で利用するべきです。

  バージョン 3.2 で追加.

class `contextlib.``ExitStack`

  他の、特にオプションであったり入力に依存するようなコンテキストマネージャーやクリーンアップ関数を動的に組み合わせるためのコンテキストマネージャーです。

  例えば、複数のファイルを1つの with 文で簡単に扱うことができます:

      with ExitStack() as stack:
          files = [stack.enter_context(open(fname)) for fname in filenames]
          # All opened files will automatically be closed at the end of
          # the with statement, even if attempts to open files later
          # in the list raise an exception

  各インスタンスは登録されたコールバックのスタックを管理し、インスタンスが (明示的に、あるいは `with` 文の終わりに暗黙的に) close されるときに逆順でそれを呼び出します。コンテキストスタックのインスタンスが暗黙的にガベージコレクトされたときには callback は呼び出され ません 。

  このスタックモデルは、(file オブジェクトのように) `__init__` メソッドでリソースを確保するコンテキストマネージャーを正しく扱うためのものです。

  登録されたコールバックが登録の逆順で実行されるので、複数のネストされた `with` 文を利用するのと同じ振る舞いをします。これは例外処理にも適用されます。内側のコールバックが例外を抑制したり置き換えたりした場合、外側のコールバックには更新された状態に応じた引数が渡されます。

  これは正しく exit callback の stack を巻き戻すための、比較的低レベルな API です。アプリケーション独自のより高レベルなコンテキストマネージャーを作るための基板として使うのに適しています。

  バージョン 3.3 で追加.

  `enter_context`(cm)

    新しいコンテキストマネージャーに enter し、その `__exit__()` method をコールバックスタックに追加します。渡されたコンテキストマネージャーの `__enter__()` メソッドの戻り値を返します。

    コンテキストマネージャーは、普段 `with` 文で利用された時と同じように、例外を抑制することができます。

  `push`(exit)

    コンテキストマネージャーの `__exit__()` メソッドをコールバックスタックに追加します。

    このメソッドは `__enter__` を 呼び出さない ので、コンテキストマネージャーを実装するときに、 `__enter__()` の実装の一部を自身の `__exit__()` メソッドでカバーするために利用できます。

    コンテキストマネージャーではないオブジェクトが渡された場合、このメソッドはそのオブジェクトをコンテキストマネージャーの `__exit__()` メソッドと同じシグネチャを持つコールバック関数だと仮定して、直接コールバックスタックに追加します。

    それらのコールバック関数も、コンテキストマネージャーの `__exit__()` と同じく、 true 値を返すことで例外を抑制することができます。

    この関数はデコレータとしても使えるように、受け取ったオブジェクトをそのまま返します。

  `callback`(callback, *args, **kwds)

    任意の関数と引数を受け取り、コールバックスタックに追加します。

    他のメソッドと異なり、このメソッドで追加されたコールバックは例外を抑制しません (例外の詳細も渡されません)。

    この関数はデコレータとしても使えるように、受け取った callback をそのまま返します。

  `pop_all`()

    コールバックスタックを新しい `ExitStack` インスタンスに移して、それを返します。このメソッドは callback を実行しません。代わりに、新しい stack が (明示的に、あるいは `with` 文の終わりに暗黙的に) close されるときに実行されます。

    例えば、複数のファイルを "all or nothing" に開く処理を次のように書けます:

        with ExitStack() as stack:
            files = [stack.enter_context(open(fname)) for fname in filenames]
            # Hold onto the close method, but don't call it yet.
            close_files = stack.pop_all().close
            # If opening any file fails, all previously opened files will be
            # closed automatically. If all files are opened successfully,
            # they will remain open even after the with statement ends.
            # close_files() can then be invoked explicitly to close them all.

  `close`()

    すぐにコールバックスタックを巻き戻し、コールバック関数を登録の逆順に呼び出します。登録されたすべてのコンテキストマネージャーと終了 callback に、例外が起こらなかった場合の引数が渡されます。

class `contextlib.``AsyncExitStack`

  An asynchronous context manager, similar to `ExitStack`, that supports combining both synchronous and asynchronous context managers, as well as having coroutines for cleanup logic.

  The `close()` method is not implemented, `aclose()` must be used instead.

  `enter_async_context`(cm)

    Similar to `enter_context()` but expects an asynchronous context manager.

  `push_async_exit`(exit)

    Similar to `push()` but expects either an asynchronous context manager or a coroutine function.

  `push_async_callback`(callback, *args, **kwds)

    Similar to `callback()` but expects a coroutine function.

  `aclose`()

    Similar to `close()` but properly handles awaitables.

  Continuing the example for `asynccontextmanager()`:

      async with AsyncExitStack() as stack:
          connections = [await stack.enter_async_context(get_connection())
              for i in range(5)]
          # All opened connections will automatically be released at the end of
          # the async with statement, even if attempts to open a connection
          # later in the list raise an exception.

  バージョン 3.7 で追加.

## 例とレシピ

このセクションでは、 `contextlib` が提供するツールの効果的な使い方を示す例とレシピを紹介します。

### 可変数個のコンテキストマネージャーをサポートする

`ExitStack` の第一のユースケースは、クラスのドキュメントにかかれている通り、一つの `with` 文で可変数個のコンテキストマネージャーや他のクリーンアップ関数をサポートすることです。ユーザーの入力 (指定された複数個のファイルを開く場合など) に応じて複数個のコンテキストマネージャーが必要となる場合や、いくつかのコンテキストマネージャーがオプションとなる場合に、可変数個のコンテキストマネージャーが必要になります:

    with ExitStack() as stack:
        for resource in resources:
            stack.enter_context(resource)
        if need_special_resource():
            special = acquire_special_resource()
            stack.callback(release_special_resource, special)
        # Perform operations that use the acquired resources

上の例にあるように、 `ExitStack` はコンテキストマネージャープロトコルをサポートしていないリソースの管理を `with` 文を使って簡単に行えるようにします。

### `__enter__` メソッドからの例外をキャッチする

稀に、 `__enter__` メソッドからの例外を、 `with` 文の body やコンテキストマネージャーの `__exit__` メソッドからの例外は間違えて捕まえないように、 catch したい場合があります。 `ExitStack` を使って、コンテキストマネージャープロトコル内のステップを分離することができます:

    stack = ExitStack()
    try:
        x = stack.enter_context(cm)
    except Exception:
        # handle __enter__ exception
    else:
        with stack:
            # Handle normal case

実際のところ、このようなコードが必要になるのならば、利用している API 側で `try`/`except`/`finally` 文を使った直接的なリソース管理インタフェースを提供するべきです。しかし、すべての API がそのようによく設計されているとは限りません。もしコンテキストマネージャーが提供されている唯一のリソース管理APIであるなら、 `ExitStack` を使って `with` 文を使って処理することができない様々なシチュエーションの処理をすることができます。

### `__enter__` 実装内のクリーンアップ

`ExitStack.push()` のドキュメントで言及したとおり、このメソッドはすでに獲得したリソースを、 `__enter__()` メソッドの残りのステップが失敗した時にクリーンアップするために利用することができます。

次の例では、リソースの確保と開放の関数に加えて、オプションのバリデーション関数を受け取るコンテキストマネージャーで、この方法を使ってコンテキストマネージャープロトコルを提供しています:

    from contextlib import contextmanager, AbstractContextManager, ExitStack

    class ResourceManager(AbstractContextManager):

        def __init__(self, acquire_resource, release_resource, check_resource_ok=None):
            self.acquire_resource = acquire_resource
            self.release_resource = release_resource
            if check_resource_ok is None:
                def check_resource_ok(resource):
                    return True
            self.check_resource_ok = check_resource_ok

        @contextmanager
        def _cleanup_on_error(self):
            with ExitStack() as stack:
                stack.push(self)
                yield
                # The validation check passed and didn't raise an exception
                # Accordingly, we want to keep the resource, and pass it
                # back to our caller
                stack.pop_all()

        def __enter__(self):
            resource = self.acquire_resource()
            with self._cleanup_on_error():
                if not self.check_resource_ok(resource):
                    msg = "Failed validation for {!r}"
                    raise RuntimeError(msg.format(resource))
            return resource

        def __exit__(self, *exc_details):
            # We don't need to duplicate any of our resource release logic
            self.release_resource()

### `try-finally` + flag 変数パターンを置き換える

`try-finally` 文に、`finally` 句の内容を実行するかどうかを示すフラグ変数を組み合わせたパターンを目にすることがあるかもしれません。一番シンプルな (単に `except` 句を使うだけでは処理できない) ケースでは次のようなコードになります:

    cleanup_needed = True
    try:
        result = perform_operation()
        if result:
            cleanup_needed = False
    finally:
        if cleanup_needed:
            cleanup_resources()

`try` 文を使ったコードでは、セットアップとクリーンアップのコードが任意の長さのコードで分離してしまうので、開発者やレビューアにとって問題になりえます。

`ExitStack` を使えば、代わりに `with` 文の終わりに実行されるコールバックを登録し、後でそのコールバックをスキップするかどうかを決定できます:

    from contextlib import ExitStack

    with ExitStack() as stack:
        stack.callback(cleanup_resources)
        result = perform_operation()
        if result:
            stack.pop_all()

これにより、別のフラグ変数を使う代わりに、必要なクリーンアップ処理を手前に明示しておくことができます。

もしあるアプリケーションがこのパターンを多用するのであれば、小さいヘルパークラスを導入してよりシンプルにすることができます:

    from contextlib import ExitStack

    class Callback(ExitStack):
        def __init__(self, callback, *args, **kwds):
            super(Callback, self).__init__()
            self.callback(callback, *args, **kwds)

        def cancel(self):
            self.pop_all()

    with Callback(cleanup_resources) as cb:
        result = perform_operation()
        if result:
            cb.cancel()

もしリソースのクリーンアップが単体の関数にまとまってない場合でも、 `ExitStack.callback()` のデコレーター形式を利用してリソース開放処理を宣言することができます:

    from contextlib import ExitStack

    with ExitStack() as stack:
        @stack.callback
        def cleanup_resources():
            ...
        result = perform_operation()
        if result:
            stack.pop_all()

デコレータープロトコルの使用上、このように宣言されたコールバック関数は引数を取ることができません。その代わりに、リリースするリソースをクロージャー変数としてアクセスできる必要があります。

### コンテキストマネージャーを関数デコレーターとして使う

`ContextDecorator` はコンテキストマネージャーを通常の `with` 文に加えて関数デコレーターとしても利用できるようにします。

例えば、関数やまとまった文を、そこに入った時と出た時の時間をトラックするロガーでラップしたい場合があります。そのために関数デコレーターとコンテキストマネージャーを別々に書く代わりに、 `ContextDecorator` を継承すると1つの定義で両方の機能を提供できます:

    from contextlib import ContextDecorator
    import logging

    logging.basicConfig(level=logging.INFO)

    class track_entry_and_exit(ContextDecorator):
        def __init__(self, name):
            self.name = name

        def __enter__(self):
            logging.info('Entering: %s', self.name)

        def __exit__(self, exc_type, exc, exc_tb):
            logging.info('Exiting: %s', self.name)

このクラスのインスタンスはコンテキストマネージャーとしても利用でき:

    with track_entry_and_exit('widget loader'):
        print('Some time consuming activity goes here')
        load_widget()

また関数デコレーターとしても利用できます:

    @track_entry_and_exit('widget loader')
    def activity():
        print('Some time consuming activity goes here')
        load_widget()

コンテキストマネージャーを関数デコレーターとして使う場合、 `__enter__()` メソッドの戻り値にアクセスする手段がないという制限があることに注意してください。もしその値が必要であれば、明示的な `with` 文を使う必要があります。

参考

PEP 343 - "with" 文

  Python の `with` 文の仕様、背景、および例が記載されています。

## Single use, reusable and reentrant context managers

Most context managers are written in a way that means they can only be used effectively in a `with` statement once. These single use context managers must be created afresh each time they're used - attempting to use them a second time will trigger an exception or otherwise not work correctly.

This common limitation means that it is generally advisable to create context managers directly in the header of the `with` statement where they are used (as shown in all of the usage examples above).

Files are an example of effectively single use context managers, since the first `with` statement will close the file, preventing any further IO operations using that file object.

Context managers created using `contextmanager()` are also single use context managers, and will complain about the underlying generator failing to yield if an attempt is made to use them a second time:

    >>> from contextlib import contextmanager
    >>> @contextmanager
    ... def singleuse():
    ...     print("Before")
    ...     yield
    ...     print("After")
    ...
    >>> cm = singleuse()
    >>> with cm:
    ...     pass
    ...
    Before
    After
    >>> with cm:
    ...     pass
    ...
    Traceback (most recent call last):
        ...
    RuntimeError: generator didn't yield

### Reentrant context managers

More sophisticated context managers may be "reentrant". These context managers can not only be used in multiple `with` statements, but may also be used inside a `with` statement that is already using the same context manager.

`threading.RLock` is an example of a reentrant context manager, as are `suppress()` and `redirect_stdout()`. Here's a very simple example of reentrant use:

    >>> from contextlib import redirect_stdout
    >>> from io import StringIO
    >>> stream = StringIO()
    >>> write_to_stream = redirect_stdout(stream)
    >>> with write_to_stream:
    ...     print("This is written to the stream rather than stdout")
    ...     with write_to_stream:
    ...         print("This is also written to the stream")
    ...
    >>> print("This is written directly to stdout")
    This is written directly to stdout
    >>> print(stream.getvalue())
    This is written to the stream rather than stdout
    This is also written to the stream

Real world examples of reentrancy are more likely to involve multiple functions calling each other and hence be far more complicated than this example.

Note also that being reentrant is not the same thing as being thread safe. `redirect_stdout()`, for example, is definitely not thread safe, as it makes a global modification to the system state by binding `sys.stdout` to a different stream.

### Reusable context managers

Distinct from both single use and reentrant context managers are "reusable" context managers (or, to be completely explicit, "reusable, but not reentrant" context managers, since reentrant context managers are also reusable). These context managers support being used multiple times, but will fail (or otherwise not work correctly) if the specific context manager instance has already been used in a containing with statement.

`threading.Lock` is an example of a reusable, but not reentrant, context manager (for a reentrant lock, it is necessary to use `threading.RLock` instead).

Another example of a reusable, but not reentrant, context manager is `ExitStack`, as it invokes all currently registered callbacks when leaving any with statement, regardless of where those callbacks were added:

    >>> from contextlib import ExitStack
    >>> stack = ExitStack()
    >>> with stack:
    ...     stack.callback(print, "Callback: from first context")
    ...     print("Leaving first context")
    ...
    Leaving first context
    Callback: from first context
    >>> with stack:
    ...     stack.callback(print, "Callback: from second context")
    ...     print("Leaving second context")
    ...
    Leaving second context
    Callback: from second context
    >>> with stack:
    ...     stack.callback(print, "Callback: from outer context")
    ...     with stack:
    ...         stack.callback(print, "Callback: from inner context")
    ...         print("Leaving inner context")
    ...     print("Leaving outer context")
    ...
    Leaving inner context
    Callback: from inner context
    Callback: from outer context
    Leaving outer context

As the output from the example shows, reusing a single stack object across multiple with statements works correctly, but attempting to nest them will cause the stack to be cleared at the end of the innermost with statement, which is unlikely to be desirable behaviour.

Using separate `ExitStack` instances instead of reusing a single instance avoids that problem:

    >>> from contextlib import ExitStack
    >>> with ExitStack() as outer_stack:
    ...     outer_stack.callback(print, "Callback: from outer context")
    ...     with ExitStack() as inner_stack:
    ...         inner_stack.callback(print, "Callback: from inner context")
    ...         print("Leaving inner context")
    ...     print("Leaving outer context")
    ...
    Leaving inner context
    Callback: from inner context
    Leaving outer context
    Callback: from outer context