# Copyright (C) 2001 Python Software Foundation # This file is distributed under the same license as the Python package. # # Translators: msgid "" msgstr "" "Project-Id-Version: Python 3.14\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-03 00:19+0000\n" "PO-Revision-Date: 2018-05-23 14:41+0000\n" "Last-Translator: Adrian Liaw \n" "Language-Team: Chinese - TAIWAN (https://github.com/python/python-docs-zh-" "tw)\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../../library/contextlib.rst:2 msgid "" ":mod:`!contextlib` --- Utilities for :keyword:`!with`\\ -statement contexts" msgstr ":mod:`!contextlib` --- :keyword:`!with` 陳述式工具程式" #: ../../library/contextlib.rst:7 msgid "**Source code:** :source:`Lib/contextlib.py`" msgstr "**原始碼:**\\ :source:`Lib/contextlib.py`" #: ../../library/contextlib.rst:11 msgid "" "This module provides utilities for common tasks involving the :keyword:" "`with` statement. For more information see also :ref:`typecontextmanager` " "and :ref:`context-managers`." msgstr "" #: ../../library/contextlib.rst:17 msgid "Utilities" msgstr "" #: ../../library/contextlib.rst:19 msgid "Functions and classes provided:" msgstr "" #: ../../library/contextlib.rst:23 msgid "" "An :term:`abstract base class` for classes that implement :meth:`~object." "__enter__` and :meth:`~object.__exit__`. A default implementation for :meth:" "`~object.__enter__` is provided which returns ``self`` while :meth:`~object." "__exit__` is an abstract method which by default returns ``None``. See also " "the definition of :ref:`typecontextmanager`." msgstr "" #: ../../library/contextlib.rst:34 msgid "" "An :term:`abstract base class` for classes that implement :meth:`~object." "__aenter__` and :meth:`~object.__aexit__`. A default implementation for :" "meth:`~object.__aenter__` is provided which returns ``self`` while :meth:" "`~object.__aexit__` is an abstract method which by default returns ``None``. " "See also the definition of :ref:`async-context-managers`." msgstr "" #: ../../library/contextlib.rst:46 msgid "" "This function is a :term:`decorator` that can be used to define a factory " "function for :keyword:`with` statement context managers, without needing to " "create a class or separate :meth:`~object.__enter__` and :meth:`~object." "__exit__` methods." msgstr "" #: ../../library/contextlib.rst:50 msgid "" "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``." msgstr "" #: ../../library/contextlib.rst:54 msgid "" "An abstract example would be the following to ensure correct resource " "management::" msgstr "" #: ../../library/contextlib.rst:57 msgid "" "from contextlib import contextmanager\n" "\n" "@contextmanager\n" "def managed_resource(*args, **kwds):\n" " # Code to acquire resource, e.g.:\n" " resource = acquire_resource(*args, **kwds)\n" " try:\n" " yield resource\n" " finally:\n" " # Code to release resource, e.g.:\n" " release_resource(resource)" msgstr "" #: ../../library/contextlib.rst:69 msgid "The function can then be used like this::" msgstr "" #: ../../library/contextlib.rst:71 msgid "" ">>> with managed_resource(timeout=3600) as resource:\n" "... # Resource is released at the end of this block,\n" "... # even if code in the block raises an exception" msgstr "" #: ../../library/contextlib.rst:75 msgid "" "The function being decorated must return a :term:`generator`-iterator when " "called. This iterator must yield exactly one value, which will be bound to " "the targets in the :keyword:`with` statement's :keyword:`!as` clause, if any." msgstr "" #: ../../library/contextlib.rst:79 msgid "" "At the point where the generator yields, the block nested in the :keyword:" "`with` statement is executed. The generator is then resumed after the block " "is exited. If an unhandled exception occurs in the block, it is reraised " "inside the generator at the point where the yield occurred. Thus, you can " "use a :keyword:`try`...\\ :keyword:`except`...\\ :keyword:`finally` " "statement to trap the error (if any), or ensure that some cleanup takes " "place. If an exception is trapped merely in order to log it or to perform " "some action (rather than to suppress it entirely), the generator must " "reraise that exception. Otherwise the generator context manager will " "indicate to the :keyword:`!with` statement that the exception has been " "handled, and execution will resume with the statement immediately following " "the :keyword:`!with` statement." msgstr "" #: ../../library/contextlib.rst:91 msgid "" ":func:`contextmanager` uses :class:`ContextDecorator` so the context " "managers it creates can be used as decorators as well as in :keyword:`with` " "statements. When used as a decorator, a new generator instance is implicitly " "created on each function call (this allows the otherwise \"one-shot\" " "context managers created by :func:`contextmanager` to meet the requirement " "that context managers support multiple invocations in order to be used as " "decorators)." msgstr "" #: ../../library/contextlib.rst:98 msgid "Use of :class:`ContextDecorator`." msgstr "" #: ../../library/contextlib.rst:104 msgid "" "Similar to :func:`~contextlib.contextmanager`, but creates an :ref:" "`asynchronous context manager `." msgstr "" #: ../../library/contextlib.rst:107 msgid "" "This function is a :term:`decorator` that can be used to define a factory " "function for :keyword:`async with` statement asynchronous context managers, " "without needing to create a class or separate :meth:`~object.__aenter__` " "and :meth:`~object.__aexit__` methods. It must be applied to an :term:" "`asynchronous generator` function." msgstr "" #: ../../library/contextlib.rst:113 msgid "A simple example::" msgstr "一個簡單範例: ::" #: ../../library/contextlib.rst:115 msgid "" "from contextlib import asynccontextmanager\n" "\n" "@asynccontextmanager\n" "async def get_connection():\n" " conn = await acquire_db_connection()\n" " try:\n" " yield conn\n" " finally:\n" " await release_db_connection(conn)\n" "\n" "async def get_all_users():\n" " async with get_connection() as conn:\n" " return conn.query('SELECT ...')" msgstr "" "from contextlib import asynccontextmanager\n" "\n" "@asynccontextmanager\n" "async def get_connection():\n" " conn = await acquire_db_connection()\n" " try:\n" " yield conn\n" " finally:\n" " await release_db_connection(conn)\n" "\n" "async def get_all_users():\n" " async with get_connection() as conn:\n" " return conn.query('SELECT ...')" #: ../../library/contextlib.rst:131 msgid "" "Context managers defined with :func:`asynccontextmanager` can be used either " "as decorators or with :keyword:`async with` statements::" msgstr "" #: ../../library/contextlib.rst:134 msgid "" "import time\n" "from contextlib import asynccontextmanager\n" "\n" "@asynccontextmanager\n" "async def timeit():\n" " now = time.monotonic()\n" " try:\n" " yield\n" " finally:\n" " print(f'it took {time.monotonic() - now}s to run')\n" "\n" "@timeit()\n" "async def main():\n" " # ... async code ..." msgstr "" #: ../../library/contextlib.rst:149 msgid "" "When used as a decorator, a new generator instance is implicitly created on " "each function call. This allows the otherwise \"one-shot\" context managers " "created by :func:`asynccontextmanager` to meet the requirement that context " "managers support multiple invocations in order to be used as decorators." msgstr "" #: ../../library/contextlib.rst:154 msgid "" "Async context managers created with :func:`asynccontextmanager` can be used " "as decorators." msgstr "" #: ../../library/contextlib.rst:161 msgid "" "Return a context manager that closes *thing* upon completion of the block. " "This is basically equivalent to::" msgstr "" #: ../../library/contextlib.rst:164 msgid "" "from contextlib import contextmanager\n" "\n" "@contextmanager\n" "def closing(thing):\n" " try:\n" " yield thing\n" " finally:\n" " thing.close()" msgstr "" "from contextlib import contextmanager\n" "\n" "@contextmanager\n" "def closing(thing):\n" " try:\n" " yield thing\n" " finally:\n" " thing.close()" #: ../../library/contextlib.rst:173 msgid "And lets you write code like this::" msgstr "" #: ../../library/contextlib.rst:175 msgid "" "from contextlib import closing\n" "from urllib.request import urlopen\n" "\n" "with closing(urlopen('https://www.python.org')) as page:\n" " for line in page:\n" " print(line)" msgstr "" "from contextlib import closing\n" "from urllib.request import urlopen\n" "\n" "with closing(urlopen('https://www.python.org')) as page:\n" " for line in page:\n" " print(line)" #: ../../library/contextlib.rst:182 msgid "" "without needing to explicitly close ``page``. Even if an error occurs, " "``page.close()`` will be called when the :keyword:`with` block is exited." msgstr "" #: ../../library/contextlib.rst:187 msgid "" "Most types managing resources support the :term:`context manager` protocol, " "which closes *thing* on leaving the :keyword:`with` statement. As such, :" "func:`!closing` is most useful for third party types that don't support " "context managers. This example is purely for illustration purposes, as :func:" "`~urllib.request.urlopen` would normally be used in a context manager." msgstr "" #: ../../library/contextlib.rst:196 msgid "" "Return an async context manager that calls the ``aclose()`` method of " "*thing* upon completion of the block. This is basically equivalent to::" msgstr "" #: ../../library/contextlib.rst:199 msgid "" "from contextlib import asynccontextmanager\n" "\n" "@asynccontextmanager\n" "async def aclosing(thing):\n" " try:\n" " yield thing\n" " finally:\n" " await thing.aclose()" msgstr "" "from contextlib import asynccontextmanager\n" "\n" "@asynccontextmanager\n" "async def aclosing(thing):\n" " try:\n" " yield thing\n" " finally:\n" " await thing.aclose()" #: ../../library/contextlib.rst:208 msgid "" "Significantly, ``aclosing()`` supports deterministic cleanup of async " "generators when they happen to exit early by :keyword:`break` or an " "exception. For example::" msgstr "" #: ../../library/contextlib.rst:212 msgid "" "from contextlib import aclosing\n" "\n" "async with aclosing(my_generator()) as values:\n" " async for value in values:\n" " if value == 42:\n" " break" msgstr "" "from contextlib import aclosing\n" "\n" "async with aclosing(my_generator()) as values:\n" " async for value in values:\n" " if value == 42:\n" " break" #: ../../library/contextlib.rst:219 msgid "" "This pattern ensures that the generator's async exit code is executed in the " "same context as its iterations (so that exceptions and context variables " "work as expected, and the exit code isn't run after the lifetime of some " "task it depends on)." msgstr "" #: ../../library/contextlib.rst:231 msgid "" "Return a context manager that returns *enter_result* from :meth:`~object." "__enter__`, but otherwise does nothing. It is intended to be used as a stand-" "in for an optional context manager, for example::" msgstr "" #: ../../library/contextlib.rst:235 msgid "" "def myfunction(arg, ignore_exceptions=False):\n" " if ignore_exceptions:\n" " # Use suppress to ignore all exceptions.\n" " cm = contextlib.suppress(Exception)\n" " else:\n" " # Do not ignore any exceptions, cm has no effect.\n" " cm = contextlib.nullcontext()\n" " with cm:\n" " # Do something" msgstr "" #: ../../library/contextlib.rst:245 msgid "An example using *enter_result*::" msgstr "一個使用 *enter_result* 的範例: ::" #: ../../library/contextlib.rst:247 msgid "" "def process_file(file_or_path):\n" " if isinstance(file_or_path, str):\n" " # If string, open file\n" " cm = open(file_or_path)\n" " else:\n" " # Caller is responsible for closing file\n" " cm = nullcontext(file_or_path)\n" "\n" " with cm as file:\n" " # Perform processing on the file" msgstr "" #: ../../library/contextlib.rst:258 msgid "" "It can also be used as a stand-in for :ref:`asynchronous context managers " "`::" msgstr "" #: ../../library/contextlib.rst:261 msgid "" "async def send_http(session=None):\n" " if not session:\n" " # If no http session, create it with aiohttp\n" " cm = aiohttp.ClientSession()\n" " else:\n" " # Caller is responsible for closing the session\n" " cm = nullcontext(session)\n" "\n" " async with cm as session:\n" " # Send http requests with session" msgstr "" #: ../../library/contextlib.rst:274 msgid ":term:`asynchronous context manager` support was added." msgstr "" #: ../../library/contextlib.rst:281 msgid "" "Return a context manager that suppresses any of the specified exceptions if " "they occur in the body of a :keyword:`!with` statement and then resumes " "execution with the first statement following the end of the :keyword:`!with` " "statement." msgstr "" #: ../../library/contextlib.rst:286 msgid "" "As with any other mechanism that completely suppresses exceptions, this " "context manager should be used only to cover very specific errors where " "silently continuing with program execution is known to be the right thing to " "do." msgstr "" #: ../../library/contextlib.rst:291 msgid "For example::" msgstr "舉例來說: ::" #: ../../library/contextlib.rst:293 msgid "" "from contextlib import suppress\n" "\n" "with suppress(FileNotFoundError):\n" " os.remove('somefile.tmp')\n" "\n" "with suppress(FileNotFoundError):\n" " os.remove('someotherfile.tmp')" msgstr "" "from contextlib import suppress\n" "\n" "with suppress(FileNotFoundError):\n" " os.remove('somefile.tmp')\n" "\n" "with suppress(FileNotFoundError):\n" " os.remove('someotherfile.tmp')" #: ../../library/contextlib.rst:301 msgid "This code is equivalent to::" msgstr "" #: ../../library/contextlib.rst:303 msgid "" "try:\n" " os.remove('somefile.tmp')\n" "except FileNotFoundError:\n" " pass\n" "\n" "try:\n" " os.remove('someotherfile.tmp')\n" "except FileNotFoundError:\n" " pass" msgstr "" "try:\n" " os.remove('somefile.tmp')\n" "except FileNotFoundError:\n" " pass\n" "\n" "try:\n" " os.remove('someotherfile.tmp')\n" "except FileNotFoundError:\n" " pass" #: ../../library/contextlib.rst:313 ../../library/contextlib.rst:362 #: ../../library/contextlib.rst:372 ../../library/contextlib.rst:389 msgid "This context manager is :ref:`reentrant `." msgstr "" #: ../../library/contextlib.rst:315 msgid "" "If the code within the :keyword:`!with` block raises a :exc:" "`BaseExceptionGroup`, suppressed exceptions are removed from the group. Any " "exceptions of the group which are not suppressed are re-raised in a new " "group which is created using the original group's :meth:`~BaseExceptionGroup." "derive` method." msgstr "" #: ../../library/contextlib.rst:323 msgid "" "``suppress`` now supports suppressing exceptions raised as part of a :exc:" "`BaseExceptionGroup`." msgstr "" #: ../../library/contextlib.rst:329 msgid "" "Context manager for temporarily redirecting :data:`sys.stdout` to another " "file or file-like object." msgstr "" #: ../../library/contextlib.rst:332 msgid "" "This tool adds flexibility to existing functions or classes whose output is " "hardwired to stdout." msgstr "" #: ../../library/contextlib.rst:335 msgid "" "For example, the output of :func:`help` normally is sent to *sys.stdout*. " "You can capture that output in a string by redirecting the output to an :" "class:`io.StringIO` object. The replacement stream is returned from the :" "meth:`~object.__enter__` method and so is available as the target of the :" "keyword:`with` statement::" msgstr "" #: ../../library/contextlib.rst:341 msgid "" "with redirect_stdout(io.StringIO()) as f:\n" " help(pow)\n" "s = f.getvalue()" msgstr "" "with redirect_stdout(io.StringIO()) as f:\n" " help(pow)\n" "s = f.getvalue()" #: ../../library/contextlib.rst:345 msgid "" "To send the output of :func:`help` to a file on disk, redirect the output to " "a regular file::" msgstr "" #: ../../library/contextlib.rst:348 msgid "" "with open('help.txt', 'w') as f:\n" " with redirect_stdout(f):\n" " help(pow)" msgstr "" "with open('help.txt', 'w') as f:\n" " with redirect_stdout(f):\n" " help(pow)" #: ../../library/contextlib.rst:352 msgid "To send the output of :func:`help` to *sys.stderr*::" msgstr "" #: ../../library/contextlib.rst:354 msgid "" "with redirect_stdout(sys.stderr):\n" " help(pow)" msgstr "" "with redirect_stdout(sys.stderr):\n" " help(pow)" #: ../../library/contextlib.rst:357 msgid "" "Note that the global side effect on :data:`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." msgstr "" #: ../../library/contextlib.rst:369 msgid "" "Similar to :func:`~contextlib.redirect_stdout` but redirecting :data:`sys." "stderr` to another file or file-like object." msgstr "" #: ../../library/contextlib.rst:379 msgid "" "Non parallel-safe context manager to change the current working directory. " "As this changes a global state, the working directory, it is not suitable " "for use in most threaded or async contexts. It is also not suitable for most " "non-linear code execution, like generators, where the program execution is " "temporarily relinquished -- unless explicitly desired, you should not yield " "when this context manager is active." msgstr "" #: ../../library/contextlib.rst:386 msgid "" "This is a simple wrapper around :func:`~os.chdir`, it changes the current " "working directory upon entering and restores the old one on exit." msgstr "" #: ../../library/contextlib.rst:396 msgid "" "A base class that enables a context manager to also be used as a decorator." msgstr "" #: ../../library/contextlib.rst:398 msgid "" "Context managers inheriting from ``ContextDecorator`` have to implement :" "meth:`~object.__enter__` and :meth:`~object.__exit__` as normal. " "``__exit__`` retains its optional exception handling even when used as a " "decorator." msgstr "" #: ../../library/contextlib.rst:403 msgid "" "``ContextDecorator`` is used by :func:`contextmanager`, so you get this " "functionality automatically." msgstr "" #: ../../library/contextlib.rst:406 msgid "Example of ``ContextDecorator``::" msgstr "``ContextDecorator`` 範例: ::" #: ../../library/contextlib.rst:408 msgid "" "from contextlib import ContextDecorator\n" "\n" "class mycontext(ContextDecorator):\n" " def __enter__(self):\n" " print('Starting')\n" " return self\n" "\n" " def __exit__(self, *exc):\n" " print('Finishing')\n" " return False" msgstr "" "from contextlib import ContextDecorator\n" "\n" "class mycontext(ContextDecorator):\n" " def __enter__(self):\n" " print('Starting')\n" " return self\n" "\n" " def __exit__(self, *exc):\n" " print('Finishing')\n" " return False" #: ../../library/contextlib.rst:419 ../../library/contextlib.rst:491 msgid "The class can then be used like this::" msgstr "這個類別可以這樣使用: ::" #: ../../library/contextlib.rst:421 msgid "" ">>> @mycontext()\n" "... def function():\n" "... print('The bit in the middle')\n" "...\n" ">>> function()\n" "Starting\n" "The bit in the middle\n" "Finishing\n" "\n" ">>> with mycontext():\n" "... print('The bit in the middle')\n" "...\n" "Starting\n" "The bit in the middle\n" "Finishing" msgstr "" ">>> @mycontext()\n" "... def function():\n" "... print('The bit in the middle')\n" "...\n" ">>> function()\n" "Starting\n" "The bit in the middle\n" "Finishing\n" "\n" ">>> with mycontext():\n" "... print('The bit in the middle')\n" "...\n" "Starting\n" "The bit in the middle\n" "Finishing" #: ../../library/contextlib.rst:437 msgid "" "This change is just syntactic sugar for any construct of the following form::" msgstr "這個變更只是以下形式的語法糖: ::" #: ../../library/contextlib.rst:439 msgid "" "def f():\n" " with cm():\n" " # Do stuff" msgstr "" "def f():\n" " with cm():\n" " # Do stuff" #: ../../library/contextlib.rst:443 msgid "``ContextDecorator`` lets you instead write::" msgstr "``ContextDecorator`` 讓你可以改寫成: ::" #: ../../library/contextlib.rst:445 msgid "" "@cm()\n" "def f():\n" " # Do stuff" msgstr "" "@cm()\n" "def f():\n" " # Do stuff" #: ../../library/contextlib.rst:449 msgid "" "It makes it clear that the ``cm`` applies to the whole function, rather than " "just a piece of it (and saving an indentation level is nice, too)." msgstr "" #: ../../library/contextlib.rst:452 msgid "" "Existing context managers that already have a base class can be extended by " "using ``ContextDecorator`` as a mixin class::" msgstr "" #: ../../library/contextlib.rst:455 msgid "" "from contextlib import ContextDecorator\n" "\n" "class mycontext(ContextBaseClass, ContextDecorator):\n" " def __enter__(self):\n" " return self\n" "\n" " def __exit__(self, *exc):\n" " return False" msgstr "" "from contextlib import ContextDecorator\n" "\n" "class mycontext(ContextBaseClass, ContextDecorator):\n" " def __enter__(self):\n" " return self\n" "\n" " def __exit__(self, *exc):\n" " return False" #: ../../library/contextlib.rst:465 msgid "" "As the decorated function must be able to be called multiple times, the " "underlying context manager must support use in multiple :keyword:`with` " "statements. If this is not the case, then the original construct with the " "explicit :keyword:`!with` statement inside the function should be used." msgstr "" #: ../../library/contextlib.rst:475 msgid "" "Similar to :class:`ContextDecorator` but only for asynchronous functions." msgstr "和 :class:`ContextDecorator` 類似,但僅用於非同步函式。" #: ../../library/contextlib.rst:477 msgid "Example of ``AsyncContextDecorator``::" msgstr "``AsyncContextDecorator`` 範例: ::" #: ../../library/contextlib.rst:479 msgid "" "from asyncio import run\n" "from contextlib import AsyncContextDecorator\n" "\n" "class mycontext(AsyncContextDecorator):\n" " async def __aenter__(self):\n" " print('Starting')\n" " return self\n" "\n" " async def __aexit__(self, *exc):\n" " print('Finishing')\n" " return False" msgstr "" "from asyncio import run\n" "from contextlib import AsyncContextDecorator\n" "\n" "class mycontext(AsyncContextDecorator):\n" " async def __aenter__(self):\n" " print('Starting')\n" " return self\n" "\n" " async def __aexit__(self, *exc):\n" " print('Finishing')\n" " return False" #: ../../library/contextlib.rst:493 msgid "" ">>> @mycontext()\n" "... async def function():\n" "... print('The bit in the middle')\n" "...\n" ">>> run(function())\n" "Starting\n" "The bit in the middle\n" "Finishing\n" "\n" ">>> async def function():\n" "... async with mycontext():\n" "... print('The bit in the middle')\n" "...\n" ">>> run(function())\n" "Starting\n" "The bit in the middle\n" "Finishing" msgstr "" ">>> @mycontext()\n" "... async def function():\n" "... print('The bit in the middle')\n" "...\n" ">>> run(function())\n" "Starting\n" "The bit in the middle\n" "Finishing\n" "\n" ">>> async def function():\n" "... async with mycontext():\n" "... print('The bit in the middle')\n" "...\n" ">>> run(function())\n" "Starting\n" "The bit in the middle\n" "Finishing" #: ../../library/contextlib.rst:516 msgid "" "A context manager that is designed to make it easy to programmatically " "combine other context managers and cleanup functions, especially those that " "are optional or otherwise driven by input data." msgstr "" #: ../../library/contextlib.rst:520 msgid "" "For example, a set of files may easily be handled in a single with statement " "as follows::" msgstr "" #: ../../library/contextlib.rst:523 msgid "" "with ExitStack() as stack:\n" " files = [stack.enter_context(open(fname)) for fname in filenames]\n" " # All opened files will automatically be closed at the end of\n" " # the with statement, even if attempts to open files later\n" " # in the list raise an exception" msgstr "" #: ../../library/contextlib.rst:529 msgid "" "The :meth:`~object.__enter__` method returns the :class:`ExitStack` " "instance, and performs no additional operations." msgstr "" #: ../../library/contextlib.rst:532 msgid "" "Each instance maintains a stack of registered callbacks that are called in " "reverse order when the instance is closed (either explicitly or implicitly " "at the end of a :keyword:`with` statement). Note that callbacks are *not* " "invoked implicitly when the context stack instance is garbage collected." msgstr "" #: ../../library/contextlib.rst:537 msgid "" "This stack model is used so that context managers that acquire their " "resources in their ``__init__`` method (such as file objects) can be handled " "correctly." msgstr "" #: ../../library/contextlib.rst:541 msgid "" "Since registered callbacks are invoked in the reverse order of registration, " "this ends up behaving as if multiple nested :keyword:`with` statements had " "been used with the registered set of callbacks. This even extends to " "exception handling - if an inner callback suppresses or replaces an " "exception, then outer callbacks will be passed arguments based on that " "updated state." msgstr "" #: ../../library/contextlib.rst:548 msgid "" "This is a relatively low level API that takes care of the details of " "correctly unwinding the stack of exit callbacks. It provides a suitable " "foundation for higher level context managers that manipulate the exit stack " "in application specific ways." msgstr "" #: ../../library/contextlib.rst:557 msgid "" "Enters a new context manager and adds its :meth:`~object.__exit__` method to " "the callback stack. The return value is the result of the context manager's " "own :meth:`~object.__enter__` method." msgstr "" #: ../../library/contextlib.rst:561 msgid "" "These context managers may suppress exceptions just as they normally would " "if used directly as part of a :keyword:`with` statement." msgstr "" #: ../../library/contextlib.rst:564 msgid "" "Raises :exc:`TypeError` instead of :exc:`AttributeError` if *cm* is not a " "context manager." msgstr "" #: ../../library/contextlib.rst:570 msgid "" "Adds a context manager's :meth:`~object.__exit__` method to the callback " "stack." msgstr "" #: ../../library/contextlib.rst:572 msgid "" "As ``__enter__`` is *not* invoked, this method can be used to cover part of " "an :meth:`~object.__enter__` implementation with a context manager's own :" "meth:`~object.__exit__` method." msgstr "" #: ../../library/contextlib.rst:576 msgid "" "If passed an object that is not a context manager, this method assumes it is " "a callback with the same signature as a context manager's :meth:`~object." "__exit__` method and adds it directly to the callback stack." msgstr "" #: ../../library/contextlib.rst:580 msgid "" "By returning true values, these callbacks can suppress exceptions the same " "way context manager :meth:`~object.__exit__` methods can." msgstr "" #: ../../library/contextlib.rst:583 msgid "" "The passed in object is returned from the function, allowing this method to " "be used as a function decorator." msgstr "" #: ../../library/contextlib.rst:588 msgid "" "Accepts an arbitrary callback function and arguments and adds it to the " "callback stack." msgstr "" #: ../../library/contextlib.rst:591 msgid "" "Unlike the other methods, callbacks added this way cannot suppress " "exceptions (as they are never passed the exception details)." msgstr "" #: ../../library/contextlib.rst:594 msgid "" "The passed in callback is returned from the function, allowing this method " "to be used as a function decorator." msgstr "" #: ../../library/contextlib.rst:599 msgid "" "Transfers the callback stack to a fresh :class:`ExitStack` instance and " "returns it. No callbacks are invoked by this operation - instead, they will " "now be invoked when the new stack is closed (either explicitly or implicitly " "at the end of a :keyword:`with` statement)." msgstr "" #: ../../library/contextlib.rst:604 msgid "" "For example, a group of files can be opened as an \"all or nothing\" " "operation as follows::" msgstr "" #: ../../library/contextlib.rst:607 msgid "" "with ExitStack() as stack:\n" " files = [stack.enter_context(open(fname)) for fname in filenames]\n" " # Hold onto the close method, but don't call it yet.\n" " close_files = stack.pop_all().close\n" " # If opening any file fails, all previously opened files will be\n" " # closed automatically. If all files are opened successfully,\n" " # they will remain open even after the with statement ends.\n" " # close_files() can then be invoked explicitly to close them all." msgstr "" #: ../../library/contextlib.rst:618 msgid "" "Immediately unwinds the callback stack, invoking callbacks in the reverse " "order of registration. For any context managers and exit callbacks " "registered, the arguments passed in will indicate that no exception occurred." msgstr "" #: ../../library/contextlib.rst:625 msgid "" "An :ref:`asynchronous context manager `, similar to :" "class:`ExitStack`, that supports combining both synchronous and asynchronous " "context managers, as well as having coroutines for cleanup logic." msgstr "" #: ../../library/contextlib.rst:630 msgid "" "The :meth:`~ExitStack.close` method is not implemented; :meth:`aclose` must " "be used instead." msgstr "" #: ../../library/contextlib.rst:636 msgid "" "Similar to :meth:`ExitStack.enter_context` but expects an asynchronous " "context manager." msgstr "" #: ../../library/contextlib.rst:639 msgid "" "Raises :exc:`TypeError` instead of :exc:`AttributeError` if *cm* is not an " "asynchronous context manager." msgstr "" #: ../../library/contextlib.rst:645 msgid "" "Similar to :meth:`ExitStack.push` but expects either an asynchronous context " "manager or a coroutine function." msgstr "" #: ../../library/contextlib.rst:650 msgid "Similar to :meth:`ExitStack.callback` but expects a coroutine function." msgstr "和 :meth:`ExitStack.callback` 類似,但預期接受一個協程函式。" #: ../../library/contextlib.rst:655 msgid "Similar to :meth:`ExitStack.close` but properly handles awaitables." msgstr "和 :meth:`ExitStack.close` 類似,但能夠正確處理可等待物件。" #: ../../library/contextlib.rst:657 msgid "Continuing the example for :func:`asynccontextmanager`::" msgstr "延續 :func:`asynccontextmanager` 的範例: ::" #: ../../library/contextlib.rst:659 msgid "" "async with AsyncExitStack() as stack:\n" " connections = [await stack.enter_async_context(get_connection())\n" " for i in range(5)]\n" " # All opened connections will automatically be released at the end of\n" " # the async with statement, even if attempts to open a connection\n" " # later in the list raise an exception." msgstr "" #: ../../library/contextlib.rst:669 msgid "Examples and Recipes" msgstr "" #: ../../library/contextlib.rst:671 msgid "" "This section describes some examples and recipes for making effective use of " "the tools provided by :mod:`!contextlib`." msgstr "" #: ../../library/contextlib.rst:676 msgid "Supporting a variable number of context managers" msgstr "" #: ../../library/contextlib.rst:678 msgid "" "The primary use case for :class:`ExitStack` is the one given in the class " "documentation: supporting a variable number of context managers and other " "cleanup operations in a single :keyword:`with` statement. The variability " "may come from the number of context managers needed being driven by user " "input (such as opening a user specified collection of files), or from some " "of the context managers being optional::" msgstr "" #: ../../library/contextlib.rst:685 msgid "" "with ExitStack() as stack:\n" " for resource in resources:\n" " stack.enter_context(resource)\n" " if need_special_resource():\n" " special = acquire_special_resource()\n" " stack.callback(release_special_resource, special)\n" " # Perform operations that use the acquired resources" msgstr "" #: ../../library/contextlib.rst:693 msgid "" "As shown, :class:`ExitStack` also makes it quite easy to use :keyword:`with` " "statements to manage arbitrary resources that don't natively support the " "context management protocol." msgstr "" #: ../../library/contextlib.rst:699 msgid "Catching exceptions from ``__enter__`` methods" msgstr "" #: ../../library/contextlib.rst:701 msgid "" "It is occasionally desirable to catch exceptions from an :meth:`~object." "__enter__` method implementation, *without* inadvertently catching " "exceptions from the :keyword:`with` statement body or the context manager's :" "meth:`~object.__exit__` method. By using :class:`ExitStack` the steps in the " "context management protocol can be separated slightly in order to allow " "this::" msgstr "" #: ../../library/contextlib.rst:707 msgid "" "stack = ExitStack()\n" "try:\n" " x = stack.enter_context(cm)\n" "except Exception:\n" " # handle __enter__ exception\n" "else:\n" " with stack:\n" " # Handle normal case" msgstr "" #: ../../library/contextlib.rst:716 msgid "" "Actually needing to do this is likely to indicate that the underlying API " "should be providing a direct resource management interface for use with :" "keyword:`try`/:keyword:`except`/:keyword:`finally` statements, but not all " "APIs are well designed in that regard. When a context manager is the only " "resource management API provided, then :class:`ExitStack` can make it easier " "to handle various situations that can't be handled directly in a :keyword:" "`with` statement." msgstr "" #: ../../library/contextlib.rst:726 msgid "Cleaning up in an ``__enter__`` implementation" msgstr "" #: ../../library/contextlib.rst:728 msgid "" "As noted in the documentation of :meth:`ExitStack.push`, this method can be " "useful in cleaning up an already allocated resource if later steps in the :" "meth:`~object.__enter__` implementation fail." msgstr "" #: ../../library/contextlib.rst:732 msgid "" "Here's an example of doing this for a context manager that accepts resource " "acquisition and release functions, along with an optional validation " "function, and maps them to the context management protocol::" msgstr "" #: ../../library/contextlib.rst:736 msgid "" "from contextlib import contextmanager, AbstractContextManager, ExitStack\n" "\n" "class ResourceManager(AbstractContextManager):\n" "\n" " def __init__(self, acquire_resource, release_resource, " "check_resource_ok=None):\n" " self.acquire_resource = acquire_resource\n" " self.release_resource = release_resource\n" " if check_resource_ok is None:\n" " def check_resource_ok(resource):\n" " return True\n" " self.check_resource_ok = check_resource_ok\n" "\n" " @contextmanager\n" " def _cleanup_on_error(self):\n" " with ExitStack() as stack:\n" " stack.push(self)\n" " yield\n" " # The validation check passed and didn't raise an exception\n" " # Accordingly, we want to keep the resource, and pass it\n" " # back to our caller\n" " stack.pop_all()\n" "\n" " def __enter__(self):\n" " resource = self.acquire_resource()\n" " with self._cleanup_on_error():\n" " if not self.check_resource_ok(resource):\n" " msg = \"Failed validation for {!r}\"\n" " raise RuntimeError(msg.format(resource))\n" " return resource\n" "\n" " def __exit__(self, *exc_details):\n" " # We don't need to duplicate any of our resource release logic\n" " self.release_resource()" msgstr "" #: ../../library/contextlib.rst:772 msgid "Replacing any use of ``try-finally`` and flag variables" msgstr "" #: ../../library/contextlib.rst:774 msgid "" "A pattern you will sometimes see is a ``try-finally`` statement with a flag " "variable to indicate whether or not the body of the ``finally`` clause " "should be executed. In its simplest form (that can't already be handled just " "by using an ``except`` clause instead), it looks something like this::" msgstr "" #: ../../library/contextlib.rst:779 msgid "" "cleanup_needed = True\n" "try:\n" " result = perform_operation()\n" " if result:\n" " cleanup_needed = False\n" "finally:\n" " if cleanup_needed:\n" " cleanup_resources()" msgstr "" "cleanup_needed = True\n" "try:\n" " result = perform_operation()\n" " if result:\n" " cleanup_needed = False\n" "finally:\n" " if cleanup_needed:\n" " cleanup_resources()" #: ../../library/contextlib.rst:788 msgid "" "As with any ``try`` statement based code, this can cause problems for " "development and review, because the setup code and the cleanup code can end " "up being separated by arbitrarily long sections of code." msgstr "" #: ../../library/contextlib.rst:792 msgid "" ":class:`ExitStack` makes it possible to instead register a callback for " "execution at the end of a ``with`` statement, and then later decide to skip " "executing that callback::" msgstr "" #: ../../library/contextlib.rst:796 msgid "" "from contextlib import ExitStack\n" "\n" "with ExitStack() as stack:\n" " stack.callback(cleanup_resources)\n" " result = perform_operation()\n" " if result:\n" " stack.pop_all()" msgstr "" "from contextlib import ExitStack\n" "\n" "with ExitStack() as stack:\n" " stack.callback(cleanup_resources)\n" " result = perform_operation()\n" " if result:\n" " stack.pop_all()" #: ../../library/contextlib.rst:804 msgid "" "This allows the intended cleanup behaviour to be made explicit up front, " "rather than requiring a separate flag variable." msgstr "" #: ../../library/contextlib.rst:807 msgid "" "If a particular application uses this pattern a lot, it can be simplified " "even further by means of a small helper class::" msgstr "" #: ../../library/contextlib.rst:810 msgid "" "from contextlib import ExitStack\n" "\n" "class Callback(ExitStack):\n" " def __init__(self, callback, /, *args, **kwds):\n" " super().__init__()\n" " self.callback(callback, *args, **kwds)\n" "\n" " def cancel(self):\n" " self.pop_all()\n" "\n" "with Callback(cleanup_resources) as cb:\n" " result = perform_operation()\n" " if result:\n" " cb.cancel()" msgstr "" "from contextlib import ExitStack\n" "\n" "class Callback(ExitStack):\n" " def __init__(self, callback, /, *args, **kwds):\n" " super().__init__()\n" " self.callback(callback, *args, **kwds)\n" "\n" " def cancel(self):\n" " self.pop_all()\n" "\n" "with Callback(cleanup_resources) as cb:\n" " result = perform_operation()\n" " if result:\n" " cb.cancel()" #: ../../library/contextlib.rst:825 msgid "" "If the resource cleanup isn't already neatly bundled into a standalone " "function, then it is still possible to use the decorator form of :meth:" "`ExitStack.callback` to declare the resource cleanup in advance::" msgstr "" #: ../../library/contextlib.rst:830 msgid "" "from contextlib import ExitStack\n" "\n" "with ExitStack() as stack:\n" " @stack.callback\n" " def cleanup_resources():\n" " ...\n" " result = perform_operation()\n" " if result:\n" " stack.pop_all()" msgstr "" "from contextlib import ExitStack\n" "\n" "with ExitStack() as stack:\n" " @stack.callback\n" " def cleanup_resources():\n" " ...\n" " result = perform_operation()\n" " if result:\n" " stack.pop_all()" #: ../../library/contextlib.rst:840 msgid "" "Due to the way the decorator protocol works, a callback function declared " "this way cannot take any parameters. Instead, any resources to be released " "must be accessed as closure variables." msgstr "" #: ../../library/contextlib.rst:846 msgid "Using a context manager as a function decorator" msgstr "" #: ../../library/contextlib.rst:848 msgid "" ":class:`ContextDecorator` makes it possible to use a context manager in both " "an ordinary ``with`` statement and also as a function decorator." msgstr "" #: ../../library/contextlib.rst:851 msgid "" "For example, it is sometimes useful to wrap functions or groups of " "statements with a logger that can track the time of entry and time of exit. " "Rather than writing both a function decorator and a context manager for the " "task, inheriting from :class:`ContextDecorator` provides both capabilities " "in a single definition::" msgstr "" #: ../../library/contextlib.rst:857 msgid "" "from contextlib import ContextDecorator\n" "import logging\n" "\n" "logging.basicConfig(level=logging.INFO)\n" "\n" "class track_entry_and_exit(ContextDecorator):\n" " def __init__(self, name):\n" " self.name = name\n" "\n" " def __enter__(self):\n" " logging.info('Entering: %s', self.name)\n" "\n" " def __exit__(self, exc_type, exc, exc_tb):\n" " logging.info('Exiting: %s', self.name)" msgstr "" "from contextlib import ContextDecorator\n" "import logging\n" "\n" "logging.basicConfig(level=logging.INFO)\n" "\n" "class track_entry_and_exit(ContextDecorator):\n" " def __init__(self, name):\n" " self.name = name\n" "\n" " def __enter__(self):\n" " logging.info('Entering: %s', self.name)\n" "\n" " def __exit__(self, exc_type, exc, exc_tb):\n" " logging.info('Exiting: %s', self.name)" #: ../../library/contextlib.rst:872 msgid "Instances of this class can be used as both a context manager::" msgstr "" #: ../../library/contextlib.rst:874 msgid "" "with track_entry_and_exit('widget loader'):\n" " print('Some time consuming activity goes here')\n" " load_widget()" msgstr "" "with track_entry_and_exit('widget loader'):\n" " print('Some time consuming activity goes here')\n" " load_widget()" #: ../../library/contextlib.rst:878 msgid "And also as a function decorator::" msgstr "" #: ../../library/contextlib.rst:880 msgid "" "@track_entry_and_exit('widget loader')\n" "def activity():\n" " print('Some time consuming activity goes here')\n" " load_widget()" msgstr "" "@track_entry_and_exit('widget loader')\n" "def activity():\n" " print('Some time consuming activity goes here')\n" " load_widget()" #: ../../library/contextlib.rst:885 msgid "" "Note that there is one additional limitation when using context managers as " "function decorators: there's no way to access the return value of :meth:" "`~object.__enter__`. If that value is needed, then it is still necessary to " "use an explicit ``with`` statement." msgstr "" #: ../../library/contextlib.rst:892 msgid ":pep:`343` - The \"with\" statement" msgstr ":pep:`343` - \"with\" 陳述式" #: ../../library/contextlib.rst:893 msgid "" "The specification, background, and examples for the Python :keyword:`with` " "statement." msgstr "" #: ../../library/contextlib.rst:899 msgid "Single use, reusable and reentrant context managers" msgstr "" #: ../../library/contextlib.rst:901 msgid "" "Most context managers are written in a way that means they can only be used " "effectively in a :keyword:`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." msgstr "" #: ../../library/contextlib.rst:907 msgid "" "This common limitation means that it is generally advisable to create " "context managers directly in the header of the :keyword:`with` statement " "where they are used (as shown in all of the usage examples above)." msgstr "" #: ../../library/contextlib.rst:911 msgid "" "Files are an example of effectively single use context managers, since the " "first :keyword:`with` statement will close the file, preventing any further " "IO operations using that file object." msgstr "" #: ../../library/contextlib.rst:915 msgid "" "Context managers created using :func:`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::" msgstr "" #: ../../library/contextlib.rst:919 msgid "" ">>> from contextlib import contextmanager\n" ">>> @contextmanager\n" "... def singleuse():\n" "... print(\"Before\")\n" "... yield\n" "... print(\"After\")\n" "...\n" ">>> cm = singleuse()\n" ">>> with cm:\n" "... pass\n" "...\n" "Before\n" "After\n" ">>> with cm:\n" "... pass\n" "...\n" "Traceback (most recent call last):\n" " ...\n" "RuntimeError: generator didn't yield" msgstr "" ">>> from contextlib import contextmanager\n" ">>> @contextmanager\n" "... def singleuse():\n" "... print(\"Before\")\n" "... yield\n" "... print(\"After\")\n" "...\n" ">>> cm = singleuse()\n" ">>> with cm:\n" "... pass\n" "...\n" "Before\n" "After\n" ">>> with cm:\n" "... pass\n" "...\n" "Traceback (most recent call last):\n" " ...\n" "RuntimeError: generator didn't yield" #: ../../library/contextlib.rst:943 msgid "Reentrant context managers" msgstr "" #: ../../library/contextlib.rst:945 msgid "" "More sophisticated context managers may be \"reentrant\". These context " "managers can not only be used in multiple :keyword:`with` statements, but " "may also be used *inside* a :keyword:`!with` statement that is already using " "the same context manager." msgstr "" #: ../../library/contextlib.rst:950 msgid "" ":class:`threading.RLock` is an example of a reentrant context manager, as " "are :func:`suppress`, :func:`redirect_stdout`, and :func:`chdir`. Here's a " "very simple example of reentrant use::" msgstr "" #: ../../library/contextlib.rst:954 msgid "" ">>> from contextlib import redirect_stdout\n" ">>> from io import StringIO\n" ">>> stream = StringIO()\n" ">>> write_to_stream = redirect_stdout(stream)\n" ">>> with write_to_stream:\n" "... print(\"This is written to the stream rather than stdout\")\n" "... with write_to_stream:\n" "... print(\"This is also written to the stream\")\n" "...\n" ">>> print(\"This is written directly to stdout\")\n" "This is written directly to stdout\n" ">>> print(stream.getvalue())\n" "This is written to the stream rather than stdout\n" "This is also written to the stream" msgstr "" ">>> from contextlib import redirect_stdout\n" ">>> from io import StringIO\n" ">>> stream = StringIO()\n" ">>> write_to_stream = redirect_stdout(stream)\n" ">>> with write_to_stream:\n" "... print(\"This is written to the stream rather than stdout\")\n" "... with write_to_stream:\n" "... print(\"This is also written to the stream\")\n" "...\n" ">>> print(\"This is written directly to stdout\")\n" "This is written directly to stdout\n" ">>> print(stream.getvalue())\n" "This is written to the stream rather than stdout\n" "This is also written to the stream" #: ../../library/contextlib.rst:969 msgid "" "Real world examples of reentrancy are more likely to involve multiple " "functions calling each other and hence be far more complicated than this " "example." msgstr "" #: ../../library/contextlib.rst:973 msgid "" "Note also that being reentrant is *not* the same thing as being thread " "safe. :func:`redirect_stdout`, for example, is definitely not thread safe, " "as it makes a global modification to the system state by binding :data:`sys." "stdout` to a different stream." msgstr "" #: ../../library/contextlib.rst:982 msgid "Reusable context managers" msgstr "" #: ../../library/contextlib.rst:984 msgid "" "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." msgstr "" #: ../../library/contextlib.rst:991 msgid "" ":class:`threading.Lock` is an example of a reusable, but not reentrant, " "context manager (for a reentrant lock, it is necessary to use :class:" "`threading.RLock` instead)." msgstr "" #: ../../library/contextlib.rst:995 msgid "" "Another example of a reusable, but not reentrant, context manager is :class:" "`ExitStack`, as it invokes *all* currently registered callbacks when leaving " "any with statement, regardless of where those callbacks were added::" msgstr "" #: ../../library/contextlib.rst:1000 msgid "" ">>> from contextlib import ExitStack\n" ">>> stack = ExitStack()\n" ">>> with stack:\n" "... stack.callback(print, \"Callback: from first context\")\n" "... print(\"Leaving first context\")\n" "...\n" "Leaving first context\n" "Callback: from first context\n" ">>> with stack:\n" "... stack.callback(print, \"Callback: from second context\")\n" "... print(\"Leaving second context\")\n" "...\n" "Leaving second context\n" "Callback: from second context\n" ">>> with stack:\n" "... stack.callback(print, \"Callback: from outer context\")\n" "... with stack:\n" "... stack.callback(print, \"Callback: from inner context\")\n" "... print(\"Leaving inner context\")\n" "... print(\"Leaving outer context\")\n" "...\n" "Leaving inner context\n" "Callback: from inner context\n" "Callback: from outer context\n" "Leaving outer context" msgstr "" ">>> from contextlib import ExitStack\n" ">>> stack = ExitStack()\n" ">>> with stack:\n" "... stack.callback(print, \"Callback: from first context\")\n" "... print(\"Leaving first context\")\n" "...\n" "Leaving first context\n" "Callback: from first context\n" ">>> with stack:\n" "... stack.callback(print, \"Callback: from second context\")\n" "... print(\"Leaving second context\")\n" "...\n" "Leaving second context\n" "Callback: from second context\n" ">>> with stack:\n" "... stack.callback(print, \"Callback: from outer context\")\n" "... with stack:\n" "... stack.callback(print, \"Callback: from inner context\")\n" "... print(\"Leaving inner context\")\n" "... print(\"Leaving outer context\")\n" "...\n" "Leaving inner context\n" "Callback: from inner context\n" "Callback: from outer context\n" "Leaving outer context" #: ../../library/contextlib.rst:1026 msgid "" "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." msgstr "" #: ../../library/contextlib.rst:1031 msgid "" "Using separate :class:`ExitStack` instances instead of reusing a single " "instance avoids that problem::" msgstr "" #: ../../library/contextlib.rst:1034 msgid "" ">>> from contextlib import ExitStack\n" ">>> with ExitStack() as outer_stack:\n" "... outer_stack.callback(print, \"Callback: from outer context\")\n" "... with ExitStack() as inner_stack:\n" "... inner_stack.callback(print, \"Callback: from inner context\")\n" "... print(\"Leaving inner context\")\n" "... print(\"Leaving outer context\")\n" "...\n" "Leaving inner context\n" "Callback: from inner context\n" "Leaving outer context\n" "Callback: from outer context" msgstr "" ">>> from contextlib import ExitStack\n" ">>> with ExitStack() as outer_stack:\n" "... outer_stack.callback(print, \"Callback: from outer context\")\n" "... with ExitStack() as inner_stack:\n" "... inner_stack.callback(print, \"Callback: from inner context\")\n" "... print(\"Leaving inner context\")\n" "... print(\"Leaving outer context\")\n" "...\n" "Leaving inner context\n" "Callback: from inner context\n" "Leaving outer context\n" "Callback: from outer context"